From 5784f87df16619f6a55255aa827faba431a6ee7a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 6 Jul 2017 09:42:20 +0300 Subject: ath10k: fix indenting in ath10k_wmi_update_noa() Commit 59ae1d127ac0 ("networking: introduce and use skb_put_data()") introduced a new checkpatch warning: drivers/net/wireless/ath/ath10k/wmi.c:3308: code indent should use tabs where possible Fix that. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 3efb404b83c0..96cd1ebd6a7e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3305,7 +3305,7 @@ static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif, if (arvif->u.ap.noa_data) if (!pskb_expand_head(bcn, 0, arvif->u.ap.noa_len, GFP_ATOMIC)) skb_put_data(bcn, arvif->u.ap.noa_data, - arvif->u.ap.noa_len); + arvif->u.ap.noa_len); } static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb, -- cgit v1.2.3 From 641fe28ad30544991253d8ca172aaa0fa12bd1e7 Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Wed, 28 Jun 2017 10:18:26 +0530 Subject: ath10k: make CE layer bus agnostic Remove bus specific dependencies from CE layer to have common CE layer across multiple targets. This is required for adding support for WCN3990 chipset support as WCN3990 chipset uses SNOC bus interface with Copy Engine endpoint. Signed-off-by: Govind Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ahb.c | 3 +- drivers/net/wireless/ath/ath10k/ce.c | 299 ++++++++++++++++++--------------- drivers/net/wireless/ath/ath10k/ce.h | 30 +++- drivers/net/wireless/ath/ath10k/core.h | 2 + drivers/net/wireless/ath/ath10k/pci.c | 51 +++--- drivers/net/wireless/ath/ath10k/pci.h | 14 +- 6 files changed, 224 insertions(+), 175 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index da770af83036..b36dd792fbb2 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -787,8 +787,9 @@ static int ath10k_ahb_probe(struct platform_device *pdev) ar_pci->mem = ar_ahb->mem; ar_pci->mem_len = ar_ahb->mem_len; ar_pci->ar = ar; - ar_pci->bus_ops = &ath10k_ahb_bus_ops; + ar_pci->ce.bus_ops = &ath10k_ahb_bus_ops; ar_pci->targ_cpu_to_ce_addr = ath10k_ahb_qca4019_targ_cpu_to_ce_addr; + ar->ce_priv = &ar_pci->ce; ret = ath10k_pci_setup_resource(ar); if (ret) { diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 08b84c8c3614..a8afd690290f 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -16,7 +16,6 @@ */ #include "hif.h" -#include "pci.h" #include "ce.h" #include "debug.h" @@ -33,7 +32,7 @@ * Each ring consists of a number of descriptors which specify * an address, length, and meta-data. * - * Typically, one side of the PCIe interconnect (Host or Target) + * Typically, one side of the PCIe/AHB/SNOC interconnect (Host or Target) * controls one ring and the other side controls the other ring. * The source side chooses when to initiate a transfer and it * chooses what to send (buffer address, length). The destination @@ -73,57 +72,71 @@ ath10k_get_ring_byte(unsigned int offset, return ((offset & addr_map->mask) >> (addr_map->lsb)); } +static inline u32 ath10k_ce_read32(struct ath10k *ar, u32 offset) +{ + struct ath10k_ce *ce = ath10k_ce_priv(ar); + + return ce->bus_ops->read32(ar, offset); +} + +static inline void ath10k_ce_write32(struct ath10k *ar, u32 offset, u32 value) +{ + struct ath10k_ce *ce = ath10k_ce_priv(ar); + + ce->bus_ops->write32(ar, offset, value); +} + static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ath10k_pci_write32(ar, ce_ctrl_addr + - ar->hw_ce_regs->dst_wr_index_addr, n); + ath10k_ce_write32(ar, ce_ctrl_addr + + ar->hw_ce_regs->dst_wr_index_addr, n); } static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { - return ath10k_pci_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->dst_wr_index_addr); + return ath10k_ce_read32(ar, ce_ctrl_addr + + ar->hw_ce_regs->dst_wr_index_addr); } static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ath10k_pci_write32(ar, ce_ctrl_addr + - ar->hw_ce_regs->sr_wr_index_addr, n); + ath10k_ce_write32(ar, ce_ctrl_addr + + ar->hw_ce_regs->sr_wr_index_addr, n); } static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { - return ath10k_pci_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->sr_wr_index_addr); + return ath10k_ce_read32(ar, ce_ctrl_addr + + ar->hw_ce_regs->sr_wr_index_addr); } static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { - return ath10k_pci_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->current_srri_addr); + return ath10k_ce_read32(ar, ce_ctrl_addr + + ar->hw_ce_regs->current_srri_addr); } static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int addr) { - ath10k_pci_write32(ar, ce_ctrl_addr + - ar->hw_ce_regs->sr_base_addr, addr); + ath10k_ce_write32(ar, ce_ctrl_addr + + ar->hw_ce_regs->sr_base_addr, addr); } static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ath10k_pci_write32(ar, ce_ctrl_addr + - ar->hw_ce_regs->sr_size_addr, n); + ath10k_ce_write32(ar, ce_ctrl_addr + + ar->hw_ce_regs->sr_size_addr, n); } static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar, @@ -131,12 +144,13 @@ static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar, unsigned int n) { struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; - u32 ctrl1_addr = ath10k_pci_read32(ar, - ce_ctrl_addr + ctrl_regs->addr); - ath10k_pci_write32(ar, ce_ctrl_addr + ctrl_regs->addr, - (ctrl1_addr & ~(ctrl_regs->dmax->mask)) | - ath10k_set_ring_byte(n, ctrl_regs->dmax)); + u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr + + ctrl_regs->addr); + + ath10k_ce_write32(ar, ce_ctrl_addr + ctrl_regs->addr, + (ctrl1_addr & ~(ctrl_regs->dmax->mask)) | + ath10k_set_ring_byte(n, ctrl_regs->dmax)); } static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar, @@ -144,11 +158,13 @@ static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar, unsigned int n) { struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; - u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + ctrl_regs->addr); - ath10k_pci_write32(ar, ce_ctrl_addr + ctrl_regs->addr, - (ctrl1_addr & ~(ctrl_regs->src_ring->mask)) | - ath10k_set_ring_byte(n, ctrl_regs->src_ring)); + u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr + + ctrl_regs->addr); + + ath10k_ce_write32(ar, ce_ctrl_addr + ctrl_regs->addr, + (ctrl1_addr & ~(ctrl_regs->src_ring->mask)) | + ath10k_set_ring_byte(n, ctrl_regs->src_ring)); } static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar, @@ -156,34 +172,36 @@ static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar, unsigned int n) { struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; - u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + ctrl_regs->addr); - ath10k_pci_write32(ar, ce_ctrl_addr + ctrl_regs->addr, - (ctrl1_addr & ~(ctrl_regs->dst_ring->mask)) | - ath10k_set_ring_byte(n, ctrl_regs->dst_ring)); + u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr + + ctrl_regs->addr); + + ath10k_ce_write32(ar, ce_ctrl_addr + ctrl_regs->addr, + (ctrl1_addr & ~(ctrl_regs->dst_ring->mask)) | + ath10k_set_ring_byte(n, ctrl_regs->dst_ring)); } static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { - return ath10k_pci_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->current_drri_addr); + return ath10k_ce_read32(ar, ce_ctrl_addr + + ar->hw_ce_regs->current_drri_addr); } static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar, u32 ce_ctrl_addr, u32 addr) { - ath10k_pci_write32(ar, ce_ctrl_addr + - ar->hw_ce_regs->dr_base_addr, addr); + ath10k_ce_write32(ar, ce_ctrl_addr + + ar->hw_ce_regs->dr_base_addr, addr); } static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ath10k_pci_write32(ar, ce_ctrl_addr + - ar->hw_ce_regs->dr_size_addr, n); + ath10k_ce_write32(ar, ce_ctrl_addr + + ar->hw_ce_regs->dr_size_addr, n); } static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar, @@ -191,11 +209,11 @@ static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar, unsigned int n) { struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; - u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + srcr_wm->addr); + u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + srcr_wm->addr); - ath10k_pci_write32(ar, ce_ctrl_addr + srcr_wm->addr, - (addr & ~(srcr_wm->wm_high->mask)) | - (ath10k_set_ring_byte(n, srcr_wm->wm_high))); + ath10k_ce_write32(ar, ce_ctrl_addr + srcr_wm->addr, + (addr & ~(srcr_wm->wm_high->mask)) | + (ath10k_set_ring_byte(n, srcr_wm->wm_high))); } static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar, @@ -203,11 +221,11 @@ static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar, unsigned int n) { struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; - u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + srcr_wm->addr); + u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + srcr_wm->addr); - ath10k_pci_write32(ar, ce_ctrl_addr + srcr_wm->addr, - (addr & ~(srcr_wm->wm_low->mask)) | - (ath10k_set_ring_byte(n, srcr_wm->wm_low))); + ath10k_ce_write32(ar, ce_ctrl_addr + srcr_wm->addr, + (addr & ~(srcr_wm->wm_low->mask)) | + (ath10k_set_ring_byte(n, srcr_wm->wm_low))); } static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar, @@ -215,11 +233,11 @@ static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar, unsigned int n) { struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; - u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + dstr_wm->addr); + u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + dstr_wm->addr); - ath10k_pci_write32(ar, ce_ctrl_addr + dstr_wm->addr, - (addr & ~(dstr_wm->wm_high->mask)) | - (ath10k_set_ring_byte(n, dstr_wm->wm_high))); + ath10k_ce_write32(ar, ce_ctrl_addr + dstr_wm->addr, + (addr & ~(dstr_wm->wm_high->mask)) | + (ath10k_set_ring_byte(n, dstr_wm->wm_high))); } static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar, @@ -227,66 +245,73 @@ static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar, unsigned int n) { struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; - u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + dstr_wm->addr); + u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + dstr_wm->addr); - ath10k_pci_write32(ar, ce_ctrl_addr + dstr_wm->addr, - (addr & ~(dstr_wm->wm_low->mask)) | - (ath10k_set_ring_byte(n, dstr_wm->wm_low))); + ath10k_ce_write32(ar, ce_ctrl_addr + dstr_wm->addr, + (addr & ~(dstr_wm->wm_low->mask)) | + (ath10k_set_ring_byte(n, dstr_wm->wm_low))); } static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar, u32 ce_ctrl_addr) { struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; - u32 host_ie_addr = ath10k_pci_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->host_ie_addr); - ath10k_pci_write32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr, - host_ie_addr | host_ie->copy_complete->mask); + u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + + ar->hw_ce_regs->host_ie_addr); + + ath10k_ce_write32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr, + host_ie_addr | host_ie->copy_complete->mask); } static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; - u32 host_ie_addr = ath10k_pci_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->host_ie_addr); - ath10k_pci_write32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr, - host_ie_addr & ~(host_ie->copy_complete->mask)); + u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + + ar->hw_ce_regs->host_ie_addr); + + ath10k_ce_write32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr, + host_ie_addr & ~(host_ie->copy_complete->mask)); } static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; - u32 host_ie_addr = ath10k_pci_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->host_ie_addr); - ath10k_pci_write32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr, - host_ie_addr & ~(wm_regs->wm_mask)); + u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + + ar->hw_ce_regs->host_ie_addr); + + ath10k_ce_write32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr, + host_ie_addr & ~(wm_regs->wm_mask)); } static inline void ath10k_ce_error_intr_enable(struct ath10k *ar, u32 ce_ctrl_addr) { struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs; - u32 misc_ie_addr = ath10k_pci_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->misc_ie_addr); - ath10k_pci_write32(ar, ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr, - misc_ie_addr | misc_regs->err_mask); + u32 misc_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + + ar->hw_ce_regs->misc_ie_addr); + + ath10k_ce_write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr, + misc_ie_addr | misc_regs->err_mask); } static inline void ath10k_ce_error_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs; - u32 misc_ie_addr = ath10k_pci_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->misc_ie_addr); - ath10k_pci_write32(ar, ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr, - misc_ie_addr & ~(misc_regs->err_mask)); + u32 misc_ie_addr = ath10k_ce_read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr); + + ath10k_ce_write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr, + misc_ie_addr & ~(misc_regs->err_mask)); } static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, @@ -295,7 +320,7 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, { struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; - ath10k_pci_write32(ar, ce_ctrl_addr + wm_regs->addr, mask); + ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask); } /* @@ -362,11 +387,11 @@ exit: void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe) { struct ath10k *ar = pipe->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); struct ath10k_ce_ring *src_ring = pipe->src_ring; u32 ctrl_addr = pipe->ctrl_addr; - lockdep_assert_held(&ar_pci->ce_lock); + lockdep_assert_held(&ce->ce_lock); /* * This function must be called only if there is an incomplete @@ -394,13 +419,13 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, unsigned int flags) { struct ath10k *ar = ce_state->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); int ret; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); ret = ath10k_ce_send_nolock(ce_state, per_transfer_context, buffer, nbytes, transfer_id, flags); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return ret; } @@ -408,14 +433,14 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe) { struct ath10k *ar = pipe->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); int delta; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); delta = CE_RING_DELTA(pipe->src_ring->nentries_mask, pipe->src_ring->write_index, pipe->src_ring->sw_index - 1); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return delta; } @@ -423,13 +448,13 @@ int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe) int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe) { struct ath10k *ar = pipe->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); struct ath10k_ce_ring *dest_ring = pipe->dest_ring; unsigned int nentries_mask = dest_ring->nentries_mask; unsigned int write_index = dest_ring->write_index; unsigned int sw_index = dest_ring->sw_index; - lockdep_assert_held(&ar_pci->ce_lock); + lockdep_assert_held(&ce->ce_lock); return CE_RING_DELTA(nentries_mask, write_index, sw_index - 1); } @@ -437,7 +462,7 @@ int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe) int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr) { struct ath10k *ar = pipe->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); struct ath10k_ce_ring *dest_ring = pipe->dest_ring; unsigned int nentries_mask = dest_ring->nentries_mask; unsigned int write_index = dest_ring->write_index; @@ -446,7 +471,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr) struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index); u32 ctrl_addr = pipe->ctrl_addr; - lockdep_assert_held(&ar_pci->ce_lock); + lockdep_assert_held(&ce->ce_lock); if ((pipe->id != 5) && CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0) @@ -486,12 +511,12 @@ void ath10k_ce_rx_update_write_idx(struct ath10k_ce_pipe *pipe, u32 nentries) int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr) { struct ath10k *ar = pipe->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); int ret; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); ret = __ath10k_ce_rx_post_buf(pipe, ctx, paddr); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return ret; } @@ -554,14 +579,14 @@ int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state, unsigned int *nbytesp) { struct ath10k *ar = ce_state->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); int ret; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); ret = ath10k_ce_completed_recv_next_nolock(ce_state, per_transfer_contextp, nbytesp); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return ret; } @@ -576,7 +601,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, unsigned int write_index; int ret; struct ath10k *ar; - struct ath10k_pci *ar_pci; + struct ath10k_ce *ce; dest_ring = ce_state->dest_ring; @@ -584,9 +609,9 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, return -EIO; ar = ce_state->ar; - ar_pci = ath10k_pci_priv(ar); + ce = ath10k_ce_priv(ar); - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); nentries_mask = dest_ring->nentries_mask; sw_index = dest_ring->sw_index; @@ -614,7 +639,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, ret = -EIO; } - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return ret; } @@ -686,7 +711,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, unsigned int write_index; int ret; struct ath10k *ar; - struct ath10k_pci *ar_pci; + struct ath10k_ce *ce; src_ring = ce_state->src_ring; @@ -694,9 +719,9 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, return -EIO; ar = ce_state->ar; - ar_pci = ath10k_pci_priv(ar); + ce = ath10k_ce_priv(ar); - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); nentries_mask = src_ring->nentries_mask; sw_index = src_ring->sw_index; @@ -727,7 +752,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, ret = -EIO; } - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return ret; } @@ -736,13 +761,13 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp) { struct ath10k *ar = ce_state->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); int ret; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); ret = ath10k_ce_completed_send_next_nolock(ce_state, per_transfer_contextp); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return ret; } @@ -755,17 +780,18 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, */ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; + struct ath10k_ce *ce = ath10k_ce_priv(ar); + struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id]; struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; u32 ctrl_addr = ce_state->ctrl_addr; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); /* Clear the copy-complete interrupts that will be handled here. */ - ath10k_ce_engine_int_status_clear(ar, ctrl_addr, wm_regs->cc_mask); + ath10k_ce_engine_int_status_clear(ar, ctrl_addr, + wm_regs->cc_mask); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); if (ce_state->recv_cb) ce_state->recv_cb(ce_state); @@ -773,7 +799,7 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) if (ce_state->send_cb) ce_state->send_cb(ce_state); - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); /* * Misc CE interrupts are not being handled, but still need @@ -781,7 +807,7 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) */ ath10k_ce_engine_int_status_clear(ar, ctrl_addr, wm_regs->wm_mask); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); } /* @@ -795,7 +821,7 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar) int ce_id; u32 intr_summary; - intr_summary = CE_INTERRUPT_SUMMARY(ar); + intr_summary = ath10k_ce_interrupt_summary(ar); for (ce_id = 0; intr_summary && (ce_id < CE_COUNT); ce_id++) { if (intr_summary & (1 << ce_id)) @@ -847,22 +873,25 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar) void ath10k_ce_enable_interrupts(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); int ce_id; + struct ath10k_ce_pipe *ce_state; /* Skip the last copy engine, CE7 the diagnostic window, as that * uses polling and isn't initialized for interrupts. */ - for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++) - ath10k_ce_per_engine_handler_adjust(&ar_pci->ce_states[ce_id]); + for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++) { + ce_state = &ce->ce_states[ce_id]; + ath10k_ce_per_engine_handler_adjust(ce_state); + } } static int ath10k_ce_init_src_ring(struct ath10k *ar, unsigned int ce_id, const struct ce_attr *attr) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; + struct ath10k_ce *ce = ath10k_ce_priv(ar); + struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id]; struct ath10k_ce_ring *src_ring = ce_state->src_ring; u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id); @@ -898,8 +927,8 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, unsigned int ce_id, const struct ce_attr *attr) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; + struct ath10k_ce *ce = ath10k_ce_priv(ar); + struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id]; struct ath10k_ce_ring *dest_ring = ce_state->dest_ring; u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id); @@ -1081,8 +1110,8 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id) int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, const struct ce_attr *attr) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; + struct ath10k_ce *ce = ath10k_ce_priv(ar); + struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id]; int ret; /* @@ -1138,8 +1167,8 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; + struct ath10k_ce *ce = ath10k_ce_priv(ar); + struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id]; if (ce_state->src_ring) { dma_free_coherent(ar->dev, @@ -1168,38 +1197,38 @@ void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id) void ath10k_ce_dump_registers(struct ath10k *ar, struct ath10k_fw_crash_data *crash_data) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_ce_crash_data ce; + struct ath10k_ce *ce = ath10k_ce_priv(ar); + struct ath10k_ce_crash_data ce_data; u32 addr, id; lockdep_assert_held(&ar->data_lock); ath10k_err(ar, "Copy Engine register dump:\n"); - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); for (id = 0; id < CE_COUNT; id++) { addr = ath10k_ce_base_address(ar, id); - ce.base_addr = cpu_to_le32(addr); + ce_data.base_addr = cpu_to_le32(addr); - ce.src_wr_idx = + ce_data.src_wr_idx = cpu_to_le32(ath10k_ce_src_ring_write_index_get(ar, addr)); - ce.src_r_idx = + ce_data.src_r_idx = cpu_to_le32(ath10k_ce_src_ring_read_index_get(ar, addr)); - ce.dst_wr_idx = + ce_data.dst_wr_idx = cpu_to_le32(ath10k_ce_dest_ring_write_index_get(ar, addr)); - ce.dst_r_idx = + ce_data.dst_r_idx = cpu_to_le32(ath10k_ce_dest_ring_read_index_get(ar, addr)); if (crash_data) - crash_data->ce_crash_data[id] = ce; + crash_data->ce_crash_data[id] = ce_data; ath10k_err(ar, "[%02d]: 0x%08x %3u %3u %3u %3u", id, - le32_to_cpu(ce.base_addr), - le32_to_cpu(ce.src_wr_idx), - le32_to_cpu(ce.src_r_idx), - le32_to_cpu(ce.dst_wr_idx), - le32_to_cpu(ce.dst_r_idx)); + le32_to_cpu(ce_data.base_addr), + le32_to_cpu(ce_data.src_wr_idx), + le32_to_cpu(ce_data.src_r_idx), + le32_to_cpu(ce_data.dst_wr_idx), + le32_to_cpu(ce_data.dst_r_idx)); } - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); } diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index 95743a57525d..bdec794704d9 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -122,6 +122,24 @@ struct ath10k_ce_pipe { /* Copy Engine settable attributes */ struct ce_attr; +struct ath10k_bus_ops { + u32 (*read32)(struct ath10k *ar, u32 offset); + void (*write32)(struct ath10k *ar, u32 offset, u32 value); + int (*get_num_banks)(struct ath10k *ar); +}; + +static inline struct ath10k_ce *ath10k_ce_priv(struct ath10k *ar) +{ + return (struct ath10k_ce *)ar->ce_priv; +} + +struct ath10k_ce { + /* protects CE info */ + spinlock_t ce_lock; + const struct ath10k_bus_ops *bus_ops; + struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; +}; + /*==================Send====================*/ /* ath10k_ce_send flags */ @@ -291,9 +309,13 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id) CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB) #define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS 0x0000 -#define CE_INTERRUPT_SUMMARY(ar) \ - CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \ - ath10k_pci_read32((ar), CE_WRAPPER_BASE_ADDRESS + \ - CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)) +static inline u32 ath10k_ce_interrupt_summary(struct ath10k *ar) +{ + struct ath10k_ce *ce = ath10k_ce_priv(ar); + + return CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( + ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS + + CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)); +} #endif /* _CE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 1aa5cf12fce0..2b499af722ad 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -993,6 +993,8 @@ struct ath10k { u32 reg_ack_cts_timeout_orig; } fw_coverage; + void *ce_priv; + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 7ebfc409018d..6a91276ce4d7 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -672,16 +672,16 @@ static u32 ath10k_bus_pci_read32(struct ath10k *ar, u32 offset) inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); - ar_pci->bus_ops->write32(ar, offset, value); + ce->bus_ops->write32(ar, offset, value); } inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); - return ar_pci->bus_ops->read32(ar, offset); + return ce->bus_ops->read32(ar, offset); } u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr) @@ -761,7 +761,7 @@ static inline const char *ath10k_pci_get_irq_method(struct ath10k *ar) static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe) { struct ath10k *ar = pipe->hif_ce_state; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl; struct sk_buff *skb; dma_addr_t paddr; @@ -784,9 +784,9 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe) ATH10K_SKB_RXCB(skb)->paddr = paddr; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); if (ret) { dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); @@ -801,6 +801,7 @@ static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) { struct ath10k *ar = pipe->hif_ce_state; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl; int ret, num; @@ -810,9 +811,9 @@ static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) if (!ce_pipe->dest_ring) return; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); num = __ath10k_ce_rx_num_free_bufs(ce_pipe); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); while (num >= 0) { ret = __ath10k_pci_rx_post_buf(pipe); @@ -882,6 +883,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, int nbytes) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); int ret = 0; u32 *buf; unsigned int completed_nbytes, alloc_nbytes, remaining_bytes; @@ -892,7 +894,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, void *data_buf = NULL; int i; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); ce_diag = ar_pci->ce_diag; @@ -986,7 +988,7 @@ done: dma_free_coherent(ar->dev, alloc_nbytes, data_buf, ce_data_base); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return ret; } @@ -1034,6 +1036,7 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, const void *data, int nbytes) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); int ret = 0; u32 *buf; unsigned int completed_nbytes, orig_nbytes, remaining_bytes; @@ -1043,7 +1046,7 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, dma_addr_t ce_data_base = 0; int i; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); ce_diag = ar_pci->ce_diag; @@ -1147,7 +1150,7 @@ done: ath10k_warn(ar, "failed to write diag value at 0x%x: %d\n", address, ret); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return ret; } @@ -1342,6 +1345,7 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, struct ath10k_hif_sg_item *items, int n_items) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id]; struct ath10k_ce_pipe *ce_pipe = pci_pipe->ce_hdl; struct ath10k_ce_ring *src_ring = ce_pipe->src_ring; @@ -1350,7 +1354,7 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, unsigned int write_index; int err, i = 0; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ce->ce_lock); nentries_mask = src_ring->nentries_mask; sw_index = src_ring->sw_index; @@ -1396,14 +1400,14 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, if (err) goto err; - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return 0; err: for (; i > 0; i--) __ath10k_ce_send_revert(ce_pipe); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ce->ce_lock); return err; } @@ -2000,9 +2004,9 @@ static int ath10k_pci_get_num_banks(struct ath10k *ar) static int ath10k_bus_get_num_banks(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); - return ar_pci->bus_ops->get_num_banks(ar); + return ce->bus_ops->get_num_banks(ar); } int ath10k_pci_init_config(struct ath10k *ar) @@ -2173,11 +2177,12 @@ int ath10k_pci_alloc_pipes(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci_pipe *pipe; + struct ath10k_ce *ce = ath10k_ce_priv(ar); int i, ret; for (i = 0; i < CE_COUNT; i++) { pipe = &ar_pci->pipe_info[i]; - pipe->ce_hdl = &ar_pci->ce_states[i]; + pipe->ce_hdl = &ce->ce_states[i]; pipe->pipe_num = i; pipe->hif_ce_state = ar; @@ -2825,7 +2830,7 @@ static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget) * interrupts safer to check for pending interrupts for * immediate servicing. */ - if (CE_INTERRUPT_SUMMARY(ar)) { + if (ath10k_ce_interrupt_summary(ar)) { napi_reschedule(ctx); goto out; } @@ -3142,9 +3147,10 @@ static bool ath10k_pci_chip_is_supported(u32 dev_id, u32 chip_id) int ath10k_pci_setup_resource(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce *ce = ath10k_ce_priv(ar); int ret; - spin_lock_init(&ar_pci->ce_lock); + spin_lock_init(&ce->ce_lock); spin_lock_init(&ar_pci->ps_lock); setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry, @@ -3263,10 +3269,11 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ar_pci->ar = ar; ar->dev_id = pci_dev->device; ar_pci->pci_ps = pci_ps; - ar_pci->bus_ops = &ath10k_pci_bus_ops; + ar_pci->ce.bus_ops = &ath10k_pci_bus_ops; ar_pci->pci_soft_reset = pci_soft_reset; ar_pci->pci_hard_reset = pci_hard_reset; ar_pci->targ_cpu_to_ce_addr = targ_cpu_to_ce_addr; + ar->ce_priv = &ar_pci->ce; ar->id.vendor = pdev->vendor; ar->id.device = pdev->device; diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index c1e08ad63940..424ff323b2dc 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -150,12 +150,6 @@ struct ath10k_pci_supp_chip { u32 rev_id; }; -struct ath10k_bus_ops { - u32 (*read32)(struct ath10k *ar, u32 offset); - void (*write32)(struct ath10k *ar, u32 offset, u32 value); - int (*get_num_banks)(struct ath10k *ar); -}; - enum ath10k_pci_irq_mode { ATH10K_PCI_IRQ_AUTO = 0, ATH10K_PCI_IRQ_LEGACY = 1, @@ -177,11 +171,7 @@ struct ath10k_pci { /* Copy Engine used for Diagnostic Accesses */ struct ath10k_ce_pipe *ce_diag; - /* FIXME: document what this really protects */ - spinlock_t ce_lock; - - /* Map CE id to ce_state */ - struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; + struct ath10k_ce ce; struct timer_list rx_post_retry; /* Due to HW quirks it is recommended to disable ASPM during device @@ -225,8 +215,6 @@ struct ath10k_pci { */ bool pci_ps; - const struct ath10k_bus_ops *bus_ops; - /* Chip specific pci reset routine used to do a safe reset */ int (*pci_soft_reset)(struct ath10k *ar); -- cgit v1.2.3 From f9e18304544e7fbabe9e4b5da8c7a052542000ca Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Wed, 28 Jun 2017 10:18:36 +0530 Subject: ath10k: add copy engine register MAP for wcn3990 target Copy engine is a host to target communication interface between wlan firmware and wlan wcn3990 platform driver. Add copy engine register map for wcn3990 wlan module. This add support for the copy engine source/destination ring configuration for wcn3990 chipset. Signed-off-by: Govind Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 5 ++ drivers/net/wireless/ath/ath10k/hw.c | 150 +++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/hw.h | 9 ++ drivers/net/wireless/ath/ath10k/pci.c | 4 + 4 files changed, 168 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 75c5c903c8a6..8ff47458207c 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2516,6 +2516,11 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, ar->hw_ce_regs = &qcax_ce_regs; ar->hw_values = &qca4019_values; break; + case ATH10K_HW_WCN3990: + ar->regs = &wcn3990_regs; + ar->hw_ce_regs = &wcn3990_ce_regs; + ar->hw_values = &wcn3990_values; + break; default: ath10k_err(ar, "unsupported core hardware revision %d\n", hw_rev); diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index afb0c01cbb55..a860691d635d 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -192,6 +192,156 @@ const struct ath10k_hw_values qca4019_values = { .ce_desc_meta_data_lsb = 4, }; +const struct ath10k_hw_regs wcn3990_regs = { + .rtc_soc_base_address = 0x00000000, + .rtc_wmac_base_address = 0x00000000, + .soc_core_base_address = 0x00000000, + .ce_wrapper_base_address = 0x0024C000, + .ce0_base_address = 0x00240000, + .ce1_base_address = 0x00241000, + .ce2_base_address = 0x00242000, + .ce3_base_address = 0x00243000, + .ce4_base_address = 0x00244000, + .ce5_base_address = 0x00245000, + .ce6_base_address = 0x00246000, + .ce7_base_address = 0x00247000, + .ce8_base_address = 0x00248000, + .ce9_base_address = 0x00249000, + .ce10_base_address = 0x0024A000, + .ce11_base_address = 0x0024B000, + .soc_chip_id_address = 0x000000f0, + .soc_reset_control_si0_rst_mask = 0x00000001, + .soc_reset_control_ce_rst_mask = 0x00000100, + .ce_wrap_intr_sum_host_msi_lsb = 0x0000000c, + .ce_wrap_intr_sum_host_msi_mask = 0x00fff000, + .pcie_intr_fw_mask = 0x00100000, +}; + +static struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = { + .msb = 0x00000010, + .lsb = 0x00000010, + .mask = GENMASK(17, 17), +}; + +static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = { + .msb = 0x00000012, + .lsb = 0x00000012, + .mask = GENMASK(18, 18), +}; + +static struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = { + .msb = 0x00000000, + .lsb = 0x00000000, + .mask = GENMASK(15, 0), +}; + +static struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = { + .addr = 0x00000018, + .src_ring = &wcn3990_src_ring, + .dst_ring = &wcn3990_dst_ring, + .dmax = &wcn3990_dmax, +}; + +static struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = { + .mask = GENMASK(0, 0), +}; + +static struct ath10k_hw_ce_host_ie wcn3990_host_ie = { + .copy_complete = &wcn3990_host_ie_cc, +}; + +static struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = { + .dstr_lmask = 0x00000010, + .dstr_hmask = 0x00000008, + .srcr_lmask = 0x00000004, + .srcr_hmask = 0x00000002, + .cc_mask = 0x00000001, + .wm_mask = 0x0000001E, + .addr = 0x00000030, +}; + +static struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = { + .axi_err = 0x00000100, + .dstr_add_err = 0x00000200, + .srcr_len_err = 0x00000100, + .dstr_mlen_vio = 0x00000080, + .dstr_overflow = 0x00000040, + .srcr_overflow = 0x00000020, + .err_mask = 0x000003E0, + .addr = 0x00000038, +}; + +static struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = { + .msb = 0x00000000, + .lsb = 0x00000010, + .mask = GENMASK(31, 16), +}; + +static struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = { + .msb = 0x0000000f, + .lsb = 0x00000000, + .mask = GENMASK(15, 0), +}; + +static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = { + .addr = 0x0000004c, + .low_rst = 0x00000000, + .high_rst = 0x00000000, + .wm_low = &wcn3990_src_wm_low, + .wm_high = &wcn3990_src_wm_high, +}; + +static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = { + .lsb = 0x00000010, + .mask = GENMASK(31, 16), +}; + +static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = { + .msb = 0x0000000f, + .lsb = 0x00000000, + .mask = GENMASK(15, 0), +}; + +static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = { + .addr = 0x00000050, + .low_rst = 0x00000000, + .high_rst = 0x00000000, + .wm_low = &wcn3990_dst_wm_low, + .wm_high = &wcn3990_dst_wm_high, +}; + +struct ath10k_hw_ce_regs wcn3990_ce_regs = { + .sr_base_addr = 0x00000000, + .sr_size_addr = 0x00000008, + .dr_base_addr = 0x0000000c, + .dr_size_addr = 0x00000014, + .misc_ie_addr = 0x00000034, + .sr_wr_index_addr = 0x0000003c, + .dst_wr_index_addr = 0x00000040, + .current_srri_addr = 0x00000044, + .current_drri_addr = 0x00000048, + .ddr_addr_for_rri_low = 0x00000004, + .ddr_addr_for_rri_high = 0x00000008, + .ce_rri_low = 0x0024C004, + .ce_rri_high = 0x0024C008, + .host_ie_addr = 0x0000002c, + .ctrl1_regs = &wcn3990_ctrl1, + .host_ie = &wcn3990_host_ie, + .wm_regs = &wcn3990_wm_reg, + .misc_regs = &wcn3990_misc_reg, + .wm_srcr = &wcn3990_wm_src_ring, + .wm_dstr = &wcn3990_wm_dst_ring, +}; + +const struct ath10k_hw_values wcn3990_values = { + .rtc_state_val_on = 5, + .ce_count = 12, + .msi_assign_ce_max = 12, + .num_target_ce_config_wlan = 12, + .ce_desc_meta_data_mask = 0xFFF0, + .ce_desc_meta_data_lsb = 4, +}; + static struct ath10k_hw_ce_regs_addr_map qcax_src_ring = { .msb = 0x00000010, .lsb = 0x00000010, diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 97dc1479f44e..19e43512af50 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -231,6 +231,7 @@ enum ath10k_hw_rev { ATH10K_HW_QCA9377, ATH10K_HW_QCA4019, ATH10K_HW_QCA9887, + ATH10K_HW_WCN3990, }; struct ath10k_hw_regs { @@ -247,6 +248,10 @@ struct ath10k_hw_regs { u32 ce5_base_address; u32 ce6_base_address; u32 ce7_base_address; + u32 ce8_base_address; + u32 ce9_base_address; + u32 ce10_base_address; + u32 ce11_base_address; u32 soc_reset_control_si0_rst_mask; u32 soc_reset_control_ce_rst_mask; u32 soc_chip_id_address; @@ -267,6 +272,7 @@ extern const struct ath10k_hw_regs qca988x_regs; extern const struct ath10k_hw_regs qca6174_regs; extern const struct ath10k_hw_regs qca99x0_regs; extern const struct ath10k_hw_regs qca4019_regs; +extern const struct ath10k_hw_regs wcn3990_regs; struct ath10k_hw_ce_regs_addr_map { u32 msb; @@ -362,6 +368,8 @@ extern const struct ath10k_hw_values qca6174_values; extern const struct ath10k_hw_values qca99x0_values; extern const struct ath10k_hw_values qca9888_values; extern const struct ath10k_hw_values qca4019_values; +extern const struct ath10k_hw_values wcn3990_values; +extern struct ath10k_hw_ce_regs wcn3990_ce_regs; extern struct ath10k_hw_ce_regs qcax_ce_regs; void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, @@ -375,6 +383,7 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, #define QCA_REV_9984(ar) ((ar)->hw_rev == ATH10K_HW_QCA9984) #define QCA_REV_9377(ar) ((ar)->hw_rev == ATH10K_HW_QCA9377) #define QCA_REV_40XX(ar) ((ar)->hw_rev == ATH10K_HW_QCA4019) +#define QCA_REV_WCN3990(ar) ((ar)->hw_rev == ATH10K_HW_WCN3990) /* Known peculiarities: * - raw appears in nwifi decap, raw and nwifi appear in ethernet decap diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 6a91276ce4d7..a697caec6579 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1597,6 +1597,8 @@ void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar) * to mask irq/MSI. */ break; + case ATH10K_HW_WCN3990: + break; } } @@ -1623,6 +1625,8 @@ static void ath10k_pci_irq_msi_fw_unmask(struct ath10k *ar) * to unmask irq/MSI. */ break; + case ATH10K_HW_WCN3990: + break; } } -- cgit v1.2.3 From 75e0dde2abf6ae8561b133c4069c473641d50c12 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Mon, 3 Jul 2017 11:02:36 +0530 Subject: ath10k: increase buffer len to print all wmi services All wmi_services are not printing when we give below command. cat /sys/kernel/debug/ieee80211/phyX/ath10k/wmi_services This patch increases the buffer_len to 8192 to print all the wmi_services. Signed-off-by: Tamizh chelvam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 389fcb7a9fd0..56404fe4e8f5 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -237,7 +237,7 @@ static ssize_t ath10k_read_wmi_services(struct file *file, { struct ath10k *ar = file->private_data; char *buf; - size_t len = 0, buf_len = 4096; + size_t len = 0, buf_len = 8192; const char *name; ssize_t ret_cnt; bool enabled; -- cgit v1.2.3 From 98524e04e0500a04fc461195c99e4385001fc18f Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 6 Jul 2017 16:57:10 -0700 Subject: sunvnet: add support for IPv6 checksum offloads The original code didn't handle non-IPv4 packets very well, so the offload advertising had to be scaled back down to just IP. Here we add the bits needed to support TCP and UDP packets over IPv6 and turn the offload advertising back on. Orabug: 26289579 Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/ldmvsw.c | 2 +- drivers/net/ethernet/sun/sunvnet.c | 2 +- drivers/net/ethernet/sun/sunvnet_common.c | 90 +++++++++++++++++++++++++++---- 3 files changed, 81 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c index 8603e397097e..5b56c24b6ed2 100644 --- a/drivers/net/ethernet/sun/ldmvsw.c +++ b/drivers/net/ethernet/sun/ldmvsw.c @@ -248,7 +248,7 @@ static struct net_device *vsw_alloc_netdev(u8 hwaddr[], dev->ethtool_ops = &vsw_ethtool_ops; dev->watchdog_timeo = VSW_TX_TIMEOUT; - dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG; + dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG; dev->features = dev->hw_features; /* MTU range: 68 - 65535 */ diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 75b167e3fe98..0b95105f7060 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -312,7 +312,7 @@ static struct vnet *vnet_new(const u64 *local_mac, dev->watchdog_timeo = VNET_TX_TIMEOUT; dev->hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GSO_SOFTWARE | - NETIF_F_IP_CSUM | NETIF_F_SG; + NETIF_F_HW_CSUM | NETIF_F_SG; dev->features = dev->hw_features; /* MTU range: 68 - 65535 */ diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 9e86833249d4..ecf456c7b6d1 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -303,7 +303,7 @@ static struct sk_buff *alloc_and_align_skb(struct net_device *dev, return skb; } -static inline void vnet_fullcsum(struct sk_buff *skb) +static inline void vnet_fullcsum_ipv4(struct sk_buff *skb) { struct iphdr *iph = ip_hdr(skb); int offset = skb_transport_offset(skb); @@ -335,6 +335,40 @@ static inline void vnet_fullcsum(struct sk_buff *skb) } } +#if IS_ENABLED(CONFIG_IPV6) +static inline void vnet_fullcsum_ipv6(struct sk_buff *skb) +{ + struct ipv6hdr *ip6h = ipv6_hdr(skb); + int offset = skb_transport_offset(skb); + + if (skb->protocol != htons(ETH_P_IPV6)) + return; + if (ip6h->nexthdr != IPPROTO_TCP && + ip6h->nexthdr != IPPROTO_UDP) + return; + skb->ip_summed = CHECKSUM_NONE; + skb->csum_level = 1; + skb->csum = 0; + if (ip6h->nexthdr == IPPROTO_TCP) { + struct tcphdr *ptcp = tcp_hdr(skb); + + ptcp->check = 0; + skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); + ptcp->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, + skb->len - offset, IPPROTO_TCP, + skb->csum); + } else if (ip6h->nexthdr == IPPROTO_UDP) { + struct udphdr *pudp = udp_hdr(skb); + + pudp->check = 0; + skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); + pudp->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, + skb->len - offset, IPPROTO_UDP, + skb->csum); + } +} +#endif + static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc) { struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port); @@ -394,9 +428,14 @@ static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc) struct iphdr *iph = ip_hdr(skb); int ihl = iph->ihl * 4; - skb_reset_transport_header(skb); skb_set_transport_header(skb, ihl); - vnet_fullcsum(skb); + vnet_fullcsum_ipv4(skb); +#if IS_ENABLED(CONFIG_IPV6) + } else if (skb->protocol == htons(ETH_P_IPV6)) { + skb_set_transport_header(skb, + sizeof(struct ipv6hdr)); + vnet_fullcsum_ipv6(skb); +#endif } } if (dext->flags & VNET_PKT_HCK_IPV4_HDRCKSUM_OK) { @@ -1115,24 +1154,47 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, int ncookies) if (skb->ip_summed == CHECKSUM_PARTIAL) start = skb_checksum_start_offset(skb); if (start) { - struct iphdr *iph = ip_hdr(nskb); int offset = start + nskb->csum_offset; + /* copy the headers, no csum here */ if (skb_copy_bits(skb, 0, nskb->data, start)) { dev_kfree_skb(nskb); dev_kfree_skb(skb); return NULL; } + + /* copy the rest, with csum calculation */ *(__sum16 *)(skb->data + offset) = 0; csum = skb_copy_and_csum_bits(skb, start, nskb->data + start, skb->len - start, 0); - if (iph->protocol == IPPROTO_TCP || - iph->protocol == IPPROTO_UDP) { - csum = csum_tcpudp_magic(iph->saddr, iph->daddr, - skb->len - start, - iph->protocol, csum); + + /* add in the header checksums */ + if (skb->protocol == htons(ETH_P_IP)) { + struct iphdr *iph = ip_hdr(nskb); + + if (iph->protocol == IPPROTO_TCP || + iph->protocol == IPPROTO_UDP) { + csum = csum_tcpudp_magic(iph->saddr, + iph->daddr, + skb->len - start, + iph->protocol, + csum); + } + } else if (skb->protocol == htons(ETH_P_IPV6)) { + struct ipv6hdr *ip6h = ipv6_hdr(nskb); + + if (ip6h->nexthdr == IPPROTO_TCP || + ip6h->nexthdr == IPPROTO_UDP) { + csum = csum_ipv6_magic(&ip6h->saddr, + &ip6h->daddr, + skb->len - start, + ip6h->nexthdr, + csum); + } } + + /* save the final result */ *(__sum16 *)(nskb->data + offset) = csum; nskb->ip_summed = CHECKSUM_NONE; @@ -1318,8 +1380,14 @@ int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev, if (unlikely(!skb)) goto out_dropped; - if (skb->ip_summed == CHECKSUM_PARTIAL) - vnet_fullcsum(skb); + if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (skb->protocol == htons(ETH_P_IP)) + vnet_fullcsum_ipv4(skb); +#if IS_ENABLED(CONFIG_IPV6) + else if (skb->protocol == htons(ETH_P_IPV6)) + vnet_fullcsum_ipv6(skb); +#endif + } dr = &port->vio.drings[VIO_DRIVER_TX_RING]; i = skb_get_queue_mapping(skb); -- cgit v1.2.3 From ee06b1728b95643668e40fc58ae118aeb7c1753e Mon Sep 17 00:00:00 2001 From: "Alvaro G. M" Date: Mon, 17 Jul 2017 09:12:28 +0200 Subject: net: axienet: add support for standard phy-mode binding Keep supporting proprietary "xlnx,phy-type" attribute and add support for MII connectivity to the PHY. Reviewed-by: Andrew Lunn Signed-off-by: Alvaro Gamez Machado Signed-off-by: David S. Miller --- .../devicetree/bindings/net/xilinx_axienet.txt | 55 ++++++++++++++++++++++ drivers/net/ethernet/xilinx/xilinx_axienet.h | 4 +- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 48 ++++++++++++++----- 3 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/xilinx_axienet.txt (limited to 'drivers/net') diff --git a/Documentation/devicetree/bindings/net/xilinx_axienet.txt b/Documentation/devicetree/bindings/net/xilinx_axienet.txt new file mode 100644 index 000000000000..38f9ec076743 --- /dev/null +++ b/Documentation/devicetree/bindings/net/xilinx_axienet.txt @@ -0,0 +1,55 @@ +XILINX AXI ETHERNET Device Tree Bindings +-------------------------------------------------------- + +Also called AXI 1G/2.5G Ethernet Subsystem, the xilinx axi ethernet IP core +provides connectivity to an external ethernet PHY supporting different +interfaces: MII, GMII, RGMII, SGMII, 1000BaseX. It also includes two +segments of memory for buffering TX and RX, as well as the capability of +offloading TX/RX checksum calculation off the processor. + +Management configuration is done through the AXI interface, while payload is +sent and received through means of an AXI DMA controller. This driver +includes the DMA driver code, so this driver is incompatible with AXI DMA +driver. + +For more details about mdio please refer phy.txt file in the same directory. + +Required properties: +- compatible : Must be one of "xlnx,axi-ethernet-1.00.a", + "xlnx,axi-ethernet-1.01.a", "xlnx,axi-ethernet-2.01.a" +- reg : Address and length of the IO space. +- interrupts : Should be a list of two interrupt, TX and RX. +- phy-handle : Should point to the external phy device. + See ethernet.txt file in the same directory. +- xlnx,rxmem : Set to allocated memory buffer for Rx/Tx in the hardware + +Optional properties: +- phy-mode : See ethernet.txt +- xlnx,phy-type : Deprecated, do not use, but still accepted in preference + to phy-mode. +- xlnx,txcsum : 0 or empty for disabling TX checksum offload, + 1 to enable partial TX checksum offload, + 2 to enable full TX checksum offload +- xlnx,rxcsum : Same values as xlnx,txcsum but for RX checksum offload + +Example: + axi_ethernet_eth: ethernet@40c00000 { + compatible = "xlnx,axi-ethernet-1.00.a"; + device_type = "network"; + interrupt-parent = <µblaze_0_axi_intc>; + interrupts = <2 0>; + phy-mode = "mii"; + reg = <0x40c00000 0x40000>; + xlnx,rxcsum = <0x2>; + xlnx,rxmem = <0x800>; + xlnx,txcsum = <0x2>; + phy-handle = <&phy0>; + axi_ethernetlite_0_mdio: mdio { + #address-cells = <1>; + #size-cells = <0>; + phy0: phy@0 { + device_type = "ethernet-phy"; + reg = <1>; + }; + }; + }; diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index af27f7d1cbf3..5ef626331f85 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -389,7 +389,7 @@ struct axidma_bd { * @dma_err_tasklet: Tasklet structure to process Axi DMA errors * @tx_irq: Axidma TX IRQ number * @rx_irq: Axidma RX IRQ number - * @phy_type: Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X + * @phy_mode: Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X * @options: AxiEthernet option word * @last_link: Phy link state in which the PHY was negotiated earlier * @features: Stores the extended features supported by the axienet hw @@ -432,7 +432,7 @@ struct axienet_local { int tx_irq; int rx_irq; - u32 phy_type; + phy_interface_t phy_mode; u32 options; /* Current options word */ u32 last_link; diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 33c595f4691d..e74e1e897864 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -531,11 +531,11 @@ static void axienet_adjust_link(struct net_device *ndev) link_state = phy->speed | (phy->duplex << 1) | phy->link; if (lp->last_link != link_state) { if ((phy->speed == SPEED_10) || (phy->speed == SPEED_100)) { - if (lp->phy_type == XAE_PHY_TYPE_1000BASE_X) + if (lp->phy_mode == PHY_INTERFACE_MODE_1000BASEX) setspeed = 0; } else { if ((phy->speed == SPEED_1000) && - (lp->phy_type == XAE_PHY_TYPE_MII)) + (lp->phy_mode == PHY_INTERFACE_MODE_MII)) setspeed = 0; } @@ -935,15 +935,8 @@ static int axienet_open(struct net_device *ndev) return ret; if (lp->phy_node) { - if (lp->phy_type == XAE_PHY_TYPE_GMII) { - phydev = of_phy_connect(lp->ndev, lp->phy_node, - axienet_adjust_link, 0, - PHY_INTERFACE_MODE_GMII); - } else if (lp->phy_type == XAE_PHY_TYPE_RGMII_2_0) { - phydev = of_phy_connect(lp->ndev, lp->phy_node, - axienet_adjust_link, 0, - PHY_INTERFACE_MODE_RGMII_ID); - } + phydev = of_phy_connect(lp->ndev, lp->phy_node, + axienet_adjust_link, 0, lp->phy_mode); if (!phydev) dev_err(lp->dev, "of_phy_connect() failed\n"); @@ -1539,7 +1532,38 @@ static int axienet_probe(struct platform_device *pdev) * the device-tree and accordingly set flags. */ of_property_read_u32(pdev->dev.of_node, "xlnx,rxmem", &lp->rxmem); - of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type", &lp->phy_type); + + /* Start with the proprietary, and broken phy_type */ + ret = of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type", &value); + if (!ret) { + netdev_warn(ndev, "Please upgrade your device tree binary blob to use phy-mode"); + switch (value) { + case XAE_PHY_TYPE_MII: + lp->phy_mode = PHY_INTERFACE_MODE_MII; + break; + case XAE_PHY_TYPE_GMII: + lp->phy_mode = PHY_INTERFACE_MODE_GMII; + break; + case XAE_PHY_TYPE_RGMII_2_0: + lp->phy_mode = PHY_INTERFACE_MODE_RGMII_ID; + break; + case XAE_PHY_TYPE_SGMII: + lp->phy_mode = PHY_INTERFACE_MODE_SGMII; + break; + case XAE_PHY_TYPE_1000BASE_X: + lp->phy_mode = PHY_INTERFACE_MODE_1000BASEX; + break; + default: + ret = -EINVAL; + goto free_netdev; + } + } else { + lp->phy_mode = of_get_phy_mode(pdev->dev.of_node); + if (lp->phy_mode < 0) { + ret = -EINVAL; + goto free_netdev; + } + } /* Find the DMA node, map the DMA registers, and decode the DMA IRQs */ np = of_parse_phandle(pdev->dev.of_node, "axistream-connected", 0); -- cgit v1.2.3 From 5f57e0909136f912b1a8ed677ef6eed8cbb3ec4f Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Mon, 17 Jul 2017 14:07:26 +0200 Subject: mlxsw: acl: Add ip ttl acl element Define new element for ip ttl and place it into scratch area. Signed-off-by: Or Gerlitz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h index 9807ef814e42..789ebb31f4e5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h @@ -57,6 +57,7 @@ enum mlxsw_afk_element { MLXSW_AFK_ELEMENT_VID, MLXSW_AFK_ELEMENT_PCP, MLXSW_AFK_ELEMENT_TCP_FLAGS, + MLXSW_AFK_ELEMENT_IP_TTL_, MLXSW_AFK_ELEMENT_MAX, }; @@ -104,6 +105,7 @@ static const struct mlxsw_afk_element_info mlxsw_afk_element_infos[] = { MLXSW_AFK_ELEMENT_INFO_U32(VID, 0x10, 8, 12), MLXSW_AFK_ELEMENT_INFO_U32(PCP, 0x10, 20, 3), MLXSW_AFK_ELEMENT_INFO_U32(TCP_FLAGS, 0x10, 23, 9), + MLXSW_AFK_ELEMENT_INFO_U32(IP_TTL_, 0x14, 0, 8), MLXSW_AFK_ELEMENT_INFO_U32(SRC_IP4, 0x18, 0, 32), MLXSW_AFK_ELEMENT_INFO_U32(DST_IP4, 0x1C, 0, 32), MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP6_HI, 0x18, 8), -- cgit v1.2.3 From 046759a6cf36118f5f4468f5a3998aada040ca5d Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Mon, 17 Jul 2017 14:07:27 +0200 Subject: mlxsw: spectrum: Add ttl to the ipv4 acl block Add ttl field to the ipv4 acl block. Signed-off-by: Or Gerlitz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h index 85d5001a5818..8a4767ca6909 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h @@ -70,6 +70,7 @@ static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4_dip[] = { static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4[] = { MLXSW_AFK_ELEMENT_INST_U32(SRC_IP4, 0x00, 0, 32), + MLXSW_AFK_ELEMENT_INST_U32(IP_TTL_, 0x04, 24, 8), MLXSW_AFK_ELEMENT_INST_U32(TCP_FLAGS, 0x08, 8, 9), /* TCP_CONTROL+TCP_ECN */ }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c index 61a10f166f97..2c57be7a8399 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c @@ -984,6 +984,7 @@ static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = { MLXSW_AFK_ELEMENT_VID, MLXSW_AFK_ELEMENT_PCP, MLXSW_AFK_ELEMENT_TCP_FLAGS, + MLXSW_AFK_ELEMENT_IP_TTL_, }; static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv6[] = { -- cgit v1.2.3 From fcbca8217d62405e91b14953cfb005f83cfa90f1 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Mon, 17 Jul 2017 14:07:28 +0200 Subject: mlxsw: spectrum_flower: Add support for ip ttl Support offloading rules that match on ip ttl. Signed-off-by: Or Gerlitz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 37 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 21bb2bf62d3e..84fe33cf1e95 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -212,11 +212,39 @@ static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp, return 0; } +static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + struct tc_cls_flower_offload *f, + u16 n_proto) +{ + struct flow_dissector_key_ip *key, *mask; + + if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IP)) + return 0; + + if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6) { + dev_err(mlxsw_sp->bus_info->dev, "IP keys supported only for IPv4/6\n"); + return -EINVAL; + } + + key = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_IP, + f->key); + mask = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_IP, + f->mask); + mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_, + key->ttl, mask->ttl); + return 0; +} + static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, struct mlxsw_sp_acl_rule_info *rulei, struct tc_cls_flower_offload *f) { + u16 n_proto_mask = 0; + u16 n_proto_key = 0; u16 addr_type = 0; u8 ip_proto = 0; int err; @@ -229,6 +257,7 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | BIT(FLOW_DISSECTOR_KEY_PORTS) | BIT(FLOW_DISSECTOR_KEY_TCP) | + BIT(FLOW_DISSECTOR_KEY_IP) | BIT(FLOW_DISSECTOR_KEY_VLAN))) { dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n"); return -EOPNOTSUPP; @@ -253,8 +282,8 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, skb_flow_dissector_target(f->dissector, FLOW_DISSECTOR_KEY_BASIC, f->mask); - u16 n_proto_key = ntohs(key->n_proto); - u16 n_proto_mask = ntohs(mask->n_proto); + n_proto_key = ntohs(key->n_proto); + n_proto_mask = ntohs(mask->n_proto); if (n_proto_key == ETH_P_ALL) { n_proto_key = 0; @@ -324,6 +353,10 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, if (err) return err; + err = mlxsw_sp_flower_parse_ip(mlxsw_sp, rulei, f, n_proto_key & n_proto_mask); + if (err) + return err; + return mlxsw_sp_flower_parse_actions(mlxsw_sp, dev, rulei, f->exts); } -- cgit v1.2.3 From 80d0fe4710c80e2ade26d7e3030c5c985f34907e Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Mon, 17 Jul 2017 14:07:29 +0200 Subject: mlxsw: acl: Add ip tos acl element Define new element for ip tos (ecn, dscp) and place it into scratch area. Signed-off-by: Or Gerlitz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h index 789ebb31f4e5..f6963b0b4a55 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h @@ -58,6 +58,8 @@ enum mlxsw_afk_element { MLXSW_AFK_ELEMENT_PCP, MLXSW_AFK_ELEMENT_TCP_FLAGS, MLXSW_AFK_ELEMENT_IP_TTL_, + MLXSW_AFK_ELEMENT_IP_ECN, + MLXSW_AFK_ELEMENT_IP_DSCP, MLXSW_AFK_ELEMENT_MAX, }; @@ -106,6 +108,8 @@ static const struct mlxsw_afk_element_info mlxsw_afk_element_infos[] = { MLXSW_AFK_ELEMENT_INFO_U32(PCP, 0x10, 20, 3), MLXSW_AFK_ELEMENT_INFO_U32(TCP_FLAGS, 0x10, 23, 9), MLXSW_AFK_ELEMENT_INFO_U32(IP_TTL_, 0x14, 0, 8), + MLXSW_AFK_ELEMENT_INFO_U32(IP_ECN, 0x14, 9, 2), + MLXSW_AFK_ELEMENT_INFO_U32(IP_DSCP, 0x14, 11, 6), MLXSW_AFK_ELEMENT_INFO_U32(SRC_IP4, 0x18, 0, 32), MLXSW_AFK_ELEMENT_INFO_U32(DST_IP4, 0x1C, 0, 32), MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP6_HI, 0x18, 8), -- cgit v1.2.3 From abac7b011d527cfc98dba6a7422bdedcdedda039 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Mon, 17 Jul 2017 14:07:30 +0200 Subject: mlxsw: spectrum: Add tos to the ipv4 acl block Add ecn and dscp fields to the ipv4 acl block. Signed-off-by: Or Gerlitz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h | 2 ++ drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h index 8a4767ca6909..fb8031828454 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h @@ -70,7 +70,9 @@ static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4_dip[] = { static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4[] = { MLXSW_AFK_ELEMENT_INST_U32(SRC_IP4, 0x00, 0, 32), + MLXSW_AFK_ELEMENT_INST_U32(IP_ECN, 0x04, 4, 2), MLXSW_AFK_ELEMENT_INST_U32(IP_TTL_, 0x04, 24, 8), + MLXSW_AFK_ELEMENT_INST_U32(IP_DSCP, 0x08, 0, 6), MLXSW_AFK_ELEMENT_INST_U32(TCP_FLAGS, 0x08, 8, 9), /* TCP_CONTROL+TCP_ECN */ }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c index 2c57be7a8399..bc5173f1b5c1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c @@ -985,6 +985,8 @@ static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = { MLXSW_AFK_ELEMENT_PCP, MLXSW_AFK_ELEMENT_TCP_FLAGS, MLXSW_AFK_ELEMENT_IP_TTL_, + MLXSW_AFK_ELEMENT_IP_ECN, + MLXSW_AFK_ELEMENT_IP_DSCP, }; static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv6[] = { -- cgit v1.2.3 From 87996f91f739b7971097428372165a0b66683d0d Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Mon, 17 Jul 2017 14:07:31 +0200 Subject: mlxsw: spectrum_flower: Add support for ip tos Support offloading rules that match on ip tos. Signed-off-by: Or Gerlitz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 84fe33cf1e95..400ad4081660 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -235,6 +235,13 @@ static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp, f->mask); mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_, key->ttl, mask->ttl); + + mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_ECN, + key->tos & 0x3, mask->tos & 0x3); + + mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_DSCP, + key->tos >> 6, mask->tos >> 6); + return 0; } -- cgit v1.2.3 From 0fcc484748c9dcad5238373a4b2e1b2f309392eb Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 17 Jul 2017 14:15:29 +0200 Subject: mlxsw: spectrum: Mark packets trapped in router In commit 1c6c6d221e2b ("mlxsw: spectrum: Mirror certain packets to CPU") we marked packets that were mirrored to the CPU, so that they won't be flooded again by the bridge driver. However, certain packets are trapped in the device's router block, after passing through the bridge block where they were potentially flooded. Mark all packets coming from L3 traps, so that they won't be potentially flooded again by the bridge driver. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 60bf8f27cc00..83e77b959703 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3334,14 +3334,14 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_MARK(ARPUC, MIRROR_TO_CPU, ARP, false), MLXSW_SP_RXL_NO_MARK(FID_MISS, TRAP_TO_CPU, IP2ME, false), /* L3 traps */ - MLXSW_SP_RXL_NO_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_NO_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_NO_MARK(LBERROR, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(LBERROR, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(OSPF, TRAP_TO_CPU, OSPF, false), - MLXSW_SP_RXL_NO_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false), - MLXSW_SP_RXL_NO_MARK(RTR_INGRESS0, TRAP_TO_CPU, REMOTE_ROUTE, false), - MLXSW_SP_RXL_NO_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, ARP_MISS, false), - MLXSW_SP_RXL_NO_MARK(BGP_IPV4, TRAP_TO_CPU, BGP_IPV4, false), + MLXSW_SP_RXL_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false), + MLXSW_SP_RXL_MARK(RTR_INGRESS0, TRAP_TO_CPU, REMOTE_ROUTE, false), + MLXSW_SP_RXL_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, ARP_MISS, false), + MLXSW_SP_RXL_MARK(BGP_IPV4, TRAP_TO_CPU, BGP_IPV4, false), /* PKT Sample trap */ MLXSW_RXL(mlxsw_sp_rx_listener_sample_func, PKT_SAMPLE, MIRROR_TO_CPU, false, SP_IP2ME, DISCARD), -- cgit v1.2.3 From 7607dd35fc34893214284cca740d015154d20452 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 17 Jul 2017 14:15:30 +0200 Subject: mlxsw: spectrum: Trap IPv4 packets with Router Alert option In case local sockets have the IP_ROUTER_ALERT socket option set, then they expect to get packets with the Router Alert option. Trap such packets, so that the kernel could inspect them and potentially send them to interested sockets. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 1 + drivers/net/ethernet/mellanox/mlxsw/trap.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 83e77b959703..bc35b400e6f1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3342,6 +3342,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_MARK(RTR_INGRESS0, TRAP_TO_CPU, REMOTE_ROUTE, false), MLXSW_SP_RXL_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, ARP_MISS, false), MLXSW_SP_RXL_MARK(BGP_IPV4, TRAP_TO_CPU, BGP_IPV4, false), + MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV4, TRAP_TO_CPU, ROUTER_EXP, false), /* PKT Sample trap */ MLXSW_RXL(mlxsw_sp_rx_listener_sample_func, PKT_SAMPLE, MIRROR_TO_CPU, false, SP_IP2ME, DISCARD), diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h index 12b5ed58f3eb..4946d4e2b3f9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/trap.h +++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h @@ -66,6 +66,7 @@ enum { MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70, MLXSW_TRAP_ID_BGP_IPV4 = 0x88, MLXSW_TRAP_ID_HOST_MISS_IPV4 = 0x90, + MLXSW_TRAP_ID_ROUTER_ALERT_IPV4 = 0xD6, MLXSW_TRAP_ID_ACL0 = 0x1C0, MLXSW_TRAP_ID_MAX = 0x1FF -- cgit v1.2.3 From 588823f97df3d5bf219d8f0bfea1c23ce367b84d Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Mon, 17 Jul 2017 14:15:31 +0200 Subject: mlxsw: spectrum: Add support for IPv6 MLDv1/2 traps Add support for IPv6 MLDv1/2 packet trapping. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 10 ++++++++++ drivers/net/ethernet/mellanox/mlxsw/trap.h | 4 ++++ 3 files changed, 15 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 1bd34d9a7b9e..0ca196899e18 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3688,6 +3688,7 @@ enum mlxsw_reg_htgt_trap_group { MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME, MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP, MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT, + MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD, }; /* reg_htgt_trap_group diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index bc35b400e6f1..1aa6298ea6cd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3333,6 +3333,14 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_MARK(ARPBC, MIRROR_TO_CPU, ARP, false), MLXSW_SP_RXL_MARK(ARPUC, MIRROR_TO_CPU, ARP, false), MLXSW_SP_RXL_NO_MARK(FID_MISS, TRAP_TO_CPU, IP2ME, false), + MLXSW_SP_RXL_MARK(IPV6_MLDV12_LISTENER_QUERY, MIRROR_TO_CPU, IPV6_MLD, + false), + MLXSW_SP_RXL_NO_MARK(IPV6_MLDV1_LISTENER_REPORT, TRAP_TO_CPU, IPV6_MLD, + false), + MLXSW_SP_RXL_NO_MARK(IPV6_MLDV1_LISTENER_DONE, TRAP_TO_CPU, IPV6_MLD, + false), + MLXSW_SP_RXL_NO_MARK(IPV6_MLDV2_LISTENER_REPORT, TRAP_TO_CPU, IPV6_MLD, + false), /* L3 traps */ MLXSW_SP_RXL_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false), @@ -3377,6 +3385,7 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core) burst_size = 7; break; case MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD: rate = 16 * 1024; burst_size = 10; break; @@ -3441,6 +3450,7 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) break; case MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD: priority = 3; tc = 3; break; diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h index 4946d4e2b3f9..891b4ee6eeb2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/trap.h +++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h @@ -63,6 +63,10 @@ enum { MLXSW_TRAP_ID_LBERROR = 0x54, MLXSW_TRAP_ID_OSPF = 0x55, MLXSW_TRAP_ID_IP2ME = 0x5F, + MLXSW_TRAP_ID_IPV6_MLDV12_LISTENER_QUERY = 0x65, + MLXSW_TRAP_ID_IPV6_MLDV1_LISTENER_REPORT = 0x66, + MLXSW_TRAP_ID_IPV6_MLDV1_LISTENER_DONE = 0x67, + MLXSW_TRAP_ID_IPV6_MLDV2_LISTENER_REPORT = 0x68, MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70, MLXSW_TRAP_ID_BGP_IPV4 = 0x88, MLXSW_TRAP_ID_HOST_MISS_IPV4 = 0x90, -- cgit v1.2.3 From 9df552ef3e214e32b7a0458d3bdc430643aa553b Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Mon, 17 Jul 2017 14:15:32 +0200 Subject: mlxsw: spectrum: Improve IPv6 unregistered multicast flooding Up until now IPv6 unregistered multicast traffic would be flooded like broadcast, even when MLD snooping was enabled on the bridge. This was intentional as MLD packet traps were missing, preventing the bridge driver from programming MDB entries to the device. Previous patch added these traps, so we can now finally flood IPv6 unregistered multicast packets to specific ports via the multicast table instead of flooding them to all ports via the broadcast table. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c index 6afbe9ec64e2..bbd238e50f05 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c @@ -109,7 +109,6 @@ static const int mlxsw_sp_sfgc_uc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { static const int mlxsw_sp_sfgc_bc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { [MLXSW_REG_SFGC_TYPE_BROADCAST] = 1, - [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6] = 1, [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP] = 1, [MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL] = 1, [MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST] = 1, @@ -117,6 +116,7 @@ static const int mlxsw_sp_sfgc_bc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { static const int mlxsw_sp_sfgc_mc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4] = 1, + [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6] = 1, }; static const int *mlxsw_sp_packet_type_sfgc_types[] = { -- cgit v1.2.3 From 90382dca61137444cb8f639db0a76c4d084763d7 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:26:24 -0700 Subject: ixgbe: NULL xdp_tx rings on resource cleanup tx_rings and rx_rings are cleaned up on close paths in ixgbe driver however, xdp_rings are not. Set the xdp_rings to NULL here so that we can use the pointer to indicate if the XDP rings are initialized. Signed-off-by: John Fastabend Acked-by: Daniel Borkmann Acked-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index b45fdc98033d..f1bfae0c41d0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -1018,8 +1018,12 @@ static void ixgbe_free_q_vector(struct ixgbe_adapter *adapter, int v_idx) struct ixgbe_q_vector *q_vector = adapter->q_vector[v_idx]; struct ixgbe_ring *ring; - ixgbe_for_each_ring(ring, q_vector->tx) - adapter->tx_ring[ring->queue_index] = NULL; + ixgbe_for_each_ring(ring, q_vector->tx) { + if (ring_is_xdp(ring)) + adapter->xdp_ring[ring->queue_index] = NULL; + else + adapter->tx_ring[ring->queue_index] = NULL; + } ixgbe_for_each_ring(ring, q_vector->rx) adapter->rx_ring[ring->queue_index] = NULL; -- cgit v1.2.3 From 6453073987ba392510ab6c8b657844a9312c67f7 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:28:12 -0700 Subject: ixgbe: add initial support for xdp redirect There are optimizations we can add after the basic feature is enabled. But, for now keep the patch simple. Signed-off-by: John Fastabend Acked-by: Daniel Borkmann Acked-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 41 ++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index f1dbdf26d8e1..3db04736a048 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2214,7 +2214,7 @@ static struct sk_buff *ixgbe_run_xdp(struct ixgbe_adapter *adapter, struct ixgbe_ring *rx_ring, struct xdp_buff *xdp) { - int result = IXGBE_XDP_PASS; + int err, result = IXGBE_XDP_PASS; struct bpf_prog *xdp_prog; u32 act; @@ -2231,6 +2231,13 @@ static struct sk_buff *ixgbe_run_xdp(struct ixgbe_adapter *adapter, case XDP_TX: result = ixgbe_xmit_xdp_ring(adapter, xdp); break; + case XDP_REDIRECT: + err = xdp_do_redirect(adapter->netdev, xdp); + if (!err) + result = IXGBE_XDP_TX; + else + result = IXGBE_XDP_CONSUMED; + break; default: bpf_warn_invalid_xdp_action(act); /* fallthrough */ @@ -9823,6 +9830,37 @@ static int ixgbe_xdp(struct net_device *dev, struct netdev_xdp *xdp) } } +static int ixgbe_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct ixgbe_ring *ring; + int err; + + if (unlikely(test_bit(__IXGBE_DOWN, &adapter->state))) + return -EINVAL; + + /* During program transitions its possible adapter->xdp_prog is assigned + * but ring has not been configured yet. In this case simply abort xmit. + */ + ring = adapter->xdp_prog ? adapter->xdp_ring[smp_processor_id()] : NULL; + if (unlikely(!ring)) + return -EINVAL; + + err = ixgbe_xmit_xdp_ring(adapter, xdp); + if (err != IXGBE_XDP_TX) + return -ENOMEM; + + /* Force memory writes to complete before letting h/w know there + * are new descriptors to fetch. + */ + wmb(); + + ring = adapter->xdp_ring[smp_processor_id()]; + writel(ring->next_to_use, ring->tail); + + return 0; +} + static const struct net_device_ops ixgbe_netdev_ops = { .ndo_open = ixgbe_open, .ndo_stop = ixgbe_close, @@ -9869,6 +9907,7 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_udp_tunnel_del = ixgbe_del_udp_tunnel_port, .ndo_features_check = ixgbe_features_check, .ndo_xdp = ixgbe_xdp, + .ndo_xdp_xmit = ixgbe_xdp_xmit, }; /** -- cgit v1.2.3 From 5acaee0a8964c9bab7775ab8bedcd1f66a2a1011 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:28:35 -0700 Subject: xdp: add trace event for xdp redirect This adds a trace event for xdp redirect which may help when debugging XDP programs that use redirect bpf commands. Signed-off-by: John Fastabend Acked-by: Daniel Borkmann Acked-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- include/linux/filter.h | 4 +++- include/trace/events/xdp.h | 31 ++++++++++++++++++++++++++- net/core/filter.c | 13 +++++++---- 4 files changed, 43 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 3db04736a048..38f7ff97d636 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2232,7 +2232,7 @@ static struct sk_buff *ixgbe_run_xdp(struct ixgbe_adapter *adapter, result = ixgbe_xmit_xdp_ring(adapter, xdp); break; case XDP_REDIRECT: - err = xdp_do_redirect(adapter->netdev, xdp); + err = xdp_do_redirect(adapter->netdev, xdp, xdp_prog); if (!err) result = IXGBE_XDP_TX; else diff --git a/include/linux/filter.h b/include/linux/filter.h index 10df7daf5ec6..ce8211fa91c7 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -713,7 +713,9 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, const struct bpf_insn *patch, u32 len); int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb); -int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp); +int xdp_do_redirect(struct net_device *dev, + struct xdp_buff *xdp, + struct bpf_prog *prog); void bpf_warn_invalid_xdp_action(u32 act); void bpf_warn_invalid_xdp_redirect(u32 ifindex); diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index 1b61357d3f57..7b1eb7b4be41 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -12,7 +12,8 @@ FN(ABORTED) \ FN(DROP) \ FN(PASS) \ - FN(TX) + FN(TX) \ + FN(REDIRECT) #define __XDP_ACT_TP_FN(x) \ TRACE_DEFINE_ENUM(XDP_##x); @@ -48,6 +49,34 @@ TRACE_EVENT(xdp_exception, __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB)) ); +TRACE_EVENT(xdp_redirect, + + TP_PROTO(const struct net_device *from, + const struct net_device *to, + const struct bpf_prog *xdp, u32 act), + + TP_ARGS(from, to, xdp, act), + + TP_STRUCT__entry( + __string(name_from, from->name) + __string(name_to, to->name) + __array(u8, prog_tag, 8) + __field(u32, act) + ), + + TP_fast_assign( + BUILD_BUG_ON(sizeof(__entry->prog_tag) != sizeof(xdp->tag)); + memcpy(__entry->prog_tag, xdp->tag, sizeof(xdp->tag)); + __assign_str(name_from, from->name); + __assign_str(name_to, to->name); + __entry->act = act; + ), + + TP_printk("prog=%s from=%s to=%s action=%s", + __print_hex_str(__entry->prog_tag, 8), + __get_str(name_from), __get_str(name_to), + __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB)) +); #endif /* _TRACE_XDP_H */ #include diff --git a/net/core/filter.c b/net/core/filter.c index eeb713461c25..e30d38b27f21 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -55,6 +55,7 @@ #include #include #include +#include /** * sk_filter_trim_cap - run a packet through a socket filter @@ -2422,18 +2423,22 @@ static int __bpf_tx_xdp(struct net_device *dev, struct xdp_buff *xdp) return -EOPNOTSUPP; } -int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp) +int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, + struct bpf_prog *xdp_prog) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); + struct net_device *fwd; - dev = dev_get_by_index_rcu(dev_net(dev), ri->ifindex); + fwd = dev_get_by_index_rcu(dev_net(dev), ri->ifindex); ri->ifindex = 0; - if (unlikely(!dev)) { + if (unlikely(!fwd)) { bpf_warn_invalid_xdp_redirect(ri->ifindex); return -EINVAL; } - return __bpf_tx_xdp(dev, xdp); + trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); + + return __bpf_tx_xdp(fwd, xdp); } EXPORT_SYMBOL_GPL(xdp_do_redirect); -- cgit v1.2.3 From 11393cc9b9be2a1f61559e6fb9c27bc8fa20b1ff Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:29:40 -0700 Subject: xdp: Add batching support to redirect map For performance reasons we want to avoid updating the tail pointer in the driver tx ring as much as possible. To accomplish this we add batching support to the redirect path in XDP. This adds another ndo op "xdp_flush" that is used to inform the driver that it should bump the tail pointer on the TX ring. Signed-off-by: John Fastabend Signed-off-by: Jesper Dangaard Brouer Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 28 ++++++++- include/linux/bpf.h | 2 + include/linux/filter.h | 7 +++ include/linux/netdevice.h | 5 +- kernel/bpf/devmap.c | 84 ++++++++++++++++++++++++++- net/core/filter.c | 55 ++++++++++++++---- 6 files changed, 166 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 38f7ff97d636..0f867dcda65f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2415,6 +2415,8 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, */ wmb(); writel(ring->next_to_use, ring->tail); + + xdp_do_flush_map(); } u64_stats_update_begin(&rx_ring->syncp); @@ -5817,6 +5819,9 @@ void ixgbe_down(struct ixgbe_adapter *adapter) usleep_range(10000, 20000); + /* synchronize_sched() needed for pending XDP buffers to drain */ + if (adapter->xdp_ring[0]) + synchronize_sched(); netif_tx_stop_all_queues(netdev); /* call carrier off first to avoid false dev_watchdog timeouts */ @@ -9850,15 +9855,31 @@ static int ixgbe_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp) if (err != IXGBE_XDP_TX) return -ENOMEM; + return 0; +} + +static void ixgbe_xdp_flush(struct net_device *dev) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct ixgbe_ring *ring; + + /* Its possible the device went down between xdp xmit and flush so + * we need to ensure device is still up. + */ + if (unlikely(test_bit(__IXGBE_DOWN, &adapter->state))) + return; + + ring = adapter->xdp_prog ? adapter->xdp_ring[smp_processor_id()] : NULL; + if (unlikely(!ring)) + return; + /* Force memory writes to complete before letting h/w know there * are new descriptors to fetch. */ wmb(); - - ring = adapter->xdp_ring[smp_processor_id()]; writel(ring->next_to_use, ring->tail); - return 0; + return; } static const struct net_device_ops ixgbe_netdev_ops = { @@ -9908,6 +9929,7 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_features_check = ixgbe_features_check, .ndo_xdp = ixgbe_xdp, .ndo_xdp_xmit = ixgbe_xdp_xmit, + .ndo_xdp_flush = ixgbe_xdp_flush, }; /** diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d0d3281ac678..6850a760dc94 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -381,5 +381,7 @@ u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); /* Map specifics */ struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key); +void __dev_map_insert_ctx(struct bpf_map *map, u32 index); +void __dev_map_flush(struct bpf_map *map); #endif /* _LINUX_BPF_H */ diff --git a/include/linux/filter.h b/include/linux/filter.h index ce8211fa91c7..3323ee91c172 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -712,10 +712,17 @@ bool bpf_helper_changes_pkt_data(void *func); struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, const struct bpf_insn *patch, u32 len); +/* The pair of xdp_do_redirect and xdp_do_flush_map MUST be called in the + * same cpu context. Further for best results no more than a single map + * for the do_redirect/do_flush pair should be used. This limitation is + * because we only track one map and force a flush when the map changes. + * This does not appear to be a real limiation for existing software. + */ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb); int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, struct bpf_prog *prog); +void xdp_do_flush_map(void); void bpf_warn_invalid_xdp_action(u32 act); void bpf_warn_invalid_xdp_redirect(u32 ifindex); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 77f5376005e6..03b104908235 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1142,7 +1142,9 @@ struct xfrmdev_ops { * int (*ndo_xdp_xmit)(struct net_device *dev, struct xdp_buff *xdp); * This function is used to submit a XDP packet for transmit on a * netdevice. - * + * void (*ndo_xdp_flush)(struct net_device *dev); + * This function is used to inform the driver to flush a paticular + * xpd tx queue. Must be called on same CPU as xdp_xmit. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1329,6 +1331,7 @@ struct net_device_ops { struct netdev_xdp *xdp); int (*ndo_xdp_xmit)(struct net_device *dev, struct xdp_buff *xdp); + void (*ndo_xdp_flush)(struct net_device *dev); }; /** diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 36dc13deb2e1..b2ef04a1c86a 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -53,6 +53,7 @@ struct bpf_dtab_netdev { struct bpf_dtab { struct bpf_map map; struct bpf_dtab_netdev **netdev_map; + unsigned long int __percpu *flush_needed; }; static struct bpf_map *dev_map_alloc(union bpf_attr *attr) @@ -87,6 +88,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) /* make sure page count doesn't overflow */ cost = (u64) dtab->map.max_entries * sizeof(struct bpf_dtab_netdev *); + cost += BITS_TO_LONGS(attr->max_entries) * sizeof(unsigned long); if (cost >= U32_MAX - PAGE_SIZE) goto free_dtab; @@ -97,6 +99,14 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) if (err) goto free_dtab; + /* A per cpu bitfield with a bit per possible net device */ + dtab->flush_needed = __alloc_percpu( + BITS_TO_LONGS(attr->max_entries) * + sizeof(unsigned long), + __alignof__(unsigned long)); + if (!dtab->flush_needed) + goto free_dtab; + dtab->netdev_map = bpf_map_area_alloc(dtab->map.max_entries * sizeof(struct bpf_dtab_netdev *)); if (!dtab->netdev_map) @@ -105,6 +115,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) return &dtab->map; free_dtab: + free_percpu(dtab->flush_needed); kfree(dtab); return ERR_PTR(err); } @@ -112,7 +123,7 @@ free_dtab: static void dev_map_free(struct bpf_map *map) { struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); - int i; + int i, cpu; /* At this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, * so the programs (can be more than one that used this map) were @@ -123,6 +134,18 @@ static void dev_map_free(struct bpf_map *map) */ synchronize_rcu(); + /* To ensure all pending flush operations have completed wait for flush + * bitmap to indicate all flush_needed bits to be zero on _all_ cpus. + * Because the above synchronize_rcu() ensures the map is disconnected + * from the program we can assume no new bits will be set. + */ + for_each_online_cpu(cpu) { + unsigned long *bitmap = per_cpu_ptr(dtab->flush_needed, cpu); + + while (!bitmap_empty(bitmap, dtab->map.max_entries)) + cpu_relax(); + } + for (i = 0; i < dtab->map.max_entries; i++) { struct bpf_dtab_netdev *dev; @@ -137,6 +160,7 @@ static void dev_map_free(struct bpf_map *map) /* At this point bpf program is detached and all pending operations * _must_ be complete */ + free_percpu(dtab->flush_needed); bpf_map_area_free(dtab->netdev_map); kfree(dtab); } @@ -159,6 +183,14 @@ static int dev_map_get_next_key(struct bpf_map *map, void *key, void *next_key) return 0; } +void __dev_map_insert_ctx(struct bpf_map *map, u32 key) +{ + struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); + unsigned long *bitmap = this_cpu_ptr(dtab->flush_needed); + + __set_bit(key, bitmap); +} + struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key) { struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); @@ -171,6 +203,39 @@ struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key) return dev ? dev->dev : NULL; } +/* __dev_map_flush is called from xdp_do_flush_map() which _must_ be signaled + * from the driver before returning from its napi->poll() routine. The poll() + * routine is called either from busy_poll context or net_rx_action signaled + * from NET_RX_SOFTIRQ. Either way the poll routine must complete before the + * net device can be torn down. On devmap tear down we ensure the ctx bitmap + * is zeroed before completing to ensure all flush operations have completed. + */ +void __dev_map_flush(struct bpf_map *map) +{ + struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); + unsigned long *bitmap = this_cpu_ptr(dtab->flush_needed); + u32 bit; + + for_each_set_bit(bit, bitmap, map->max_entries) { + struct bpf_dtab_netdev *dev = READ_ONCE(dtab->netdev_map[bit]); + struct net_device *netdev; + + /* This is possible if the dev entry is removed by user space + * between xdp redirect and flush op. + */ + if (unlikely(!dev)) + continue; + + netdev = dev->dev; + + __clear_bit(bit, bitmap); + if (unlikely(!netdev || !netdev->netdev_ops->ndo_xdp_flush)) + continue; + + netdev->netdev_ops->ndo_xdp_flush(netdev); + } +} + /* rcu_read_lock (from syscall and BPF contexts) ensures that if a delete and/or * update happens in parallel here a dev_put wont happen until after reading the * ifindex. @@ -188,11 +253,28 @@ static void *dev_map_lookup_elem(struct bpf_map *map, void *key) return dev ? &dev->dev->ifindex : NULL; } +static void dev_map_flush_old(struct bpf_dtab_netdev *old_dev) +{ + if (old_dev->dev->netdev_ops->ndo_xdp_flush) { + struct net_device *fl = old_dev->dev; + unsigned long *bitmap; + int cpu; + + for_each_online_cpu(cpu) { + bitmap = per_cpu_ptr(old_dev->dtab->flush_needed, cpu); + __clear_bit(old_dev->key, bitmap); + + fl->netdev_ops->ndo_xdp_flush(old_dev->dev); + } + } +} + static void __dev_map_entry_free(struct rcu_head *rcu) { struct bpf_dtab_netdev *old_dev; old_dev = container_of(rcu, struct bpf_dtab_netdev, rcu); + dev_map_flush_old(old_dev); dev_put(old_dev->dev); kfree(old_dev); } diff --git a/net/core/filter.c b/net/core/filter.c index e93a558324b5..e23aa6fa1119 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1780,6 +1780,7 @@ struct redirect_info { u32 ifindex; u32 flags; struct bpf_map *map; + struct bpf_map *map_to_flush; }; static DEFINE_PER_CPU(struct redirect_info, redirect_info); @@ -2438,34 +2439,68 @@ static const struct bpf_func_proto bpf_xdp_adjust_head_proto = { .arg2_type = ARG_ANYTHING, }; -static int __bpf_tx_xdp(struct net_device *dev, struct xdp_buff *xdp) +static int __bpf_tx_xdp(struct net_device *dev, + struct bpf_map *map, + struct xdp_buff *xdp, + u32 index) { - if (dev->netdev_ops->ndo_xdp_xmit) { - dev->netdev_ops->ndo_xdp_xmit(dev, xdp); - return 0; + int err; + + if (!dev->netdev_ops->ndo_xdp_xmit) { + bpf_warn_invalid_xdp_redirect(dev->ifindex); + return -EOPNOTSUPP; } - bpf_warn_invalid_xdp_redirect(dev->ifindex); - return -EOPNOTSUPP; + + err = dev->netdev_ops->ndo_xdp_xmit(dev, xdp); + if (err) + return err; + + if (map) + __dev_map_insert_ctx(map, index); + else + dev->netdev_ops->ndo_xdp_flush(dev); + + return err; } +void xdp_do_flush_map(void) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + struct bpf_map *map = ri->map_to_flush; + + ri->map = NULL; + ri->map_to_flush = NULL; + + if (map) + __dev_map_flush(map); +} +EXPORT_SYMBOL_GPL(xdp_do_flush_map); + int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, struct bpf_prog *xdp_prog) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); struct bpf_map *map = ri->map; + u32 index = ri->ifindex; struct net_device *fwd; int err = -EINVAL; ri->ifindex = 0; ri->map = NULL; - fwd = __dev_map_lookup_elem(map, ri->ifindex); + fwd = __dev_map_lookup_elem(map, index); if (!fwd) goto out; - trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); - err = __bpf_tx_xdp(fwd, xdp); + if (ri->map_to_flush && (ri->map_to_flush != map)) + xdp_do_flush_map(); + + err = __bpf_tx_xdp(fwd, map, xdp, index); + if (likely(!err)) + ri->map_to_flush = map; + out: + trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); return err; } @@ -2488,7 +2523,7 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); - return __bpf_tx_xdp(fwd, xdp); + return __bpf_tx_xdp(fwd, NULL, xdp, 0); } EXPORT_SYMBOL_GPL(xdp_do_redirect); -- cgit v1.2.3 From ed22e2f6b72de9dc2a2fe43cd553cf4d36f70785 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 06:28:56 -0700 Subject: s2io: Remove UFO support. Signed-off-by: David S. Miller --- drivers/net/ethernet/neterion/s2io.c | 45 ------------------------------------ 1 file changed, 45 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index fd2ec36c6fa1..462eda926b1c 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -42,8 +42,6 @@ * aggregated as a single large packet * napi: This parameter used to enable/disable NAPI (polling Rx) * Possible values '1' for enable and '0' for disable. Default is '1' - * ufo: This parameter used to enable/disable UDP Fragmentation Offload(UFO) - * Possible values '1' for enable and '0' for disable. Default is '0' * vlan_tag_strip: This can be used to enable or disable vlan stripping. * Possible values '1' for enable , '0' for disable. * Default is '2' - which means disable in promisc mode @@ -453,7 +451,6 @@ S2IO_PARM_INT(lro_max_pkts, 0xFFFF); S2IO_PARM_INT(indicate_max_pkts, 0); S2IO_PARM_INT(napi, 1); -S2IO_PARM_INT(ufo, 0); S2IO_PARM_INT(vlan_tag_strip, NO_STRIP_IN_PROMISC); static unsigned int tx_fifo_len[MAX_TX_FIFOS] = @@ -4128,32 +4125,6 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev) } frg_len = skb_headlen(skb); - if (offload_type == SKB_GSO_UDP) { - int ufo_size; - - ufo_size = s2io_udp_mss(skb); - ufo_size &= ~7; - txdp->Control_1 |= TXD_UFO_EN; - txdp->Control_1 |= TXD_UFO_MSS(ufo_size); - txdp->Control_1 |= TXD_BUFFER0_SIZE(8); -#ifdef __BIG_ENDIAN - /* both variants do cpu_to_be64(be32_to_cpu(...)) */ - fifo->ufo_in_band_v[put_off] = - (__force u64)skb_shinfo(skb)->ip6_frag_id; -#else - fifo->ufo_in_band_v[put_off] = - (__force u64)skb_shinfo(skb)->ip6_frag_id << 32; -#endif - txdp->Host_Control = (unsigned long)fifo->ufo_in_band_v; - txdp->Buffer_Pointer = pci_map_single(sp->pdev, - fifo->ufo_in_band_v, - sizeof(u64), - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) - goto pci_map_failed; - txdp++; - } - txdp->Buffer_Pointer = pci_map_single(sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE); if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) @@ -4161,8 +4132,6 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev) txdp->Host_Control = (unsigned long)skb; txdp->Control_1 |= TXD_BUFFER0_SIZE(frg_len); - if (offload_type == SKB_GSO_UDP) - txdp->Control_1 |= TXD_UFO_EN; frg_cnt = skb_shinfo(skb)->nr_frags; /* For fragmented SKB. */ @@ -4177,14 +4146,9 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev) skb_frag_size(frag), DMA_TO_DEVICE); txdp->Control_1 = TXD_BUFFER0_SIZE(skb_frag_size(frag)); - if (offload_type == SKB_GSO_UDP) - txdp->Control_1 |= TXD_UFO_EN; } txdp->Control_1 |= TXD_GATHER_CODE_LAST; - if (offload_type == SKB_GSO_UDP) - frg_cnt++; /* as Txd0 was used for inband header */ - tx_fifo = mac_control->tx_FIFO_start[queue]; val64 = fifo->list_info[put_off].list_phy_addr; writeq(val64, &tx_fifo->TxDL_Pointer); @@ -7910,11 +7874,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) NETIF_F_RXCSUM | NETIF_F_LRO; dev->features |= dev->hw_features | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; - if (sp->device_type & XFRAME_II_DEVICE) { - dev->hw_features |= NETIF_F_UFO; - if (ufo) - dev->features |= NETIF_F_UFO; - } if (sp->high_dma_flag == true) dev->features |= NETIF_F_HIGHDMA; dev->watchdog_timeo = WATCH_DOG_TIMEOUT; @@ -8147,10 +8106,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) DBG_PRINT(ERR_DBG, "%s: Large receive offload enabled\n", dev->name); - if (ufo) - DBG_PRINT(ERR_DBG, - "%s: UDP Fragmentation Offload(UFO) enabled\n", - dev->name); /* Initialize device name */ snprintf(sp->name, sizeof(sp->name), "%s Neterion %s", dev->name, sp->product_name); -- cgit v1.2.3 From f9c45ae020bab86a820d8ef9097d021d5496b855 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 06:31:05 -0700 Subject: macb: Remove bogus reference to NETIF_F_UFO. This driver doesn't actually support UFO explicitly yet it advertises this in netdev->features. Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 26d25749c3e4..6df2cad61647 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -68,7 +68,7 @@ #define GEM_MAX_TX_LEN ((unsigned int)((1 << GEM_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1))) #define GEM_MTU_MIN_SIZE ETH_MIN_MTU -#define MACB_NETIF_LSO (NETIF_F_TSO | NETIF_F_UFO) +#define MACB_NETIF_LSO NETIF_F_TSO #define MACB_WOL_HAS_MAGIC_PACKET (0x1 << 0) #define MACB_WOL_ENABLED (0x1 << 1) -- cgit v1.2.3 From 182e0b6b58463b85f9a34dd038847e4ab3604a4f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 06:32:14 -0700 Subject: ipvlan: Stop advertising NETIF_F_UFO support. It is going away. Signed-off-by: David S. Miller --- drivers/net/ipvlan/ipvlan_main.c | 2 +- drivers/net/ipvlan/ipvtap.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index f37e3c1fd4e7..fdde20735416 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -169,7 +169,7 @@ static void ipvlan_port_destroy(struct net_device *dev) #define IPVLAN_FEATURES \ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \ - NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_GSO_ROBUST | \ + NETIF_F_GSO | NETIF_F_TSO | NETIF_F_GSO_ROBUST | \ NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \ NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER) diff --git a/drivers/net/ipvlan/ipvtap.c b/drivers/net/ipvlan/ipvtap.c index 22f133ea8d7b..5dea2063dbc8 100644 --- a/drivers/net/ipvlan/ipvtap.c +++ b/drivers/net/ipvlan/ipvtap.c @@ -24,7 +24,7 @@ #include #define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \ - NETIF_F_TSO6 | NETIF_F_UFO) + NETIF_F_TSO6) static dev_t ipvtap_major; static struct cdev ipvtap_cdev; -- cgit v1.2.3 From fb652fdfe83710da0ca13448a41b7ed027d0a984 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 06:33:08 -0700 Subject: macvlan/macvtap: Remove NETIF_F_UFO advertisement. It is going away. Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 2 +- drivers/net/macvtap.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 0f581ee74fe4..ca35c6ba7947 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -841,7 +841,7 @@ static struct lock_class_key macvlan_netdev_addr_lock_key; #define MACVLAN_FEATURES \ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \ - NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_LRO | \ + NETIF_F_GSO | NETIF_F_TSO | NETIF_F_LRO | \ NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \ NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 91e7b19bbf86..c2d0ea2fb019 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -49,7 +49,7 @@ static struct class macvtap_class = { static struct cdev macvtap_cdev; #define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \ - NETIF_F_TSO6 | NETIF_F_UFO) + NETIF_F_TSO6) static void macvtap_count_tx_dropped(struct tap_dev *tap) { -- cgit v1.2.3 From d591a1f3aad92ade4642e4173f4c368006c27f0f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 06:35:32 -0700 Subject: tun/tap: Remove references to NETIF_F_UFO. It is going away. Signed-off-by: David S. Miller --- drivers/net/tap.c | 7 ++----- drivers/net/tun.c | 7 +------ 2 files changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 3570c7576993..ca267fd28ab8 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -943,9 +943,6 @@ static int set_offload(struct tap_queue *q, unsigned long arg) if (arg & TUN_F_TSO6) feature_mask |= NETIF_F_TSO6; } - - if (arg & TUN_F_UFO) - feature_mask |= NETIF_F_UFO; } /* tun/tap driver inverts the usage for TSO offloads, where @@ -956,7 +953,7 @@ static int set_offload(struct tap_queue *q, unsigned long arg) * When user space turns off TSO, we turn off GSO/LRO so that * user-space will not receive TSO frames. */ - if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO)) + if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6)) features |= RX_OFFLOADS; else features &= ~RX_OFFLOADS; @@ -1078,7 +1075,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd, case TUNSETOFFLOAD: /* let the user check for future flags */ if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | - TUN_F_TSO_ECN | TUN_F_UFO)) + TUN_F_TSO_ECN)) return -EINVAL; rtnl_lock(); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3d4c24572ecd..a93392d7a340 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -199,7 +199,7 @@ struct tun_struct { struct net_device *dev; netdev_features_t set_features; #define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \ - NETIF_F_TSO6|NETIF_F_UFO) + NETIF_F_TSO6) int align; int vnet_hdr_sz; @@ -1921,11 +1921,6 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) features |= NETIF_F_TSO6; arg &= ~(TUN_F_TSO4|TUN_F_TSO6); } - - if (arg & TUN_F_UFO) { - features |= NETIF_F_UFO; - arg &= ~TUN_F_UFO; - } } /* This gives the user a way to test for new features in future by -- cgit v1.2.3 From 2082499a95ad31b88466e50f4c61513e3873ba9e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 06:36:07 -0700 Subject: dummy: Remove references to NETIF_F_UFO. It is going away. Signed-off-by: David S. Miller --- drivers/net/dummy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index d0c165d2086e..d0a1f9ce3168 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -345,7 +345,7 @@ static void dummy_setup(struct net_device *dev) dev->flags &= ~IFF_MULTICAST; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST; - dev->features |= NETIF_F_ALL_TSO | NETIF_F_UFO; + dev->features |= NETIF_F_ALL_TSO; dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX; dev->features |= NETIF_F_GSO_ENCAP_ALL; dev->hw_features |= dev->features; -- cgit v1.2.3 From e078de03788353b220f3d501fc3607cc92db28c1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 06:37:32 -0700 Subject: virtio_net: Remove references to NETIF_F_UFO. It is going away. Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 99a26a9efec1..99830167ea2f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2429,7 +2429,7 @@ static int virtnet_probe(struct virtio_device *vdev) dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG; if (virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) { - dev->hw_features |= NETIF_F_TSO | NETIF_F_UFO + dev->hw_features |= NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6; } /* Individual feature bits: what can host handle? */ @@ -2439,13 +2439,11 @@ static int virtnet_probe(struct virtio_device *vdev) dev->hw_features |= NETIF_F_TSO6; if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN)) dev->hw_features |= NETIF_F_TSO_ECN; - if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO)) - dev->hw_features |= NETIF_F_UFO; dev->features |= NETIF_F_GSO_ROBUST; if (gso) - dev->features |= dev->hw_features & (NETIF_F_ALL_TSO|NETIF_F_UFO); + dev->features |= dev->hw_features & NETIF_F_ALL_TSO; /* (!csum && gso) case will be fixed by register_netdev() */ } if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_CSUM)) -- cgit v1.2.3 From c744cf5b9dea1da57a6fdb314c8b403cb22075c0 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:41:52 +0530 Subject: net: cadence: macb: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 791 336 0 1127 467 net/ethernet/cadence/macb_pci.o File size After adding 'const': text data bss dec hex filename 855 272 0 1127 467 net/ethernet/cadence/macb_pci.o Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c index 9906fda76087..248a8fc45069 100644 --- a/drivers/net/ethernet/cadence/macb_pci.c +++ b/drivers/net/ethernet/cadence/macb_pci.c @@ -128,7 +128,7 @@ static void macb_remove(struct pci_dev *pdev) clk_unregister(plat_data->hclk); } -static struct pci_device_id dev_id_table[] = { +static const struct pci_device_id dev_id_table[] = { { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), }, { 0, } }; -- cgit v1.2.3 From 7924a42133324d1cbec1c217eca598712707bd03 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:42:34 +0530 Subject: net: ec_bhf: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 5113 384 0 5497 1579 drivers/net/ethernet/ec_bhf.o File size After adding 'const': text data bss dec hex filename 5177 320 0 5497 1579 drivers/net/ethernet/ec_bhf.o Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/ec_bhf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ec_bhf.c b/drivers/net/ethernet/ec_bhf.c index 4ee042c034a1..1b79a6defd56 100644 --- a/drivers/net/ethernet/ec_bhf.c +++ b/drivers/net/ethernet/ec_bhf.c @@ -73,7 +73,7 @@ #define ETHERCAT_MASTER_ID 0x14 -static struct pci_device_id ids[] = { +static const struct pci_device_id ids[] = { { PCI_DEVICE(0x15ec, 0x5000), }, { 0, } }; -- cgit v1.2.3 From 95b80bf3db03c2bf572a357cf74b9a6aefef0a4a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 17 Jul 2017 18:09:09 -0300 Subject: mdio_bus: Remove unneeded gpiod NULL check The gpiod API checks for NULL descriptors, so there is no need to duplicate the check in the driver. Signed-off-by: Fabio Estevam Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 2df7b62c1a36..b6f9fa670168 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -399,8 +399,7 @@ error: } /* Put PHYs in RESET to save power */ - if (bus->reset_gpiod) - gpiod_set_value_cansleep(bus->reset_gpiod, 1); + gpiod_set_value_cansleep(bus->reset_gpiod, 1); device_del(&bus->dev); return err; @@ -425,8 +424,7 @@ void mdiobus_unregister(struct mii_bus *bus) } /* Put PHYs in RESET to save power */ - if (bus->reset_gpiod) - gpiod_set_value_cansleep(bus->reset_gpiod, 1); + gpiod_set_value_cansleep(bus->reset_gpiod, 1); device_del(&bus->dev); } -- cgit v1.2.3 From 7b23268c9d81a3603ccd9e1bde64e2a1cfe548b7 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:35 -0400 Subject: net: dsa: mv88e6xxx: remove unneeded dsa header phy.c does not need to include the DSA public header. Remove it. Signed-off-by: Vivien Didelot Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/phy.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/phy.c b/drivers/net/dsa/mv88e6xxx/phy.c index 3500ac0ea848..436668bd50dc 100644 --- a/drivers/net/dsa/mv88e6xxx/phy.c +++ b/drivers/net/dsa/mv88e6xxx/phy.c @@ -13,7 +13,6 @@ #include #include -#include #include "chip.h" #include "phy.h" -- cgit v1.2.3 From c56a71a92114e3198e249593841cb744abaadcb7 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:36 -0400 Subject: net: dsa: mv88e6xxx: remove LED control register We don't support LED control yet, remove its register definition. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/port.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 8f3991bf1851..b16d5f0e6e9c 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -216,9 +216,6 @@ /* Offset 0x13: OutFiltered Counter */ #define MV88E6XXX_PORT_OUT_FILTERED 0x13 -/* Offset 0x16: LED Control */ -#define MV88E6XXX_PORT_LED_CONTROL 0x16 - /* Offset 0x18: IEEE Priority Mapping Table */ #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18 #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000 -- cgit v1.2.3 From bd80720468c46f4c712aaf0f3b319ec1440e0cf9 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:37 -0400 Subject: net: dsa: mv88e6xxx: fix 88E6321 family comment MV88E6XXX_FAMILY_6321 is undefined, 88E6321's family is 88E6320, fix this. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 53b088166c28..51f2797ecb52 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2959,7 +2959,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { }; static const struct mv88e6xxx_ops mv88e6321_ops = { - /* MV88E6XXX_FAMILY_6321 */ + /* MV88E6XXX_FAMILY_6320 */ .irl_init_all = mv88e6352_g2_irl_init_all, .get_eeprom = mv88e6xxx_g2_get_eeprom16, .set_eeprom = mv88e6xxx_g2_set_eeprom16, -- cgit v1.2.3 From 2466f64ae4e9d0bb80ffd73e5529911a535d6bc4 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:38 -0400 Subject: net: dsa: mv88e6xxx: remove unused capabilities Remove the forgotten capabilities and related flags from previous cleanups. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.h | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 086444016352..9ccf5d03346a 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -109,11 +109,6 @@ enum mv88e6xxx_cap { MV88E6XXX_CAP_SMI_CMD, /* (0x00) SMI Command */ MV88E6XXX_CAP_SMI_DATA, /* (0x01) SMI Data */ - /* Switch Global (1) Registers. - */ - MV88E6XXX_CAP_G1_ATU_FID, /* (0x01) ATU FID Register */ - MV88E6XXX_CAP_G1_VTU_FID, /* (0x02) VTU FID Register */ - /* Switch Global 2 Registers. * The device contains a second set of global 16-bit registers. */ @@ -122,17 +117,6 @@ enum mv88e6xxx_cap { MV88E6XXX_CAP_G2_MGMT_EN_2X, /* (0x02) MGMT Enable Register 2x */ MV88E6XXX_CAP_G2_MGMT_EN_0X, /* (0x03) MGMT Enable Register 0x */ MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */ - - /* Per VLAN Spanning Tree Unit (STU). - * The Port State database, if present, is accessed through VTU - * operations and dedicated SID registers. See MV88E6352_G1_VTU_SID. - */ - MV88E6XXX_CAP_STU, - - /* VLAN Table Unit. - * The VTU is used to program 802.1Q VLANs. See MV88E6XXX_G1_VTU_OP. - */ - MV88E6XXX_CAP_VTU, }; /* Bitmask of capabilities */ @@ -141,8 +125,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_SMI_CMD BIT_ULL(MV88E6XXX_CAP_SMI_CMD) #define MV88E6XXX_FLAG_SMI_DATA BIT_ULL(MV88E6XXX_CAP_SMI_DATA) -#define MV88E6XXX_FLAG_G1_VTU_FID BIT_ULL(MV88E6XXX_CAP_G1_VTU_FID) - #define MV88E6XXX_FLAG_GLOBAL2 BIT_ULL(MV88E6XXX_CAP_GLOBAL2) #define MV88E6XXX_FLAG_G2_INT BIT_ULL(MV88E6XXX_CAP_G2_INT) #define MV88E6XXX_FLAG_G2_MGMT_EN_2X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_2X) @@ -160,8 +142,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6097 \ - (MV88E6XXX_FLAG_G1_VTU_FID | \ - MV88E6XXX_FLAG_GLOBAL2 | \ + (MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ @@ -169,8 +150,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6165 \ - (MV88E6XXX_FLAG_G1_VTU_FID | \ - MV88E6XXX_FLAG_GLOBAL2 | \ + (MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ @@ -193,15 +173,13 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6341 \ (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6351 \ - (MV88E6XXX_FLAG_G1_VTU_FID | \ - MV88E6XXX_FLAG_GLOBAL2 | \ + (MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ @@ -210,7 +188,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6352 \ (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ -- cgit v1.2.3 From 74e60241ce140ac8c312cde1dc823681c6a86f99 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:39 -0400 Subject: net: dsa: mv88e6xxx: remove 88E6185 G2 interrupt The 88E6185 family has no Global 2 Interrupt Source or Mask registers. Remove the MV88E6XXX_FLAG_G2_INT from MV88E6XXX_FLAGS_FAMILY_6185. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 9ccf5d03346a..8eab123f0fed 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -159,7 +159,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6185 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAGS_MULTI_CHIP) -- cgit v1.2.3 From d6c5e6aff50cadeab0b7e381ce50be836ae55097 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:40 -0400 Subject: net: dsa: mv88e6xxx: add number of Global 2 IRQs Similarly to g1_irqs, add a g2_irqs member to the info structure to indicates the presence of the Global 2 Interrupt Source and Mask registers. At the same time, provide helpers and document the registers since they differ a bit between 88E6352 and 88E6390 families. This allows us to get rid of the MV88E6XXX_FLAG_G2_INT flag. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 27 ++++++++++++++++++++++++--- drivers/net/dsa/mv88e6xxx/chip.h | 9 +-------- drivers/net/dsa/mv88e6xxx/global2.c | 22 ++++++++++++++++++++-- drivers/net/dsa/mv88e6xxx/global2.h | 19 +++++++++++++++++-- 4 files changed, 62 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 51f2797ecb52..1be0bc5e7c3f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3207,6 +3207,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, @@ -3242,6 +3243,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3260,6 +3262,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3295,6 +3298,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .atu_move_port_mask = 0x1f, + .g2_irqs = 10, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6341, @@ -3312,6 +3316,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3330,6 +3335,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, @@ -3348,6 +3354,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3366,6 +3373,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3384,6 +3392,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3402,6 +3411,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3438,6 +3448,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .tag_protocol = DSA_TAG_PROTO_DSA, .age_time_coeff = 3750, .g1_irqs = 9, + .g2_irqs = 14, .pvt = true, .atu_move_port_mask = 0x1f, .flags = MV88E6XXX_FLAGS_FAMILY_6390, @@ -3455,6 +3466,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, @@ -3473,6 +3485,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, @@ -3491,6 +3504,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3509,6 +3523,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, @@ -3562,6 +3577,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .atu_move_port_mask = 0x1f, + .g2_irqs = 10, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6341, @@ -3579,6 +3595,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3597,6 +3614,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3615,6 +3633,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, @@ -3632,6 +3651,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, @@ -3649,6 +3669,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, @@ -3970,7 +3991,7 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) if (err) goto out; - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) { + if (chip->info->g2_irqs > 0) { err = mv88e6xxx_g2_irq_setup(chip); if (err) goto out_g1_irq; @@ -3990,7 +4011,7 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) out_mdio: mv88e6xxx_mdios_unregister(chip); out_g2_irq: - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0) + if (chip->info->g2_irqs > 0 && chip->irq > 0) mv88e6xxx_g2_irq_free(chip); out_g1_irq: if (chip->irq > 0) { @@ -4012,7 +4033,7 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) mv88e6xxx_mdios_unregister(chip); if (chip->irq > 0) { - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) + if (chip->info->g2_irqs > 0) mv88e6xxx_g2_irq_free(chip); mv88e6xxx_g1_irq_free(chip); } diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 8eab123f0fed..2e760fd0ad24 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -113,7 +113,6 @@ enum mv88e6xxx_cap { * The device contains a second set of global 16-bit registers. */ MV88E6XXX_CAP_GLOBAL2, - MV88E6XXX_CAP_G2_INT, /* (0x00) Interrupt Status */ MV88E6XXX_CAP_G2_MGMT_EN_2X, /* (0x02) MGMT Enable Register 2x */ MV88E6XXX_CAP_G2_MGMT_EN_0X, /* (0x03) MGMT Enable Register 0x */ MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */ @@ -126,7 +125,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_SMI_DATA BIT_ULL(MV88E6XXX_CAP_SMI_DATA) #define MV88E6XXX_FLAG_GLOBAL2 BIT_ULL(MV88E6XXX_CAP_GLOBAL2) -#define MV88E6XXX_FLAG_G2_INT BIT_ULL(MV88E6XXX_CAP_G2_INT) #define MV88E6XXX_FLAG_G2_MGMT_EN_2X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_2X) #define MV88E6XXX_FLAG_G2_MGMT_EN_0X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_0X) #define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT) @@ -143,7 +141,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6097 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ @@ -151,7 +148,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6165 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ @@ -173,13 +169,11 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6341 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6351 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ @@ -188,7 +182,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6352 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ @@ -197,7 +190,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6390 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAGS_MULTI_CHIP) struct mv88e6xxx_ops; @@ -213,6 +205,7 @@ struct mv88e6xxx_info { unsigned int global1_addr; unsigned int age_time_coeff; unsigned int g1_irqs; + unsigned int g2_irqs; bool pvt; enum dsa_tag_protocol tag_protocol; unsigned long long flags; diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 158d0f499874..be704c98dcbb 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -40,6 +40,21 @@ static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) return mv88e6xxx_wait(chip, MV88E6XXX_G2, reg, mask); } +/* Offset 0x00: Interrupt Source Register */ + +static int mv88e6xxx_g2_int_source(struct mv88e6xxx_chip *chip, u16 *src) +{ + /* Read (and clear most of) the Interrupt Source bits */ + return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_INT_SRC, src); +} + +/* Offset 0x01: Interrupt Mask Register */ + +static int mv88e6xxx_g2_int_mask(struct mv88e6xxx_chip *chip, u16 mask) +{ + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_INT_MASK, mask); +} + /* Offset 0x02: Management Enable 2x */ /* Offset 0x03: Management Enable 0x */ @@ -933,7 +948,7 @@ static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id) u16 reg; mutex_lock(&chip->reg_lock); - err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_INT_SOURCE, ®); + err = mv88e6xxx_g2_int_source(chip, ®); mutex_unlock(&chip->reg_lock); if (err) goto out; @@ -959,8 +974,11 @@ static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d) static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d) { struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + int err; - mv88e6xxx_g2_write(chip, MV88E6XXX_G2_INT_MASK, ~chip->g2_irq.masked); + err = mv88e6xxx_g2_int_mask(chip, ~chip->g2_irq.masked); + if (err) + dev_err(chip->dev, "failed to mask interrupts\n"); mutex_unlock(&chip->reg_lock); } diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 317ffd8f323d..7b21b2556af2 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -20,11 +20,26 @@ #define MV88E6XXX_G2 0x1c /* Offset 0x00: Interrupt Source Register */ -#define MV88E6XXX_G2_INT_SOURCE 0x00 +#define MV88E6XXX_G2_INT_SRC 0x00 +#define MV88E6XXX_G2_INT_SRC_WDOG 0x8000 +#define MV88E6XXX_G2_INT_SRC_JAM_LIMIT 0x4000 +#define MV88E6XXX_G2_INT_SRC_DUPLEX_MISMATCH 0x2000 +#define MV88E6XXX_G2_INT_SRC_WAKE_EVENT 0x1000 +#define MV88E6352_G2_INT_SRC_SERDES 0x0800 +#define MV88E6352_G2_INT_SRC_PHY 0x001f +#define MV88E6390_G2_INT_SRC_PHY 0x07fe + #define MV88E6XXX_G2_INT_SOURCE_WATCHDOG 15 /* Offset 0x01: Interrupt Mask Register */ -#define MV88E6XXX_G2_INT_MASK 0x01 +#define MV88E6XXX_G2_INT_MASK 0x01 +#define MV88E6XXX_G2_INT_MASK_WDOG 0x8000 +#define MV88E6XXX_G2_INT_MASK_JAM_LIMIT 0x4000 +#define MV88E6XXX_G2_INT_MASK_DUPLEX_MISMATCH 0x2000 +#define MV88E6XXX_G2_INT_MASK_WAKE_EVENT 0x1000 +#define MV88E6352_G2_INT_MASK_SERDES 0x0800 +#define MV88E6352_G2_INT_MASK_PHY 0x001f +#define MV88E6390_G2_INT_MASK_PHY 0x07fe /* Offset 0x02: MGMT Enable Register 2x */ #define MV88E6XXX_G2_MGMT_EN_2X 0x02 -- cgit v1.2.3 From 51c901a7756215561b331226f23054da056b8e42 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:41 -0400 Subject: net: dsa: mv88e6xxx: distinguish Global 2 Rsvd2CPU The 88E6185 family only has one 16-bit register to mark the 16 802.1D reserved multicast addresses in the range of 01:80:C2:00:00:0x as MGMT. The 88E6352 family also has one 16-bit register to mark the 16 GARP reserved multicast addresses in the range of 01:80:C2:00:00:2x as MGMT. Split the existing mv88e6095 prefixed mgmt_rsvd2cpu operation into two distinct mv88e6185 and mv88e6352 prefixed operations, and wrap its call into a mv88e6xxx_rsvd2cpu_setup helper. This allows us to also get rid of the MV88E6XXX_CAP_G2_MGMT_EN_* flags. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 55 ++++++++++++++++---------------- drivers/net/dsa/mv88e6xxx/chip.h | 17 ---------- drivers/net/dsa/mv88e6xxx/global2.c | 63 ++++++++++++++++++++++++++++--------- drivers/net/dsa/mv88e6xxx/global2.h | 11 +++++-- 4 files changed, 85 insertions(+), 61 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 1be0bc5e7c3f..874e2a154834 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -926,6 +926,14 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, dev_err(ds->dev, "p%d: failed to update state\n", port); } +static int mv88e6xxx_rsvd2cpu_setup(struct mv88e6xxx_chip *chip) +{ + if (chip->info->ops->mgmt_rsvd2cpu) + return chip->info->ops->mgmt_rsvd2cpu(chip); + + return 0; +} + static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip) { int err; @@ -2142,16 +2150,9 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) if (err) goto unlock; - /* Some generations have the configuration of sending reserved - * management frames to the CPU in global2, others in - * global1. Hence it does not fit the two setup functions - * above. - */ - if (chip->info->ops->mgmt_rsvd2cpu) { - err = chip->info->ops->mgmt_rsvd2cpu(chip); - if (err) - goto unlock; - } + err = mv88e6xxx_rsvd2cpu_setup(chip); + if (err) + goto unlock; unlock: mutex_unlock(&chip->reg_lock); @@ -2385,7 +2386,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, @@ -2408,7 +2409,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6185_g2_mgmt_rsvd2cpu, .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, @@ -2441,7 +2442,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2467,7 +2468,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2496,7 +2497,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6185_g2_mgmt_rsvd2cpu, .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, @@ -2563,7 +2564,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2587,7 +2588,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2619,7 +2620,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2653,7 +2654,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2686,7 +2687,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2720,7 +2721,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2746,7 +2747,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6185_g2_mgmt_rsvd2cpu, .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, @@ -2884,7 +2885,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2952,7 +2953,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .stats_get_stats = mv88e6320_stats_get_stats, .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, @@ -3049,7 +3050,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -3081,7 +3082,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -3115,7 +3116,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 2e760fd0ad24..48233971759e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -113,8 +113,6 @@ enum mv88e6xxx_cap { * The device contains a second set of global 16-bit registers. */ MV88E6XXX_CAP_GLOBAL2, - MV88E6XXX_CAP_G2_MGMT_EN_2X, /* (0x02) MGMT Enable Register 2x */ - MV88E6XXX_CAP_G2_MGMT_EN_0X, /* (0x03) MGMT Enable Register 0x */ MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */ }; @@ -125,8 +123,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_SMI_DATA BIT_ULL(MV88E6XXX_CAP_SMI_DATA) #define MV88E6XXX_FLAG_GLOBAL2 BIT_ULL(MV88E6XXX_CAP_GLOBAL2) -#define MV88E6XXX_FLAG_G2_MGMT_EN_2X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_2X) -#define MV88E6XXX_FLAG_G2_MGMT_EN_0X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_0X) #define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT) /* Multi-chip Addressing Mode */ @@ -136,33 +132,25 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6095 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6097 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ - MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6165 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ - MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6185 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6320 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ - MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -174,16 +162,12 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6351 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ - MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6352 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ - MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -418,7 +402,6 @@ struct mv88e6xxx_ops { int (*set_egress_port)(struct mv88e6xxx_chip *chip, int port); const struct mv88e6xxx_irq_ops *watchdog_ops; - /* Can be either in g1 or g2, so don't use a prefix */ int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip); /* Power on/off a SERDES interface */ diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index be704c98dcbb..6b6ebbd6d322 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -56,29 +56,65 @@ static int mv88e6xxx_g2_int_mask(struct mv88e6xxx_chip *chip, u16 mask) } /* Offset 0x02: Management Enable 2x */ + +static int mv88e6xxx_g2_mgmt_enable_2x(struct mv88e6xxx_chip *chip, u16 en2x) +{ + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_2X, en2x); +} + /* Offset 0x03: Management Enable 0x */ -int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +static int mv88e6xxx_g2_mgmt_enable_0x(struct mv88e6xxx_chip *chip, u16 en0x) +{ + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_0X, en0x); +} + +/* Offset 0x05: Switch Management Register */ + +static int mv88e6xxx_g2_switch_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip, + bool enable) +{ + u16 val; + int err; + + err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SWITCH_MGMT, &val); + if (err) + return err; + + if (enable) + val |= MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU; + else + val &= ~MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU; + + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MGMT, val); +} + +int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) { int err; /* Consider the frames with reserved multicast destination - * addresses matching 01:80:c2:00:00:2x as MGMT. + * addresses matching 01:80:c2:00:00:0x as MGMT. */ - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) { - err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_2X, 0xffff); - if (err) - return err; - } + err = mv88e6xxx_g2_mgmt_enable_0x(chip, 0xffff); + if (err) + return err; + + return mv88e6xxx_g2_switch_mgmt_rsvd2cpu(chip, true); +} + +int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +{ + int err; /* Consider the frames with reserved multicast destination - * addresses matching 01:80:c2:00:00:0x as MGMT. + * addresses matching 01:80:c2:00:00:2x as MGMT. */ - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) - return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_0X, - 0xffff); + err = mv88e6xxx_g2_mgmt_enable_2x(chip, 0xffff); + if (err) + return err; - return 0; + return mv88e6185_g2_mgmt_rsvd2cpu(chip); } /* Offset 0x06: Device Mapping Table register */ @@ -1081,9 +1117,6 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) * port at the highest priority. */ reg = MV88E6XXX_G2_SWITCH_MGMT_FORCE_FLOW_CTL_PRI | (0x7 << 4); - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) || - mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) - reg |= MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU | 0x7; err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MGMT, reg); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 7b21b2556af2..487a81146c31 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -260,7 +260,9 @@ int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip); void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip); -int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); + +int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); +int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops; extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops; @@ -362,7 +364,12 @@ static inline void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) { } -static inline int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +static inline int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +{ + return -EOPNOTSUPP; +} + +static inline int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) { return -EOPNOTSUPP; } -- cgit v1.2.3 From a2a05db8a5ed5f97d269155508da801646683ad6 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:42 -0400 Subject: net: dsa: mv88e6xxx: add POT flag to 88E6390 The 88E6390 family clear the Priority Override Table the same way as 88E6352, thus add MV88E6XXX_FLAG_G2_POT to MV88E6XXX_FLAGS_FAMILY_6390. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 48233971759e..52b52423df1f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -174,6 +174,7 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6390 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) struct mv88e6xxx_ops; -- cgit v1.2.3 From 9e907d739cc3caf3afa0af45835cb82ff929207c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:43 -0400 Subject: net: dsa: mv88e6xxx: add POT operation Add a pot_clear operation to clear the Priority Override Table and wrap its call into a mv88e6xxx_pot_setup helper. This allows us to get rid of the MV88E6XXX_FLAG_G2_POT flag. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 34 ++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/chip.h | 12 +++--------- drivers/net/dsa/mv88e6xxx/global2.c | 9 +-------- drivers/net/dsa/mv88e6xxx/global2.h | 7 +++++++ 4 files changed, 45 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 874e2a154834..6351230f82ad 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -926,6 +926,14 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, dev_err(ds->dev, "p%d: failed to update state\n", port); } +static int mv88e6xxx_pot_setup(struct mv88e6xxx_chip *chip) +{ + if (chip->info->ops->pot_clear) + return chip->info->ops->pot_clear(chip); + + return 0; +} + static int mv88e6xxx_rsvd2cpu_setup(struct mv88e6xxx_chip *chip) { if (chip->info->ops->mgmt_rsvd2cpu) @@ -2150,6 +2158,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) if (err) goto unlock; + err = mv88e6xxx_pot_setup(chip); + if (err) + goto unlock; + err = mv88e6xxx_rsvd2cpu_setup(chip); if (err) goto unlock; @@ -2387,6 +2399,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, @@ -2443,6 +2456,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2469,6 +2483,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2534,6 +2549,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .set_egress_port = mv88e6390_g1_set_egress_port, .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2565,6 +2581,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2589,6 +2606,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2621,6 +2639,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2655,6 +2674,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2688,6 +2708,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2722,6 +2743,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2783,6 +2805,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .set_egress_port = mv88e6390_g1_set_egress_port, .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, @@ -2817,6 +2840,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .set_egress_port = mv88e6390_g1_set_egress_port, .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, @@ -2851,6 +2875,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .set_egress_port = mv88e6390_g1_set_egress_port, .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, @@ -2886,6 +2911,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -2921,6 +2947,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .set_egress_port = mv88e6390_g1_set_egress_port, .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, @@ -2954,6 +2981,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, @@ -3019,6 +3047,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .set_egress_port = mv88e6390_g1_set_egress_port, .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -3051,6 +3080,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -3083,6 +3113,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -3117,6 +3148,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, @@ -3154,6 +3186,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .set_egress_port = mv88e6390_g1_set_egress_port, .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, @@ -3190,6 +3223,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .set_egress_port = mv88e6390_g1_set_egress_port, .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 52b52423df1f..822286250aff 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -113,7 +113,6 @@ enum mv88e6xxx_cap { * The device contains a second set of global 16-bit registers. */ MV88E6XXX_CAP_GLOBAL2, - MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */ }; /* Bitmask of capabilities */ @@ -123,7 +122,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_SMI_DATA BIT_ULL(MV88E6XXX_CAP_SMI_DATA) #define MV88E6XXX_FLAG_GLOBAL2 BIT_ULL(MV88E6XXX_CAP_GLOBAL2) -#define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT) /* Multi-chip Addressing Mode */ #define MV88E6XXX_FLAGS_MULTI_CHIP \ @@ -136,12 +134,10 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6097 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6165 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6185 \ @@ -151,30 +147,25 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6320 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6341 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6351 \ (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6352 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6390 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAGS_MULTI_CHIP) struct mv88e6xxx_ops; @@ -313,6 +304,9 @@ struct mv88e6xxx_ops { struct mii_bus *bus, int addr, int reg, u16 val); + /* Priority Override Table operations */ + int (*pot_clear)(struct mv88e6xxx_chip *chip); + /* PHY Polling Unit (PPU) operations */ int (*ppu_enable)(struct mv88e6xxx_chip *chip); int (*ppu_disable)(struct mv88e6xxx_chip *chip); diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 6b6ebbd6d322..aaf98e818d0d 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -311,7 +311,7 @@ static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer, return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_PRIO_OVERRIDE, val); } -static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip) +int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) { int i, err; @@ -1131,12 +1131,5 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) if (err) return err; - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) { - /* Clear the priority override table. */ - err = mv88e6xxx_g2_clear_pot(chip); - if (err) - return err; - } - return 0; } diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 487a81146c31..d89d7b810a45 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -264,6 +264,8 @@ void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip); int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip); + extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops; extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops; @@ -374,6 +376,11 @@ static inline int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) return -EOPNOTSUPP; } +static inline int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) +{ + return -EOPNOTSUPP; +} + static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {}; static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {}; -- cgit v1.2.3 From 9069c13a48675001c59e9864b25429aa7fb1c96a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:44 -0400 Subject: net: dsa: mv88e6xxx: add a global2_addr info flag Similarly to global1_addr, add a global2_addr member in the info structure to describe the presence of the Global 2 Registers. This allows us to get rid of the MV88E6XXX_FLAG_GLOBAL2 flag. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 28 +++++++++++++++++++++++++++- drivers/net/dsa/mv88e6xxx/chip.h | 27 ++++++--------------------- drivers/net/dsa/mv88e6xxx/global2.c | 8 ++++---- drivers/net/dsa/mv88e6xxx/global2.h | 4 +--- 4 files changed, 38 insertions(+), 29 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 6351230f82ad..eb4871a66076 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2132,7 +2132,7 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) goto unlock; /* Setup Switch Global 2 Registers */ - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) { + if (chip->info->global2_addr) { err = mv88e6xxx_g2_setup(chip); if (err) goto unlock; @@ -3240,6 +3240,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 8, .g2_irqs = 10, @@ -3259,6 +3260,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, @@ -3276,6 +3278,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 8, .g2_irqs = 10, @@ -3295,6 +3298,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3314,6 +3318,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, @@ -3331,6 +3336,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 3750, .atu_move_port_mask = 0x1f, .g2_irqs = 10, @@ -3349,6 +3355,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3368,6 +3375,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3387,6 +3395,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3406,6 +3415,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3425,6 +3435,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3444,6 +3455,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3463,6 +3475,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, @@ -3480,6 +3493,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, + .global2_addr = 0x1c, .tag_protocol = DSA_TAG_PROTO_DSA, .age_time_coeff = 3750, .g1_irqs = 9, @@ -3499,6 +3513,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 3750, .g1_irqs = 9, .g2_irqs = 14, @@ -3518,6 +3533,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 3750, .g1_irqs = 9, .g2_irqs = 14, @@ -3537,6 +3553,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3556,6 +3573,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 3750, .g1_irqs = 9, .g2_irqs = 14, @@ -3575,6 +3593,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, @@ -3593,6 +3612,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, @@ -3610,6 +3630,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 3750, .atu_move_port_mask = 0x1f, .g2_irqs = 10, @@ -3628,6 +3649,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3647,6 +3669,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3666,6 +3689,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, @@ -3684,6 +3708,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 3750, .g1_irqs = 9, .g2_irqs = 14, @@ -3702,6 +3727,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, + .global2_addr = 0x1c, .age_time_coeff = 3750, .g1_irqs = 9, .g2_irqs = 14, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 822286250aff..15b793446400 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -108,11 +108,6 @@ enum mv88e6xxx_cap { */ MV88E6XXX_CAP_SMI_CMD, /* (0x00) SMI Command */ MV88E6XXX_CAP_SMI_DATA, /* (0x01) SMI Data */ - - /* Switch Global 2 Registers. - * The device contains a second set of global 16-bit registers. - */ - MV88E6XXX_CAP_GLOBAL2, }; /* Bitmask of capabilities */ @@ -121,51 +116,40 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_SMI_CMD BIT_ULL(MV88E6XXX_CAP_SMI_CMD) #define MV88E6XXX_FLAG_SMI_DATA BIT_ULL(MV88E6XXX_CAP_SMI_DATA) -#define MV88E6XXX_FLAG_GLOBAL2 BIT_ULL(MV88E6XXX_CAP_GLOBAL2) - /* Multi-chip Addressing Mode */ #define MV88E6XXX_FLAGS_MULTI_CHIP \ (MV88E6XXX_FLAG_SMI_CMD | \ MV88E6XXX_FLAG_SMI_DATA) #define MV88E6XXX_FLAGS_FAMILY_6095 \ - (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAGS_MULTI_CHIP) + (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6097 \ - (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAGS_MULTI_CHIP) + (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6165 \ - (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAGS_MULTI_CHIP) + (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6185 \ - (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAGS_MULTI_CHIP) + (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6320 \ (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6341 \ (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6351 \ - (MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAGS_MULTI_CHIP) + (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6352 \ (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6390 \ (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAGS_MULTI_CHIP) struct mv88e6xxx_ops; @@ -179,6 +163,7 @@ struct mv88e6xxx_info { unsigned int max_vid; unsigned int port_base_addr; unsigned int global1_addr; + unsigned int global2_addr; unsigned int age_time_coeff; unsigned int g1_irqs; unsigned int g2_irqs; diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index aaf98e818d0d..16f556261022 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -22,22 +22,22 @@ static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) { - return mv88e6xxx_read(chip, MV88E6XXX_G2, reg, val); + return mv88e6xxx_read(chip, chip->info->global2_addr, reg, val); } static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) { - return mv88e6xxx_write(chip, MV88E6XXX_G2, reg, val); + return mv88e6xxx_write(chip, chip->info->global2_addr, reg, val); } static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update) { - return mv88e6xxx_update(chip, MV88E6XXX_G2, reg, update); + return mv88e6xxx_update(chip, chip->info->global2_addr, reg, update); } static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) { - return mv88e6xxx_wait(chip, MV88E6XXX_G2, reg, mask); + return mv88e6xxx_wait(chip, chip->info->global2_addr, reg, mask); } /* Offset 0x00: Interrupt Source Register */ diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index d89d7b810a45..669f59017b12 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -17,8 +17,6 @@ #include "chip.h" -#define MV88E6XXX_G2 0x1c - /* Offset 0x00: Interrupt Source Register */ #define MV88E6XXX_G2_INT_SRC 0x00 #define MV88E6XXX_G2_INT_SRC_WDOG 0x8000 @@ -273,7 +271,7 @@ extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops; static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) { - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) { + if (chip->info->global2_addr) { dev_err(chip->dev, "this chip requires CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 enabled\n"); return -EOPNOTSUPP; } -- cgit v1.2.3 From 68b8f60cf70d57459a75c25ccd78cf0cbd4637f9 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:45 -0400 Subject: net: dsa: mv88e6xxx: add Energy Detect ops The 88E6352 family supports Energy Detect and has one bit for Sense and one bit for periodically transmit NLP (Energy Detect+TM). The 88E6390 family adds another bit to distinguish Auto or SW wake-up. Chips supporting EEE all have an EEE Enabled bit in the Port Status Register. This patch adds new ops for the PHY Energy Detect accesses. This also allows us to get rid of the MV88E6XXX_FLAG_EEE flag. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 87 ++++++++++++++++++++++-------------- drivers/net/dsa/mv88e6xxx/chip.h | 24 +++++----- drivers/net/dsa/mv88e6xxx/phy.c | 96 ++++++++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/phy.h | 22 +++++++++ drivers/net/dsa/mv88e6xxx/port.c | 17 +++++++ drivers/net/dsa/mv88e6xxx/port.h | 3 ++ 6 files changed, 202 insertions(+), 47 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index eb4871a66076..be61983dfed4 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -810,31 +810,40 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, mutex_unlock(&chip->reg_lock); } -static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +static int mv88e6xxx_energy_detect_read(struct mv88e6xxx_chip *chip, int port, + struct ethtool_eee *eee) { - struct mv88e6xxx_chip *chip = ds->priv; - u16 reg; int err; - if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE)) + if (!chip->info->ops->phy_energy_detect_read) return -EOPNOTSUPP; - mutex_lock(&chip->reg_lock); - - err = mv88e6xxx_phy_read(chip, port, 16, ®); + /* assign eee->eee_enabled and eee->tx_lpi_enabled */ + err = chip->info->ops->phy_energy_detect_read(chip, port, eee); if (err) - goto out; + return err; - e->eee_enabled = !!(reg & 0x0200); - e->tx_lpi_enabled = !!(reg & 0x0100); + /* assign eee->eee_active */ + return mv88e6xxx_port_status_eee(chip, port, eee); +} - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); - if (err) - goto out; +static int mv88e6xxx_energy_detect_write(struct mv88e6xxx_chip *chip, int port, + struct ethtool_eee *eee) +{ + if (!chip->info->ops->phy_energy_detect_write) + return -EOPNOTSUPP; - e->eee_active = !!(reg & MV88E6352_PORT_STS_EEE); -out: + return chip->info->ops->phy_energy_detect_write(chip, port, eee); +} + +static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + mutex_lock(&chip->reg_lock); + err = mv88e6xxx_energy_detect_read(chip, port, e); mutex_unlock(&chip->reg_lock); return err; @@ -844,26 +853,10 @@ static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, struct phy_device *phydev, struct ethtool_eee *e) { struct mv88e6xxx_chip *chip = ds->priv; - u16 reg; int err; - if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE)) - return -EOPNOTSUPP; - mutex_lock(&chip->reg_lock); - - err = mv88e6xxx_phy_read(chip, port, 16, ®); - if (err) - goto out; - - reg &= ~0x0300; - if (e->eee_enabled) - reg |= 0x0200; - if (e->tx_lpi_enabled) - reg |= 0x0100; - - err = mv88e6xxx_phy_write(chip, port, 16, reg); -out: + err = mv88e6xxx_energy_detect_write(chip, port, e); mutex_unlock(&chip->reg_lock); return err; @@ -2528,6 +2521,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2653,6 +2648,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, @@ -2722,6 +2719,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, @@ -2785,6 +2784,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2820,6 +2821,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2855,6 +2858,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2890,6 +2895,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, @@ -2926,6 +2933,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2962,6 +2971,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, @@ -2995,6 +3006,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, @@ -3026,6 +3039,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -3127,6 +3142,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, @@ -3163,6 +3180,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -3201,6 +3220,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, + .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 15b793446400..3fbee01d2d84 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -98,10 +98,6 @@ enum mv88e6xxx_family { }; enum mv88e6xxx_cap { - /* Energy Efficient Ethernet. - */ - MV88E6XXX_CAP_EEE, - /* Multi-chip Addressing Mode. * Some chips respond to only 2 registers of its own SMI device address * when it is non-zero, and use indirect access to internal registers. @@ -111,8 +107,6 @@ enum mv88e6xxx_cap { }; /* Bitmask of capabilities */ -#define MV88E6XXX_FLAG_EEE BIT_ULL(MV88E6XXX_CAP_EEE) - #define MV88E6XXX_FLAG_SMI_CMD BIT_ULL(MV88E6XXX_CAP_SMI_CMD) #define MV88E6XXX_FLAG_SMI_DATA BIT_ULL(MV88E6XXX_CAP_SMI_DATA) @@ -134,23 +128,19 @@ enum mv88e6xxx_cap { (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6320 \ - (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAGS_MULTI_CHIP) + (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6341 \ - (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAGS_MULTI_CHIP) + (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6351 \ (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6352 \ - (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAGS_MULTI_CHIP) + (MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6390 \ - (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAGS_MULTI_CHIP) + (MV88E6XXX_FLAGS_MULTI_CHIP) struct mv88e6xxx_ops; @@ -289,6 +279,12 @@ struct mv88e6xxx_ops { struct mii_bus *bus, int addr, int reg, u16 val); + /* Copper Energy Detect operations */ + int (*phy_energy_detect_read)(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee); + int (*phy_energy_detect_write)(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee); + /* Priority Override Table operations */ int (*pot_clear)(struct mv88e6xxx_chip *chip); diff --git a/drivers/net/dsa/mv88e6xxx/phy.c b/drivers/net/dsa/mv88e6xxx/phy.c index 436668bd50dc..317ae89cfa68 100644 --- a/drivers/net/dsa/mv88e6xxx/phy.c +++ b/drivers/net/dsa/mv88e6xxx/phy.c @@ -246,3 +246,99 @@ int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip) { return mv88e6xxx_phy_ppu_enable(chip); } + +/* Page 0, Register 16: Copper Specific Control Register 1 */ + +int mv88e6352_phy_energy_detect_read(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee) +{ + u16 val; + int err; + + err = mv88e6xxx_phy_read(chip, phy, MV88E6XXX_PHY_CSCTL1, &val); + if (err) + return err; + + val &= MV88E6352_PHY_CSCTL1_ENERGY_DETECT_MASK; + + eee->eee_enabled = false; + eee->tx_lpi_enabled = false; + + switch (val) { + case MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP: + eee->tx_lpi_enabled = true; + /* fall through... */ + case MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV: + eee->eee_enabled = true; + } + + return 0; +} + +int mv88e6352_phy_energy_detect_write(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee) +{ + u16 val; + int err; + + err = mv88e6xxx_phy_read(chip, phy, MV88E6XXX_PHY_CSCTL1, &val); + if (err) + return err; + + val &= ~MV88E6352_PHY_CSCTL1_ENERGY_DETECT_MASK; + + if (eee->eee_enabled) + val |= MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV; + if (eee->tx_lpi_enabled) + val |= MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP; + + return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_CSCTL1, val); +} + +int mv88e6390_phy_energy_detect_read(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee) +{ + u16 val; + int err; + + err = mv88e6xxx_phy_read(chip, phy, MV88E6XXX_PHY_CSCTL1, &val); + if (err) + return err; + + val &= MV88E6390_PHY_CSCTL1_ENERGY_DETECT_MASK; + + eee->eee_enabled = false; + eee->tx_lpi_enabled = false; + + switch (val) { + case MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_AUTO: + case MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_SW: + eee->tx_lpi_enabled = true; + /* fall through... */ + case MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_AUTO: + case MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_SW: + eee->eee_enabled = true; + } + + return 0; +} + +int mv88e6390_phy_energy_detect_write(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee) +{ + u16 val; + int err; + + err = mv88e6xxx_phy_read(chip, phy, MV88E6XXX_PHY_CSCTL1, &val); + if (err) + return err; + + val &= ~MV88E6390_PHY_CSCTL1_ENERGY_DETECT_MASK; + + if (eee->eee_enabled) + val |= MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_AUTO; + if (eee->tx_lpi_enabled) + val |= MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_AUTO; + + return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_CSCTL1, val); +} diff --git a/drivers/net/dsa/mv88e6xxx/phy.h b/drivers/net/dsa/mv88e6xxx/phy.h index 556b74a0502a..988802799ad6 100644 --- a/drivers/net/dsa/mv88e6xxx/phy.h +++ b/drivers/net/dsa/mv88e6xxx/phy.h @@ -17,6 +17,19 @@ #define MV88E6XXX_PHY_PAGE 0x16 #define MV88E6XXX_PHY_PAGE_COPPER 0x00 +/* Page 0, Register 16: Copper Specific Control Register 1 */ +#define MV88E6XXX_PHY_CSCTL1 16 +#define MV88E6352_PHY_CSCTL1_ENERGY_DETECT_MASK 0x0300 +#define MV88E6352_PHY_CSCTL1_ENERGY_DETECT_OFF_MASK 0x0100 /* 0x */ +#define MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV 0x0200 +#define MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP 0x0300 +#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_MASK 0x0380 +#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_OFF_MASK 0x0180 /* 0xx */ +#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_AUTO 0x0200 +#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_SW 0x0280 +#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_AUTO 0x0300 +#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_SW 0x0380 + /* PHY Registers accesses implementations */ int mv88e6165_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, int addr, int reg, u16 *val); @@ -40,4 +53,13 @@ void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip); void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip); int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip); +int mv88e6352_phy_energy_detect_read(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee); +int mv88e6352_phy_energy_detect_write(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee); +int mv88e6390_phy_energy_detect_read(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee); +int mv88e6390_phy_energy_detect_write(struct mv88e6xxx_chip *chip, int phy, + struct ethtool_eee *eee); + #endif /*_MV88E6XXX_PHY_H */ diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index a7801f6668a5..2837a9128557 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -35,6 +35,23 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, return mv88e6xxx_write(chip, addr, reg, val); } +/* Offset 0x00: Port Status Register */ + +int mv88e6xxx_port_status_eee(struct mv88e6xxx_chip *chip, int port, + struct ethtool_eee *eee) +{ + u16 val; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &val); + if (err) + return err; + + eee->eee_active = !!(val & MV88E6352_PORT_STS_EEE); + + return 0; +} + /* Offset 0x01: MAC (or PCS or Physical) Control Register * * Link, Duplex and Flow Control have one force bit, one value bit. diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index b16d5f0e6e9c..6fcab309cd85 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -241,6 +241,9 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, u16 val); +int mv88e6xxx_port_status_eee(struct mv88e6xxx_chip *chip, int port, + struct ethtool_eee *eee); + int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, -- cgit v1.2.3 From b3e05aa12319f01ce5db6cb80402b554de02cc3d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 17 Jul 2017 13:03:46 -0400 Subject: net: dsa: mv88e6xxx: add a multi_chip info flag Instead of relying on a bitmap flag, add a new multi_chip info flag to describe the presence of the indirect SMI access though the two device registers 0x0 and 0x1. All remaining capabilities and flags are now unused. Remove the mv88e6xxx_cap enum and the info flags bitmaps. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 54 ++++++++++++++++++------------------- drivers/net/dsa/mv88e6xxx/chip.h | 58 +++++----------------------------------- 2 files changed, 33 insertions(+), 79 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index be61983dfed4..947ea352a57a 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3267,8 +3267,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6085_ops, }, @@ -3285,8 +3285,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6095, .ops = &mv88e6095_ops, }, @@ -3305,8 +3305,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6097_ops, }, @@ -3325,8 +3325,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6123_ops, }, @@ -3343,8 +3343,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6185, .ops = &mv88e6131_ops, }, @@ -3362,8 +3362,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .atu_move_port_mask = 0x1f, .g2_irqs = 10, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6341, .ops = &mv88e6141_ops, }, @@ -3382,8 +3382,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6161_ops, }, @@ -3402,8 +3402,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6165_ops, }, @@ -3422,8 +3422,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6171_ops, }, @@ -3442,8 +3442,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6172_ops, }, @@ -3462,8 +3462,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6175_ops, }, @@ -3482,8 +3482,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6176_ops, }, @@ -3500,8 +3500,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6185, .ops = &mv88e6185_ops, }, @@ -3520,8 +3520,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g1_irqs = 9, .g2_irqs = 14, .pvt = true, + .multi_chip = true, .atu_move_port_mask = 0x1f, - .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6190_ops, }, @@ -3540,8 +3540,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6190x_ops, }, @@ -3560,8 +3560,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6191_ops, }, @@ -3580,8 +3580,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6240_ops, }, @@ -3600,8 +3600,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6290_ops, }, @@ -3619,8 +3619,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g1_irqs = 8, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6320, .ops = &mv88e6320_ops, }, @@ -3637,8 +3637,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6320, .ops = &mv88e6321_ops, }, @@ -3656,8 +3656,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .atu_move_port_mask = 0x1f, .g2_irqs = 10, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6341, .ops = &mv88e6341_ops, }, @@ -3676,8 +3676,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6350_ops, }, @@ -3696,8 +3696,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6351_ops, }, @@ -3716,8 +3716,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 10, .atu_move_port_mask = 0xf, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6352_ops, }, [MV88E6390] = { @@ -3735,8 +3735,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6390_ops, }, [MV88E6390X] = { @@ -3754,8 +3754,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .g2_irqs = 14, .atu_move_port_mask = 0x1f, .pvt = true, + .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6390x_ops, }, }; @@ -3825,7 +3825,7 @@ static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, { if (sw_addr == 0) chip->smi_ops = &mv88e6xxx_smi_single_chip_ops; - else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP)) + else if (chip->info->multi_chip) chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops; else return -EINVAL; diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 3fbee01d2d84..9111e1316250 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -97,51 +97,6 @@ enum mv88e6xxx_family { MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */ }; -enum mv88e6xxx_cap { - /* Multi-chip Addressing Mode. - * Some chips respond to only 2 registers of its own SMI device address - * when it is non-zero, and use indirect access to internal registers. - */ - MV88E6XXX_CAP_SMI_CMD, /* (0x00) SMI Command */ - MV88E6XXX_CAP_SMI_DATA, /* (0x01) SMI Data */ -}; - -/* Bitmask of capabilities */ -#define MV88E6XXX_FLAG_SMI_CMD BIT_ULL(MV88E6XXX_CAP_SMI_CMD) -#define MV88E6XXX_FLAG_SMI_DATA BIT_ULL(MV88E6XXX_CAP_SMI_DATA) - -/* Multi-chip Addressing Mode */ -#define MV88E6XXX_FLAGS_MULTI_CHIP \ - (MV88E6XXX_FLAG_SMI_CMD | \ - MV88E6XXX_FLAG_SMI_DATA) - -#define MV88E6XXX_FLAGS_FAMILY_6095 \ - (MV88E6XXX_FLAGS_MULTI_CHIP) - -#define MV88E6XXX_FLAGS_FAMILY_6097 \ - (MV88E6XXX_FLAGS_MULTI_CHIP) - -#define MV88E6XXX_FLAGS_FAMILY_6165 \ - (MV88E6XXX_FLAGS_MULTI_CHIP) - -#define MV88E6XXX_FLAGS_FAMILY_6185 \ - (MV88E6XXX_FLAGS_MULTI_CHIP) - -#define MV88E6XXX_FLAGS_FAMILY_6320 \ - (MV88E6XXX_FLAGS_MULTI_CHIP) - -#define MV88E6XXX_FLAGS_FAMILY_6341 \ - (MV88E6XXX_FLAGS_MULTI_CHIP) - -#define MV88E6XXX_FLAGS_FAMILY_6351 \ - (MV88E6XXX_FLAGS_MULTI_CHIP) - -#define MV88E6XXX_FLAGS_FAMILY_6352 \ - (MV88E6XXX_FLAGS_MULTI_CHIP) - -#define MV88E6XXX_FLAGS_FAMILY_6390 \ - (MV88E6XXX_FLAGS_MULTI_CHIP) - struct mv88e6xxx_ops; struct mv88e6xxx_info { @@ -158,8 +113,13 @@ struct mv88e6xxx_info { unsigned int g1_irqs; unsigned int g2_irqs; bool pvt; + + /* Multi-chip Addressing Mode. + * Some chips respond to only 2 registers of its own SMI device address + * when it is non-zero, and use indirect access to internal registers. + */ + bool multi_chip; enum dsa_tag_protocol tag_protocol; - unsigned long long flags; /* Mask for FromPort and ToPort value of PortVec used in ATU Move * operation. 0 means that the ATU Move operation is not supported. @@ -410,12 +370,6 @@ struct mv88e6xxx_hw_stat { int type; }; -static inline bool mv88e6xxx_has(struct mv88e6xxx_chip *chip, - unsigned long flags) -{ - return (chip->info->flags & flags) == flags; -} - static inline bool mv88e6xxx_has_pvt(struct mv88e6xxx_chip *chip) { return chip->info->pvt; -- cgit v1.2.3 From 0ab10314747364e621ab95b528c2bd874ff3f528 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:18 +0200 Subject: drivers: net: add missing interrupt.h include these drivers use tasklets or irq apis, but don't include interrupt.h. Once flow cache is removed the implicit interrupt.h inclusion goes away which will break the build. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- drivers/net/arcnet/arcdevice.h | 2 +- drivers/net/ethernet/amd/xgbe/xgbe.h | 1 + drivers/net/ethernet/synopsys/dwc-xlgmac-net.c | 1 + drivers/net/ieee802154/ca8210.c | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/arcnet/arcdevice.h b/drivers/net/arcnet/arcdevice.h index cbb4f8566bbe..d09b2b46ab63 100644 --- a/drivers/net/arcnet/arcdevice.h +++ b/drivers/net/arcnet/arcdevice.h @@ -20,7 +20,7 @@ #include #ifdef __KERNEL__ -#include +#include /* * RECON_THRESHOLD is the maximum number of RECON messages to receive diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 0938294f640a..e9282c924621 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -129,6 +129,7 @@ #include #include #include +#include #define XGBE_DRV_NAME "amd-xgbe" #define XGBE_DRV_VERSION "1.0.3" diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c index 3b91257683bc..e1b55b8fb8e0 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c @@ -17,6 +17,7 @@ #include #include +#include #include "dwc-xlgmac.h" #include "dwc-xlgmac-reg.h" diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index a626c539fb17..326243fae7e2 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From e29237e7bb4ad79f5011cd0af9c8baeca16dce5c Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 18 Jul 2017 10:10:09 +0200 Subject: mlxsw: spectrum_router: Enable IPv6 router Before we add IPv6 constructs like traps and router interfaces, we first need to enable IPv6 routing in the device. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 4 +++- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 0ca196899e18..ce6c63d0cf2a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3953,10 +3953,12 @@ MLXSW_ITEM32(reg, rgcr, pcp_rw, 0x18, 16, 2); */ MLXSW_ITEM32(reg, rgcr, activity_dis, 0x20, 0, 8); -static inline void mlxsw_reg_rgcr_pack(char *payload, bool ipv4_en) +static inline void mlxsw_reg_rgcr_pack(char *payload, bool ipv4_en, + bool ipv6_en) { MLXSW_REG_ZERO(rgcr, payload); mlxsw_reg_rgcr_ipv4_en_set(payload, ipv4_en); + mlxsw_reg_rgcr_ipv6_en_set(payload, ipv6_en); } /* RITR - Router Interface Table Register diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 383fef5a8e24..060134d7db1c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3697,7 +3697,7 @@ static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) return -EIO; max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); - mlxsw_reg_rgcr_pack(rgcr_pl, true); + mlxsw_reg_rgcr_pack(rgcr_pl, true, true); mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); if (err) @@ -3709,7 +3709,7 @@ static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) { char rgcr_pl[MLXSW_REG_RGCR_LEN]; - mlxsw_reg_rgcr_pack(rgcr_pl, false); + mlxsw_reg_rgcr_pack(rgcr_pl, false, false); mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); } -- cgit v1.2.3 From e717e011ff5238a991e69aa0c62468b625fc29c7 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 18 Jul 2017 10:10:10 +0200 Subject: mlxsw: reg: Enable IPv6 on router interfaces Enable IPv6 and IPv6 forwarding on router interfaces (RIFs), so that they will be able to receive and forward IPv6 traffic. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index ce6c63d0cf2a..8282acb8f18f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4206,10 +4206,12 @@ static inline void mlxsw_reg_ritr_pack(char *payload, bool enable, MLXSW_REG_ZERO(ritr, payload); mlxsw_reg_ritr_enable_set(payload, enable); mlxsw_reg_ritr_ipv4_set(payload, 1); + mlxsw_reg_ritr_ipv6_set(payload, 1); mlxsw_reg_ritr_type_set(payload, type); mlxsw_reg_ritr_op_set(payload, op); mlxsw_reg_ritr_rif_set(payload, rif); mlxsw_reg_ritr_ipv4_fe_set(payload, 1); + mlxsw_reg_ritr_ipv6_fe_set(payload, 1); mlxsw_reg_ritr_lb_en_set(payload, 1); mlxsw_reg_ritr_virtual_router_set(payload, vr_id); mlxsw_reg_ritr_mtu_set(payload, mtu); -- cgit v1.2.3 From 8d54814e5233f92bdc2e5185c5dbf4978f7c2742 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 18 Jul 2017 10:10:11 +0200 Subject: mlxsw: spectrum: Add support for IPv6 traps Before we can start using IPv6, we need to trap certain control packets to the CPU. Among others, these include Neighbour Discovery, DHCP and neighbour misses. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 5 ++-- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 39 +++++++++++++++++++++----- drivers/net/ethernet/mellanox/mlxsw/trap.h | 20 +++++++++++-- 3 files changed, 53 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 8282acb8f18f..fe196fd84491 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3679,16 +3679,17 @@ enum mlxsw_reg_htgt_trap_group { MLXSW_REG_HTGT_TRAP_GROUP_SP_LACP, MLXSW_REG_HTGT_TRAP_GROUP_SP_LLDP, MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP, - MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4, + MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP, MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF, MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP, - MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS, + MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS, MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP, MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE, MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME, MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP, MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT, MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD, + MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND, }; /* reg_htgt_trap_group diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 1aa6298ea6cd..d6dc3dc4f319 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3345,12 +3345,35 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(LBERROR, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_MARK(OSPF, TRAP_TO_CPU, OSPF, false), MLXSW_SP_RXL_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false), + MLXSW_SP_RXL_MARK(IPV6_UNSPECIFIED_ADDRESS, TRAP_TO_CPU, ROUTER_EXP, + false), + MLXSW_SP_RXL_MARK(IPV6_LINK_LOCAL_DEST, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPV6_LINK_LOCAL_SRC, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPV6_ALL_NODES_LINK, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPV6_ALL_ROUTERS_LINK, TRAP_TO_CPU, ROUTER_EXP, + false), + MLXSW_SP_RXL_MARK(IPV4_OSPF, TRAP_TO_CPU, OSPF, false), + MLXSW_SP_RXL_MARK(IPV6_OSPF, TRAP_TO_CPU, OSPF, false), + MLXSW_SP_RXL_MARK(IPV6_DHCP, TRAP_TO_CPU, DHCP, false), MLXSW_SP_RXL_MARK(RTR_INGRESS0, TRAP_TO_CPU, REMOTE_ROUTE, false), - MLXSW_SP_RXL_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, ARP_MISS, false), - MLXSW_SP_RXL_MARK(BGP_IPV4, TRAP_TO_CPU, BGP_IPV4, false), + MLXSW_SP_RXL_MARK(IPV4_BGP, TRAP_TO_CPU, BGP, false), + MLXSW_SP_RXL_MARK(IPV6_BGP, TRAP_TO_CPU, BGP, false), + MLXSW_SP_RXL_MARK(L3_IPV6_ROUTER_SOLICITATION, TRAP_TO_CPU, IPV6_ND, + false), + MLXSW_SP_RXL_MARK(L3_IPV6_ROUTER_ADVERTISMENT, TRAP_TO_CPU, IPV6_ND, + false), + MLXSW_SP_RXL_MARK(L3_IPV6_NEIGHBOR_SOLICITATION, TRAP_TO_CPU, IPV6_ND, + false), + MLXSW_SP_RXL_MARK(L3_IPV6_NEIGHBOR_ADVERTISMENT, TRAP_TO_CPU, IPV6_ND, + false), + MLXSW_SP_RXL_MARK(L3_IPV6_REDIRECTION, TRAP_TO_CPU, IPV6_ND, false), + MLXSW_SP_RXL_MARK(IPV6_MC_LINK_LOCAL_DEST, TRAP_TO_CPU, ROUTER_EXP, + false), + MLXSW_SP_RXL_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, HOST_MISS, false), + MLXSW_SP_RXL_MARK(HOST_MISS_IPV6, TRAP_TO_CPU, HOST_MISS, false), MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV4, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV6, TRAP_TO_CPU, ROUTER_EXP, false), /* PKT Sample trap */ MLXSW_RXL(mlxsw_sp_rx_listener_sample_func, PKT_SAMPLE, MIRROR_TO_CPU, false, SP_IP2ME, DISCARD), @@ -3389,12 +3412,13 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core) rate = 16 * 1024; burst_size = 10; break; - case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP: - case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS: case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND: rate = 1024; burst_size = 7; break; @@ -3443,7 +3467,7 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) priority = 5; tc = 5; break; - case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP: priority = 4; tc = 4; @@ -3455,10 +3479,11 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) tc = 3; break; case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND: priority = 2; tc = 2; break; - case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS: case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE: priority = 1; diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h index 891b4ee6eeb2..61652396bf75 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/trap.h +++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h @@ -61,16 +61,32 @@ enum { MLXSW_TRAP_ID_MTUERROR = 0x52, MLXSW_TRAP_ID_TTLERROR = 0x53, MLXSW_TRAP_ID_LBERROR = 0x54, - MLXSW_TRAP_ID_OSPF = 0x55, + MLXSW_TRAP_ID_IPV4_OSPF = 0x55, MLXSW_TRAP_ID_IP2ME = 0x5F, + MLXSW_TRAP_ID_IPV6_UNSPECIFIED_ADDRESS = 0x60, + MLXSW_TRAP_ID_IPV6_LINK_LOCAL_DEST = 0x61, + MLXSW_TRAP_ID_IPV6_LINK_LOCAL_SRC = 0x62, + MLXSW_TRAP_ID_IPV6_ALL_NODES_LINK = 0x63, + MLXSW_TRAP_ID_IPV6_OSPF = 0x64, MLXSW_TRAP_ID_IPV6_MLDV12_LISTENER_QUERY = 0x65, MLXSW_TRAP_ID_IPV6_MLDV1_LISTENER_REPORT = 0x66, MLXSW_TRAP_ID_IPV6_MLDV1_LISTENER_DONE = 0x67, MLXSW_TRAP_ID_IPV6_MLDV2_LISTENER_REPORT = 0x68, + MLXSW_TRAP_ID_IPV6_DHCP = 0x69, + MLXSW_TRAP_ID_IPV6_ALL_ROUTERS_LINK = 0x6F, MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70, - MLXSW_TRAP_ID_BGP_IPV4 = 0x88, + MLXSW_TRAP_ID_IPV4_BGP = 0x88, + MLXSW_TRAP_ID_IPV6_BGP = 0x89, + MLXSW_TRAP_ID_L3_IPV6_ROUTER_SOLICITATION = 0x8A, + MLXSW_TRAP_ID_L3_IPV6_ROUTER_ADVERTISMENT = 0x8B, + MLXSW_TRAP_ID_L3_IPV6_NEIGHBOR_SOLICITATION = 0x8C, + MLXSW_TRAP_ID_L3_IPV6_NEIGHBOR_ADVERTISMENT = 0x8D, + MLXSW_TRAP_ID_L3_IPV6_REDIRECTION = 0x8E, MLXSW_TRAP_ID_HOST_MISS_IPV4 = 0x90, + MLXSW_TRAP_ID_IPV6_MC_LINK_LOCAL_DEST = 0x91, + MLXSW_TRAP_ID_HOST_MISS_IPV6 = 0x92, MLXSW_TRAP_ID_ROUTER_ALERT_IPV4 = 0xD6, + MLXSW_TRAP_ID_ROUTER_ALERT_IPV6 = 0xD7, MLXSW_TRAP_ID_ACL0 = 0x1C0, MLXSW_TRAP_ID_MAX = 0x1FF -- cgit v1.2.3 From 0d284818aff7752c71f1dc5dd6ad8ee64a4adac6 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:12 +0200 Subject: mlxsw: spectrum_router: Flood unregistered multicast packets to router Up until now we only flooded broadcast packets to the router when an L3 interface was configured on top of a bridge. However, IPv6 Neighbour Discovery packets are trapped to the CPU inside the router and these can be sent with a multicast address. Flood unregistered multicast packets to the router port, so that relevant packets could be trapped there. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 060134d7db1c..690bb9fed57a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3558,6 +3558,11 @@ static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif) if (err) return err; + err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), true); + if (err) + goto err_fid_mc_flood_set; + err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), true); if (err) @@ -3566,6 +3571,9 @@ static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif) return 0; err_fid_bc_flood_set: + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), false); +err_fid_mc_flood_set: mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false); return err; } @@ -3577,6 +3585,8 @@ static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif) mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), false); + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), false); mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false); } @@ -3607,6 +3617,11 @@ static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif) if (err) return err; + err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), true); + if (err) + goto err_fid_mc_flood_set; + err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), true); if (err) @@ -3615,6 +3630,9 @@ static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif) return 0; err_fid_bc_flood_set: + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), false); +err_fid_mc_flood_set: mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false); return err; } @@ -3626,6 +3644,8 @@ static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif) mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), false); + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), false); mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false); } -- cgit v1.2.3 From 5ea1237f94906937ded990316c79d3b6d942da2a Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 18 Jul 2017 10:10:13 +0200 Subject: mlxsw: spectrum_router: Configure RIFs based on IPv6 addresses When a netdev is configured with an IP address a router interface (RIF) should be configured for it in the device. Allow configuration of RIFs based on IPv6 address notifications as well as IPv4. Note that the RIF exists as long as an IP address is configured on the netdev, regardless of the address family. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 8 +++ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 + .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 79 ++++++++++++++++++++-- 3 files changed, 84 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index d6dc3dc4f319..88b668ba0d8a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -58,6 +58,7 @@ #include #include #include +#include #include "spectrum.h" #include "pci.h" @@ -4393,6 +4394,10 @@ static struct notifier_block mlxsw_sp_inetaddr_nb __read_mostly = { .priority = 10, /* Must be called before FIB notifier block */ }; +static struct notifier_block mlxsw_sp_inet6addr_nb __read_mostly = { + .notifier_call = mlxsw_sp_inet6addr_event, +}; + static struct notifier_block mlxsw_sp_router_netevent_nb __read_mostly = { .notifier_call = mlxsw_sp_router_netevent_event, }; @@ -4413,6 +4418,7 @@ static int __init mlxsw_sp_module_init(void) register_netdevice_notifier(&mlxsw_sp_netdevice_nb); register_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); + register_inet6addr_notifier(&mlxsw_sp_inet6addr_nb); register_netevent_notifier(&mlxsw_sp_router_netevent_nb); err = mlxsw_core_driver_register(&mlxsw_sp_driver); @@ -4429,6 +4435,7 @@ err_pci_driver_register: mlxsw_core_driver_unregister(&mlxsw_sp_driver); err_core_driver_register: unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb); + unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb); unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); return err; @@ -4439,6 +4446,7 @@ static void __exit mlxsw_sp_module_exit(void) mlxsw_pci_driver_unregister(&mlxsw_sp_pci_driver); mlxsw_core_driver_unregister(&mlxsw_sp_driver); unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb); + unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb); unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 5ef98d4d0ab6..e848f06e34e6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -384,6 +384,8 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused, int mlxsw_sp_netdevice_router_port_event(struct net_device *dev); int mlxsw_sp_inetaddr_event(struct notifier_block *unused, unsigned long event, void *ptr); +int mlxsw_sp_inet6addr_event(struct notifier_block *unused, + unsigned long event, void *ptr); int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event, struct netdev_notifier_changeupper_info *info); void diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 690bb9fed57a..7224066ac589 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -49,6 +49,7 @@ #include #include #include +#include #include "spectrum.h" #include "core.h" @@ -2941,17 +2942,30 @@ static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif); } -static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, - const struct in_device *in_dev, - unsigned long event) +static bool +mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev, + unsigned long event) { + struct inet6_dev *inet6_dev; + bool addr_list_empty = true; + struct in_device *idev; + switch (event) { case NETDEV_UP: if (!rif) return true; return false; case NETDEV_DOWN: - if (rif && !in_dev->ifa_list && + idev = __in_dev_get_rtnl(dev); + if (idev && idev->ifa_list) + addr_list_empty = false; + + inet6_dev = __in6_dev_get(dev); + if (addr_list_empty && inet6_dev && + !list_empty(&inet6_dev->addr_list)) + addr_list_empty = false; + + if (rif && addr_list_empty && !netif_is_l3_slave(rif->dev)) return true; /* It is possible we already removed the RIF ourselves @@ -3349,7 +3363,7 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused, goto out; rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!mlxsw_sp_rif_should_config(rif, ifa->ifa_dev, event)) + if (!mlxsw_sp_rif_should_config(rif, dev, event)) goto out; err = __mlxsw_sp_inetaddr_event(dev, event); @@ -3357,6 +3371,61 @@ out: return notifier_from_errno(err); } +struct mlxsw_sp_inet6addr_event_work { + struct work_struct work; + struct net_device *dev; + unsigned long event; +}; + +static void mlxsw_sp_inet6addr_event_work(struct work_struct *work) +{ + struct mlxsw_sp_inet6addr_event_work *inet6addr_work = + container_of(work, struct mlxsw_sp_inet6addr_event_work, work); + struct net_device *dev = inet6addr_work->dev; + unsigned long event = inet6addr_work->event; + struct mlxsw_sp *mlxsw_sp; + struct mlxsw_sp_rif *rif; + + rtnl_lock(); + mlxsw_sp = mlxsw_sp_lower_get(dev); + if (!mlxsw_sp) + goto out; + + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!mlxsw_sp_rif_should_config(rif, dev, event)) + goto out; + + __mlxsw_sp_inetaddr_event(dev, event); +out: + rtnl_unlock(); + dev_put(dev); + kfree(inet6addr_work); +} + +/* Called with rcu_read_lock() */ +int mlxsw_sp_inet6addr_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr; + struct mlxsw_sp_inet6addr_event_work *inet6addr_work; + struct net_device *dev = if6->idev->dev; + + if (!mlxsw_sp_port_dev_lower_find_rcu(dev)) + return NOTIFY_DONE; + + inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC); + if (!inet6addr_work) + return NOTIFY_BAD; + + INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work); + inet6addr_work->dev = dev; + inet6addr_work->event = event; + dev_hold(dev); + mlxsw_core_schedule_work(&inet6addr_work->work); + + return NOTIFY_DONE; +} + static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index, const char *mac, int mtu) { -- cgit v1.2.3 From 6929e50736d909f6a61fb638cc4f58ec58feafd2 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 18 Jul 2017 10:10:14 +0200 Subject: mlxsw: reg: Update RAUHT register with IPv6 support Update the register, so the IPv6 neighbours could be programmed to the device's neighbour table. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index fe196fd84491..abfa63181ec1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4960,6 +4960,7 @@ MLXSW_ITEM32(reg, rauht, rif, 0x00, 0, 16); * Access: Index */ MLXSW_ITEM32(reg, rauht, dip4, 0x1C, 0x0, 32); +MLXSW_ITEM_BUF(reg, rauht, dip6, 0x10, 16); enum mlxsw_reg_rauht_trap_action { MLXSW_REG_RAUHT_TRAP_ACTION_NOP, @@ -5024,6 +5025,15 @@ static inline void mlxsw_reg_rauht_pack4(char *payload, mlxsw_reg_rauht_dip4_set(payload, dip); } +static inline void mlxsw_reg_rauht_pack6(char *payload, + enum mlxsw_reg_rauht_op op, u16 rif, + const char *mac, const char *dip) +{ + mlxsw_reg_rauht_pack(payload, op, rif, mac); + mlxsw_reg_rauht_type_set(payload, MLXSW_REG_RAUHT_TYPE_IPV6); + mlxsw_reg_rauht_dip6_memcpy_to(payload, dip); +} + /* RALEU - Router Algorithmic LPM ECMP Update Register * --------------------------------------------------- * The register enables updating the ECMP section in the action for multiple -- cgit v1.2.3 From d5eb89cf68d674d165e36c49b486b5a487172d1c Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 18 Jul 2017 10:10:15 +0200 Subject: mlxsw: spectrum_router: Reflect IPv6 neighbours to the device As with IPv4, listen to NEIGH_UPDATE events from the ndisc table and program relevant neighbours to the device's neighbour table. Note that neighbours with a link-local IP address aren't programmed, as packets with a link-local destination IP are trapped after LPM lookup and never reach the neighbour table. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 40 ++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 7224066ac589..a0de7365d57e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -50,6 +50,8 @@ #include #include #include +#include +#include #include "spectrum.h" #include "core.h" @@ -1147,6 +1149,32 @@ mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp, mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); } +static void +mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + enum mlxsw_reg_rauht_op op) +{ + struct neighbour *n = neigh_entry->key.n; + char rauht_pl[MLXSW_REG_RAUHT_LEN]; + const char *dip = n->primary_key; + + mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha, + dip); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); +} + +static bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n) +{ + /* Packets with a link-local destination address are trapped + * after LPM lookup and never reach the neighbour table, so + * there is no need to program such neighbours to the device. + */ + if (ipv6_addr_type((struct in6_addr *) &n->primary_key) & + IPV6_ADDR_LINKLOCAL) + return true; + return false; +} + static void mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_neigh_entry *neigh_entry, @@ -1155,11 +1183,17 @@ mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp, if (!adding && !neigh_entry->connected) return; neigh_entry->connected = adding; - if (neigh_entry->key.n->tbl == &arp_tbl) + if (neigh_entry->key.n->tbl == &arp_tbl) { mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry, mlxsw_sp_rauht_op(adding)); - else + } else if (neigh_entry->key.n->tbl == &nd_tbl) { + if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n)) + return; + mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry, + mlxsw_sp_rauht_op(adding)); + } else { WARN_ON_ONCE(1); + } } struct mlxsw_sp_neigh_event_work { @@ -1247,7 +1281,7 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused, case NETEVENT_NEIGH_UPDATE: n = ptr; - if (n->tbl != &arp_tbl) + if (n->tbl != &arp_tbl && n->tbl != &nd_tbl) return NOTIFY_DONE; mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev); -- cgit v1.2.3 From 72e8ebe1b3cc57b379eaeeab76b9652bd27e7bfb Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 18 Jul 2017 10:10:16 +0200 Subject: mlxsw: reg: Update RAUHTD register with IPv6 support Update the register so that the active IPv6 neighbours could be dumped from the device's neighbour table. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index abfa63181ec1..0fc2263dee3a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5232,6 +5232,30 @@ MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv4_ent_rif, MLXSW_REG_RAUHTD_BASE_LEN, 0, MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv4_ent_dip, MLXSW_REG_RAUHTD_BASE_LEN, 0, 32, MLXSW_REG_RAUHTD_IPV4_ENT_LEN, 0x04, false); +#define MLXSW_REG_RAUHTD_IPV6_ENT_LEN 0x20 + +/* reg_rauhtd_ipv6_ent_a + * Activity. Set for new entries. Set if a packet lookup has hit on the + * specific entry. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv6_ent_a, MLXSW_REG_RAUHTD_BASE_LEN, 16, 1, + MLXSW_REG_RAUHTD_IPV6_ENT_LEN, 0x00, false); + +/* reg_rauhtd_ipv6_ent_rif + * Router interface. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv6_ent_rif, MLXSW_REG_RAUHTD_BASE_LEN, 0, + 16, MLXSW_REG_RAUHTD_IPV6_ENT_LEN, 0x00, false); + +/* reg_rauhtd_ipv6_ent_dip + * Destination IPv6 address. + * Access: RO + */ +MLXSW_ITEM_BUF_INDEXED(reg, rauhtd, ipv6_ent_dip, MLXSW_REG_RAUHTD_BASE_LEN, + 16, MLXSW_REG_RAUHTD_IPV6_ENT_LEN, 0x10); + static inline void mlxsw_reg_rauhtd_ent_ipv4_unpack(char *payload, int ent_index, u16 *p_rif, u32 *p_dip) @@ -5240,6 +5264,14 @@ static inline void mlxsw_reg_rauhtd_ent_ipv4_unpack(char *payload, *p_dip = mlxsw_reg_rauhtd_ipv4_ent_dip_get(payload, ent_index); } +static inline void mlxsw_reg_rauhtd_ent_ipv6_unpack(char *payload, + int rec_index, u16 *p_rif, + char *p_dip) +{ + *p_rif = mlxsw_reg_rauhtd_ipv6_ent_rif_get(payload, rec_index); + mlxsw_reg_rauhtd_ipv6_ent_dip_memcpy_from(payload, rec_index, p_dip); +} + /* MFCR - Management Fan Control Register * -------------------------------------- * This register controls the settings of the Fan Speed PWM mechanism. -- cgit v1.2.3 From 60f040ca11b968371fc1cd910d4648e58e6b80d3 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 18 Jul 2017 10:10:17 +0200 Subject: mlxsw: spectrum_router: Periodically dump active IPv6 neighbours In addition to IPv4, periodically dump IPv6 neighbours and update the kernel about them. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 79 +++++++++++++++++++--- 1 file changed, 69 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index a0de7365d57e..312fb67277d7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -968,6 +968,36 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp, neigh_release(n); } +static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + int rec_index) +{ + struct net_device *dev; + struct neighbour *n; + struct in6_addr dip; + u16 rif; + + mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif, + (char *) &dip); + + if (!mlxsw_sp->router->rifs[rif]) { + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n"); + return; + } + + dev = mlxsw_sp->router->rifs[rif]->dev; + n = neigh_lookup(&nd_tbl, &dip, dev); + if (!n) { + netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n", + &dip); + return; + } + + netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip); + neigh_event_send(n, NULL); + neigh_release(n); +} + static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp, char *rauhtd_pl, int rec_index) @@ -991,6 +1021,15 @@ static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp, } +static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + int rec_index) +{ + /* One record contains one entry. */ + mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl, + rec_index); +} + static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp, char *rauhtd_pl, int rec_index) { @@ -1000,7 +1039,8 @@ static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp, rec_index); break; case MLXSW_REG_RAUHTD_TYPE_IPV6: - WARN_ON_ONCE(1); + mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl, + rec_index); break; } } @@ -1025,22 +1065,20 @@ static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl) return false; } -static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp) +static int +__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + enum mlxsw_reg_rauhtd_type type) { - char *rauhtd_pl; - u8 num_rec; - int i, err; - - rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL); - if (!rauhtd_pl) - return -ENOMEM; + int i, num_rec; + int err; /* Make sure the neighbour's netdev isn't removed in the * process. */ rtnl_lock(); do { - mlxsw_reg_rauhtd_pack(rauhtd_pl, MLXSW_REG_RAUHTD_TYPE_IPV4); + mlxsw_reg_rauhtd_pack(rauhtd_pl, type); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd), rauhtd_pl); if (err) { @@ -1054,6 +1092,27 @@ static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp) } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl)); rtnl_unlock(); + return err; +} + +static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp) +{ + enum mlxsw_reg_rauhtd_type type; + char *rauhtd_pl; + int err; + + rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL); + if (!rauhtd_pl) + return -ENOMEM; + + type = MLXSW_REG_RAUHTD_TYPE_IPV4; + err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type); + if (err) + goto out; + + type = MLXSW_REG_RAUHTD_TYPE_IPV6; + err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type); +out: kfree(rauhtd_pl); return err; } -- cgit v1.2.3 From a6c9b5d1990577eed514110f4ba4d3bc032c57fd Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 18 Jul 2017 10:10:18 +0200 Subject: mlxsw: spectrum_router: Set activity interval according to both neighbour tables The neighbours' activity is currently dumped according to the ARP table's DELAY_PROBE time, but with the introduction of IPv6 offload we should set the interval according to the minimum between the ARP and ndisc tables. Signed-off-by: Arkadi Sharshvesky Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 312fb67277d7..cb797fb43b3d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -932,8 +932,11 @@ mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) static void mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp) { - unsigned long interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME); + unsigned long interval; + interval = min_t(unsigned long, + NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME), + NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME)); mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval); } @@ -1321,7 +1324,7 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused, p = ptr; /* We don't care about changes in the default table. */ - if (!p->dev || p->tbl != &arp_tbl) + if (!p->dev || (p->tbl != &arp_tbl && p->tbl != &nd_tbl)) return NOTIFY_DONE; /* We are in atomic context and can't take RTNL mutex, -- cgit v1.2.3 From 58adf2c48003d1a7469fe0d0b438300ab6173031 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:19 +0200 Subject: mlxsw: spectrum_router: Don't assume neighbour type Thankfully, the neighbour subsystem is agnostic to the upper protocol and used by both IPv4 and IPv6. By removing assumptions regarding the neighbour type we can thus re-use much of the neighbour-related code for both IPv4 and IPv6. For each nexthop, store its gateway IP and for nexthop group store the neighbour table used by its nexthops. Use this information throughout the code and remove assumption about the neighbour type. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index cb797fb43b3d..36f2b6843f17 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1437,6 +1437,7 @@ struct mlxsw_sp_nexthop { */ struct rhash_head ht_node; struct mlxsw_sp_nexthop_key key; + unsigned char gw_addr[sizeof(struct in6_addr)]; struct mlxsw_sp_rif *rif; u8 should_offload:1, /* set indicates this neigh is connected and * should be put to KVD linear area of this group. @@ -1457,6 +1458,7 @@ struct mlxsw_sp_nexthop_group_key { struct mlxsw_sp_nexthop_group { struct rhash_head ht_node; struct list_head fib_list; /* list of fib entries that use this group */ + struct neigh_table *neigh_tbl; struct mlxsw_sp_nexthop_group_key key; u8 adj_index_valid:1, gateway:1; /* routes using the group use a gateway */ @@ -1774,7 +1776,6 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh) { struct mlxsw_sp_neigh_entry *neigh_entry; - struct fib_nh *fib_nh = nh->key.fib_nh; struct neighbour *n; u8 nud_state, dead; int err; @@ -1787,9 +1788,10 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp, * The reference is taken either in neigh_lookup() or * in neigh_create() in case n is not found. */ - n = neigh_lookup(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev); + n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev); if (!n) { - n = neigh_create(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev); + n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr, + nh->rif->dev); if (IS_ERR(n)) return PTR_ERR(n); neigh_event_send(n, NULL); @@ -1863,6 +1865,7 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp, nh->nh_grp = nh_grp; nh->key.fib_nh = fib_nh; + memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw)); err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh); if (err) return err; @@ -1961,6 +1964,8 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) if (!nh_grp) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&nh_grp->fib_list); + nh_grp->neigh_tbl = &arp_tbl; + nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK; nh_grp->count = fi->fib_nhs; nh_grp->key.fi = fi; -- cgit v1.2.3 From 160e22aa2629875c23092e38eded442002d1ebda Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:20 +0200 Subject: mlxsw: spectrum_router: Don't create FIB node during lookup When looking up a FIB entry we shouldn't create the FIB node where it's supposed to be linked in case the node doesn't already exist. Instead, lookup the node and fail if it doesn't exist. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 36f2b6843f17..da8590ba2558 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2314,8 +2314,8 @@ static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp, } static struct mlxsw_sp_fib_node * -mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp, - const struct fib_entry_notifier_info *fen_info); +mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr, + size_t addr_len, unsigned char prefix_len); static struct mlxsw_sp_fib_entry * mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp, @@ -2323,9 +2323,18 @@ mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_fib_entry *fib_entry; struct mlxsw_sp_fib_node *fib_node; + struct mlxsw_sp_fib *fib; + struct mlxsw_sp_vr *vr; - fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info); - if (IS_ERR(fib_node)) + vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id); + if (!vr) + return NULL; + fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4); + + fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst, + sizeof(fen_info->dst), + fen_info->dst_len); + if (!fib_node) return NULL; list_for_each_entry(fib_entry, &fib_node->entry_list, list) { -- cgit v1.2.3 From 731ea1ca429fe7c959522129c526bdda442d7c54 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:21 +0200 Subject: mlxsw: spectrum_router: Make FIB node retrieval family agnostic A FIB node is an entity which stores routes sharing the same prefix and length. The data structure itself is already family agnostic, but we make some of its operations agnostic as well and thus re-use them for IPv6 offload. Instead of passing an IPv4-specific structure to fib4_node_get(), pass general routing parameters and rename the function accordingly. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index da8590ba2558..936d96a56ede 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2499,28 +2499,25 @@ static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp, } static struct mlxsw_sp_fib_node * -mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp, - const struct fib_entry_notifier_info *fen_info) +mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr, + size_t addr_len, unsigned char prefix_len, + enum mlxsw_sp_l3proto proto) { struct mlxsw_sp_fib_node *fib_node; struct mlxsw_sp_fib *fib; struct mlxsw_sp_vr *vr; int err; - vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->tb_id); + vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id); if (IS_ERR(vr)) return ERR_CAST(vr); - fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4); + fib = mlxsw_sp_vr_fib(vr, proto); - fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst, - sizeof(fen_info->dst), - fen_info->dst_len); + fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len); if (fib_node) return fib_node; - fib_node = mlxsw_sp_fib_node_create(fib, &fen_info->dst, - sizeof(fen_info->dst), - fen_info->dst_len); + fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len); if (!fib_node) { err = -ENOMEM; goto err_fib_node_create; @@ -2539,8 +2536,8 @@ err_fib_node_create: return ERR_PTR(err); } -static void mlxsw_sp_fib4_node_put(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_node *fib_node) +static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_node *fib_node) { struct mlxsw_sp_vr *vr = fib_node->fib->vr; @@ -2725,7 +2722,7 @@ static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced); mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced); - mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } static int @@ -2740,7 +2737,10 @@ mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp, if (mlxsw_sp->router->aborted) return 0; - fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info); + fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id, + &fen_info->dst, sizeof(fen_info->dst), + fen_info->dst_len, + MLXSW_SP_L3_PROTO_IPV4); if (IS_ERR(fib_node)) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n"); return PTR_ERR(fib_node); @@ -2767,7 +2767,7 @@ mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp, err_fib4_node_entry_link: mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry); err_fib4_entry_create: - mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); return err; } @@ -2787,7 +2787,7 @@ static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry); mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry); - mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) @@ -2846,7 +2846,7 @@ static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry); mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry); - mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); /* Break when entry list is empty and node was freed. * Otherwise, we'll access freed memory in the next * iteration. -- cgit v1.2.3 From a3d9bc506d6434c5557e8d069a611d1e60a2a62e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:22 +0200 Subject: mlxsw: spectrum_router: Extend virtual routers with IPv6 support A Virtual Router (VR) is an entity which corresponds to a VRF and performs FIB lookup in an LPM tree according to the {VR, IP Proto} -> Tree binding. Extend the virtual router data structure towards IPv6 FIB offload. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 28 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 936d96a56ede..00d5449e1417 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -431,6 +431,7 @@ struct mlxsw_sp_vr { u32 tb_id; /* kernel fib table id */ unsigned int rif_count; struct mlxsw_sp_fib *fib4; + struct mlxsw_sp_fib *fib6; }; static const struct rhashtable_params mlxsw_sp_fib_ht_params; @@ -628,7 +629,7 @@ static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp) static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr) { - return !!vr->fib4; + return !!vr->fib4 || !!vr->fib6; } static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp) @@ -697,7 +698,7 @@ static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr, case MLXSW_SP_L3_PROTO_IPV4: return vr->fib4; case MLXSW_SP_L3_PROTO_IPV6: - BUG_ON(1); + return vr->fib6; } return NULL; } @@ -706,6 +707,7 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp, u32 tb_id) { struct mlxsw_sp_vr *vr; + int err; vr = mlxsw_sp_vr_find_unused(mlxsw_sp); if (!vr) @@ -713,12 +715,24 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp, vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4); if (IS_ERR(vr->fib4)) return ERR_CAST(vr->fib4); + vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6); + if (IS_ERR(vr->fib6)) { + err = PTR_ERR(vr->fib6); + goto err_fib6_create; + } vr->tb_id = tb_id; return vr; + +err_fib6_create: + mlxsw_sp_fib_destroy(vr->fib4); + vr->fib4 = NULL; + return ERR_PTR(err); } static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr) { + mlxsw_sp_fib_destroy(vr->fib6); + vr->fib6 = NULL; mlxsw_sp_fib_destroy(vr->fib4); vr->fib4 = NULL; } @@ -776,7 +790,8 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id) static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr) { - if (!vr->rif_count && list_empty(&vr->fib4->node_list)) + if (!vr->rif_count && list_empty(&vr->fib4->node_list) && + list_empty(&vr->fib6->node_list)) mlxsw_sp_vr_destroy(vr); } @@ -2895,6 +2910,13 @@ static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp) if (!mlxsw_sp_vr_is_used(vr)) continue; mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4); + + /* If virtual router was only used for IPv4, then it's no + * longer used. + */ + if (!mlxsw_sp_vr_is_used(vr)) + continue; + mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6); } } -- cgit v1.2.3 From 62547f407fa13e02c4d8ddb1ed481a7c60068b2f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:23 +0200 Subject: mlxsw: reg: Update RALUE register with IPv6 support Update the register so that IPv6 LPM entries could be programmed to the device's table. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 0fc2263dee3a..c6c508941d23 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4724,6 +4724,7 @@ MLXSW_ITEM32(reg, ralue, prefix_len, 0x08, 0, 8); * Access: Index */ MLXSW_ITEM32(reg, ralue, dip4, 0x18, 0, 32); +MLXSW_ITEM_BUF(reg, ralue, dip6, 0x0C, 16); enum mlxsw_reg_ralue_entry_type { MLXSW_REG_RALUE_ENTRY_TYPE_MARKER_ENTRY = 1, @@ -4857,6 +4858,16 @@ static inline void mlxsw_reg_ralue_pack4(char *payload, mlxsw_reg_ralue_dip4_set(payload, dip); } +static inline void mlxsw_reg_ralue_pack6(char *payload, + enum mlxsw_reg_ralxx_protocol protocol, + enum mlxsw_reg_ralue_op op, + u16 virtual_router, u8 prefix_len, + const void *dip) +{ + mlxsw_reg_ralue_pack(payload, protocol, op, virtual_router, prefix_len); + mlxsw_reg_ralue_dip6_memcpy_to(payload, dip); +} + static inline void mlxsw_reg_ralue_act_remote_pack(char *payload, enum mlxsw_reg_ralue_trap_action trap_action, -- cgit v1.2.3 From 9dbf4d76d07d3b581238149d6e87796c39c94716 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:24 +0200 Subject: mlxsw: spectrum_router: Allow IPv6 routes to be programmed Take advantage of previous patch and allow the RALUE register to be called with IPv6 routes. In order to re-use as much code as possible between IPv4 and IPv6, only the lowest-level function that actually does the register packing is demuxed based on the passed protocol. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 87 ++++++++++++---------- 1 file changed, 46 insertions(+), 41 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 00d5449e1417..1141d742468b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2126,13 +2126,37 @@ mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry, } } -static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - enum mlxsw_reg_ralue_op op) +static void +mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl, + const struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) { - char ralue_pl[MLXSW_REG_RALUE_LEN]; struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; - u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; + enum mlxsw_reg_ralxx_protocol proto; + u32 *p_dip; + + proto = (enum mlxsw_reg_ralxx_protocol) fib->proto; + + switch (fib->proto) { + case MLXSW_SP_L3_PROTO_IPV4: + p_dip = (u32 *) fib_entry->fib_node->key.addr; + mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id, + fib_entry->fib_node->key.prefix_len, + *p_dip); + break; + case MLXSW_SP_L3_PROTO_IPV6: + mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id, + fib_entry->fib_node->key.prefix_len, + fib_entry->fib_node->key.addr); + break; + } +} + +static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) +{ + char ralue_pl[MLXSW_REG_RALUE_LEN]; enum mlxsw_reg_ralue_trap_action trap_action; u16 trap_id = 0; u32 adjacency_index = 0; @@ -2151,24 +2175,19 @@ static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp, trap_id = MLXSW_TRAP_ID_RTR_INGRESS0; } - mlxsw_reg_ralue_pack4(ralue_pl, - (enum mlxsw_reg_ralxx_protocol) fib->proto, op, - fib->vr->id, fib_entry->fib_node->key.prefix_len, - *p_dip); + mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id, adjacency_index, ecmp_size); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); } -static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - enum mlxsw_reg_ralue_op op) +static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) { struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif; - struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; enum mlxsw_reg_ralue_trap_action trap_action; char ralue_pl[MLXSW_REG_RALUE_LEN]; - u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; u16 trap_id = 0; u16 rif_index = 0; @@ -2180,42 +2199,34 @@ static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp, trap_id = MLXSW_TRAP_ID_RTR_INGRESS0; } - mlxsw_reg_ralue_pack4(ralue_pl, - (enum mlxsw_reg_ralxx_protocol) fib->proto, op, - fib->vr->id, fib_entry->fib_node->key.prefix_len, - *p_dip); + mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, rif_index); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); } -static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - enum mlxsw_reg_ralue_op op) +static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) { - struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; char ralue_pl[MLXSW_REG_RALUE_LEN]; - u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; - mlxsw_reg_ralue_pack4(ralue_pl, - (enum mlxsw_reg_ralxx_protocol) fib->proto, op, - fib->vr->id, fib_entry->fib_node->key.prefix_len, - *p_dip); + mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); mlxsw_reg_ralue_act_ip2me_pack(ralue_pl); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); } -static int mlxsw_sp_fib_entry_op4(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - enum mlxsw_reg_ralue_op op) +static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) { switch (fib_entry->type) { case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE: - return mlxsw_sp_fib_entry_op4_remote(mlxsw_sp, fib_entry, op); + return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op); case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL: - return mlxsw_sp_fib_entry_op4_local(mlxsw_sp, fib_entry, op); + return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op); case MLXSW_SP_FIB_ENTRY_TYPE_TRAP: - return mlxsw_sp_fib_entry_op4_trap(mlxsw_sp, fib_entry, op); + return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op); } return -EINVAL; } @@ -2224,16 +2235,10 @@ static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry, enum mlxsw_reg_ralue_op op) { - int err = -EINVAL; + int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op); - switch (fib_entry->fib_node->fib->proto) { - case MLXSW_SP_L3_PROTO_IPV4: - err = mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op); - break; - case MLXSW_SP_L3_PROTO_IPV6: - return err; - } mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err); + return err; } -- cgit v1.2.3 From bc65a8a4f4d214c30c21cba90607c93ac676f6cf Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:25 +0200 Subject: mlxsw: spectrum_router: Set abort trap for IPv6 When we fail to insert a route we invoke the abort mechanism which flushes all the tables and inserts a default route in each, so that all packets incoming to the router will be trapped to the CPU. Upon abort, add an IPv6 default route to the IPv6 tables. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 39 ++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 1141d742468b..0017a611ed16 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2810,19 +2810,20 @@ static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } -static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) +static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_reg_ralxx_protocol proto, + u8 tree_id) { char ralta_pl[MLXSW_REG_RALTA_LEN]; char ralst_pl[MLXSW_REG_RALST_LEN]; int i, err; - mlxsw_reg_ralta_pack(ralta_pl, true, MLXSW_REG_RALXX_PROTOCOL_IPV4, - MLXSW_SP_LPM_TREE_MIN); + mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); if (err) return err; - mlxsw_reg_ralst_pack(ralst_pl, 0xff, MLXSW_SP_LPM_TREE_MIN); + mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl); if (err) return err; @@ -2835,17 +2836,14 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) if (!mlxsw_sp_vr_is_used(vr)) continue; - mlxsw_reg_raltb_pack(raltb_pl, vr->id, - MLXSW_REG_RALXX_PROTOCOL_IPV4, - MLXSW_SP_LPM_TREE_MIN); + mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); if (err) return err; - mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_SP_L3_PROTO_IPV4, - MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0, - 0); + mlxsw_reg_ralue_pack(ralue_pl, proto, + MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0); mlxsw_reg_ralue_act_ip2me_pack(ralue_pl); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); @@ -2856,6 +2854,21 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) return 0; } +static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) +{ + enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4; + int err; + + err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto, + MLXSW_SP_LPM_TREE_MIN); + if (err) + return err; + + proto = MLXSW_REG_RALXX_PROTOCOL_IPV6; + return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto, + MLXSW_SP_LPM_TREE_MIN + 1); +} + static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { @@ -2925,7 +2938,7 @@ static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp) } } -static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp) +static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp) { int err; @@ -2970,7 +2983,7 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info, replace, append); if (err) - mlxsw_sp_router_fib4_abort(mlxsw_sp); + mlxsw_sp_router_fib_abort(mlxsw_sp); fib_info_put(fib_work->fen_info.fi); break; case FIB_EVENT_ENTRY_DEL: @@ -2981,7 +2994,7 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) case FIB_EVENT_RULE_DEL: rule = fib_work->fr_info.rule; if (!fib4_rule_default(rule) && !rule->l3mdev) - mlxsw_sp_router_fib4_abort(mlxsw_sp); + mlxsw_sp_router_fib_abort(mlxsw_sp); fib_rule_put(rule); break; case FIB_EVENT_NH_ADD: /* fall through */ -- cgit v1.2.3 From 4f1c7f1f2e954cca8230fad4b33b192f84307f50 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:26 +0200 Subject: mlxsw: spectrum_router: Create IPv4 specific entry struct Some of the parameters stored in the FIB entry structure are specific to IPv4 and therefore better placed in an IPv4 specific structure. Create an IPv4 specific structure that encapsulates the common FIB entry structure and contains IPv4 specific parameters. In a follow-up patchset an IPv6 specific structure will be introduced. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 223 ++++++++++++--------- 1 file changed, 123 insertions(+), 100 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 0017a611ed16..52487a4db09e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -387,23 +387,23 @@ struct mlxsw_sp_fib_node { struct mlxsw_sp_fib_key key; }; -struct mlxsw_sp_fib_entry_params { - u32 tb_id; - u32 prio; - u8 tos; - u8 type; -}; - struct mlxsw_sp_fib_entry { struct list_head list; struct mlxsw_sp_fib_node *fib_node; enum mlxsw_sp_fib_entry_type type; struct list_head nexthop_group_node; struct mlxsw_sp_nexthop_group *nh_group; - struct mlxsw_sp_fib_entry_params params; bool offloaded; }; +struct mlxsw_sp_fib4_entry { + struct mlxsw_sp_fib_entry common; + u32 tb_id; + u32 prio; + u8 tos; + u8 type; +}; + enum mlxsw_sp_l3proto { MLXSW_SP_L3_PROTO_IPV4, MLXSW_SP_L3_PROTO_IPV6, @@ -2057,13 +2057,29 @@ static void mlxsw_sp_nexthop_group_put(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_nexthop_group_destroy(mlxsw_sp, nh_grp); } +static bool +mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_fib4_entry *fib4_entry; + + fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry, + common); + return !fib4_entry->tos; +} + static bool mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry) { struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group; - if (fib_entry->params.tos) - return false; + switch (fib_entry->fib_node->fib->proto) { + case MLXSW_SP_L3_PROTO_IPV4: + if (!mlxsw_sp_fib4_entry_should_offload(fib_entry)) + return false; + break; + case MLXSW_SP_L3_PROTO_IPV6: + break; + } switch (fib_entry->type) { case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE: @@ -2288,19 +2304,19 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp, } } -static struct mlxsw_sp_fib_entry * +static struct mlxsw_sp_fib4_entry * mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node, const struct fib_entry_notifier_info *fen_info) { + struct mlxsw_sp_fib4_entry *fib4_entry; struct mlxsw_sp_fib_entry *fib_entry; int err; - fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL); - if (!fib_entry) { - err = -ENOMEM; - goto err_fib_entry_alloc; - } + fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL); + if (!fib4_entry) + return ERR_PTR(-ENOMEM); + fib_entry = &fib4_entry->common; err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry); if (err) @@ -2310,38 +2326,37 @@ mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp, if (err) goto err_nexthop_group_get; - fib_entry->params.prio = fen_info->fi->fib_priority; - fib_entry->params.tb_id = fen_info->tb_id; - fib_entry->params.type = fen_info->type; - fib_entry->params.tos = fen_info->tos; + fib4_entry->prio = fen_info->fi->fib_priority; + fib4_entry->tb_id = fen_info->tb_id; + fib4_entry->type = fen_info->type; + fib4_entry->tos = fen_info->tos; fib_entry->fib_node = fib_node; - return fib_entry; + return fib4_entry; err_nexthop_group_get: err_fib4_entry_type_set: - kfree(fib_entry); -err_fib_entry_alloc: + kfree(fib4_entry); return ERR_PTR(err); } static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) + struct mlxsw_sp_fib4_entry *fib4_entry) { - mlxsw_sp_nexthop_group_put(mlxsw_sp, fib_entry); - kfree(fib_entry); + mlxsw_sp_nexthop_group_put(mlxsw_sp, &fib4_entry->common); + kfree(fib4_entry); } static struct mlxsw_sp_fib_node * mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr, size_t addr_len, unsigned char prefix_len); -static struct mlxsw_sp_fib_entry * +static struct mlxsw_sp_fib4_entry * mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp, const struct fib_entry_notifier_info *fen_info) { - struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_fib4_entry *fib4_entry; struct mlxsw_sp_fib_node *fib_node; struct mlxsw_sp_fib *fib; struct mlxsw_sp_vr *vr; @@ -2357,12 +2372,12 @@ mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp, if (!fib_node) return NULL; - list_for_each_entry(fib_entry, &fib_node->entry_list, list) { - if (fib_entry->params.tb_id == fen_info->tb_id && - fib_entry->params.tos == fen_info->tos && - fib_entry->params.type == fen_info->type && - fib_entry->nh_group->key.fi == fen_info->fi) { - return fib_entry; + list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) { + if (fib4_entry->tb_id == fen_info->tb_id && + fib4_entry->tos == fen_info->tos && + fib4_entry->type == fen_info->type && + fib4_entry->common.nh_group->key.fi == fen_info->fi) { + return fib4_entry; } } @@ -2568,88 +2583,93 @@ static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_vr_put(vr); } -static struct mlxsw_sp_fib_entry * +static struct mlxsw_sp_fib4_entry * mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, - const struct mlxsw_sp_fib_entry_params *params) + const struct mlxsw_sp_fib4_entry *new4_entry) { - struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_fib4_entry *fib4_entry; - list_for_each_entry(fib_entry, &fib_node->entry_list, list) { - if (fib_entry->params.tb_id > params->tb_id) + list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) { + if (fib4_entry->tb_id > new4_entry->tb_id) continue; - if (fib_entry->params.tb_id != params->tb_id) + if (fib4_entry->tb_id != new4_entry->tb_id) break; - if (fib_entry->params.tos > params->tos) + if (fib4_entry->tos > new4_entry->tos) continue; - if (fib_entry->params.prio >= params->prio || - fib_entry->params.tos < params->tos) - return fib_entry; + if (fib4_entry->prio >= new4_entry->prio || + fib4_entry->tos < new4_entry->tos) + return fib4_entry; } return NULL; } -static int mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib_entry *fib_entry, - struct mlxsw_sp_fib_entry *new_entry) +static int +mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry, + struct mlxsw_sp_fib4_entry *new4_entry) { struct mlxsw_sp_fib_node *fib_node; - if (WARN_ON(!fib_entry)) + if (WARN_ON(!fib4_entry)) return -EINVAL; - fib_node = fib_entry->fib_node; - list_for_each_entry_from(fib_entry, &fib_node->entry_list, list) { - if (fib_entry->params.tb_id != new_entry->params.tb_id || - fib_entry->params.tos != new_entry->params.tos || - fib_entry->params.prio != new_entry->params.prio) + fib_node = fib4_entry->common.fib_node; + list_for_each_entry_from(fib4_entry, &fib_node->entry_list, + common.list) { + if (fib4_entry->tb_id != new4_entry->tb_id || + fib4_entry->tos != new4_entry->tos || + fib4_entry->prio != new4_entry->prio) break; } - list_add_tail(&new_entry->list, &fib_entry->list); + list_add_tail(&new4_entry->common.list, &fib4_entry->common.list); return 0; } static int mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib_node *fib_node, - struct mlxsw_sp_fib_entry *new_entry, + struct mlxsw_sp_fib4_entry *new4_entry, bool replace, bool append) { - struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_fib4_entry *fib4_entry; - fib_entry = mlxsw_sp_fib4_node_entry_find(fib_node, &new_entry->params); + fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry); if (append) - return mlxsw_sp_fib4_node_list_append(fib_entry, new_entry); - if (replace && WARN_ON(!fib_entry)) + return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry); + if (replace && WARN_ON(!fib4_entry)) return -EINVAL; /* Insert new entry before replaced one, so that we can later * remove the second. */ - if (fib_entry) { - list_add_tail(&new_entry->list, &fib_entry->list); + if (fib4_entry) { + list_add_tail(&new4_entry->common.list, + &fib4_entry->common.list); } else { - struct mlxsw_sp_fib_entry *last; + struct mlxsw_sp_fib4_entry *last; - list_for_each_entry(last, &fib_node->entry_list, list) { - if (new_entry->params.tb_id > last->params.tb_id) + list_for_each_entry(last, &fib_node->entry_list, common.list) { + if (new4_entry->tb_id > last->tb_id) break; - fib_entry = last; + fib4_entry = last; } - if (fib_entry) - list_add(&new_entry->list, &fib_entry->list); + if (fib4_entry) + list_add(&new4_entry->common.list, + &fib4_entry->common.list); else - list_add(&new_entry->list, &fib_node->entry_list); + list_add(&new4_entry->common.list, + &fib_node->entry_list); } return 0; } static void -mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib_entry *fib_entry) +mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry) { - list_del(&fib_entry->list); + list_del(&fib4_entry->common.list); } static int @@ -2695,50 +2715,52 @@ mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp, } static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, + struct mlxsw_sp_fib4_entry *fib4_entry, bool replace, bool append) { - struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; + struct mlxsw_sp_fib_node *fib_node; int err; - err = mlxsw_sp_fib4_node_list_insert(fib_node, fib_entry, replace, + fib_node = fib4_entry->common.fib_node; + err = mlxsw_sp_fib4_node_list_insert(fib_node, fib4_entry, replace, append); if (err) return err; - err = mlxsw_sp_fib4_node_entry_add(mlxsw_sp, fib_node, fib_entry); + err = mlxsw_sp_fib4_node_entry_add(mlxsw_sp, fib_node, + &fib4_entry->common); if (err) goto err_fib4_node_entry_add; return 0; err_fib4_node_entry_add: - mlxsw_sp_fib4_node_list_remove(fib_entry); + mlxsw_sp_fib4_node_list_remove(fib4_entry); return err; } static void mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) + struct mlxsw_sp_fib4_entry *fib4_entry) { - struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; + struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; - mlxsw_sp_fib4_node_entry_del(mlxsw_sp, fib_node, fib_entry); - mlxsw_sp_fib4_node_list_remove(fib_entry); + mlxsw_sp_fib4_node_entry_del(mlxsw_sp, fib_node, &fib4_entry->common); + mlxsw_sp_fib4_node_list_remove(fib4_entry); } static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, + struct mlxsw_sp_fib4_entry *fib4_entry, bool replace) { - struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; - struct mlxsw_sp_fib_entry *replaced; + struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; + struct mlxsw_sp_fib4_entry *replaced; if (!replace) return; /* We inserted the new entry before replaced one */ - replaced = list_next_entry(fib_entry, list); + replaced = list_next_entry(fib4_entry, common.list); mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced); mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced); @@ -2750,7 +2772,7 @@ mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp, const struct fib_entry_notifier_info *fen_info, bool replace, bool append) { - struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_fib4_entry *fib4_entry; struct mlxsw_sp_fib_node *fib_node; int err; @@ -2766,26 +2788,26 @@ mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp, return PTR_ERR(fib_node); } - fib_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info); - if (IS_ERR(fib_entry)) { + fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info); + if (IS_ERR(fib4_entry)) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n"); - err = PTR_ERR(fib_entry); + err = PTR_ERR(fib4_entry); goto err_fib4_entry_create; } - err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib_entry, replace, + err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace, append); if (err) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n"); goto err_fib4_node_entry_link; } - mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib_entry, replace); + mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace); return 0; err_fib4_node_entry_link: - mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry); + mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); err_fib4_entry_create: mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); return err; @@ -2794,19 +2816,19 @@ err_fib4_entry_create: static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp, struct fib_entry_notifier_info *fen_info) { - struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_fib4_entry *fib4_entry; struct mlxsw_sp_fib_node *fib_node; if (mlxsw_sp->router->aborted) return; - fib_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info); - if (WARN_ON(!fib_entry)) + fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info); + if (WARN_ON(!fib4_entry)) return; - fib_node = fib_entry->fib_node; + fib_node = fib4_entry->common.fib_node; - mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry); - mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry); + mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry); + mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } @@ -2872,13 +2894,14 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { - struct mlxsw_sp_fib_entry *fib_entry, *tmp; + struct mlxsw_sp_fib4_entry *fib4_entry, *tmp; - list_for_each_entry_safe(fib_entry, tmp, &fib_node->entry_list, list) { - bool do_break = &tmp->list == &fib_node->entry_list; + list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list, + common.list) { + bool do_break = &tmp->common.list == &fib_node->entry_list; - mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry); - mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry); + mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry); + mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); /* Break when entry list is empty and node was freed. * Otherwise, we'll access freed memory in the next -- cgit v1.2.3 From 0e6ea2a4eaef5dcf30abb5c4e02988b5d0b9342f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:27 +0200 Subject: mlxsw: spectrum_router: Mark IPv4 specific function accordingly The functions to create and destroy a nexthop group are IPv4 specific and should be renamed accordingly, so that they won't be confused with the IPv6 specific functions in follow-up patches. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 58 +++++++++++----------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 52487a4db09e..5d787c17f306 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1868,10 +1868,10 @@ static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp, neigh_release(n); } -static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop_group *nh_grp, - struct mlxsw_sp_nexthop *nh, - struct fib_nh *fib_nh) +static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp, + struct mlxsw_sp_nexthop *nh, + struct fib_nh *fib_nh) { struct net_device *dev = fib_nh->nh_dev; struct in_device *in_dev; @@ -1910,16 +1910,16 @@ err_nexthop_neigh_init: return err; } -static void mlxsw_sp_nexthop_fini(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop *nh) +static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) { mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); mlxsw_sp_nexthop_rif_fini(nh); mlxsw_sp_nexthop_remove(mlxsw_sp, nh); } -static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp, - unsigned long event, struct fib_nh *fib_nh) +static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp, + unsigned long event, struct fib_nh *fib_nh) { struct mlxsw_sp_nexthop_key key; struct mlxsw_sp_nexthop *nh; @@ -1964,7 +1964,7 @@ static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, } static struct mlxsw_sp_nexthop_group * -mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) +mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) { struct mlxsw_sp_nexthop_group *nh_grp; struct mlxsw_sp_nexthop *nh; @@ -1988,9 +1988,9 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) for (i = 0; i < nh_grp->count; i++) { nh = &nh_grp->nexthops[i]; fib_nh = &fi->fib_nh[i]; - err = mlxsw_sp_nexthop_init(mlxsw_sp, nh_grp, nh, fib_nh); + err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh); if (err) - goto err_nexthop_init; + goto err_nexthop4_init; } err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp); if (err) @@ -1999,10 +1999,10 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) return nh_grp; err_nexthop_group_insert: -err_nexthop_init: +err_nexthop4_init: for (i--; i >= 0; i--) { nh = &nh_grp->nexthops[i]; - mlxsw_sp_nexthop_fini(mlxsw_sp, nh); + mlxsw_sp_nexthop4_fini(mlxsw_sp, nh); } fib_info_put(nh_grp->key.fi); kfree(nh_grp); @@ -2010,8 +2010,8 @@ err_nexthop_init: } static void -mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop_group *nh_grp) +mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp) { struct mlxsw_sp_nexthop *nh; int i; @@ -2019,7 +2019,7 @@ mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp); for (i = 0; i < nh_grp->count; i++) { nh = &nh_grp->nexthops[i]; - mlxsw_sp_nexthop_fini(mlxsw_sp, nh); + mlxsw_sp_nexthop4_fini(mlxsw_sp, nh); } mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); WARN_ON_ONCE(nh_grp->adj_index_valid); @@ -2027,9 +2027,9 @@ mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp, kfree(nh_grp); } -static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - struct fib_info *fi) +static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + struct fib_info *fi) { struct mlxsw_sp_nexthop_group_key key; struct mlxsw_sp_nexthop_group *nh_grp; @@ -2037,7 +2037,7 @@ static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp, key.fi = fi; nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key); if (!nh_grp) { - nh_grp = mlxsw_sp_nexthop_group_create(mlxsw_sp, fi); + nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi); if (IS_ERR(nh_grp)) return PTR_ERR(nh_grp); } @@ -2046,15 +2046,15 @@ static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp, return 0; } -static void mlxsw_sp_nexthop_group_put(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) +static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; list_del(&fib_entry->nexthop_group_node); if (!list_empty(&nh_grp->fib_list)) return; - mlxsw_sp_nexthop_group_destroy(mlxsw_sp, nh_grp); + mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp); } static bool @@ -2322,9 +2322,9 @@ mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp, if (err) goto err_fib4_entry_type_set; - err = mlxsw_sp_nexthop_group_get(mlxsw_sp, fib_entry, fen_info->fi); + err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi); if (err) - goto err_nexthop_group_get; + goto err_nexthop4_group_get; fib4_entry->prio = fen_info->fi->fib_priority; fib4_entry->tb_id = fen_info->tb_id; @@ -2335,7 +2335,7 @@ mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp, return fib4_entry; -err_nexthop_group_get: +err_nexthop4_group_get: err_fib4_entry_type_set: kfree(fib4_entry); return ERR_PTR(err); @@ -2344,7 +2344,7 @@ err_fib4_entry_type_set: static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib4_entry *fib4_entry) { - mlxsw_sp_nexthop_group_put(mlxsw_sp, &fib4_entry->common); + mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common); kfree(fib4_entry); } @@ -3022,8 +3022,8 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) break; case FIB_EVENT_NH_ADD: /* fall through */ case FIB_EVENT_NH_DEL: - mlxsw_sp_nexthop_event(mlxsw_sp, fib_work->event, - fib_work->fnh_info.fib_nh); + mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event, + fib_work->fnh_info.fib_nh); fib_info_put(fib_work->fnh_info.fib_nh->nh_parent); break; } -- cgit v1.2.3 From 9efbee6fead645db2fe8d9e779c36875aa2b98d4 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:28 +0200 Subject: mlxsw: spectrum_router: Drop unnecessary parameter Functions that take as argument a FIB entry don't need to take FIB node as well, as it can be extracted from the entry. Remove unnecessary FIB node parameter. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 32 +++++++++------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 5d787c17f306..c8c8187a8f0a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2627,10 +2627,10 @@ mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry, } static int -mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib_node *fib_node, - struct mlxsw_sp_fib4_entry *new4_entry, +mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry, bool replace, bool append) { + struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node; struct mlxsw_sp_fib4_entry *fib4_entry; fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry); @@ -2672,11 +2672,11 @@ mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry) list_del(&fib4_entry->common.list); } -static int -mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_fib_node *fib_node, - struct mlxsw_sp_fib_entry *fib_entry) +static int mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { + struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; + if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry)) return 0; @@ -2693,11 +2693,11 @@ mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp, return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); } -static void -mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_fib_node *fib_node, - struct mlxsw_sp_fib_entry *fib_entry) +static void mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { + struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; + if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry)) return; @@ -2718,17 +2718,13 @@ static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib4_entry *fib4_entry, bool replace, bool append) { - struct mlxsw_sp_fib_node *fib_node; int err; - fib_node = fib4_entry->common.fib_node; - err = mlxsw_sp_fib4_node_list_insert(fib_node, fib4_entry, replace, - append); + err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append); if (err) return err; - err = mlxsw_sp_fib4_node_entry_add(mlxsw_sp, fib_node, - &fib4_entry->common); + err = mlxsw_sp_fib4_node_entry_add(mlxsw_sp, &fib4_entry->common); if (err) goto err_fib4_node_entry_add; @@ -2743,9 +2739,7 @@ static void mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib4_entry *fib4_entry) { - struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; - - mlxsw_sp_fib4_node_entry_del(mlxsw_sp, fib_node, &fib4_entry->common); + mlxsw_sp_fib4_node_entry_del(mlxsw_sp, &fib4_entry->common); mlxsw_sp_fib4_node_list_remove(fib4_entry); } -- cgit v1.2.3 From 80c238f91b1f607b32fae54f0462a073c3674140 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:29 +0200 Subject: mlxsw: spectrum_router: Rename functions to add / delete a FIB entry These functions aren't specific to IPv4 and can be re-used for IPv6. Drop the '4' designation from their name. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index c8c8187a8f0a..32ed2b65b395 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2672,8 +2672,8 @@ mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry) list_del(&fib4_entry->common.list); } -static int mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) +static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; @@ -2693,8 +2693,8 @@ static int mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp, return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); } -static void mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) +static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; @@ -2724,13 +2724,13 @@ static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp, if (err) return err; - err = mlxsw_sp_fib4_node_entry_add(mlxsw_sp, &fib4_entry->common); + err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common); if (err) - goto err_fib4_node_entry_add; + goto err_fib_node_entry_add; return 0; -err_fib4_node_entry_add: +err_fib_node_entry_add: mlxsw_sp_fib4_node_list_remove(fib4_entry); return err; } @@ -2739,7 +2739,7 @@ static void mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib4_entry *fib4_entry) { - mlxsw_sp_fib4_node_entry_del(mlxsw_sp, &fib4_entry->common); + mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common); mlxsw_sp_fib4_node_list_remove(fib4_entry); } -- cgit v1.2.3 From 7dcc18adad31d15d528414bdff12bf98d33d9a20 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Jul 2017 10:10:30 +0200 Subject: mlxsw: spectrum_router: Update prefix count for IPv6 The number of possible prefix lengths for IPv6 is 129 and not 128. Fixes following warning from UBSAN when /128 routes are offloaded: UBSAN: Undefined behaviour in drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:2510:27 index 128 is out of range for type 'long unsigned int [128]' Fixes: 5e9c16cc83a7 ("mlxsw: spectrum_router: Implement private fib") Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 32ed2b65b395..e6d629f40f93 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -307,7 +307,7 @@ static struct mlxsw_sp_rif * mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, const struct net_device *dev); -#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE) +#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1) struct mlxsw_sp_prefix_usage { DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT); -- cgit v1.2.3 From 43f51ef11957a5ba2575dc0f20c84ba7d2ae38b9 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:13:45 +0530 Subject: net: cdc_ncm: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 13275 928 1 14204 377c drivers/net/usb/cdc_ncm.o File size After adding 'const': text data bss dec hex filename 13339 864 1 14204 377c drivers/net/usb/cdc_ncm.o Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index d103a1d4fb36..b401ba9e6ddc 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -367,7 +367,7 @@ static struct attribute *cdc_ncm_sysfs_attrs[] = { NULL, }; -static struct attribute_group cdc_ncm_sysfs_attr_group = { +static const struct attribute_group cdc_ncm_sysfs_attr_group = { .name = "cdc_ncm", .attrs = cdc_ncm_sysfs_attrs, }; -- cgit v1.2.3 From 7ec2796e6375607104ee516b2330dd51dbb7abcd Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:14:18 +0530 Subject: net: can: at91_can: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 6164 304 0 6468 1944 drivers/net/can/at91_can.o File size After adding 'const': text data bss dec hex filename 6228 240 0 6468 1944 drivers/net/can/at91_can.o Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/can/at91_can.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index 0e0df0ba288c..f37ce0e1b603 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -1232,7 +1232,7 @@ static struct attribute *at91_sysfs_attrs[] = { NULL, }; -static struct attribute_group at91_sysfs_attr_group = { +static const struct attribute_group at91_sysfs_attr_group = { .attrs = at91_sysfs_attrs, }; -- cgit v1.2.3 From 7eaf0d93f9d85527086a483d5d200b421aa325c6 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:14:19 +0530 Subject: net: can: janz-ican3: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 11800 368 0 12168 2f88 drivers/net/can/janz-ican3.o File size After adding 'const': text data bss dec hex filename 11864 304 0 12168 2f88 drivers/net/can/janz-ican3.o Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/can/janz-ican3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index 2ba1a81500c1..12a53c8e8e1d 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -1875,7 +1875,7 @@ static struct attribute *ican3_sysfs_attrs[] = { NULL, }; -static struct attribute_group ican3_sysfs_attr_group = { +static const struct attribute_group ican3_sysfs_attr_group = { .attrs = ican3_sysfs_attrs, }; -- cgit v1.2.3 From d7979553ef3e8927fe3f035e5d2a7e1749c27780 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:14:59 +0530 Subject: wireless: ipw2200: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/wireless/intel/ipw2x00/ipw2200.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index 9368abdf18e2..c311b1a994c1 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -11500,7 +11500,7 @@ static struct attribute *ipw_sysfs_entries[] = { NULL }; -static struct attribute_group ipw_attribute_group = { +static const struct attribute_group ipw_attribute_group = { .name = NULL, /* put in device directory */ .attrs = ipw_sysfs_entries, }; -- cgit v1.2.3 From e00b6c6d98adf7277c6c897783c57f467a8b62b0 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:15:00 +0530 Subject: wireless: ipw2100: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index aaaca4d08e2b..ccbe74589eec 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -4324,7 +4324,7 @@ static struct attribute *ipw2100_sysfs_entries[] = { NULL, }; -static struct attribute_group ipw2100_attribute_group = { +static const struct attribute_group ipw2100_attribute_group = { .attrs = ipw2100_sysfs_entries, }; -- cgit v1.2.3 From 64571ca71bbfa890321ff14cf18c74089a23e4d1 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:15:01 +0530 Subject: wireless: iwlegacy: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/wireless/intel/iwlegacy/3945-mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index 38bf403bb1e1..329f3a63dadd 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -3464,7 +3464,7 @@ static struct attribute *il3945_sysfs_entries[] = { NULL }; -static struct attribute_group il3945_attribute_group = { +static const struct attribute_group il3945_attribute_group = { .name = NULL, /* put in device directory */ .attrs = il3945_sysfs_entries, }; -- cgit v1.2.3 From 6cbbd7ec06fa605b219cb2a91b653960a5d21aa6 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:15:02 +0530 Subject: wireless: iwlegacy: Constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/wireless/intel/iwlegacy/4965-mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 5b51fba75595..de9b6522c43f 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -4654,7 +4654,7 @@ static struct attribute *il_sysfs_entries[] = { NULL }; -static struct attribute_group il_attribute_group = { +static const struct attribute_group il_attribute_group = { .name = NULL, /* put in device directory */ .attrs = il_sysfs_entries, }; -- cgit v1.2.3 From c5567669fc638849fbd1057385846ac75fa42a0e Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:15:36 +0530 Subject: arcnet: com20020-pci: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 3409 948 28 4385 1121 drivers/net/arcnet/com20020-pci.o File size After adding 'const': text data bss dec hex filename 3473 884 28 4385 1121 drivers/net/arcnet/com20020-pci.o Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/arcnet/com20020-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index 01cab9548785..eb7f76753c9c 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -109,7 +109,7 @@ static struct attribute *com20020_state_attrs[] = { NULL, }; -static struct attribute_group com20020_state_group = { +static const struct attribute_group com20020_state_group = { .name = NULL, .attrs = com20020_state_attrs, }; -- cgit v1.2.3 From 02dbbef0548678b9066e68bb1b6c9eda5c077a00 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:15:56 +0530 Subject: net: bonding: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 4512 1472 0 5984 1760 drivers/net/bonding/bond_sysfs.o File size After adding 'const': text data bss dec hex filename 4576 1408 0 5984 1760 drivers/net/bonding/bond_sysfs.o Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/bonding/bond_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 770623a0cc01..040b493f60ae 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -759,7 +759,7 @@ static struct attribute *per_bond_attrs[] = { NULL, }; -static struct attribute_group bonding_group = { +static const struct attribute_group bonding_group = { .name = "bonding", .attrs = per_bond_attrs, }; -- cgit v1.2.3 From 98dc8373db89b242a7888cdc7b0788e21e0a4cbe Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 18 Jul 2017 15:16:19 +0530 Subject: net: chelsio: cxgb3: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 28720 985 12 29717 7415 net/.../cxgb3/cxgb3_main.o File size After adding 'const': text data bss dec hex filename 28848 857 12 29717 7415 net/.../cxgb3/cxgb3_main.o Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 0bc6a4ffce30..6a015362c340 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -793,7 +793,9 @@ static struct attribute *cxgb3_attrs[] = { NULL }; -static struct attribute_group cxgb3_attr_group = {.attrs = cxgb3_attrs }; +static const struct attribute_group cxgb3_attr_group = { + .attrs = cxgb3_attrs, +}; static ssize_t tm_attr_show(struct device *d, char *buf, int sched) @@ -880,7 +882,9 @@ static struct attribute *offload_attrs[] = { NULL }; -static struct attribute_group offload_attr_group = {.attrs = offload_attrs }; +static const struct attribute_group offload_attr_group = { + .attrs = offload_attrs, +}; /* * Sends an sk_buff to an offload queue driver -- cgit v1.2.3 From 741912c55365f1a48611e20d3291433b686f6846 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Mon, 17 Jul 2017 13:33:14 -0700 Subject: liquidio: support new firmware statistic fw_err_pki Added support for new firmware statistic 'tx_err_pki'. Signed-off-by: Rick Farrington Signed-off-by: Derek Chickles Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 4 ++++ drivers/net/ethernet/cavium/liquidio/liquidio_common.h | 1 + 2 files changed, 5 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 28ecda3d3404..976a50f67551 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -105,6 +105,7 @@ static const char oct_stats_strings[][ETH_GSTRING_LEN] = { "tx_total_sent", "tx_total_fwd", "tx_err_pko", + "tx_err_pki", "tx_err_link", "tx_err_drop", @@ -826,6 +827,8 @@ lio_get_ethtool_stats(struct net_device *netdev, data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_total_fwd); /*per_core_stats[j].link_stats[i].fromhost.fw_err_pko */ data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_err_pko); + /*per_core_stats[j].link_stats[i].fromhost.fw_err_pki */ + data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_err_pki); /*per_core_stats[j].link_stats[i].fromhost.fw_err_link */ data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_err_link); /*per_core_stats[cvmx_get_core_num()].link_stats[idx].fromhost. @@ -1568,6 +1571,7 @@ octnet_nic_stats_callback(struct octeon_device *oct_dev, tstats->fw_total_sent = rsp_tstats->fw_total_sent; tstats->fw_total_fwd = rsp_tstats->fw_total_fwd; tstats->fw_err_pko = rsp_tstats->fw_err_pko; + tstats->fw_err_pki = rsp_tstats->fw_err_pki; tstats->fw_err_link = rsp_tstats->fw_err_link; tstats->fw_err_drop = rsp_tstats->fw_err_drop; tstats->fw_tso = rsp_tstats->fw_tso; diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 231dd7fbfb80..53aaf417e722 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -814,6 +814,7 @@ struct nic_tx_stats { u64 fw_tso; /* number of tso requests */ u64 fw_tso_fwd; /* number of packets segmented in tso */ u64 fw_tx_vxlan; + u64 fw_err_pki; }; struct oct_link_stats { -- cgit v1.2.3 From 00587f2fa70866a2c3425ee799c91657b4717b7c Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Mon, 17 Jul 2017 17:50:47 -0700 Subject: liquidio: lowmem: init allocated memory to 0 Fix GPF in octeon_init_droq(); zero the allocated block 'recv_buf_list'. This prevents a GPF trying to access an invalid 'recv_buf_list[i]' entry in octeon_droq_destroy_ring_buffers() if init didn't alloc all entries. Signed-off-by: Rick Farrington Signed-off-by: Satanand Burla Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index 2e190deb2233..645668339620 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -275,12 +275,12 @@ int octeon_init_droq(struct octeon_device *oct, droq->max_count); droq->recv_buf_list = (struct octeon_recv_buffer *) - vmalloc_node(droq->max_count * + vzalloc_node(droq->max_count * OCT_DROQ_RECVBUF_SIZE, numa_node); if (!droq->recv_buf_list) droq->recv_buf_list = (struct octeon_recv_buffer *) - vmalloc(droq->max_count * + vzalloc(droq->max_count * OCT_DROQ_RECVBUF_SIZE); if (!droq->recv_buf_list) { dev_err(&oct->pci_dev->dev, "Output queue recv buf list alloc failed\n"); -- cgit v1.2.3 From 689062a18c00f49f9c32ac8d5366c075ab691c30 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Mon, 17 Jul 2017 17:51:10 -0700 Subject: liquidio: lowmem: do not dereference null ptr Don't dereference a NULL ptr in octeon_droq_destroy_ring_buffers(). Signed-off-by: Rick Farrington Signed-off-by: Satanand Burla Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index 645668339620..f7b5d68eb4cf 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -145,6 +145,8 @@ octeon_droq_destroy_ring_buffers(struct octeon_device *oct, for (i = 0; i < droq->max_count; i++) { pg_info = &droq->recv_buf_list[i].pg_info; + if (!pg_info) + continue; if (pg_info->dma) lio_unmap_ring(oct->pci_dev, -- cgit v1.2.3 From 2c4aac74a9f28e0431f5335401d5fc8f744fa1e1 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Mon, 17 Jul 2017 17:51:37 -0700 Subject: liquidio: lowmem: init allocated memory to 0 For defensive programming, zero the allocated block 'oct->droq[0]' in octeon_setup_output_queues() and 'oct->instr_queue[0]' in octeon_setup_instr_queues(). Signed-off-by: Rick Farrington Signed-off-by: Satanand Burla Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_device.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 623e28ca736e..f10014f7ae88 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -876,11 +876,11 @@ int octeon_setup_instr_queues(struct octeon_device *oct) oct->num_iqs = 0; - oct->instr_queue[0] = vmalloc_node(sizeof(*oct->instr_queue[0]), + oct->instr_queue[0] = vzalloc_node(sizeof(*oct->instr_queue[0]), numa_node); if (!oct->instr_queue[0]) oct->instr_queue[0] = - vmalloc(sizeof(struct octeon_instr_queue)); + vzalloc(sizeof(struct octeon_instr_queue)); if (!oct->instr_queue[0]) return 1; memset(oct->instr_queue[0], 0, sizeof(struct octeon_instr_queue)); @@ -923,9 +923,9 @@ int octeon_setup_output_queues(struct octeon_device *oct) desc_size = CFG_GET_DEF_RX_BUF_SIZE(CHIP_CONF(oct, cn23xx_vf)); } oct->num_oqs = 0; - oct->droq[0] = vmalloc_node(sizeof(*oct->droq[0]), numa_node); + oct->droq[0] = vzalloc_node(sizeof(*oct->droq[0]), numa_node); if (!oct->droq[0]) - oct->droq[0] = vmalloc(sizeof(*oct->droq[0])); + oct->droq[0] = vzalloc(sizeof(*oct->droq[0])); if (!oct->droq[0]) return 1; -- cgit v1.2.3 From 9b17010da57ae1a5d0a28c62f5e15abbce35edb0 Mon Sep 17 00:00:00 2001 From: Martin Habets Date: Tue, 18 Jul 2017 16:43:19 +0100 Subject: sfc: Add ethtool -m support for QSFP modules This also adds support for non-QSFP modules attached to QSFP. Signed-off-by: Martin Habets Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/mcdi_port.c | 224 ++++++++++++++++++++++++++++------- 1 file changed, 181 insertions(+), 43 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/sfc/mcdi_port.c b/drivers/net/ethernet/sfc/mcdi_port.c index c905971c5f3a..d3f96a8f743b 100644 --- a/drivers/net/ethernet/sfc/mcdi_port.c +++ b/drivers/net/ethernet/sfc/mcdi_port.c @@ -746,59 +746,171 @@ static const char *efx_mcdi_phy_test_name(struct efx_nic *efx, return NULL; } -#define SFP_PAGE_SIZE 128 -#define SFP_NUM_PAGES 2 -static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, - struct ethtool_eeprom *ee, u8 *data) +#define SFP_PAGE_SIZE 128 +#define SFF_DIAG_TYPE_OFFSET 92 +#define SFF_DIAG_ADDR_CHANGE BIT(2) +#define SFF_8079_NUM_PAGES 2 +#define SFF_8472_NUM_PAGES 4 +#define SFF_8436_NUM_PAGES 5 +#define SFF_DMT_LEVEL_OFFSET 94 + +/** efx_mcdi_phy_get_module_eeprom_page() - Get a single page of module eeprom + * @efx: NIC context + * @page: EEPROM page number + * @data: Destination data pointer + * @offset: Offset in page to copy from in to data + * @space: Space available in data + * + * Return: + * >=0 - amount of data copied + * <0 - error + */ +static int efx_mcdi_phy_get_module_eeprom_page(struct efx_nic *efx, + unsigned int page, + u8 *data, ssize_t offset, + ssize_t space) { MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX); MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN); size_t outlen; - int rc; unsigned int payload_len; - unsigned int space_remaining = ee->len; - unsigned int page; - unsigned int page_off; unsigned int to_copy; - u8 *user_data = data; + int rc; - BUILD_BUG_ON(SFP_PAGE_SIZE * SFP_NUM_PAGES != ETH_MODULE_SFF_8079_LEN); + if (offset > SFP_PAGE_SIZE) + return -EINVAL; - page_off = ee->offset % SFP_PAGE_SIZE; - page = ee->offset / SFP_PAGE_SIZE; + to_copy = min(space, SFP_PAGE_SIZE - offset); - while (space_remaining && (page < SFP_NUM_PAGES)) { - MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page); + MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page); + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_PHY_MEDIA_INFO, + inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), + &outlen); - rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_MEDIA_INFO, - inbuf, sizeof(inbuf), - outbuf, sizeof(outbuf), - &outlen); - if (rc) - return rc; + if (rc) + return rc; + + if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST + + SFP_PAGE_SIZE)) + return -EIO; + + payload_len = MCDI_DWORD(outbuf, GET_PHY_MEDIA_INFO_OUT_DATALEN); + if (payload_len != SFP_PAGE_SIZE) + return -EIO; - if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST + - SFP_PAGE_SIZE)) - return -EIO; + memcpy(data, MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + offset, + to_copy); - payload_len = MCDI_DWORD(outbuf, - GET_PHY_MEDIA_INFO_OUT_DATALEN); - if (payload_len != SFP_PAGE_SIZE) - return -EIO; + return to_copy; +} - /* Copy as much as we can into data */ - payload_len -= page_off; - to_copy = (space_remaining < payload_len) ? - space_remaining : payload_len; +static int efx_mcdi_phy_get_module_eeprom_byte(struct efx_nic *efx, + unsigned int page, + u8 byte) +{ + int rc; + u8 data; - memcpy(user_data, - MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + page_off, - to_copy); + rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, &data, byte, 1); + if (rc == 1) + return data; + + return rc; +} + +static int efx_mcdi_phy_diag_type(struct efx_nic *efx) +{ + /* Page zero of the EEPROM includes the diagnostic type at byte 92. */ + return efx_mcdi_phy_get_module_eeprom_byte(efx, 0, + SFF_DIAG_TYPE_OFFSET); +} - space_remaining -= to_copy; - user_data += to_copy; - page_off = 0; - page++; +static int efx_mcdi_phy_sff_8472_level(struct efx_nic *efx) +{ + /* Page zero of the EEPROM includes the DMT level at byte 94. */ + return efx_mcdi_phy_get_module_eeprom_byte(efx, 0, + SFF_DMT_LEVEL_OFFSET); +} + +static u32 efx_mcdi_phy_module_type(struct efx_nic *efx) +{ + struct efx_mcdi_phy_data *phy_data = efx->phy_data; + + if (phy_data->media != MC_CMD_MEDIA_QSFP_PLUS) + return phy_data->media; + + /* A QSFP+ NIC may actually have an SFP+ module attached. + * The ID is page 0, byte 0. + */ + switch (efx_mcdi_phy_get_module_eeprom_byte(efx, 0, 0)) { + case 0x3: + return MC_CMD_MEDIA_SFP_PLUS; + case 0xc: + case 0xd: + return MC_CMD_MEDIA_QSFP_PLUS; + default: + return 0; + } +} + +static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, + struct ethtool_eeprom *ee, u8 *data) +{ + int rc; + ssize_t space_remaining = ee->len; + unsigned int page_off; + bool ignore_missing; + int num_pages; + int page; + + switch (efx_mcdi_phy_module_type(efx)) { + case MC_CMD_MEDIA_SFP_PLUS: + num_pages = efx_mcdi_phy_sff_8472_level(efx) > 0 ? + SFF_8472_NUM_PAGES : SFF_8079_NUM_PAGES; + page = 0; + ignore_missing = false; + break; + case MC_CMD_MEDIA_QSFP_PLUS: + num_pages = SFF_8436_NUM_PAGES; + page = -1; /* We obtain the lower page by asking for -1. */ + ignore_missing = true; /* Ignore missing pages after page 0. */ + break; + default: + return -EOPNOTSUPP; + } + + page_off = ee->offset % SFP_PAGE_SIZE; + page += ee->offset / SFP_PAGE_SIZE; + + while (space_remaining && (page < num_pages)) { + rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, + data, page_off, + space_remaining); + + if (rc > 0) { + space_remaining -= rc; + data += rc; + page_off = 0; + page++; + } else if (rc == 0) { + space_remaining = 0; + } else if (ignore_missing && (page > 0)) { + int intended_size = SFP_PAGE_SIZE - page_off; + + space_remaining -= intended_size; + if (space_remaining < 0) { + space_remaining = 0; + } else { + memset(data, 0, intended_size); + data += intended_size; + page_off = 0; + page++; + rc = 0; + } + } else { + return rc; + } } return 0; @@ -807,16 +919,42 @@ static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, static int efx_mcdi_phy_get_module_info(struct efx_nic *efx, struct ethtool_modinfo *modinfo) { - struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; + int sff_8472_level; + int diag_type; - switch (phy_cfg->media) { + switch (efx_mcdi_phy_module_type(efx)) { case MC_CMD_MEDIA_SFP_PLUS: - modinfo->type = ETH_MODULE_SFF_8079; - modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; - return 0; + sff_8472_level = efx_mcdi_phy_sff_8472_level(efx); + + /* If we can't read the diagnostics level we have none. */ + if (sff_8472_level < 0) + return -EOPNOTSUPP; + + /* Check if this module requires the (unsupported) address + * change operation. + */ + diag_type = efx_mcdi_phy_diag_type(efx); + + if ((sff_8472_level == 0) || + (diag_type & SFF_DIAG_ADDR_CHANGE)) { + modinfo->type = ETH_MODULE_SFF_8079; + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; + } else { + modinfo->type = ETH_MODULE_SFF_8472; + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + } + break; + + case MC_CMD_MEDIA_QSFP_PLUS: + modinfo->type = ETH_MODULE_SFF_8436; + modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; + break; + default: return -EOPNOTSUPP; } + + return 0; } static const struct efx_phy_operations efx_mcdi_phy_ops = { -- cgit v1.2.3 From 06548fbb609d54fd8c03dd4cdad1059ddb44378c Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 18 Jul 2017 15:37:11 -0500 Subject: wireless: airo: remove unnecessary static in writerids() Remove unnecessary static on local function pointer _writer_. Such pointer is initialized before being used, on every execution path throughout the function. The static has no benefit and, removing it reduces the object file size. This issue was detected using Coccinelle and the following semantic patch: @bad exists@ position p; identifier x; type T; @@ static T x@p; ... x = <+...x...+> @@ identifier x; expression e; type T; position p != bad.p; @@ -static T x@p; ... when != x when strict ?x = e; In the following log you can see a significant difference in the object file size. This log is the output of the size command, before and after the code change: before: text data bss dec hex filename 113797 19152 1216 134165 20c15 drivers/net/wireless/cisco/airo.o after: text data bss dec hex filename 113881 19096 1152 134129 20bf1 drivers/net/wireless/cisco/airo.o Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- drivers/net/wireless/cisco/airo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c index 84143a02adce..54201c02fdb8 100644 --- a/drivers/net/wireless/cisco/airo.c +++ b/drivers/net/wireless/cisco/airo.c @@ -7837,7 +7837,7 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) { struct airo_info *ai = dev->ml_priv; int ridcode; int enabled; - static int (* writer)(struct airo_info *, u16 rid, const void *, int, int); + int (*writer)(struct airo_info *, u16 rid, const void *, int, int); unsigned char *iobuf; /* Only super-user can write RIDs */ -- cgit v1.2.3 From f55dda4bded9d2042d296b9215bf6453f7411a0d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 18 Jul 2017 15:41:06 -0500 Subject: rtlwifi: remove useless code Remove useless local variables last_read_point and last_txw_point and the code related. Signed-off-by: Gustavo A. R. Silva Acked-by: Larry Finger Signed-off-by: David S. Miller --- drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c index 55f238a2a310..c58393eab6a1 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c @@ -478,7 +478,6 @@ u16 rtl92ee_rx_desc_buff_remained_cnt(struct ieee80211_hw *hw, u8 queue_index) struct rtl_priv *rtlpriv = rtl_priv(hw); u16 read_point = 0, write_point = 0, remind_cnt = 0; u32 tmp_4byte = 0; - static u16 last_read_point; static bool start_rx; tmp_4byte = rtl_read_dword(rtlpriv, REG_RXQ_TXBD_IDX); @@ -506,7 +505,6 @@ u16 rtl92ee_rx_desc_buff_remained_cnt(struct ieee80211_hw *hw, u8 queue_index) rtlpci->rx_ring[queue_index].next_rx_rp = write_point; - last_read_point = read_point; return remind_cnt; } @@ -917,7 +915,6 @@ void rtl92ee_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, struct rtl_priv *rtlpriv = rtl_priv(hw); u16 cur_tx_rp = 0; u16 cur_tx_wp = 0; - static u16 last_txw_point; static bool over_run; u32 tmp = 0; u8 q_idx = *val; @@ -951,9 +948,6 @@ void rtl92ee_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, rtl_write_word(rtlpriv, get_desc_addr_fr_q_idx(q_idx), ring->cur_tx_wp); - - if (q_idx == 1) - last_txw_point = cur_tx_wp; } if (ring->avl_desc < (max_tx_desc - 15)) { -- cgit v1.2.3 From 648e8b86d0ae01bd5a61df2d776b70ed8b96e2b8 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 18 Jul 2017 15:43:33 -0500 Subject: net: tulip: remove useless code in tulip_init_one() Remove useless local variable multiport_cnt and the code related. Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- drivers/net/ethernet/dec/tulip/tulip_core.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 17e566a8b345..84394b43c0a1 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -1303,7 +1303,6 @@ static int tulip_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 0x00, 'L', 'i', 'n', 'u', 'x' }; static int last_irq; - static int multiport_cnt; /* For four-port boards w/one EEPROM */ int i, irq; unsigned short sum; unsigned char *ee_data; @@ -1557,7 +1556,6 @@ static int tulip_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } else if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) { sa_offset = 2; /* Grrr, damn Matrox boards. */ - multiport_cnt = 4; } #ifdef CONFIG_MIPS_COBALT if ((pdev->bus->number == 0) && -- cgit v1.2.3 From 048578a1954f6c473ba12c9b4519e8dce4a4549b Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 18 Jul 2017 15:45:29 -0500 Subject: qlcnic: remove unnecessary static in qlcnic_dump_fw() Remove unnecessary static on local variable fw_dump_ops. Such variable is initialized before being used, on every execution path throughout the function. The static has no benefit and, removing it reduces the object file size. This issue was detected using Coccinelle and the following semantic patch: @bad exists@ position p; identifier x; type T; @@ static T x@p; ... x = <+...x...+> @@ identifier x; expression e; type T; position p != bad.p; @@ -static T x@p; ... when != x when strict ?x = e; In the following log you can see a difference in the object file size. This log is the output of the size command, before and after the code change: before: text data bss dec hex filename 19032 2136 64 21232 52f0 drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.o after: text data bss dec hex filename 19020 2048 0 21068 524c drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.o Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index 0844b7c75767..afa10a163da1 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -1285,7 +1285,7 @@ flash_temp: int qlcnic_dump_fw(struct qlcnic_adapter *adapter) { struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; - static const struct qlcnic_dump_operations *fw_dump_ops; + const struct qlcnic_dump_operations *fw_dump_ops; struct qlcnic_83xx_dump_template_hdr *hdr_83xx; u32 entry_offset, dump, no_entries, buf_offset = 0; int i, k, ops_cnt, ops_index, dump_size = 0; -- cgit v1.2.3 From f03b06f3bae893d0f4fbe7c41adc1d45016bb974 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 18 Jul 2017 15:48:06 -0500 Subject: net: ethernet: mediatek: remove useless code in mtk_poll_tx() Remove useless local variable _condition_ and the code related. Signed-off-by: Gustavo A. R. Silva Acked-by: Sean Wang Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index b3d0c2e6347a..7e95cf547ff1 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1027,7 +1027,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget) unsigned int done[MTK_MAX_DEVS]; unsigned int bytes[MTK_MAX_DEVS]; u32 cpu, dma; - static int condition; int total = 0, i; memset(done, 0, sizeof(done)); @@ -1051,10 +1050,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget) mac = 1; skb = tx_buf->skb; - if (!skb) { - condition = 1; + if (!skb) break; - } if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) { bytes[mac] += skb->len; -- cgit v1.2.3 From 93fe07e52ea1218a78f3886d23479958e14a0eea Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 18 Jul 2017 15:50:15 -0500 Subject: liquidio: lio_vf_main: remove unnecessary static in setup_io_queues() Remove unnecessary static on local variables cpu_id_modulus and cpu_id. Such variables are initialized before being used, on every execution path throughout the function. The static has no benefit and, removing it reduces the object file size. This issue was detected using Coccinelle and the following semantic patch: @bad exists@ position p; identifier x; type T; @@ static T x@p; ... x = <+...x...+> @@ identifier x; expression e; type T; position p != bad.p; @@ -static T x@p; ... when != x when strict ?x = e; In the following log you can see a significant difference in the object file size. Also, there is a significant difference in the bss segment. This log is the output of the size command, before and after the code change: before: text data bss dec hex filename 55656 10680 576 66912 10560 drivers/net/ethernet/cavium/liquidio/lio_vf_main.o after: text data bss dec hex filename 55796 10536 448 66780 104dc drivers/net/ethernet/cavium/liquidio/lio_vf_main.o Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 9b247102eb92..935ff299cdd9 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1663,10 +1663,10 @@ static int setup_io_queues(struct octeon_device *octeon_dev, int ifidx) { struct octeon_droq_ops droq_ops; struct net_device *netdev; - static int cpu_id_modulus; + int cpu_id_modulus; struct octeon_droq *droq; struct napi_struct *napi; - static int cpu_id; + int cpu_id; int num_tx_descs; struct lio *lio; int retval = 0; -- cgit v1.2.3 From eb2b98742641ae544fd16311055789ab7cac62aa Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 18 Jul 2017 15:53:48 -0500 Subject: liquidio: lio_main: remove unnecessary static in setup_io_queues() Remove unnecessary static on local variables cpu_id_modulus and cpu_id. Such variables are initialized before being used, on every execution path throughout the function. The static has no benefit and, removing it reduces the object file size. This issue was detected using Coccinelle and the following semantic patch: @bad exists@ position p; identifier x; type T; @@ static T x@p; ... x = <+...x...+> @@ identifier x; expression e; type T; position p != bad.p; @@ -static T x@p; ... when != x when strict ?x = e; In the following log you can see a significant difference in the object file size. Also, there is a significant difference in the bss segment. This log is the output of the size command, before and after the code change: before: text data bss dec hex filename 78689 15272 27808 121769 1dba9 drivers/net/ethernet/cavium/liquidio/lio_main.o after: text data bss dec hex filename 78667 15128 27680 121475 1da83 drivers/net/ethernet/cavium/liquidio/lio_main.o Signed-off-by: Gustavo A. R. Silva Acked-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 51583ae4b1eb..1d8fefa9ce64 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2544,8 +2544,8 @@ static inline int setup_io_queues(struct octeon_device *octeon_dev, { struct octeon_droq_ops droq_ops; struct net_device *netdev; - static int cpu_id; - static int cpu_id_modulus; + int cpu_id; + int cpu_id_modulus; struct octeon_droq *droq; struct napi_struct *napi; int q, q_no, retval = 0; -- cgit v1.2.3 From 4a614dd3e550b66a2e57119e2d5e0fbc7f17f634 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 18 Jul 2017 15:59:26 -0700 Subject: hns: remove useless void cast There is no need to cast away return value of dev_close. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index a8db27e86a11..78cb20c67aa6 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -595,7 +595,7 @@ static void hns_nic_self_test(struct net_device *ndev, set_bit(NIC_STATE_TESTING, &priv->state); if (if_running) - (void)dev_close(ndev); + dev_close(ndev); for (i = 0; i < SELF_TEST_TPYE_NUM; i++) { if (!st_param[i][1]) -- cgit v1.2.3 From 1b01994aa6f9bad60d2b162c17dca530c21e2687 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 19 Jul 2017 11:53:12 -0700 Subject: netvsc: force link update after MTU change If two MTU changes are in less than update interval (2 seconds), then the netvsc network device may get stuck with no carrier. The netvsc driver debounces link status events which is fine for unsolicited updates, but blocks getting the update after down/up from MTU reinitialization. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 63c98bbbc596..09b07ca9e69a 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -783,6 +783,7 @@ static int netvsc_set_channels(struct net_device *net, ret = netvsc_open(net); /* We may have missed link change notifications */ + net_device_ctx->last_reconfig = 0; schedule_delayed_work(&net_device_ctx->dwork, 0); return ret; -- cgit v1.2.3 From 79e8cbe7a789a0863cc6cd874872b4dd63ec5947 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 19 Jul 2017 11:53:13 -0700 Subject: netvsc: add some rtnl_dereference annotations In a couple places RTNL is held, and the netvsc_device pointer is acquired without annotation. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 5 +++-- drivers/net/hyperv/netvsc_drv.c | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0a9167dd72fb..e202ec5d6f63 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -41,7 +41,7 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf) { struct net_device_context *net_device_ctx = netdev_priv(ndev); struct hv_device *dev = net_device_ctx->device_ctx; - struct netvsc_device *nv_dev = net_device_ctx->nvdev; + struct netvsc_device *nv_dev = rtnl_dereference(net_device_ctx->nvdev); struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt; memset(init_pkt, 0, sizeof(struct nvsp_message)); @@ -549,7 +549,8 @@ void netvsc_device_remove(struct hv_device *device) { struct net_device *ndev = hv_get_drvdata(device); struct net_device_context *net_device_ctx = netdev_priv(ndev); - struct netvsc_device *net_device = net_device_ctx->nvdev; + struct netvsc_device *net_device + = rtnl_dereference(net_device_ctx->nvdev); int i; netvsc_disconnect_vsp(device); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 09b07ca9e69a..e8e82a6a4b1a 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -69,7 +69,7 @@ static void netvsc_set_multicast_list(struct net_device *net) static int netvsc_open(struct net_device *net) { struct net_device_context *ndev_ctx = netdev_priv(net); - struct netvsc_device *nvdev = ndev_ctx->nvdev; + struct netvsc_device *nvdev = rtnl_dereference(ndev_ctx->nvdev); struct rndis_device *rdev; int ret = 0; @@ -1364,7 +1364,7 @@ static struct net_device *get_netvsc_byref(struct net_device *vf_netdev) continue; /* not a netvsc device */ net_device_ctx = netdev_priv(dev); - if (net_device_ctx->nvdev == NULL) + if (!rtnl_dereference(net_device_ctx->nvdev)) continue; /* device is removed */ if (rtnl_dereference(net_device_ctx->vf_netdev) == vf_netdev) @@ -1589,7 +1589,8 @@ static int netvsc_remove(struct hv_device *dev) * removed. Also blocks mtu and channel changes. */ rtnl_lock(); - rndis_filter_device_remove(dev, ndev_ctx->nvdev); + rndis_filter_device_remove(dev, + rtnl_dereference(ndev_ctx->nvdev)); rtnl_unlock(); unregister_netdev(net); -- cgit v1.2.3 From a5e1ec3833211bc0136649a89ee3a29355c7b43b Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 19 Jul 2017 11:53:14 -0700 Subject: netvsc: change order of steps in setting queues This fixes the error unwind logic for incorrect number of queues. If netif_set_real_num_XX_queues failed then rndis_filter_device_add would have been called twice. Since input arguments are already ranged checked this is a hypothetical only problem, not possible in actual code. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index e8e82a6a4b1a..91637336d1fb 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -724,17 +724,15 @@ static int netvsc_set_queues(struct net_device *net, struct hv_device *dev, device_info.ring_size = ring_size; device_info.max_num_vrss_chns = num_chn; - ret = rndis_filter_device_add(dev, &device_info); - if (ret) - return ret; - ret = netif_set_real_num_tx_queues(net, num_chn); if (ret) return ret; ret = netif_set_real_num_rx_queues(net, num_chn); + if (ret) + return ret; - return ret; + return rndis_filter_device_add(dev, &device_info); } static int netvsc_set_channels(struct net_device *net, -- cgit v1.2.3 From ea383bf146be1e190f1d696e7db060afa8c93c31 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 19 Jul 2017 11:53:15 -0700 Subject: netvsc: change logic for change mtu and set_queues Use device detach/attach to ensure that no packets are handed to device during state changes. Call rndis_filter_open/close directly as part of later VF related changes. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 + drivers/net/hyperv/netvsc_drv.c | 38 ++++++++++++++++++-------------------- drivers/net/hyperv/rndis_filter.c | 5 +++++ 3 files changed, 24 insertions(+), 20 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index d6c25580f8dd..5d541a1462c2 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -200,6 +200,7 @@ int netvsc_recv_callback(struct net_device *net, const struct ndis_pkt_8021q_info *vlan); void netvsc_channel_cb(void *context); int netvsc_poll(struct napi_struct *napi, int budget); +bool rndis_filter_opened(const struct netvsc_device *nvdev); int rndis_filter_open(struct netvsc_device *nvdev); int rndis_filter_close(struct netvsc_device *nvdev); int rndis_filter_device_add(struct hv_device *dev, diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 91637336d1fb..82e41c056e53 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -742,7 +742,7 @@ static int netvsc_set_channels(struct net_device *net, struct hv_device *dev = net_device_ctx->device_ctx; struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); unsigned int count = channels->combined_count; - bool was_running; + bool was_opened; int ret; /* We do not support separate count for rx, tx, or other */ @@ -762,12 +762,9 @@ static int netvsc_set_channels(struct net_device *net, if (count > nvdev->max_chn) return -EINVAL; - was_running = netif_running(net); - if (was_running) { - ret = netvsc_close(net); - if (ret) - return ret; - } + was_opened = rndis_filter_opened(nvdev); + if (was_opened) + rndis_filter_close(nvdev); rndis_filter_device_remove(dev, nvdev); @@ -777,8 +774,9 @@ static int netvsc_set_channels(struct net_device *net, else netvsc_set_queues(net, dev, nvdev->num_chn); - if (was_running) - ret = netvsc_open(net); + nvdev = rtnl_dereference(net_device_ctx->nvdev); + if (was_opened) + rndis_filter_open(nvdev); /* We may have missed link change notifications */ net_device_ctx->last_reconfig = 0; @@ -848,18 +846,15 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev); struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device_info device_info; - bool was_running; - int ret = 0; + bool was_opened; if (!nvdev || nvdev->destroy) return -ENODEV; - was_running = netif_running(ndev); - if (was_running) { - ret = netvsc_close(ndev); - if (ret) - return ret; - } + netif_device_detach(ndev); + was_opened = rndis_filter_opened(nvdev); + if (was_opened) + rndis_filter_close(nvdev); memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; @@ -877,14 +872,17 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) ndev->mtu = mtu; rndis_filter_device_add(hdev, &device_info); + nvdev = rtnl_dereference(ndevctx->nvdev); - if (was_running) - ret = netvsc_open(ndev); + if (was_opened) + rndis_filter_open(nvdev); + + netif_device_attach(ndev); /* We may have missed link change notifications */ schedule_delayed_work(&ndevctx->dwork, 0); - return ret; + return 0; } static void netvsc_get_stats64(struct net_device *net, diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 85c00e1c52b6..313c6d00d7d9 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1302,3 +1302,8 @@ int rndis_filter_close(struct netvsc_device *nvdev) return rndis_filter_close_device(nvdev->extension); } + +bool rndis_filter_opened(const struct netvsc_device *nvdev) +{ + return atomic_read(&nvdev->open_cnt) > 0; +} -- cgit v1.2.3 From 9749fed5d43d84b86f1c98b70167c31c296bb6a6 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 19 Jul 2017 11:53:16 -0700 Subject: netvsc: use ERR_PTR to avoid dereference issues The rndis_filter_device_add function is called both in probe context and RTNL context,and creates the netvsc_device inner structure. It is easier to get the RTNL lock annotation correct if it returns the object directly, rather than implicitly by updating network device private data. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 8 ++++---- drivers/net/hyperv/netvsc.c | 13 ++++++------ drivers/net/hyperv/netvsc_drv.c | 34 ++++++++++++++++++------------- drivers/net/hyperv/rndis_filter.c | 43 ++++++++++++++++----------------------- 4 files changed, 49 insertions(+), 49 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 5d541a1462c2..e620374727c8 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -183,8 +183,8 @@ struct rndis_device { /* Interface */ struct rndis_message; struct netvsc_device; -int netvsc_device_add(struct hv_device *device, - const struct netvsc_device_info *info); +struct netvsc_device *netvsc_device_add(struct hv_device *device, + const struct netvsc_device_info *info); void netvsc_device_remove(struct hv_device *device); int netvsc_send(struct hv_device *device, struct hv_netvsc_packet *packet, @@ -203,8 +203,8 @@ int netvsc_poll(struct napi_struct *napi, int budget); bool rndis_filter_opened(const struct netvsc_device *nvdev); int rndis_filter_open(struct netvsc_device *nvdev); int rndis_filter_close(struct netvsc_device *nvdev); -int rndis_filter_device_add(struct hv_device *dev, - struct netvsc_device_info *info); +struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, + struct netvsc_device_info *info); void rndis_filter_update(struct netvsc_device *nvdev); void rndis_filter_device_remove(struct hv_device *dev, struct netvsc_device *nvdev); diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index e202ec5d6f63..4a2550559442 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -29,6 +29,8 @@ #include #include #include +#include + #include #include "hyperv_net.h" @@ -1272,8 +1274,8 @@ void netvsc_channel_cb(void *context) * netvsc_device_add - Callback when the device belonging to this * driver is added */ -int netvsc_device_add(struct hv_device *device, - const struct netvsc_device_info *device_info) +struct netvsc_device *netvsc_device_add(struct hv_device *device, + const struct netvsc_device_info *device_info) { int i, ret = 0; int ring_size = device_info->ring_size; @@ -1283,7 +1285,7 @@ int netvsc_device_add(struct hv_device *device, net_device = alloc_net_device(); if (!net_device) - return -ENOMEM; + return ERR_PTR(-ENOMEM); net_device->ring_size = ring_size; @@ -1339,7 +1341,7 @@ int netvsc_device_add(struct hv_device *device, goto close; } - return ret; + return net_device; close: netif_napi_del(&net_device->chan_table[0].napi); @@ -1350,6 +1352,5 @@ close: cleanup: free_netvsc_device(&net_device->rcu); - return ret; - + return ERR_PTR(ret); } diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 82e41c056e53..0ca8c74143b4 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -717,6 +717,7 @@ static int netvsc_set_queues(struct net_device *net, struct hv_device *dev, u32 num_chn) { struct netvsc_device_info device_info; + struct netvsc_device *net_device; int ret; memset(&device_info, 0, sizeof(device_info)); @@ -732,7 +733,8 @@ static int netvsc_set_queues(struct net_device *net, struct hv_device *dev, if (ret) return ret; - return rndis_filter_device_add(dev, &device_info); + net_device = rndis_filter_device_add(dev, &device_info); + return IS_ERR(net_device) ? PTR_ERR(net_device) : 0; } static int netvsc_set_channels(struct net_device *net, @@ -845,8 +847,10 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) struct net_device_context *ndevctx = netdev_priv(ndev); struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev); struct hv_device *hdev = ndevctx->device_ctx; + int orig_mtu = ndev->mtu; struct netvsc_device_info device_info; bool was_opened; + int ret = 0; if (!nvdev || nvdev->destroy) return -ENODEV; @@ -863,16 +867,16 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) rndis_filter_device_remove(hdev, nvdev); - /* 'nvdev' has been freed in rndis_filter_device_remove() -> - * netvsc_device_remove () -> free_netvsc_device(). - * We mustn't access it before it's re-created in - * rndis_filter_device_add() -> netvsc_device_add(). - */ - ndev->mtu = mtu; - rndis_filter_device_add(hdev, &device_info); - nvdev = rtnl_dereference(ndevctx->nvdev); + nvdev = rndis_filter_device_add(hdev, &device_info); + if (IS_ERR(nvdev)) { + ret = PTR_ERR(nvdev); + + /* Attempt rollback to original MTU */ + ndev->mtu = orig_mtu; + rndis_filter_device_add(hdev, &device_info); + } if (was_opened) rndis_filter_open(nvdev); @@ -882,7 +886,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) /* We may have missed link change notifications */ schedule_delayed_work(&ndevctx->dwork, 0); - return 0; + return ret; } static void netvsc_get_stats64(struct net_device *net, @@ -1525,8 +1529,10 @@ static int netvsc_probe(struct hv_device *dev, memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; device_info.num_chn = VRSS_CHANNEL_DEFAULT; - ret = rndis_filter_device_add(dev, &device_info); - if (ret != 0) { + + nvdev = rndis_filter_device_add(dev, &device_info); + if (IS_ERR(nvdev)) { + ret = PTR_ERR(nvdev); netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); free_netdev(net); hv_set_drvdata(dev, NULL); @@ -1540,11 +1546,11 @@ static int netvsc_probe(struct hv_device *dev, NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; net->vlan_features = net->features; - /* RCU not necessary here, device not registered */ - nvdev = net_device_ctx->nvdev; netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); + netdev_lockdep_set_classes(net); + /* MTU range: 68 - 1500 or 65521 */ net->min_mtu = NETVSC_MTU_MIN; if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2) diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 313c6d00d7d9..cacf1e5536f7 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -658,9 +658,9 @@ cleanup: static int rndis_filter_set_offload_params(struct net_device *ndev, + struct netvsc_device *nvdev, struct ndis_offload_params *req_offloads) { - struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev); struct rndis_device *rdev = nvdev->extension; struct rndis_request *request; struct rndis_set_request *set; @@ -1052,8 +1052,8 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) complete(&nvscdev->channel_init_wait); } -int rndis_filter_device_add(struct hv_device *dev, - struct netvsc_device_info *device_info) +struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, + struct netvsc_device_info *device_info) { struct net_device *net = hv_get_drvdata(dev); struct net_device_context *net_device_ctx = netdev_priv(net); @@ -1072,21 +1072,20 @@ int rndis_filter_device_add(struct hv_device *dev, rndis_device = get_rndis_device(); if (!rndis_device) - return -ENODEV; + return ERR_PTR(-ENODEV); /* * Let the inner driver handle this first to create the netvsc channel * NOTE! Once the channel is created, we may get a receive callback * (RndisFilterOnReceive()) before this call is completed */ - ret = netvsc_device_add(dev, device_info); - if (ret != 0) { + net_device = netvsc_device_add(dev, device_info); + if (IS_ERR(net_device)) { kfree(rndis_device); - return ret; + return net_device; } /* Initialize the rndis device */ - net_device = net_device_ctx->nvdev; net_device->max_chn = 1; net_device->num_chn = 1; @@ -1097,10 +1096,8 @@ int rndis_filter_device_add(struct hv_device *dev, /* Send the rndis initialization message */ ret = rndis_filter_init_device(rndis_device); - if (ret != 0) { - rndis_filter_device_remove(dev, net_device); - return ret; - } + if (ret != 0) + goto err_dev_remv; /* Get the MTU from the host */ size = sizeof(u32); @@ -1112,19 +1109,15 @@ int rndis_filter_device_add(struct hv_device *dev, /* Get the mac address */ ret = rndis_filter_query_device_mac(rndis_device); - if (ret != 0) { - rndis_filter_device_remove(dev, net_device); - return ret; - } + if (ret != 0) + goto err_dev_remv; memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); /* Find HW offload capabilities */ ret = rndis_query_hwcaps(rndis_device, &hwcaps); - if (ret != 0) { - rndis_filter_device_remove(dev, net_device); - return ret; - } + if (ret != 0) + goto err_dev_remv; /* A value of zero means "no change"; now turn on what we want. */ memset(&offloads, 0, sizeof(struct ndis_offload_params)); @@ -1179,7 +1172,7 @@ int rndis_filter_device_add(struct hv_device *dev, netif_set_gso_max_size(net, gso_max_size); - ret = rndis_filter_set_offload_params(net, &offloads); + ret = rndis_filter_set_offload_params(net, net_device, &offloads); if (ret) goto err_dev_remv; @@ -1190,7 +1183,7 @@ int rndis_filter_device_add(struct hv_device *dev, rndis_device->link_state ? "down" : "up"); if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5) - return 0; + return net_device; rndis_filter_query_link_speed(rndis_device); @@ -1223,7 +1216,7 @@ int rndis_filter_device_add(struct hv_device *dev, num_rss_qs = net_device->num_chn - 1; if (num_rss_qs == 0) - return 0; + return net_device; refcount_set(&net_device->sc_offered, num_rss_qs); vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); @@ -1260,11 +1253,11 @@ out: net_device->num_chn = 1; } - return 0; /* return 0 because primary channel can be used alone */ + return net_device; err_dev_remv: rndis_filter_device_remove(dev, net_device); - return ret; + return ERR_PTR(ret); } void rndis_filter_device_remove(struct hv_device *dev, -- cgit v1.2.3 From 2a926f791211b40ba114f45e0e7bfefd2fac5d30 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 19 Jul 2017 11:53:17 -0700 Subject: netvsc: need rcu_derefence when accessing internal device info The netvsc_device structure should be accessed by rcu_dereference in the send path. Change arguments to netvsc_send() to make this easier to do correctly. Remove no longer needed hv_device_to_netvsc_device. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 10 +++------- drivers/net/hyperv/netvsc.c | 8 +++++--- drivers/net/hyperv/netvsc_drv.c | 4 ++-- drivers/net/hyperv/rndis_filter.c | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index e620374727c8..0054b6929f6e 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -183,10 +183,12 @@ struct rndis_device { /* Interface */ struct rndis_message; struct netvsc_device; +struct net_device_context; + struct netvsc_device *netvsc_device_add(struct hv_device *device, const struct netvsc_device_info *info); void netvsc_device_remove(struct hv_device *device); -int netvsc_send(struct hv_device *device, +int netvsc_send(struct net_device_context *ndc, struct hv_netvsc_packet *packet, struct rndis_message *rndis_msg, struct hv_page_buffer **page_buffer, @@ -790,12 +792,6 @@ net_device_to_netvsc_device(struct net_device *ndev) return ((struct net_device_context *)netdev_priv(ndev))->nvdev; } -static inline struct netvsc_device * -hv_device_to_netvsc_device(struct hv_device *device) -{ - return net_device_to_netvsc_device(hv_get_drvdata(device)); -} - /* NdisInitialize message */ struct rndis_initialize_request { u32 req_id; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 4a2550559442..3c6f3ae520d9 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -822,13 +822,15 @@ static inline void move_pkt_msd(struct hv_netvsc_packet **msd_send, msdp->count = 0; } -int netvsc_send(struct hv_device *device, +/* RCU already held by caller */ +int netvsc_send(struct net_device_context *ndev_ctx, struct hv_netvsc_packet *packet, struct rndis_message *rndis_msg, struct hv_page_buffer **pb, struct sk_buff *skb) { - struct netvsc_device *net_device = hv_device_to_netvsc_device(device); + struct netvsc_device *net_device = rcu_dereference(ndev_ctx->nvdev); + struct hv_device *device = ndev_ctx->device_ctx; int ret = 0; struct netvsc_channel *nvchan; u32 pktlen = packet->total_data_buflen, msd_len = 0; @@ -840,7 +842,7 @@ int netvsc_send(struct hv_device *device, bool xmit_more = (skb != NULL) ? skb->xmit_more : false; /* If device is rescinded, return error and packet will get dropped. */ - if (unlikely(net_device->destroy)) + if (unlikely(!net_device || net_device->destroy)) return -ENODEV; /* We may race with netvsc_connect_vsp()/netvsc_init_buf() and get diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 0ca8c74143b4..1238600d717e 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -505,8 +505,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) /* timestamp packet in software */ skb_tx_timestamp(skb); - ret = netvsc_send(net_device_ctx->device_ctx, packet, - rndis_msg, &pb, skb); + + ret = netvsc_send(net_device_ctx, packet, rndis_msg, &pb, skb); if (likely(ret == 0)) return NETDEV_TX_OK; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index cacf1e5536f7..9ab67c8309ff 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -243,7 +243,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, pb[0].len; } - ret = netvsc_send(net_device_ctx->device_ctx, packet, NULL, &pb, NULL); + ret = netvsc_send(net_device_ctx, packet, NULL, &pb, NULL); return ret; } -- cgit v1.2.3 From 35fbbccfb417385c1c8cc6f799154ea1ebdc22ef Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 19 Jul 2017 11:53:18 -0700 Subject: netvsc: save pointer to parent netvsc_device in channel table Keep back pointer in the per-channel data structure to avoid any possible RCU related issues when napi poll is called but netvsc_device is in RCU limbo. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 + drivers/net/hyperv/netvsc.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 0054b6929f6e..d13572879e7e 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -727,6 +727,7 @@ struct net_device_context { /* Per channel data */ struct netvsc_channel { struct vmbus_channel *channel; + struct netvsc_device *net_device; const struct vmpacket_descriptor *desc; struct napi_struct napi; struct multi_send_data msd; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 3c6f3ae520d9..c15640c6fd83 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1224,11 +1224,11 @@ int netvsc_poll(struct napi_struct *napi, int budget) { struct netvsc_channel *nvchan = container_of(napi, struct netvsc_channel, napi); + struct netvsc_device *net_device = nvchan->net_device; struct vmbus_channel *channel = nvchan->channel; struct hv_device *device = netvsc_channel_to_device(channel); u16 q_idx = channel->offermsg.offer.sub_channel_index; struct net_device *ndev = hv_get_drvdata(device); - struct netvsc_device *net_device = net_device_to_netvsc_device(ndev); int work_done = 0; /* If starting a new interval */ @@ -1307,6 +1307,7 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device, struct netvsc_channel *nvchan = &net_device->chan_table[i]; nvchan->channel = device->channel; + nvchan->net_device = net_device; } /* Enable NAPI handler before init callbacks */ -- cgit v1.2.3 From 3962981f4822aaf284234efd0500041417faea86 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 19 Jul 2017 11:53:19 -0700 Subject: netvsc: add rtnl annotations in rndis The rndis functions are used when changing device state. Therefore the references from network device to internal state are protected by RTNL mutex. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 6 ------ drivers/net/hyperv/netvsc.c | 6 ++++-- drivers/net/hyperv/netvsc_drv.c | 1 + drivers/net/hyperv/rndis_filter.c | 12 ++++++++++-- 4 files changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index d13572879e7e..afb65f753574 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -787,12 +787,6 @@ struct netvsc_device { struct rcu_head rcu; }; -static inline struct netvsc_device * -net_device_to_netvsc_device(struct net_device *ndev) -{ - return ((struct net_device_context *)netdev_priv(ndev))->nvdev; -} - /* NdisInitialize message */ struct rndis_initialize_request { u32 req_id; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index c15640c6fd83..0a9d9feedc3f 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -105,7 +105,8 @@ static void netvsc_destroy_buf(struct hv_device *device) { struct nvsp_message *revoke_packet; struct net_device *ndev = hv_get_drvdata(device); - struct netvsc_device *net_device = net_device_to_netvsc_device(ndev); + struct net_device_context *ndc = netdev_priv(ndev); + struct netvsc_device *net_device = rtnl_dereference(ndc->nvdev); int ret; /* @@ -829,7 +830,8 @@ int netvsc_send(struct net_device_context *ndev_ctx, struct hv_page_buffer **pb, struct sk_buff *skb) { - struct netvsc_device *net_device = rcu_dereference(ndev_ctx->nvdev); + struct netvsc_device *net_device + = rcu_dereference_rtnl(ndev_ctx->nvdev); struct hv_device *device = ndev_ctx->device_ctx; int ret = 0; struct netvsc_channel *nvchan; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 1238600d717e..a164981c15f7 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1548,6 +1548,7 @@ static int netvsc_probe(struct hv_device *dev, netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); + rtnl_unlock(); netdev_lockdep_set_classes(net); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 9ab67c8309ff..e439886f72c1 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -84,6 +84,14 @@ static struct rndis_device *get_rndis_device(void) return device; } +static struct netvsc_device * +net_device_to_netvsc_device(struct net_device *ndev) +{ + struct net_device_context *net_device_ctx = netdev_priv(ndev); + + return rtnl_dereference(net_device_ctx->nvdev); +} + static struct rndis_request *get_rndis_request(struct rndis_device *dev, u32 msg_type, u32 msg_len) @@ -472,7 +480,7 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) { struct net_device_context *ndevctx = netdev_priv(dev->ndev); - struct netvsc_device *nvdev = ndevctx->nvdev; + struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev); struct ndis_offload *hwcaps; u32 nvsp_version = nvdev->nvsp_version; u8 ndis_rev; @@ -944,7 +952,7 @@ static void rndis_filter_halt_device(struct rndis_device *dev) struct rndis_request *request; struct rndis_halt_request *halt; struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); - struct netvsc_device *nvdev = net_device_ctx->nvdev; + struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); /* Attempt to do a rndis device halt */ request = get_rndis_request(dev, RNDIS_MSG_HALT, -- cgit v1.2.3 From 04d8980b4a9ca178be1c703467f2ed4ac0800e90 Mon Sep 17 00:00:00 2001 From: Arjun Vynipadath Date: Wed, 19 Jul 2017 13:09:18 +0530 Subject: cxgb4: Update register ranges of T4/T5/T6 adapters Signed-off-by: Arjun Vynipadath Signed-off-by: Casey Leedom Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 51 ++++++++---------------------- 1 file changed, 14 insertions(+), 37 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 82bf7aac6cdb..570c0958a0e1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -913,7 +913,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0xd010, 0xd03c, 0xdfc0, 0xdfe0, 0xe000, 0xea7c, - 0xf000, 0x11190, + 0xf000, 0x11110, + 0x11118, 0x11190, 0x19040, 0x1906c, 0x19078, 0x19080, 0x1908c, 0x190e4, @@ -1439,8 +1440,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, 0x30000, 0x30030, - 0x30038, 0x30038, - 0x30040, 0x30040, 0x30100, 0x30144, 0x30190, 0x301a0, 0x301a8, 0x301b8, @@ -1551,8 +1550,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x33c3c, 0x33c50, 0x33cf0, 0x33cfc, 0x34000, 0x34030, - 0x34038, 0x34038, - 0x34040, 0x34040, 0x34100, 0x34144, 0x34190, 0x341a0, 0x341a8, 0x341b8, @@ -1663,8 +1660,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x37c3c, 0x37c50, 0x37cf0, 0x37cfc, 0x38000, 0x38030, - 0x38038, 0x38038, - 0x38040, 0x38040, 0x38100, 0x38144, 0x38190, 0x381a0, 0x381a8, 0x381b8, @@ -1775,8 +1770,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3bc3c, 0x3bc50, 0x3bcf0, 0x3bcfc, 0x3c000, 0x3c030, - 0x3c038, 0x3c038, - 0x3c040, 0x3c040, 0x3c100, 0x3c144, 0x3c190, 0x3c1a0, 0x3c1a8, 0x3c1b8, @@ -2040,12 +2033,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1190, 0x1194, 0x11a0, 0x11a4, 0x11b0, 0x11b4, - 0x11fc, 0x1258, - 0x1280, 0x12d4, - 0x12d9, 0x12d9, - 0x12de, 0x12de, - 0x12e3, 0x12e3, - 0x12e8, 0x133c, + 0x11fc, 0x1274, + 0x1280, 0x133c, 0x1800, 0x18fc, 0x3000, 0x302c, 0x3060, 0x30b0, @@ -2076,6 +2065,9 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x5ea0, 0x5eb0, 0x5ec0, 0x5ec0, 0x5ec8, 0x5ed0, + 0x5ee0, 0x5ee0, + 0x5ef0, 0x5ef0, + 0x5f00, 0x5f00, 0x6000, 0x6020, 0x6028, 0x6040, 0x6058, 0x609c, @@ -2133,6 +2125,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0xd300, 0xd31c, 0xdfc0, 0xdfe0, 0xe000, 0xf008, + 0xf010, 0xf018, + 0xf020, 0xf028, 0x11000, 0x11014, 0x11048, 0x1106c, 0x11074, 0x11088, @@ -2256,13 +2250,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, 0x30000, 0x30030, - 0x30038, 0x30038, - 0x30040, 0x30040, - 0x30048, 0x30048, - 0x30050, 0x30050, - 0x3005c, 0x30060, - 0x30068, 0x30068, - 0x30070, 0x30070, 0x30100, 0x30168, 0x30190, 0x301a0, 0x301a8, 0x301b8, @@ -2325,13 +2312,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x326a8, 0x326a8, 0x326ec, 0x326ec, 0x32a00, 0x32abc, - 0x32b00, 0x32b38, + 0x32b00, 0x32b18, + 0x32b20, 0x32b38, 0x32b40, 0x32b58, 0x32b60, 0x32b78, 0x32c00, 0x32c00, 0x32c08, 0x32c3c, - 0x32e00, 0x32e2c, - 0x32f00, 0x32f2c, 0x33000, 0x3302c, 0x33034, 0x33050, 0x33058, 0x33058, @@ -2396,13 +2382,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x33c38, 0x33c50, 0x33cf0, 0x33cfc, 0x34000, 0x34030, - 0x34038, 0x34038, - 0x34040, 0x34040, - 0x34048, 0x34048, - 0x34050, 0x34050, - 0x3405c, 0x34060, - 0x34068, 0x34068, - 0x34070, 0x34070, 0x34100, 0x34168, 0x34190, 0x341a0, 0x341a8, 0x341b8, @@ -2465,13 +2444,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x366a8, 0x366a8, 0x366ec, 0x366ec, 0x36a00, 0x36abc, - 0x36b00, 0x36b38, + 0x36b00, 0x36b18, + 0x36b20, 0x36b38, 0x36b40, 0x36b58, 0x36b60, 0x36b78, 0x36c00, 0x36c00, 0x36c08, 0x36c3c, - 0x36e00, 0x36e2c, - 0x36f00, 0x36f2c, 0x37000, 0x3702c, 0x37034, 0x37050, 0x37058, 0x37058, @@ -2545,8 +2523,7 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x40280, 0x40280, 0x40304, 0x40304, 0x40330, 0x4033c, - 0x41304, 0x413b8, - 0x413c0, 0x413c8, + 0x41304, 0x413c8, 0x413d0, 0x413dc, 0x413f0, 0x413f0, 0x41400, 0x4140c, -- cgit v1.2.3 From 760446f967678e14ee1b6464ee1bb8562f299fa6 Mon Sep 17 00:00:00 2001 From: Ganesh Goudar Date: Thu, 20 Jul 2017 18:28:48 +0530 Subject: cxgb4: display serial config and vpd versions print the versions of vpd and serial configuration file, flashed to adapter, and cleanup the relevant code. Signed-off-by: Casey Leedom Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 14 +- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 57 +------- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 173 ++++++++++++++++++++++++ drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h | 2 + 4 files changed, 188 insertions(+), 58 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index ef4be781fd05..1978abbc6ceb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -338,10 +338,12 @@ struct adapter_params { unsigned int sf_nsec; /* # of flash sectors */ unsigned int sf_fw_start; /* start of FW image in flash */ - unsigned int fw_vers; - unsigned int bs_vers; /* bootstrap version */ - unsigned int tp_vers; - unsigned int er_vers; /* expansion ROM version */ + unsigned int fw_vers; /* firmware version */ + unsigned int bs_vers; /* bootstrap version */ + unsigned int tp_vers; /* TP microcode version */ + unsigned int er_vers; /* expansion ROM version */ + unsigned int scfg_vers; /* Serial Configuration version */ + unsigned int vpd_vers; /* VPD Version */ u8 api_vers[7]; unsigned short mtus[NMTUS]; @@ -1407,6 +1409,10 @@ int t4_get_fw_version(struct adapter *adapter, u32 *vers); int t4_get_bs_version(struct adapter *adapter, u32 *vers); int t4_get_tp_version(struct adapter *adapter, u32 *vers); int t4_get_exprom_version(struct adapter *adapter, u32 *vers); +int t4_get_scfg_version(struct adapter *adapter, u32 *vers); +int t4_get_vpd_version(struct adapter *adapter, u32 *vers); +int t4_get_version_info(struct adapter *adapter); +void t4_dump_version_info(struct adapter *adapter); int t4_prep_fw(struct adapter *adap, struct fw_info *fw_info, const u8 *fw_data, unsigned int fw_size, struct fw_hdr *card_fw, enum dev_state state, int *reset); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index e403fa18f1b1..fdf220aa08d6 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -3610,11 +3610,8 @@ static int adap_init0(struct adapter *adap) * later reporting and B. to warn if the currently loaded firmware * is excessively mismatched relative to the driver.) */ - t4_get_fw_version(adap, &adap->params.fw_vers); - t4_get_bs_version(adap, &adap->params.bs_vers); - t4_get_tp_version(adap, &adap->params.tp_vers); - t4_get_exprom_version(adap, &adap->params.er_vers); + t4_get_version_info(adap); ret = t4_check_fw_version(adap); /* If firmware is too old (not supported by driver) force an update. */ if (ret) @@ -4560,56 +4557,8 @@ static void cxgb4_check_pcie_caps(struct adapter *adap) /* Dump basic information about the adapter */ static void print_adapter_info(struct adapter *adapter) { - /* Device information */ - dev_info(adapter->pdev_dev, "Chelsio %s rev %d\n", - adapter->params.vpd.id, - CHELSIO_CHIP_RELEASE(adapter->params.chip)); - dev_info(adapter->pdev_dev, "S/N: %s, P/N: %s\n", - adapter->params.vpd.sn, adapter->params.vpd.pn); - - /* Firmware Version */ - if (!adapter->params.fw_vers) - dev_warn(adapter->pdev_dev, "No firmware loaded\n"); - else - dev_info(adapter->pdev_dev, "Firmware version: %u.%u.%u.%u\n", - FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers)); - - /* Bootstrap Firmware Version. (Some adapters don't have Bootstrap - * Firmware, so dev_info() is more appropriate here.) - */ - if (!adapter->params.bs_vers) - dev_info(adapter->pdev_dev, "No bootstrap loaded\n"); - else - dev_info(adapter->pdev_dev, "Bootstrap version: %u.%u.%u.%u\n", - FW_HDR_FW_VER_MAJOR_G(adapter->params.bs_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.bs_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.bs_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.bs_vers)); - - /* TP Microcode Version */ - if (!adapter->params.tp_vers) - dev_warn(adapter->pdev_dev, "No TP Microcode loaded\n"); - else - dev_info(adapter->pdev_dev, - "TP Microcode version: %u.%u.%u.%u\n", - FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers)); - - /* Expansion ROM version */ - if (!adapter->params.er_vers) - dev_info(adapter->pdev_dev, "No Expansion ROM loaded\n"); - else - dev_info(adapter->pdev_dev, - "Expansion ROM version: %u.%u.%u.%u\n", - FW_HDR_FW_VER_MAJOR_G(adapter->params.er_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.er_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.er_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.er_vers)); + /* Hardware/Firmware/etc. Version/Revision IDs */ + t4_dump_version_info(adapter); /* Software/Hardware configuration */ dev_info(adapter->pdev_dev, "Configuration: %sNIC %s, %s capable\n", diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 570c0958a0e1..db41b3e99b81 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -3076,6 +3076,179 @@ int t4_get_exprom_version(struct adapter *adap, u32 *vers) return 0; } +/** + * t4_get_vpd_version - return the VPD version + * @adapter: the adapter + * @vers: where to place the version + * + * Reads the VPD via the Firmware interface (thus this can only be called + * once we're ready to issue Firmware commands). The format of the + * VPD version is adapter specific. Returns 0 on success, an error on + * failure. + * + * Note that early versions of the Firmware didn't include the ability + * to retrieve the VPD version, so we zero-out the return-value parameter + * in that case to avoid leaving it with garbage in it. + * + * Also note that the Firmware will return its cached copy of the VPD + * Revision ID, not the actual Revision ID as written in the Serial + * EEPROM. This is only an issue if a new VPD has been written and the + * Firmware/Chip haven't yet gone through a RESET sequence. So it's best + * to defer calling this routine till after a FW_RESET_CMD has been issued + * if the Host Driver will be performing a full adapter initialization. + */ +int t4_get_vpd_version(struct adapter *adapter, u32 *vers) +{ + u32 vpdrev_param; + int ret; + + vpdrev_param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_VPDREV)); + ret = t4_query_params(adapter, adapter->mbox, adapter->pf, 0, + 1, &vpdrev_param, vers); + if (ret) + *vers = 0; + return ret; +} + +/** + * t4_get_scfg_version - return the Serial Configuration version + * @adapter: the adapter + * @vers: where to place the version + * + * Reads the Serial Configuration Version via the Firmware interface + * (thus this can only be called once we're ready to issue Firmware + * commands). The format of the Serial Configuration version is + * adapter specific. Returns 0 on success, an error on failure. + * + * Note that early versions of the Firmware didn't include the ability + * to retrieve the Serial Configuration version, so we zero-out the + * return-value parameter in that case to avoid leaving it with + * garbage in it. + * + * Also note that the Firmware will return its cached copy of the Serial + * Initialization Revision ID, not the actual Revision ID as written in + * the Serial EEPROM. This is only an issue if a new VPD has been written + * and the Firmware/Chip haven't yet gone through a RESET sequence. So + * it's best to defer calling this routine till after a FW_RESET_CMD has + * been issued if the Host Driver will be performing a full adapter + * initialization. + */ +int t4_get_scfg_version(struct adapter *adapter, u32 *vers) +{ + u32 scfgrev_param; + int ret; + + scfgrev_param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_SCFGREV)); + ret = t4_query_params(adapter, adapter->mbox, adapter->pf, 0, + 1, &scfgrev_param, vers); + if (ret) + *vers = 0; + return ret; +} + +/** + * t4_get_version_info - extract various chip/firmware version information + * @adapter: the adapter + * + * Reads various chip/firmware version numbers and stores them into the + * adapter Adapter Parameters structure. If any of the efforts fails + * the first failure will be returned, but all of the version numbers + * will be read. + */ +int t4_get_version_info(struct adapter *adapter) +{ + int ret = 0; + + #define FIRST_RET(__getvinfo) \ + do { \ + int __ret = __getvinfo; \ + if (__ret && !ret) \ + ret = __ret; \ + } while (0) + + FIRST_RET(t4_get_fw_version(adapter, &adapter->params.fw_vers)); + FIRST_RET(t4_get_bs_version(adapter, &adapter->params.bs_vers)); + FIRST_RET(t4_get_tp_version(adapter, &adapter->params.tp_vers)); + FIRST_RET(t4_get_exprom_version(adapter, &adapter->params.er_vers)); + FIRST_RET(t4_get_scfg_version(adapter, &adapter->params.scfg_vers)); + FIRST_RET(t4_get_vpd_version(adapter, &adapter->params.vpd_vers)); + + #undef FIRST_RET + return ret; +} + +/** + * t4_dump_version_info - dump all of the adapter configuration IDs + * @adapter: the adapter + * + * Dumps all of the various bits of adapter configuration version/revision + * IDs information. This is typically called at some point after + * t4_get_version_info() has been called. + */ +void t4_dump_version_info(struct adapter *adapter) +{ + /* Device information */ + dev_info(adapter->pdev_dev, "Chelsio %s rev %d\n", + adapter->params.vpd.id, + CHELSIO_CHIP_RELEASE(adapter->params.chip)); + dev_info(adapter->pdev_dev, "S/N: %s, P/N: %s\n", + adapter->params.vpd.sn, adapter->params.vpd.pn); + + /* Firmware Version */ + if (!adapter->params.fw_vers) + dev_warn(adapter->pdev_dev, "No firmware loaded\n"); + else + dev_info(adapter->pdev_dev, "Firmware version: %u.%u.%u.%u\n", + FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers)); + + /* Bootstrap Firmware Version. (Some adapters don't have Bootstrap + * Firmware, so dev_info() is more appropriate here.) + */ + if (!adapter->params.bs_vers) + dev_info(adapter->pdev_dev, "No bootstrap loaded\n"); + else + dev_info(adapter->pdev_dev, "Bootstrap version: %u.%u.%u.%u\n", + FW_HDR_FW_VER_MAJOR_G(adapter->params.bs_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.bs_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.bs_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.bs_vers)); + + /* TP Microcode Version */ + if (!adapter->params.tp_vers) + dev_warn(adapter->pdev_dev, "No TP Microcode loaded\n"); + else + dev_info(adapter->pdev_dev, + "TP Microcode version: %u.%u.%u.%u\n", + FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers)); + + /* Expansion ROM version */ + if (!adapter->params.er_vers) + dev_info(adapter->pdev_dev, "No Expansion ROM loaded\n"); + else + dev_info(adapter->pdev_dev, + "Expansion ROM version: %u.%u.%u.%u\n", + FW_HDR_FW_VER_MAJOR_G(adapter->params.er_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.er_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.er_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.er_vers)); + + /* Serial Configuration version */ + dev_info(adapter->pdev_dev, "Serial Configuration version: %#x\n", + adapter->params.scfg_vers); + + /* VPD Version */ + dev_info(adapter->pdev_dev, "VPD version: %#x\n", + adapter->params.vpd_vers); +} + /** * t4_check_fw_version - check if the FW is supported with this driver * @adap: the adapter diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index 0ebed64d62d3..ad825fbc21a5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -1124,6 +1124,8 @@ enum fw_params_param_dev { FW_PARAMS_PARAM_DEV_MAXIRD_ADAPTER = 0x14, /* max supported adap IRD */ FW_PARAMS_PARAM_DEV_ULPTX_MEMWRITE_DSGL = 0x17, FW_PARAMS_PARAM_DEV_FWCACHE = 0x18, + FW_PARAMS_PARAM_DEV_SCFGREV = 0x1A, + FW_PARAMS_PARAM_DEV_VPDREV = 0x1B, FW_PARAMS_PARAM_DEV_RI_FR_NSMR_TPTE_WR = 0x1C, FW_PARAMS_PARAM_DEV_MPSBGMAP = 0x1E, }; -- cgit v1.2.3 From 28b39bc7c57e7920b0cbba3d79ba0f134e0f76f0 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 19 Jul 2017 16:54:46 +0800 Subject: virtio-net: pack headroom into ctx for mergeable buffers Pack headroom into ctx - this way when we get a buffer we can figure out the actual headroom that was allocated for the buffer. Will be helpful to optimize switching between XDP and non-XDP modes which have different headroom requirements. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 99830167ea2f..3223a36ad9a8 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -270,6 +270,23 @@ static void skb_xmit_done(struct virtqueue *vq) netif_wake_subqueue(vi->dev, vq2txq(vq)); } +#define MRG_CTX_HEADER_SHIFT 22 +static void *mergeable_len_to_ctx(unsigned int truesize, + unsigned int headroom) +{ + return (void *)(unsigned long)((headroom << MRG_CTX_HEADER_SHIFT) | truesize); +} + +static unsigned int mergeable_ctx_to_headroom(void *mrg_ctx) +{ + return (unsigned long)mrg_ctx >> MRG_CTX_HEADER_SHIFT; +} + +static unsigned int mergeable_ctx_to_truesize(void *mrg_ctx) +{ + return (unsigned long)mrg_ctx & ((1 << MRG_CTX_HEADER_SHIFT) - 1); +} + /* Called from bottom half context */ static struct sk_buff *page_to_skb(struct virtnet_info *vi, struct receive_queue *rq, @@ -639,13 +656,14 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, } rcu_read_unlock(); - if (unlikely(len > (unsigned long)ctx)) { + truesize = mergeable_ctx_to_truesize(ctx); + if (unlikely(len > truesize)) { pr_debug("%s: rx error: len %u exceeds truesize %lu\n", dev->name, len, (unsigned long)ctx); dev->stats.rx_length_errors++; goto err_skb; } - truesize = (unsigned long)ctx; + head_skb = page_to_skb(vi, rq, page, offset, len, truesize); curr_skb = head_skb; @@ -665,13 +683,14 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, } page = virt_to_head_page(buf); - if (unlikely(len > (unsigned long)ctx)) { + + truesize = mergeable_ctx_to_truesize(ctx); + if (unlikely(len > truesize)) { pr_debug("%s: rx error: len %u exceeds truesize %lu\n", dev->name, len, (unsigned long)ctx); dev->stats.rx_length_errors++; goto err_skb; } - truesize = (unsigned long)ctx; num_skb_frags = skb_shinfo(curr_skb)->nr_frags; if (unlikely(num_skb_frags == MAX_SKB_FRAGS)) { @@ -889,7 +908,7 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi, buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset; buf += headroom; /* advance address leaving hole at front of pkt */ - ctx = (void *)(unsigned long)len; + ctx = mergeable_len_to_ctx(len, headroom); get_page(alloc_frag->page); alloc_frag->offset += len + headroom; hole = alloc_frag->size - alloc_frag->offset; -- cgit v1.2.3 From 192f68cf35f5eefe28ce8acbb9a3dfc747149b64 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 19 Jul 2017 16:54:47 +0800 Subject: virtio-net: switch to use new ctx API for small buffer Use ctx API to store headroom for small buffers. Following patches will retrieve this info and use it for XDP. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 3223a36ad9a8..a8a5968a8c61 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -410,7 +410,8 @@ static unsigned int virtnet_get_headroom(struct virtnet_info *vi) static struct sk_buff *receive_small(struct net_device *dev, struct virtnet_info *vi, struct receive_queue *rq, - void *buf, unsigned int len) + void *buf, void *ctx, + unsigned int len) { struct sk_buff *skb; struct bpf_prog *xdp_prog; @@ -773,7 +774,7 @@ static int receive_buf(struct virtnet_info *vi, struct receive_queue *rq, else if (vi->big_packets) skb = receive_big(dev, vi, rq, buf, len); else - skb = receive_small(dev, vi, rq, buf, len); + skb = receive_small(dev, vi, rq, buf, ctx, len); if (unlikely(!skb)) return 0; @@ -806,12 +807,18 @@ frame_err: return 0; } +/* Unlike mergeable buffers, all buffers are allocated to the + * same size, except for the headroom. For this reason we do + * not need to use mergeable_len_to_ctx here - it is enough + * to store the headroom as the context ignoring the truesize. + */ static int add_recvbuf_small(struct virtnet_info *vi, struct receive_queue *rq, gfp_t gfp) { struct page_frag *alloc_frag = &rq->alloc_frag; char *buf; unsigned int xdp_headroom = virtnet_get_headroom(vi); + void *ctx = (void *)(unsigned long)xdp_headroom; int len = vi->hdr_len + VIRTNET_RX_PAD + GOOD_PACKET_LEN + xdp_headroom; int err; @@ -825,7 +832,7 @@ static int add_recvbuf_small(struct virtnet_info *vi, struct receive_queue *rq, alloc_frag->offset += len; sg_init_one(rq->sg, buf + VIRTNET_RX_PAD + xdp_headroom, vi->hdr_len + GOOD_PACKET_LEN); - err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, buf, gfp); + err = virtqueue_add_inbuf_ctx(rq->vq, rq->sg, 1, buf, ctx, gfp); if (err < 0) put_page(virt_to_head_page(buf)); @@ -1034,7 +1041,7 @@ static int virtnet_receive(struct receive_queue *rq, int budget) void *buf; struct virtnet_stats *stats = this_cpu_ptr(vi->stats); - if (vi->mergeable_rx_bufs) { + if (!vi->big_packets || vi->mergeable_rx_bufs) { void *ctx; while (received < budget && @@ -2202,7 +2209,7 @@ static int virtnet_find_vqs(struct virtnet_info *vi) names = kmalloc(total_vqs * sizeof(*names), GFP_KERNEL); if (!names) goto err_names; - if (vi->mergeable_rx_bufs) { + if (!vi->big_packets || vi->mergeable_rx_bufs) { ctx = kzalloc(total_vqs * sizeof(*ctx), GFP_KERNEL); if (!ctx) goto err_ctx; -- cgit v1.2.3 From 4941d472bf95b4345d6e38906fcf354e74afa311 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 19 Jul 2017 16:54:48 +0800 Subject: virtio-net: do not reset during XDP set We currently reset the device during XDP set, the main reason is that we allocate more headroom with XDP (for header adjustment). This works but causes network downtime for users. Previous patches encoded the headroom in the buffer context, this makes it possible to detect the case where a buffer with headroom insufficient for XDP is added to the queue and XDP is enabled afterwards. Upon detection, we handle this case by copying the packet (slow, but it's a temporary condition). Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 232 ++++++++++++++++++++++------------------------- 1 file changed, 106 insertions(+), 126 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index a8a5968a8c61..f894713dca2a 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -407,6 +407,69 @@ static unsigned int virtnet_get_headroom(struct virtnet_info *vi) return vi->xdp_queue_pairs ? VIRTIO_XDP_HEADROOM : 0; } +/* We copy the packet for XDP in the following cases: + * + * 1) Packet is scattered across multiple rx buffers. + * 2) Headroom space is insufficient. + * + * This is inefficient but it's a temporary condition that + * we hit right after XDP is enabled and until queue is refilled + * with large buffers with sufficient headroom - so it should affect + * at most queue size packets. + * Afterwards, the conditions to enable + * XDP should preclude the underlying device from sending packets + * across multiple buffers (num_buf > 1), and we make sure buffers + * have enough headroom. + */ +static struct page *xdp_linearize_page(struct receive_queue *rq, + u16 *num_buf, + struct page *p, + int offset, + int page_off, + unsigned int *len) +{ + struct page *page = alloc_page(GFP_ATOMIC); + + if (!page) + return NULL; + + memcpy(page_address(page) + page_off, page_address(p) + offset, *len); + page_off += *len; + + while (--*num_buf) { + unsigned int buflen; + void *buf; + int off; + + buf = virtqueue_get_buf(rq->vq, &buflen); + if (unlikely(!buf)) + goto err_buf; + + p = virt_to_head_page(buf); + off = buf - page_address(p); + + /* guard against a misconfigured or uncooperative backend that + * is sending packet larger than the MTU. + */ + if ((page_off + buflen) > PAGE_SIZE) { + put_page(p); + goto err_buf; + } + + memcpy(page_address(page) + page_off, + page_address(p) + off, buflen); + page_off += buflen; + put_page(p); + } + + /* Headroom does not contribute to packet length */ + *len = page_off - VIRTIO_XDP_HEADROOM; + return page; +err_buf: + __free_pages(page, 0); + return NULL; +} + static struct sk_buff *receive_small(struct net_device *dev, struct virtnet_info *vi, struct receive_queue *rq, @@ -415,12 +478,14 @@ static struct sk_buff *receive_small(struct net_device *dev, { struct sk_buff *skb; struct bpf_prog *xdp_prog; - unsigned int xdp_headroom = virtnet_get_headroom(vi); + unsigned int xdp_headroom = (unsigned long)ctx; unsigned int header_offset = VIRTNET_RX_PAD + xdp_headroom; unsigned int headroom = vi->hdr_len + header_offset; unsigned int buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + struct page *page = virt_to_head_page(buf); unsigned int delta = 0; + struct page *xdp_page; len -= vi->hdr_len; rcu_read_lock(); @@ -434,6 +499,27 @@ static struct sk_buff *receive_small(struct net_device *dev, if (unlikely(hdr->hdr.gso_type || hdr->hdr.flags)) goto err_xdp; + if (unlikely(xdp_headroom < virtnet_get_headroom(vi))) { + int offset = buf - page_address(page) + header_offset; + unsigned int tlen = len + vi->hdr_len; + u16 num_buf = 1; + + xdp_headroom = virtnet_get_headroom(vi); + header_offset = VIRTNET_RX_PAD + xdp_headroom; + headroom = vi->hdr_len + header_offset; + buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + xdp_page = xdp_linearize_page(rq, &num_buf, page, + offset, header_offset, + &tlen); + if (!xdp_page) + goto err_xdp; + + buf = page_address(xdp_page); + put_page(page); + page = xdp_page; + } + xdp.data_hard_start = buf + VIRTNET_RX_PAD + vi->hdr_len; xdp.data = xdp.data_hard_start + xdp_headroom; xdp.data_end = xdp.data + len; @@ -462,7 +548,7 @@ static struct sk_buff *receive_small(struct net_device *dev, skb = build_skb(buf, buflen); if (!skb) { - put_page(virt_to_head_page(buf)); + put_page(page); goto err; } skb_reserve(skb, headroom - delta); @@ -478,7 +564,7 @@ err: err_xdp: rcu_read_unlock(); dev->stats.rx_dropped++; - put_page(virt_to_head_page(buf)); + put_page(page); xdp_xmit: return NULL; } @@ -503,66 +589,6 @@ err: return NULL; } -/* The conditions to enable XDP should preclude the underlying device from - * sending packets across multiple buffers (num_buf > 1). However per spec - * it does not appear to be illegal to do so but rather just against convention. - * So in order to avoid making a system unresponsive the packets are pushed - * into a page and the XDP program is run. This will be extremely slow and we - * push a warning to the user to fix this as soon as possible. Fixing this may - * require resolving the underlying hardware to determine why multiple buffers - * are being received or simply loading the XDP program in the ingress stack - * after the skb is built because there is no advantage to running it here - * anymore. - */ -static struct page *xdp_linearize_page(struct receive_queue *rq, - u16 *num_buf, - struct page *p, - int offset, - unsigned int *len) -{ - struct page *page = alloc_page(GFP_ATOMIC); - unsigned int page_off = VIRTIO_XDP_HEADROOM; - - if (!page) - return NULL; - - memcpy(page_address(page) + page_off, page_address(p) + offset, *len); - page_off += *len; - - while (--*num_buf) { - unsigned int buflen; - void *buf; - int off; - - buf = virtqueue_get_buf(rq->vq, &buflen); - if (unlikely(!buf)) - goto err_buf; - - p = virt_to_head_page(buf); - off = buf - page_address(p); - - /* guard against a misconfigured or uncooperative backend that - * is sending packet larger than the MTU. - */ - if ((page_off + buflen) > PAGE_SIZE) { - put_page(p); - goto err_buf; - } - - memcpy(page_address(page) + page_off, - page_address(p) + off, buflen); - page_off += buflen; - put_page(p); - } - - /* Headroom does not contribute to packet length */ - *len = page_off - VIRTIO_XDP_HEADROOM; - return page; -err_buf: - __free_pages(page, 0); - return NULL; -} - static struct sk_buff *receive_mergeable(struct net_device *dev, struct virtnet_info *vi, struct receive_queue *rq, @@ -577,6 +603,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, struct sk_buff *head_skb, *curr_skb; struct bpf_prog *xdp_prog; unsigned int truesize; + unsigned int headroom = mergeable_ctx_to_headroom(ctx); head_skb = NULL; @@ -589,10 +616,13 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, u32 act; /* This happens when rx buffer size is underestimated */ - if (unlikely(num_buf > 1)) { + if (unlikely(num_buf > 1 || + headroom < virtnet_get_headroom(vi))) { /* linearize data for XDP */ xdp_page = xdp_linearize_page(rq, &num_buf, - page, offset, &len); + page, offset, + VIRTIO_XDP_HEADROOM, + &len); if (!xdp_page) goto err_xdp; offset = VIRTIO_XDP_HEADROOM; @@ -835,7 +865,6 @@ static int add_recvbuf_small(struct virtnet_info *vi, struct receive_queue *rq, err = virtqueue_add_inbuf_ctx(rq->vq, rq->sg, 1, buf, ctx, gfp); if (err < 0) put_page(virt_to_head_page(buf)); - return err; } @@ -1840,7 +1869,6 @@ static void virtnet_freeze_down(struct virtio_device *vdev) } static int init_vqs(struct virtnet_info *vi); -static void _remove_vq_common(struct virtnet_info *vi); static int virtnet_restore_up(struct virtio_device *vdev) { @@ -1869,39 +1897,6 @@ static int virtnet_restore_up(struct virtio_device *vdev) return err; } -static int virtnet_reset(struct virtnet_info *vi, int curr_qp, int xdp_qp) -{ - struct virtio_device *dev = vi->vdev; - int ret; - - virtio_config_disable(dev); - dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED; - virtnet_freeze_down(dev); - _remove_vq_common(vi); - - virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); - virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER); - - ret = virtio_finalize_features(dev); - if (ret) - goto err; - - vi->xdp_queue_pairs = xdp_qp; - ret = virtnet_restore_up(dev); - if (ret) - goto err; - ret = _virtnet_set_queues(vi, curr_qp); - if (ret) - goto err; - - virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); - virtio_config_enable(dev); - return 0; -err: - virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED); - return ret; -} - static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog, struct netlink_ext_ack *extack) { @@ -1948,35 +1943,29 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog, return PTR_ERR(prog); } - /* Changing the headroom in buffers is a disruptive operation because - * existing buffers must be flushed and reallocated. This will happen - * when a xdp program is initially added or xdp is disabled by removing - * the xdp program resulting in number of XDP queues changing. - */ - if (vi->xdp_queue_pairs != xdp_qp) { - err = virtnet_reset(vi, curr_qp + xdp_qp, xdp_qp); - if (err) { - dev_warn(&dev->dev, "XDP reset failure.\n"); - goto virtio_reset_err; - } - } + /* Make sure NAPI is not using any XDP TX queues for RX. */ + for (i = 0; i < vi->max_queue_pairs; i++) + napi_disable(&vi->rq[i].napi); netif_set_real_num_rx_queues(dev, curr_qp + xdp_qp); + err = _virtnet_set_queues(vi, curr_qp + xdp_qp); + if (err) + goto err; + vi->xdp_queue_pairs = xdp_qp; for (i = 0; i < vi->max_queue_pairs; i++) { old_prog = rtnl_dereference(vi->rq[i].xdp_prog); rcu_assign_pointer(vi->rq[i].xdp_prog, prog); if (old_prog) bpf_prog_put(old_prog); + virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); } return 0; -virtio_reset_err: - /* On reset error do our best to unwind XDP changes inflight and return - * error up to user space for resolution. The underlying reset hung on - * us so not much we can do here. - */ +err: + for (i = 0; i < vi->max_queue_pairs; i++) + virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); if (prog) bpf_prog_sub(prog, vi->max_queue_pairs - 1); return err; @@ -2622,15 +2611,6 @@ free: return err; } -static void _remove_vq_common(struct virtnet_info *vi) -{ - vi->vdev->config->reset(vi->vdev); - free_unused_bufs(vi); - _free_receive_bufs(vi); - free_receive_page_frags(vi); - virtnet_del_vqs(vi); -} - static void remove_vq_common(struct virtnet_info *vi) { vi->vdev->config->reset(vi->vdev); -- cgit v1.2.3 From 3f93522ffab2d46a36b57adf324a54e674fc9536 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 19 Jul 2017 16:54:49 +0800 Subject: virtio-net: switch off offloads on demand if possible on XDP set Current XDP implementation wants guest offloads feature to be disabled on device. This is inconvenient and means guest can't benefit from offloads if XDP is not used. This patch tries to address this limitation by disabling the offloads on demand through control guest offloads. Guest offloads will be disabled and enabled on demand on XDP set. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 70 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index f894713dca2a..d4751ce23b4f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -57,6 +57,11 @@ DECLARE_EWMA(pkt_len, 0, 64) #define VIRTNET_DRIVER_VERSION "1.0.0" +const unsigned long guest_offloads[] = { VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_UFO }; + struct virtnet_stats { struct u64_stats_sync tx_syncp; struct u64_stats_sync rx_syncp; @@ -164,10 +169,13 @@ struct virtnet_info { u8 ctrl_promisc; u8 ctrl_allmulti; u16 ctrl_vid; + u64 ctrl_offloads; /* Ethtool settings */ u8 duplex; u32 speed; + + unsigned long guest_offloads; }; struct padded_vnet_hdr { @@ -1897,6 +1905,47 @@ static int virtnet_restore_up(struct virtio_device *vdev) return err; } +static int virtnet_set_guest_offloads(struct virtnet_info *vi, u64 offloads) +{ + struct scatterlist sg; + vi->ctrl_offloads = cpu_to_virtio64(vi->vdev, offloads); + + sg_init_one(&sg, &vi->ctrl_offloads, sizeof(vi->ctrl_offloads)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_GUEST_OFFLOADS, + VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, &sg)) { + dev_warn(&vi->dev->dev, "Fail to set guest offload. \n"); + return -EINVAL; + } + + return 0; +} + +static int virtnet_clear_guest_offloads(struct virtnet_info *vi) +{ + u64 offloads = 0; + + if (!vi->guest_offloads) + return 0; + + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM)) + offloads = 1ULL << VIRTIO_NET_F_GUEST_CSUM; + + return virtnet_set_guest_offloads(vi, offloads); +} + +static int virtnet_restore_guest_offloads(struct virtnet_info *vi) +{ + u64 offloads = vi->guest_offloads; + + if (!vi->guest_offloads) + return 0; + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM)) + offloads |= 1ULL << VIRTIO_NET_F_GUEST_CSUM; + + return virtnet_set_guest_offloads(vi, offloads); +} + static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog, struct netlink_ext_ack *extack) { @@ -1906,10 +1955,11 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog, u16 xdp_qp = 0, curr_qp; int i, err; - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) || - virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) || - virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) || - virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO)) { + if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) + && (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) || + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) || + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) || + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO))) { NL_SET_ERR_MSG_MOD(extack, "Can't set XDP while host is implementing LRO, disable LRO first"); return -EOPNOTSUPP; } @@ -1956,6 +2006,12 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog, for (i = 0; i < vi->max_queue_pairs; i++) { old_prog = rtnl_dereference(vi->rq[i].xdp_prog); rcu_assign_pointer(vi->rq[i].xdp_prog, prog); + if (i == 0) { + if (!old_prog) + virtnet_clear_guest_offloads(vi); + if (!prog) + virtnet_restore_guest_offloads(vi); + } if (old_prog) bpf_prog_put(old_prog); virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); @@ -2591,6 +2647,10 @@ static int virtnet_probe(struct virtio_device *vdev) netif_carrier_on(dev); } + for (i = 0; i < ARRAY_SIZE(guest_offloads); i++) + if (virtio_has_feature(vi->vdev, guest_offloads[i])) + set_bit(guest_offloads[i], &vi->guest_offloads); + pr_debug("virtnet: registered device %s with %d RX and TX vq's\n", dev->name, max_queue_pairs); @@ -2687,7 +2747,7 @@ static struct virtio_device_id id_table[] = { VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, \ VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ, \ VIRTIO_NET_F_CTRL_MAC_ADDR, \ - VIRTIO_NET_F_MTU + VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS static unsigned int features[] = { VIRTNET_FEATURES, -- cgit v1.2.3 From f7ce91038d52780060ccb0d7ee6dd48967b3e132 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 18 Jul 2017 16:43:19 -0500 Subject: net: Convert to using %pOF instead of full_name Now that we have a custom printf format specifier, convert users of full_name to use %pOF instead. This is preparation to remove storing of the full path string for each node. Signed-off-by: Rob Herring Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- drivers/net/ethernet/apple/mace.c | 8 ++-- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 4 +- drivers/net/ethernet/freescale/fec_mpc52xx.c | 4 +- drivers/net/ethernet/freescale/fman/fman.c | 12 +++--- drivers/net/ethernet/freescale/fman/fman_port.c | 4 +- drivers/net/ethernet/freescale/fman/mac.c | 50 +++++++++++------------ drivers/net/ethernet/freescale/fsl_pq_mdio.c | 20 +++++----- drivers/net/ethernet/ibm/ehea/ehea_main.c | 5 +-- drivers/net/ethernet/ibm/emac/core.c | 53 +++++++++++-------------- drivers/net/ethernet/ibm/emac/debug.h | 2 +- drivers/net/ethernet/ibm/emac/mal.c | 8 ++-- drivers/net/ethernet/ibm/emac/rgmii.c | 18 ++++----- drivers/net/ethernet/ibm/emac/tah.c | 12 ++---- drivers/net/ethernet/ibm/emac/zmii.c | 17 ++++---- drivers/net/ethernet/sun/niu.c | 24 +++++------ drivers/net/ethernet/ti/cpsw.c | 8 ++-- drivers/net/ethernet/ti/davinci_emac.c | 4 +- drivers/net/ethernet/xilinx/ll_temac_main.c | 2 +- drivers/net/phy/mdio-mux-mmioreg.c | 18 ++++----- drivers/net/phy/mdio-mux.c | 16 ++++---- 21 files changed, 134 insertions(+), 157 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 7fa19d4a8e13..647d5d45c1d6 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2242,7 +2242,7 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, if (np) { bus->name = np->full_name; - snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name); + snprintf(bus->id, MII_BUS_ID_SIZE, "%pOF", np); } else { bus->name = "mv88e6xxx SMI"; snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++); diff --git a/drivers/net/ethernet/apple/mace.c b/drivers/net/ethernet/apple/mace.c index 96dd5300e0e5..e58b157b7d7c 100644 --- a/drivers/net/ethernet/apple/mace.c +++ b/drivers/net/ethernet/apple/mace.c @@ -114,8 +114,8 @@ static int mace_probe(struct macio_dev *mdev, const struct of_device_id *match) int j, rev, rc = -EBUSY; if (macio_resource_count(mdev) != 3 || macio_irq_count(mdev) != 3) { - printk(KERN_ERR "can't use MACE %s: need 3 addrs and 3 irqs\n", - mace->full_name); + printk(KERN_ERR "can't use MACE %pOF: need 3 addrs and 3 irqs\n", + mace); return -ENODEV; } @@ -123,8 +123,8 @@ static int mace_probe(struct macio_dev *mdev, const struct of_device_id *match) if (addr == NULL) { addr = of_get_property(mace, "local-mac-address", NULL); if (addr == NULL) { - printk(KERN_ERR "Can't get mac-address for MACE %s\n", - mace->full_name); + printk(KERN_ERR "Can't get mac-address for MACE %pOF\n", + mace); return -ENODEV; } } diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 757b873735a5..550ea1ec7b6c 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -398,8 +398,8 @@ static struct mac_device *dpaa_mac_dev_get(struct platform_device *pdev) of_dev = of_find_device_by_node(mac_node); if (!of_dev) { - dev_err(dpaa_dev, "of_find_device_by_node(%s) failed\n", - mac_node->full_name); + dev_err(dpaa_dev, "of_find_device_by_node(%pOF) failed\n", + mac_node); of_node_put(mac_node); return ERR_PTR(-EINVAL); } diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index aa8cf5d2a53c..6d7269d87a85 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -960,8 +960,8 @@ static int mpc52xx_fec_probe(struct platform_device *op) /* We're done ! */ platform_set_drvdata(op, ndev); - netdev_info(ndev, "%s MAC %pM\n", - op->dev.of_node->full_name, ndev->dev_addr); + netdev_info(ndev, "%pOF MAC %pM\n", + op->dev.of_node, ndev->dev_addr); return 0; diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index 4aefe2438969..e714b8fa55eb 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -1925,8 +1925,8 @@ static int fman_reset(struct fman *fman) guts_regs = of_iomap(guts_node, 0); if (!guts_regs) { - dev_err(fman->dev, "%s: Couldn't map %s regs\n", - __func__, guts_node->full_name); + dev_err(fman->dev, "%s: Couldn't map %pOF regs\n", + __func__, guts_node); goto guts_regs; } #define FMAN1_ALL_MACS_MASK 0xFCC00000 @@ -2780,8 +2780,8 @@ static struct fman *read_dts_node(struct platform_device *of_dev) err = of_property_read_u32(fm_node, "cell-index", &val); if (err) { - dev_err(&of_dev->dev, "%s: failed to read cell-index for %s\n", - __func__, fm_node->full_name); + dev_err(&of_dev->dev, "%s: failed to read cell-index for %pOF\n", + __func__, fm_node); goto fman_node_put; } fman->dts_params.id = (u8)val; @@ -2834,8 +2834,8 @@ static struct fman *read_dts_node(struct platform_device *of_dev) err = of_property_read_u32_array(fm_node, "fsl,qman-channel-range", &range[0], 2); if (err) { - dev_err(&of_dev->dev, "%s: failed to read fsl,qman-channel-range for %s\n", - __func__, fm_node->full_name); + dev_err(&of_dev->dev, "%s: failed to read fsl,qman-channel-range for %pOF\n", + __func__, fm_node); goto fman_node_put; } fman->dts_params.qman_channel_base = range[0]; diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c index 57bf44fa16a1..49bfa11f2d20 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.c +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -1720,8 +1720,8 @@ static int fman_port_probe(struct platform_device *of_dev) err = of_property_read_u32(port_node, "cell-index", &val); if (err) { - dev_err(port->dev, "%s: reading cell-index for %s failed\n", - __func__, port_node->full_name); + dev_err(port->dev, "%s: reading cell-index for %pOF failed\n", + __func__, port_node); err = -EINVAL; goto return_err; } diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c index 6e67d22fd0d5..14cd2c8b0024 100644 --- a/drivers/net/ethernet/freescale/fman/mac.c +++ b/drivers/net/ethernet/freescale/fman/mac.c @@ -698,8 +698,8 @@ static int mac_probe(struct platform_device *_of_dev) priv->internal_phy_node = of_parse_phandle(mac_node, "pcsphy-handle", 0); } else { - dev_err(dev, "MAC node (%s) contains unsupported MAC\n", - mac_node->full_name); + dev_err(dev, "MAC node (%pOF) contains unsupported MAC\n", + mac_node); err = -EINVAL; goto _return; } @@ -712,16 +712,15 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the FM node */ dev_node = of_get_parent(mac_node); if (!dev_node) { - dev_err(dev, "of_get_parent(%s) failed\n", - mac_node->full_name); + dev_err(dev, "of_get_parent(%pOF) failed\n", + mac_node); err = -EINVAL; goto _return_dev_set_drvdata; } of_dev = of_find_device_by_node(dev_node); if (!of_dev) { - dev_err(dev, "of_find_device_by_node(%s) failed\n", - dev_node->full_name); + dev_err(dev, "of_find_device_by_node(%pOF) failed\n", dev_node); err = -EINVAL; goto _return_of_node_put; } @@ -729,8 +728,7 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the FMan cell-index */ err = of_property_read_u32(dev_node, "cell-index", &val); if (err) { - dev_err(dev, "failed to read cell-index for %s\n", - dev_node->full_name); + dev_err(dev, "failed to read cell-index for %pOF\n", dev_node); err = -EINVAL; goto _return_of_node_put; } @@ -739,7 +737,7 @@ static int mac_probe(struct platform_device *_of_dev) priv->fman = fman_bind(&of_dev->dev); if (!priv->fman) { - dev_err(dev, "fman_bind(%s) failed\n", dev_node->full_name); + dev_err(dev, "fman_bind(%pOF) failed\n", dev_node); err = -ENODEV; goto _return_of_node_put; } @@ -749,8 +747,8 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the address of the memory mapped registers */ err = of_address_to_resource(mac_node, 0, &res); if (err < 0) { - dev_err(dev, "of_address_to_resource(%s) = %d\n", - mac_node->full_name, err); + dev_err(dev, "of_address_to_resource(%pOF) = %d\n", + mac_node, err); goto _return_dev_set_drvdata; } @@ -784,8 +782,7 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the cell-index */ err = of_property_read_u32(mac_node, "cell-index", &val); if (err) { - dev_err(dev, "failed to read cell-index for %s\n", - mac_node->full_name); + dev_err(dev, "failed to read cell-index for %pOF\n", mac_node); err = -EINVAL; goto _return_dev_set_drvdata; } @@ -794,8 +791,7 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the MAC address */ mac_addr = of_get_mac_address(mac_node); if (!mac_addr) { - dev_err(dev, "of_get_mac_address(%s) failed\n", - mac_node->full_name); + dev_err(dev, "of_get_mac_address(%pOF) failed\n", mac_node); err = -EINVAL; goto _return_dev_set_drvdata; } @@ -804,15 +800,15 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the port handles */ nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL); if (unlikely(nph < 0)) { - dev_err(dev, "of_count_phandle_with_args(%s, fsl,fman-ports) failed\n", - mac_node->full_name); + dev_err(dev, "of_count_phandle_with_args(%pOF, fsl,fman-ports) failed\n", + mac_node); err = nph; goto _return_dev_set_drvdata; } if (nph != ARRAY_SIZE(mac_dev->port)) { - dev_err(dev, "Not supported number of fman-ports handles of mac node %s from device tree\n", - mac_node->full_name); + dev_err(dev, "Not supported number of fman-ports handles of mac node %pOF from device tree\n", + mac_node); err = -EINVAL; goto _return_dev_set_drvdata; } @@ -821,24 +817,24 @@ static int mac_probe(struct platform_device *_of_dev) /* Find the port node */ dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i); if (!dev_node) { - dev_err(dev, "of_parse_phandle(%s, fsl,fman-ports) failed\n", - mac_node->full_name); + dev_err(dev, "of_parse_phandle(%pOF, fsl,fman-ports) failed\n", + mac_node); err = -EINVAL; goto _return_of_node_put; } of_dev = of_find_device_by_node(dev_node); if (!of_dev) { - dev_err(dev, "of_find_device_by_node(%s) failed\n", - dev_node->full_name); + dev_err(dev, "of_find_device_by_node(%pOF) failed\n", + dev_node); err = -EINVAL; goto _return_of_node_put; } mac_dev->port[i] = fman_port_bind(&of_dev->dev); if (!mac_dev->port[i]) { - dev_err(dev, "dev_get_drvdata(%s) failed\n", - dev_node->full_name); + dev_err(dev, "dev_get_drvdata(%pOF) failed\n", + dev_node); err = -EINVAL; goto _return_of_node_put; } @@ -849,8 +845,8 @@ static int mac_probe(struct platform_device *_of_dev) phy_if = of_get_phy_mode(mac_node); if (phy_if < 0) { dev_warn(dev, - "of_get_phy_mode() for %s failed. Defaulting to SGMII\n", - mac_node->full_name); + "of_get_phy_mode() for %pOF failed. Defaulting to SGMII\n", + mac_node); phy_if = PHY_INTERFACE_MODE_SGMII; } priv->phy_if = phy_if; diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c index a10de1e9c157..80ad16acf0f1 100644 --- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c +++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c @@ -267,8 +267,8 @@ static void ucc_configure(phys_addr_t start, phys_addr_t end) ret = of_address_to_resource(np, 0, &res); if (ret < 0) { - pr_debug("fsl-pq-mdio: no address range in node %s\n", - np->full_name); + pr_debug("fsl-pq-mdio: no address range in node %pOF\n", + np); continue; } @@ -280,8 +280,8 @@ static void ucc_configure(phys_addr_t start, phys_addr_t end) if (!iprop) { iprop = of_get_property(np, "device-id", NULL); if (!iprop) { - pr_debug("fsl-pq-mdio: no UCC ID in node %s\n", - np->full_name); + pr_debug("fsl-pq-mdio: no UCC ID in node %pOF\n", + np); continue; } } @@ -293,8 +293,8 @@ static void ucc_configure(phys_addr_t start, phys_addr_t end) * numbered from 1, not 0. */ if (ucc_set_qe_mux_mii_mng(id - 1) < 0) { - pr_debug("fsl-pq-mdio: invalid UCC ID in node %s\n", - np->full_name); + pr_debug("fsl-pq-mdio: invalid UCC ID in node %pOF\n", + np); continue; } @@ -442,8 +442,8 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev) if (data->get_tbipa) { for_each_child_of_node(np, tbi) { if (strcmp(tbi->type, "tbi-phy") == 0) { - dev_dbg(&pdev->dev, "found TBI PHY node %s\n", - strrchr(tbi->full_name, '/') + 1); + dev_dbg(&pdev->dev, "found TBI PHY node %pOFP\n", + tbi); break; } } @@ -454,8 +454,8 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev) if (!prop) { dev_err(&pdev->dev, - "missing 'reg' property in node %s\n", - tbi->full_name); + "missing 'reg' property in node %pOF\n", + tbi); err = -EBUSY; goto error; } diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index b9d310f20bcc..4878b7169e0f 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -3102,8 +3102,7 @@ static int ehea_setup_ports(struct ehea_adapter *adapter) dn_log_port_id = of_get_property(eth_dn, "ibm,hea-port-no", NULL); if (!dn_log_port_id) { - pr_err("bad device node: eth_dn name=%s\n", - eth_dn->full_name); + pr_err("bad device node: eth_dn name=%pOF\n", eth_dn); continue; } @@ -3425,7 +3424,7 @@ static int ehea_probe_adapter(struct platform_device *dev) if (!adapter->handle) { dev_err(&dev->dev, "failed getting handle for adapter" - " '%s'\n", dev->dev.of_node->full_name); + " '%pOF'\n", dev->dev.of_node); ret = -ENODEV; goto out_free_ad; } diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 259e69a52ec5..95135d20458f 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -133,8 +133,7 @@ static inline void emac_report_timeout_error(struct emac_instance *dev, EMAC_FTR_440EP_PHY_CLK_FIX)) DBG(dev, "%s" NL, error); else if (net_ratelimit()) - printk(KERN_ERR "%s: %s\n", dev->ofdev->dev.of_node->full_name, - error); + printk(KERN_ERR "%pOF: %s\n", dev->ofdev->dev.of_node, error); } /* EMAC PHY clock workaround: @@ -2258,8 +2257,8 @@ static void emac_ethtool_get_drvinfo(struct net_device *ndev, strlcpy(info->driver, "ibm_emac", sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - snprintf(info->bus_info, sizeof(info->bus_info), "PPC 4xx EMAC-%d %s", - dev->cell_index, dev->ofdev->dev.of_node->full_name); + snprintf(info->bus_info, sizeof(info->bus_info), "PPC 4xx EMAC-%d %pOF", + dev->cell_index, dev->ofdev->dev.of_node); } static const struct ethtool_ops emac_ethtool_ops = { @@ -2431,8 +2430,8 @@ static int emac_read_uint_prop(struct device_node *np, const char *name, const u32 *prop = of_get_property(np, name, &len); if (prop == NULL || len < sizeof(u32)) { if (fatal) - printk(KERN_ERR "%s: missing %s property\n", - np->full_name, name); + printk(KERN_ERR "%pOF: missing %s property\n", + np, name); return -ENODEV; } *val = *prop; @@ -2768,7 +2767,7 @@ static int emac_init_phy(struct emac_instance *dev) #endif mutex_unlock(&emac_phy_map_lock); if (i == 0x20) { - printk(KERN_WARNING "%s: can't find PHY!\n", np->full_name); + printk(KERN_WARNING "%pOF: can't find PHY!\n", np); return -ENXIO; } @@ -2894,8 +2893,8 @@ static int emac_init_config(struct emac_instance *dev) #ifdef CONFIG_IBM_EMAC_NO_FLOW_CTRL dev->features |= EMAC_FTR_NO_FLOW_CONTROL_40x; #else - printk(KERN_ERR "%s: Flow control not disabled!\n", - np->full_name); + printk(KERN_ERR "%pOF: Flow control not disabled!\n", + np); return -ENXIO; #endif } @@ -2918,8 +2917,7 @@ static int emac_init_config(struct emac_instance *dev) #ifdef CONFIG_IBM_EMAC_TAH dev->features |= EMAC_FTR_HAS_TAH; #else - printk(KERN_ERR "%s: TAH support not enabled !\n", - np->full_name); + printk(KERN_ERR "%pOF: TAH support not enabled !\n", np); return -ENXIO; #endif } @@ -2928,8 +2926,7 @@ static int emac_init_config(struct emac_instance *dev) #ifdef CONFIG_IBM_EMAC_ZMII dev->features |= EMAC_FTR_HAS_ZMII; #else - printk(KERN_ERR "%s: ZMII support not enabled !\n", - np->full_name); + printk(KERN_ERR "%pOF: ZMII support not enabled !\n", np); return -ENXIO; #endif } @@ -2938,8 +2935,7 @@ static int emac_init_config(struct emac_instance *dev) #ifdef CONFIG_IBM_EMAC_RGMII dev->features |= EMAC_FTR_HAS_RGMII; #else - printk(KERN_ERR "%s: RGMII support not enabled !\n", - np->full_name); + printk(KERN_ERR "%pOF: RGMII support not enabled !\n", np); return -ENXIO; #endif } @@ -2947,8 +2943,8 @@ static int emac_init_config(struct emac_instance *dev) /* Read MAC-address */ p = of_get_property(np, "local-mac-address", NULL); if (p == NULL) { - printk(KERN_ERR "%s: Can't find local-mac-address property\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't find local-mac-address property\n", + np); return -ENXIO; } memcpy(dev->ndev->dev_addr, p, ETH_ALEN); @@ -3043,23 +3039,21 @@ static int emac_probe(struct platform_device *ofdev) dev->emac_irq = irq_of_parse_and_map(np, 0); dev->wol_irq = irq_of_parse_and_map(np, 1); if (!dev->emac_irq) { - printk(KERN_ERR "%s: Can't map main interrupt\n", np->full_name); + printk(KERN_ERR "%pOF: Can't map main interrupt\n", np); goto err_free; } ndev->irq = dev->emac_irq; /* Map EMAC regs */ if (of_address_to_resource(np, 0, &dev->rsrc_regs)) { - printk(KERN_ERR "%s: Can't get registers address\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't get registers address\n", np); goto err_irq_unmap; } // TODO : request_mem_region dev->emacp = ioremap(dev->rsrc_regs.start, resource_size(&dev->rsrc_regs)); if (dev->emacp == NULL) { - printk(KERN_ERR "%s: Can't map device registers!\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't map device registers!\n", np); err = -ENOMEM; goto err_irq_unmap; } @@ -3068,8 +3062,7 @@ static int emac_probe(struct platform_device *ofdev) err = emac_wait_deps(dev); if (err) { printk(KERN_ERR - "%s: Timeout waiting for dependent devices\n", - np->full_name); + "%pOF: Timeout waiting for dependent devices\n", np); /* display more info about what's missing ? */ goto err_reg_unmap; } @@ -3084,8 +3077,8 @@ static int emac_probe(struct platform_device *ofdev) dev->commac.rx_chan_mask = MAL_CHAN_MASK(dev->mal_rx_chan); err = mal_register_commac(dev->mal, &dev->commac); if (err) { - printk(KERN_ERR "%s: failed to register with mal %s!\n", - np->full_name, dev->mal_dev->dev.of_node->full_name); + printk(KERN_ERR "%pOF: failed to register with mal %pOF!\n", + np, dev->mal_dev->dev.of_node); goto err_rel_deps; } dev->rx_skb_size = emac_rx_skb_size(ndev->mtu); @@ -3161,8 +3154,8 @@ static int emac_probe(struct platform_device *ofdev) err = register_netdev(ndev); if (err) { - printk(KERN_ERR "%s: failed to register net device (%d)!\n", - np->full_name, err); + printk(KERN_ERR "%pOF: failed to register net device (%d)!\n", + np, err); goto err_detach_tah; } @@ -3176,8 +3169,8 @@ static int emac_probe(struct platform_device *ofdev) wake_up_all(&emac_probe_wait); - printk(KERN_INFO "%s: EMAC-%d %s, MAC %pM\n", - ndev->name, dev->cell_index, np->full_name, ndev->dev_addr); + printk(KERN_INFO "%s: EMAC-%d %pOF, MAC %pM\n", + ndev->name, dev->cell_index, np, ndev->dev_addr); if (dev->phy_mode == PHY_MODE_SGMII) printk(KERN_NOTICE "%s: in SGMII mode\n", ndev->name); diff --git a/drivers/net/ethernet/ibm/emac/debug.h b/drivers/net/ethernet/ibm/emac/debug.h index 5bdfc174a07e..9d06d3be3161 100644 --- a/drivers/net/ethernet/ibm/emac/debug.h +++ b/drivers/net/ethernet/ibm/emac/debug.h @@ -31,7 +31,7 @@ #endif #define EMAC_DBG(d, name, fmt, arg...) \ - printk(KERN_DEBUG #name "%s: " fmt, d->ofdev->dev.of_node->full_name, ## arg) + printk(KERN_DEBUG #name "%pOF: " fmt, d->ofdev->dev.of_node, ## arg) #if DBG_LEVEL > 0 # define DBG(d,f,x...) EMAC_DBG(d, emac, f, ##x) diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index 91b1a558f37d..2c74baa2398a 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -579,8 +579,8 @@ static int mal_probe(struct platform_device *ofdev) mal->features |= (MAL_FTR_CLEAR_ICINTSTAT | MAL_FTR_COMMON_ERR_INT); #else - printk(KERN_ERR "%s: Support for 405EZ not enabled!\n", - ofdev->dev.of_node->full_name); + printk(KERN_ERR "%pOF: Support for 405EZ not enabled!\n", + ofdev->dev.of_node); err = -ENODEV; goto fail; #endif @@ -687,8 +687,8 @@ static int mal_probe(struct platform_device *ofdev) mal_enable_eob_irq(mal); printk(KERN_INFO - "MAL v%d %s, %d TX channels, %d RX channels\n", - mal->version, ofdev->dev.of_node->full_name, + "MAL v%d %pOF, %d TX channels, %d RX channels\n", + mal->version, ofdev->dev.of_node, mal->num_tx_chans, mal->num_rx_chans); /* Advertise this instance to the rest of the world */ diff --git a/drivers/net/ethernet/ibm/emac/rgmii.c b/drivers/net/ethernet/ibm/emac/rgmii.c index 206ccbbae7bb..c4a1ac38bba8 100644 --- a/drivers/net/ethernet/ibm/emac/rgmii.c +++ b/drivers/net/ethernet/ibm/emac/rgmii.c @@ -104,8 +104,8 @@ int rgmii_attach(struct platform_device *ofdev, int input, int mode) /* Check if we need to attach to a RGMII */ if (input < 0 || !rgmii_valid_mode(mode)) { - printk(KERN_ERR "%s: unsupported settings !\n", - ofdev->dev.of_node->full_name); + printk(KERN_ERR "%pOF: unsupported settings !\n", + ofdev->dev.of_node); return -ENODEV; } @@ -114,8 +114,8 @@ int rgmii_attach(struct platform_device *ofdev, int input, int mode) /* Enable this input */ out_be32(&p->fer, in_be32(&p->fer) | rgmii_mode_mask(mode, input)); - printk(KERN_NOTICE "%s: input %d in %s mode\n", - ofdev->dev.of_node->full_name, input, rgmii_mode_name(mode)); + printk(KERN_NOTICE "%pOF: input %d in %s mode\n", + ofdev->dev.of_node, input, rgmii_mode_name(mode)); ++dev->users; @@ -249,8 +249,7 @@ static int rgmii_probe(struct platform_device *ofdev) rc = -ENXIO; if (of_address_to_resource(np, 0, ®s)) { - printk(KERN_ERR "%s: Can't get registers address\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't get registers address\n", np); goto err_free; } @@ -258,8 +257,7 @@ static int rgmii_probe(struct platform_device *ofdev) dev->base = (struct rgmii_regs __iomem *)ioremap(regs.start, sizeof(struct rgmii_regs)); if (dev->base == NULL) { - printk(KERN_ERR "%s: Can't map device registers!\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't map device registers!\n", np); goto err_free; } @@ -278,8 +276,8 @@ static int rgmii_probe(struct platform_device *ofdev) out_be32(&dev->base->fer, 0); printk(KERN_INFO - "RGMII %s initialized with%s MDIO support\n", - ofdev->dev.of_node->full_name, + "RGMII %pOF initialized with%s MDIO support\n", + ofdev->dev.of_node, (dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out"); wmb(); diff --git a/drivers/net/ethernet/ibm/emac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c index 32cb6c9007c5..9912456dca48 100644 --- a/drivers/net/ethernet/ibm/emac/tah.c +++ b/drivers/net/ethernet/ibm/emac/tah.c @@ -58,8 +58,7 @@ void tah_reset(struct platform_device *ofdev) --n; if (unlikely(!n)) - printk(KERN_ERR "%s: reset timeout\n", - ofdev->dev.of_node->full_name); + printk(KERN_ERR "%pOF: reset timeout\n", ofdev->dev.of_node); /* 10KB TAH TX FIFO accommodates the max MTU of 9000 */ out_be32(&p->mr, @@ -105,8 +104,7 @@ static int tah_probe(struct platform_device *ofdev) rc = -ENXIO; if (of_address_to_resource(np, 0, ®s)) { - printk(KERN_ERR "%s: Can't get registers address\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't get registers address\n", np); goto err_free; } @@ -114,8 +112,7 @@ static int tah_probe(struct platform_device *ofdev) dev->base = (struct tah_regs __iomem *)ioremap(regs.start, sizeof(struct tah_regs)); if (dev->base == NULL) { - printk(KERN_ERR "%s: Can't map device registers!\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't map device registers!\n", np); goto err_free; } @@ -124,8 +121,7 @@ static int tah_probe(struct platform_device *ofdev) /* Initialize TAH and enable IPv4 checksum verification, no TSO yet */ tah_reset(ofdev); - printk(KERN_INFO - "TAH %s initialized\n", ofdev->dev.of_node->full_name); + printk(KERN_INFO "TAH %pOF initialized\n", ofdev->dev.of_node); wmb(); return 0; diff --git a/drivers/net/ethernet/ibm/emac/zmii.c b/drivers/net/ethernet/ibm/emac/zmii.c index 8727b865ea02..89c42d362292 100644 --- a/drivers/net/ethernet/ibm/emac/zmii.c +++ b/drivers/net/ethernet/ibm/emac/zmii.c @@ -121,15 +121,15 @@ int zmii_attach(struct platform_device *ofdev, int input, int *mode) } else dev->mode = *mode; - printk(KERN_NOTICE "%s: bridge in %s mode\n", - ofdev->dev.of_node->full_name, + printk(KERN_NOTICE "%pOF: bridge in %s mode\n", + ofdev->dev.of_node, zmii_mode_name(dev->mode)); } else { /* All inputs must use the same mode */ if (*mode != PHY_MODE_NA && *mode != dev->mode) { printk(KERN_ERR - "%s: invalid mode %d specified for input %d\n", - ofdev->dev.of_node->full_name, *mode, input); + "%pOF: invalid mode %d specified for input %d\n", + ofdev->dev.of_node, *mode, input); mutex_unlock(&dev->lock); return -EINVAL; } @@ -250,8 +250,7 @@ static int zmii_probe(struct platform_device *ofdev) rc = -ENXIO; if (of_address_to_resource(np, 0, ®s)) { - printk(KERN_ERR "%s: Can't get registers address\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't get registers address\n", np); goto err_free; } @@ -259,8 +258,7 @@ static int zmii_probe(struct platform_device *ofdev) dev->base = (struct zmii_regs __iomem *)ioremap(regs.start, sizeof(struct zmii_regs)); if (dev->base == NULL) { - printk(KERN_ERR "%s: Can't map device registers!\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't map device registers!\n", np); goto err_free; } @@ -270,8 +268,7 @@ static int zmii_probe(struct platform_device *ofdev) /* Disable all inputs by default */ out_be32(&dev->base->fer, 0); - printk(KERN_INFO - "ZMII %s initialized\n", ofdev->dev.of_node->full_name); + printk(KERN_INFO "ZMII %pOF initialized\n", ofdev->dev.of_node); wmb(); platform_set_drvdata(ofdev, dev); diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 4bb04aaf9650..6a4e8e1bbd90 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -9221,8 +9221,7 @@ static int niu_get_of_props(struct niu *np) phy_type = of_get_property(dp, "phy-type", &prop_len); if (!phy_type) { - netdev_err(dev, "%s: OF node lacks phy-type property\n", - dp->full_name); + netdev_err(dev, "%pOF: OF node lacks phy-type property\n", dp); return -EINVAL; } @@ -9232,26 +9231,25 @@ static int niu_get_of_props(struct niu *np) strcpy(np->vpd.phy_type, phy_type); if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) { - netdev_err(dev, "%s: Illegal phy string [%s]\n", - dp->full_name, np->vpd.phy_type); + netdev_err(dev, "%pOF: Illegal phy string [%s]\n", + dp, np->vpd.phy_type); return -EINVAL; } mac_addr = of_get_property(dp, "local-mac-address", &prop_len); if (!mac_addr) { - netdev_err(dev, "%s: OF node lacks local-mac-address property\n", - dp->full_name); + netdev_err(dev, "%pOF: OF node lacks local-mac-address property\n", + dp); return -EINVAL; } if (prop_len != dev->addr_len) { - netdev_err(dev, "%s: OF MAC address prop len (%d) is wrong\n", - dp->full_name, prop_len); + netdev_err(dev, "%pOF: OF MAC address prop len (%d) is wrong\n", + dp, prop_len); } memcpy(dev->dev_addr, mac_addr, dev->addr_len); if (!is_valid_ether_addr(&dev->dev_addr[0])) { - netdev_err(dev, "%s: OF MAC address is invalid\n", - dp->full_name); - netdev_err(dev, "%s: [ %pM ]\n", dp->full_name, dev->dev_addr); + netdev_err(dev, "%pOF: OF MAC address is invalid\n", dp); + netdev_err(dev, "%pOF: [ %pM ]\n", dp, dev->dev_addr); return -EINVAL; } @@ -10027,8 +10025,8 @@ static int niu_of_probe(struct platform_device *op) reg = of_get_property(op->dev.of_node, "reg", NULL); if (!reg) { - dev_err(&op->dev, "%s: No 'reg' property, aborting\n", - op->dev.of_node->full_name); + dev_err(&op->dev, "%pOF: No 'reg' property, aborting\n", + op->dev.of_node); return -ENODEV; } diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index badd0a8caeb9..c8776dbf1a55 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1321,8 +1321,8 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) phy = of_phy_connect(priv->ndev, slave->data->phy_node, &cpsw_adjust_link, 0, slave->data->phy_if); if (!phy) { - dev_err(priv->dev, "phy \"%s\" not found on slave %d\n", - slave->data->phy_node->full_name, + dev_err(priv->dev, "phy \"%pOF\" not found on slave %d\n", + slave->data->phy_node, slave->slave_num); return; } @@ -2670,8 +2670,8 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, parp = of_get_property(slave_node, "phy_id", &lenp); if (slave_data->phy_node) { dev_dbg(&pdev->dev, - "slave[%d] using phy-handle=\"%s\"\n", - i, slave_data->phy_node->full_name); + "slave[%d] using phy-handle=\"%pOF\"\n", + i, slave_data->phy_node); } else if (of_phy_is_fixed_link(slave_node)) { /* In the case of a fixed PHY, the DT node associated * to the PHY is the Ethernet MAC DT node. diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 64d5527feb2a..4bb561856af5 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1480,8 +1480,8 @@ static int emac_dev_open(struct net_device *ndev) phydev = of_phy_connect(ndev, priv->phy_node, &emac_adjust_link, 0, 0); if (!phydev) { - dev_err(emac_dev, "could not connect to phy %s\n", - priv->phy_node->full_name); + dev_err(emac_dev, "could not connect to phy %pOF\n", + priv->phy_node); ret = -ENODEV; goto err; } diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index d73da8afe08e..60abc9250f56 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -1089,7 +1089,7 @@ static int temac_of_probe(struct platform_device *op) lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0); if (lp->phy_node) - dev_dbg(lp->dev, "using PHY node %s (%p)\n", np->full_name, np); + dev_dbg(lp->dev, "using PHY node %pOF (%p)\n", np, np); /* Add the device attributes */ rc = sysfs_create_group(&lp->dev->kobj, &temac_attr_group); diff --git a/drivers/net/phy/mdio-mux-mmioreg.c b/drivers/net/phy/mdio-mux-mmioreg.c index 6a33646bdf05..c3825c7da038 100644 --- a/drivers/net/phy/mdio-mux-mmioreg.c +++ b/drivers/net/phy/mdio-mux-mmioreg.c @@ -105,7 +105,7 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev) const __be32 *iprop; int len, ret; - dev_dbg(&pdev->dev, "probing node %s\n", np->full_name); + dev_dbg(&pdev->dev, "probing node %pOF\n", np); s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); if (!s) @@ -113,8 +113,8 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev) ret = of_address_to_resource(np, 0, &res); if (ret) { - dev_err(&pdev->dev, "could not obtain memory map for node %s\n", - np->full_name); + dev_err(&pdev->dev, "could not obtain memory map for node %pOF\n", + np); return ret; } s->phys = res.start; @@ -145,15 +145,15 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev) for_each_available_child_of_node(np, np2) { iprop = of_get_property(np2, "reg", &len); if (!iprop || len != sizeof(uint32_t)) { - dev_err(&pdev->dev, "mdio-mux child node %s is " - "missing a 'reg' property\n", np2->full_name); + dev_err(&pdev->dev, "mdio-mux child node %pOF is " + "missing a 'reg' property\n", np2); of_node_put(np2); return -ENODEV; } if (be32_to_cpup(iprop) & ~s->mask) { - dev_err(&pdev->dev, "mdio-mux child node %s has " + dev_err(&pdev->dev, "mdio-mux child node %pOF has " "a 'reg' value with unmasked bits\n", - np2->full_name); + np2); of_node_put(np2); return -ENODEV; } @@ -162,8 +162,8 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev) ret = mdio_mux_init(&pdev->dev, mdio_mux_mmioreg_switch_fn, &s->mux_handle, s, NULL); if (ret) { - dev_err(&pdev->dev, "failed to register mdio-mux bus %s\n", - np->full_name); + dev_err(&pdev->dev, "failed to register mdio-mux bus %pOF\n", + np); return ret; } diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index c608e1dfaf09..942ceaf3fd3f 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -138,16 +138,16 @@ int mdio_mux_init(struct device *dev, r = of_property_read_u32(child_bus_node, "reg", &v); if (r) { dev_err(dev, - "Error: Failed to find reg for child %s\n", - of_node_full_name(child_bus_node)); + "Error: Failed to find reg for child %pOF\n", + child_bus_node); continue; } cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL); if (cb == NULL) { dev_err(dev, - "Error: Failed to allocate memory for child %s\n", - of_node_full_name(child_bus_node)); + "Error: Failed to allocate memory for child %pOF\n", + child_bus_node); ret_val = -ENOMEM; continue; } @@ -157,8 +157,8 @@ int mdio_mux_init(struct device *dev, cb->mii_bus = mdiobus_alloc(); if (!cb->mii_bus) { dev_err(dev, - "Error: Failed to allocate MDIO bus for child %s\n", - of_node_full_name(child_bus_node)); + "Error: Failed to allocate MDIO bus for child %pOF\n", + child_bus_node); ret_val = -ENOMEM; devm_kfree(dev, cb); continue; @@ -174,8 +174,8 @@ int mdio_mux_init(struct device *dev, r = of_mdiobus_register(cb->mii_bus, child_bus_node); if (r) { dev_err(dev, - "Error: Failed to register MDIO bus for child %s\n", - of_node_full_name(child_bus_node)); + "Error: Failed to register MDIO bus for child %pOF\n", + child_bus_node); mdiobus_free(cb->mii_bus); devm_kfree(dev, cb); } else { -- cgit v1.2.3 From 5b861f6baa3a22a48d7a4ad0ce38a223d36c978a Mon Sep 17 00:00:00 2001 From: Girish Moodalbail Date: Thu, 20 Jul 2017 22:44:20 -0700 Subject: geneve: add rtnl changelink support This patch adds changelink rtnl operation support for geneve devices and the code changes involve: - added geneve_quiesce() which quiesces the geneve device data path for both TX and RX. This lets us perform the changelink operation atomically w.r.t data path. Also added geneve_unquiesce() to reverse the operation of geneve_quiesce(). - refactor geneve_newlink into geneve_nl2info to be used by both geneve_newlink and geneve_changelink - geneve_nl2info takes a changelink boolean argument to isolate changelink checks. - Allow changing only a few attributes (ttl, tos, and remote tunnel endpoint IP address (within the same address family)): - return -EOPNOTSUPP for attributes that cannot be changed for now. Incremental patches can make the non-supported one available in the future if needed. Signed-off-by: Girish Moodalbail Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/geneve.c | 218 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 176 insertions(+), 42 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index de8156c6b292..0436a4283059 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -715,6 +715,7 @@ free_dst: static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, struct net_device *dev, + struct geneve_sock *gs4, struct flowi4 *fl4, const struct ip_tunnel_info *info) { @@ -724,7 +725,7 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, struct rtable *rt = NULL; __u8 tos; - if (!rcu_dereference(geneve->sock4)) + if (!gs4) return ERR_PTR(-EIO); memset(fl4, 0, sizeof(*fl4)); @@ -764,6 +765,7 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, #if IS_ENABLED(CONFIG_IPV6) static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, struct net_device *dev, + struct geneve_sock *gs6, struct flowi6 *fl6, const struct ip_tunnel_info *info) { @@ -771,10 +773,8 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, struct geneve_dev *geneve = netdev_priv(dev); struct dst_entry *dst = NULL; struct dst_cache *dst_cache; - struct geneve_sock *gs6; __u8 prio; - gs6 = rcu_dereference(geneve->sock6); if (!gs6) return ERR_PTR(-EIO); @@ -827,7 +827,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, __be16 df; int err; - rt = geneve_get_v4_rt(skb, dev, &fl4, info); + rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info); if (IS_ERR(rt)) return PTR_ERR(rt); @@ -866,7 +866,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, __be16 sport; int err; - dst = geneve_get_v6_dst(skb, dev, &fl6, info); + dst = geneve_get_v6_dst(skb, dev, gs6, &fl6, info); if (IS_ERR(dst)) return PTR_ERR(dst); @@ -951,8 +951,9 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) if (ip_tunnel_info_af(info) == AF_INET) { struct rtable *rt; struct flowi4 fl4; + struct geneve_sock *gs4 = rcu_dereference(geneve->sock4); - rt = geneve_get_v4_rt(skb, dev, &fl4, info); + rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info); if (IS_ERR(rt)) return PTR_ERR(rt); @@ -962,8 +963,9 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) } else if (ip_tunnel_info_af(info) == AF_INET6) { struct dst_entry *dst; struct flowi6 fl6; + struct geneve_sock *gs6 = rcu_dereference(geneve->sock6); - dst = geneve_get_v6_dst(skb, dev, &fl6, info); + dst = geneve_get_v6_dst(skb, dev, gs6, &fl6, info); if (IS_ERR(dst)) return PTR_ERR(dst); @@ -1140,6 +1142,15 @@ static bool is_tnl_info_zero(const struct ip_tunnel_info *info) return true; } +static bool geneve_dst_addr_equal(struct ip_tunnel_info *a, + struct ip_tunnel_info *b) +{ + if (ip_tunnel_info_af(a) == AF_INET) + return a->key.u.ipv4.dst == b->key.u.ipv4.dst; + else + return ipv6_addr_equal(&a->key.u.ipv6.dst, &b->key.u.ipv6.dst); +} + static int geneve_configure(struct net *net, struct net_device *dev, const struct ip_tunnel_info *info, bool metadata, bool ipv6_rx_csum) @@ -1197,24 +1208,22 @@ static void init_tnl_info(struct ip_tunnel_info *info, __u16 dst_port) info->key.tp_dst = htons(dst_port); } -static int geneve_newlink(struct net *net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[], - struct netlink_ext_ack *extack) +static int geneve_nl2info(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[], struct ip_tunnel_info *info, + bool *metadata, bool *use_udp6_rx_checksums, + bool changelink) { - bool use_udp6_rx_checksums = false; - struct ip_tunnel_info info; - bool metadata = false; - - init_tnl_info(&info, GENEVE_UDP_PORT); - if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) return -EINVAL; if (data[IFLA_GENEVE_REMOTE]) { - info.key.u.ipv4.dst = + if (changelink && (ip_tunnel_info_af(info) == AF_INET6)) + return -EOPNOTSUPP; + + info->key.u.ipv4.dst = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]); - if (IN_MULTICAST(ntohl(info.key.u.ipv4.dst))) { + if (IN_MULTICAST(ntohl(info->key.u.ipv4.dst))) { netdev_dbg(dev, "multicast remote is unsupported\n"); return -EINVAL; } @@ -1222,21 +1231,24 @@ static int geneve_newlink(struct net *net, struct net_device *dev, if (data[IFLA_GENEVE_REMOTE6]) { #if IS_ENABLED(CONFIG_IPV6) - info.mode = IP_TUNNEL_INFO_IPV6; - info.key.u.ipv6.dst = + if (changelink && (ip_tunnel_info_af(info) == AF_INET)) + return -EOPNOTSUPP; + + info->mode = IP_TUNNEL_INFO_IPV6; + info->key.u.ipv6.dst = nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]); - if (ipv6_addr_type(&info.key.u.ipv6.dst) & + if (ipv6_addr_type(&info->key.u.ipv6.dst) & IPV6_ADDR_LINKLOCAL) { netdev_dbg(dev, "link-local remote is unsupported\n"); return -EINVAL; } - if (ipv6_addr_is_multicast(&info.key.u.ipv6.dst)) { + if (ipv6_addr_is_multicast(&info->key.u.ipv6.dst)) { netdev_dbg(dev, "multicast remote is unsupported\n"); return -EINVAL; } - info.key.tun_flags |= TUNNEL_CSUM; - use_udp6_rx_checksums = true; + info->key.tun_flags |= TUNNEL_CSUM; + *use_udp6_rx_checksums = true; #else return -EPFNOSUPPORT; #endif @@ -1245,48 +1257,169 @@ static int geneve_newlink(struct net *net, struct net_device *dev, if (data[IFLA_GENEVE_ID]) { __u32 vni; __u8 tvni[3]; + __be64 tunid; vni = nla_get_u32(data[IFLA_GENEVE_ID]); tvni[0] = (vni & 0x00ff0000) >> 16; tvni[1] = (vni & 0x0000ff00) >> 8; tvni[2] = vni & 0x000000ff; - info.key.tun_id = vni_to_tunnel_id(tvni); + tunid = vni_to_tunnel_id(tvni); + if (changelink && (tunid != info->key.tun_id)) + return -EOPNOTSUPP; + info->key.tun_id = tunid; } + if (data[IFLA_GENEVE_TTL]) - info.key.ttl = nla_get_u8(data[IFLA_GENEVE_TTL]); + info->key.ttl = nla_get_u8(data[IFLA_GENEVE_TTL]); if (data[IFLA_GENEVE_TOS]) - info.key.tos = nla_get_u8(data[IFLA_GENEVE_TOS]); + info->key.tos = nla_get_u8(data[IFLA_GENEVE_TOS]); if (data[IFLA_GENEVE_LABEL]) { - info.key.label = nla_get_be32(data[IFLA_GENEVE_LABEL]) & + info->key.label = nla_get_be32(data[IFLA_GENEVE_LABEL]) & IPV6_FLOWLABEL_MASK; - if (info.key.label && (!(info.mode & IP_TUNNEL_INFO_IPV6))) + if (info->key.label && (!(info->mode & IP_TUNNEL_INFO_IPV6))) return -EINVAL; } - if (data[IFLA_GENEVE_PORT]) - info.key.tp_dst = nla_get_be16(data[IFLA_GENEVE_PORT]); + if (data[IFLA_GENEVE_PORT]) { + if (changelink) + return -EOPNOTSUPP; + info->key.tp_dst = nla_get_be16(data[IFLA_GENEVE_PORT]); + } - if (data[IFLA_GENEVE_COLLECT_METADATA]) - metadata = true; + if (data[IFLA_GENEVE_COLLECT_METADATA]) { + if (changelink) + return -EOPNOTSUPP; + *metadata = true; + } - if (data[IFLA_GENEVE_UDP_CSUM] && - nla_get_u8(data[IFLA_GENEVE_UDP_CSUM])) - info.key.tun_flags |= TUNNEL_CSUM; + if (data[IFLA_GENEVE_UDP_CSUM]) { + if (changelink) + return -EOPNOTSUPP; + if (nla_get_u8(data[IFLA_GENEVE_UDP_CSUM])) + info->key.tun_flags |= TUNNEL_CSUM; + } - if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX] && - nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) - info.key.tun_flags &= ~TUNNEL_CSUM; + if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { + if (changelink) + return -EOPNOTSUPP; + if (nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) + info->key.tun_flags &= ~TUNNEL_CSUM; + } - if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX] && - nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) - use_udp6_rx_checksums = false; + if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { + if (changelink) + return -EOPNOTSUPP; + if (nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) + *use_udp6_rx_checksums = false; + } + + return 0; +} + +static int geneve_newlink(struct net *net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + bool use_udp6_rx_checksums = false; + struct ip_tunnel_info info; + bool metadata = false; + int err; + + init_tnl_info(&info, GENEVE_UDP_PORT); + err = geneve_nl2info(dev, tb, data, &info, &metadata, + &use_udp6_rx_checksums, false); + if (err) + return err; return geneve_configure(net, dev, &info, metadata, use_udp6_rx_checksums); } +/* Quiesces the geneve device data path for both TX and RX. + * + * On transmit geneve checks for non-NULL geneve_sock before it proceeds. + * So, if we set that socket to NULL under RCU and wait for synchronize_net() + * to complete for the existing set of in-flight packets to be transmitted, + * then we would have quiesced the transmit data path. All the future packets + * will get dropped until we unquiesce the data path. + * + * On receive geneve dereference the geneve_sock stashed in the socket. So, + * if we set that to NULL under RCU and wait for synchronize_net() to + * complete, then we would have quiesced the receive data path. + */ +static void geneve_quiesce(struct geneve_dev *geneve, struct geneve_sock **gs4, + struct geneve_sock **gs6) +{ + *gs4 = rtnl_dereference(geneve->sock4); + rcu_assign_pointer(geneve->sock4, NULL); + if (*gs4) + rcu_assign_sk_user_data((*gs4)->sock->sk, NULL); +#if IS_ENABLED(CONFIG_IPV6) + *gs6 = rtnl_dereference(geneve->sock6); + rcu_assign_pointer(geneve->sock6, NULL); + if (*gs6) + rcu_assign_sk_user_data((*gs6)->sock->sk, NULL); +#else + *gs6 = NULL; +#endif + synchronize_net(); +} + +/* Resumes the geneve device data path for both TX and RX. */ +static void geneve_unquiesce(struct geneve_dev *geneve, struct geneve_sock *gs4, + struct geneve_sock __maybe_unused *gs6) +{ + rcu_assign_pointer(geneve->sock4, gs4); + if (gs4) + rcu_assign_sk_user_data(gs4->sock->sk, gs4); +#if IS_ENABLED(CONFIG_IPV6) + rcu_assign_pointer(geneve->sock6, gs6); + if (gs6) + rcu_assign_sk_user_data(gs6->sock->sk, gs6); +#endif + synchronize_net(); +} + +static int geneve_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct geneve_dev *geneve = netdev_priv(dev); + struct geneve_sock *gs4, *gs6; + struct ip_tunnel_info info; + bool metadata; + bool use_udp6_rx_checksums; + int err; + + /* If the geneve device is configured for metadata (or externally + * controlled, for example, OVS), then nothing can be changed. + */ + if (geneve->collect_md) + return -EOPNOTSUPP; + + /* Start with the existing info. */ + memcpy(&info, &geneve->info, sizeof(info)); + metadata = geneve->collect_md; + use_udp6_rx_checksums = geneve->use_udp6_rx_checksums; + err = geneve_nl2info(dev, tb, data, &info, &metadata, + &use_udp6_rx_checksums, true); + if (err) + return err; + + if (!geneve_dst_addr_equal(&geneve->info, &info)) + dst_cache_reset(&info.dst_cache); + + geneve_quiesce(geneve, &gs4, &gs6); + geneve->info = info; + geneve->collect_md = metadata; + geneve->use_udp6_rx_checksums = use_udp6_rx_checksums; + geneve_unquiesce(geneve, gs4, gs6); + + return 0; +} + static void geneve_dellink(struct net_device *dev, struct list_head *head) { struct geneve_dev *geneve = netdev_priv(dev); @@ -1375,6 +1508,7 @@ static struct rtnl_link_ops geneve_link_ops __read_mostly = { .setup = geneve_setup, .validate = geneve_validate, .newlink = geneve_newlink, + .changelink = geneve_changelink, .dellink = geneve_dellink, .get_size = geneve_get_size, .fill_info = geneve_fill_info, -- cgit v1.2.3 From 2d2b13fcfff101daa5388c5edac0ae5e3bcc6878 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 21 Jul 2017 12:49:32 +0200 Subject: geneve/vxlan: add support for NETDEV_UDP_TUNNEL_DROP_INFO Signed-off-by: Sabrina Dubroca Signed-off-by: David S. Miller --- drivers/net/geneve.c | 19 +++++++++++++------ drivers/net/vxlan.c | 25 +++++++++++++++++-------- 2 files changed, 30 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 0436a4283059..74545ecb9431 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1016,16 +1016,22 @@ static struct device_type geneve_type = { * supply the listening GENEVE udp ports. Callers are expected * to implement the ndo_udp_tunnel_add. */ -static void geneve_push_rx_ports(struct net_device *dev) +static void geneve_offload_rx_ports(struct net_device *dev, bool push) { struct net *net = dev_net(dev); struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; rcu_read_lock(); - list_for_each_entry_rcu(gs, &gn->sock_list, list) - udp_tunnel_push_rx_port(dev, gs->sock, - UDP_TUNNEL_TYPE_GENEVE); + list_for_each_entry_rcu(gs, &gn->sock_list, list) { + if (push) { + udp_tunnel_push_rx_port(dev, gs->sock, + UDP_TUNNEL_TYPE_GENEVE); + } else { + udp_tunnel_drop_rx_port(dev, gs->sock, + UDP_TUNNEL_TYPE_GENEVE); + } + } rcu_read_unlock(); } @@ -1560,8 +1566,9 @@ static int geneve_netdevice_event(struct notifier_block *unused, { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - if (event == NETDEV_UDP_TUNNEL_PUSH_INFO) - geneve_push_rx_ports(dev); + if (event == NETDEV_UDP_TUNNEL_PUSH_INFO || + event == NETDEV_UDP_TUNNEL_DROP_INFO) + geneve_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO); return NOTIFY_DONE; } diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 96aa7e6cf214..4642d5be2fa0 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2608,7 +2608,7 @@ static struct device_type vxlan_type = { * supply the listening VXLAN udp ports. Callers are expected * to implement the ndo_udp_tunnel_add. */ -static void vxlan_push_rx_ports(struct net_device *dev) +static void vxlan_offload_rx_ports(struct net_device *dev, bool push) { struct vxlan_sock *vs; struct net *net = dev_net(dev); @@ -2617,11 +2617,19 @@ static void vxlan_push_rx_ports(struct net_device *dev) spin_lock(&vn->sock_lock); for (i = 0; i < PORT_HASH_SIZE; ++i) { - hlist_for_each_entry_rcu(vs, &vn->sock_list[i], hlist) - udp_tunnel_push_rx_port(dev, vs->sock, - (vs->flags & VXLAN_F_GPE) ? - UDP_TUNNEL_TYPE_VXLAN_GPE : - UDP_TUNNEL_TYPE_VXLAN); + hlist_for_each_entry_rcu(vs, &vn->sock_list[i], hlist) { + unsigned short type; + + if (vs->flags & VXLAN_F_GPE) + type = UDP_TUNNEL_TYPE_VXLAN_GPE; + else + type = UDP_TUNNEL_TYPE_VXLAN; + + if (push) + udp_tunnel_push_rx_port(dev, vs->sock, type); + else + udp_tunnel_drop_rx_port(dev, vs->sock, type); + } } spin_unlock(&vn->sock_lock); } @@ -3632,8 +3640,9 @@ static int vxlan_netdevice_event(struct notifier_block *unused, if (event == NETDEV_UNREGISTER) vxlan_handle_lowerdev_unregister(vn, dev); - else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO) - vxlan_push_rx_ports(dev); + else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO || + event == NETDEV_UDP_TUNNEL_DROP_INFO) + vxlan_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO); return NOTIFY_DONE; } -- cgit v1.2.3 From 04584957b5f9b036caa9603525e12d1840d42d58 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 21 Jul 2017 12:49:33 +0200 Subject: geneve/vxlan: offload ports on register/unregister events This improves consistency of handling when moving a netdev to another netns. Most drivers currently do a full reset when the device goes up, so that will flush the offload state anyway. Signed-off-by: Sabrina Dubroca Signed-off-by: David S. Miller --- drivers/net/geneve.c | 7 ++++++- drivers/net/vxlan.c | 10 +++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 74545ecb9431..745d57ae95d7 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1567,8 +1567,13 @@ static int geneve_netdevice_event(struct notifier_block *unused, struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (event == NETDEV_UDP_TUNNEL_PUSH_INFO || - event == NETDEV_UDP_TUNNEL_DROP_INFO) + event == NETDEV_UDP_TUNNEL_DROP_INFO) { geneve_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO); + } else if (event == NETDEV_UNREGISTER) { + geneve_offload_rx_ports(dev, false); + } else if (event == NETDEV_REGISTER) { + geneve_offload_rx_ports(dev, true); + } return NOTIFY_DONE; } diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 4642d5be2fa0..dbca067540d0 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -3638,11 +3638,15 @@ static int vxlan_netdevice_event(struct notifier_block *unused, struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); - if (event == NETDEV_UNREGISTER) + if (event == NETDEV_UNREGISTER) { + vxlan_offload_rx_ports(dev, false); vxlan_handle_lowerdev_unregister(vn, dev); - else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO || - event == NETDEV_UDP_TUNNEL_DROP_INFO) + } else if (event == NETDEV_REGISTER) { + vxlan_offload_rx_ports(dev, true); + } else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO || + event == NETDEV_UDP_TUNNEL_DROP_INFO) { vxlan_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO); + } return NOTIFY_DONE; } -- cgit v1.2.3 From 4a3c67a6e7cd212fe799ab3d07782c7c8688b4cc Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 21 Jul 2017 20:31:38 +0200 Subject: mlxsw: spectrum_router: Don't batch neighbour deletion Current firmware supported by the driver doesn't support batch deletion of IPv6 neighbours on a given router interface (RIF). Until a new version that supports this functionality is made available, delete neighbours one by one. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index e6d629f40f93..548552ccec3d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1419,25 +1419,16 @@ static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp) rhashtable_destroy(&mlxsw_sp->router->neigh_ht); } -static int mlxsw_sp_neigh_rif_flush(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_rif *rif) -{ - char rauht_pl[MLXSW_REG_RAUHT_LEN]; - - mlxsw_reg_rauht_pack(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_DELETE_ALL, - rif->rif_index, rif->addr); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); -} - static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *rif) { struct mlxsw_sp_neigh_entry *neigh_entry, *tmp; - mlxsw_sp_neigh_rif_flush(mlxsw_sp, rif); list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list, - rif_list_node) + rif_list_node) { + mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false); mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry); + } } struct mlxsw_sp_nexthop_key { -- cgit v1.2.3 From 1bb79284fcf9ebacff16ea0b81b3d5b86528cfd4 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sat, 22 Jul 2017 02:54:43 +0800 Subject: netvsc: fix ptr_ret.cocci warnings drivers/net/hyperv/netvsc_drv.c:737:8-14: WARNING: PTR_ERR_OR_ZERO can be used Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR Generated by: scripts/coccinelle/api/ptr_ret.cocci Fixes: 9749fed5d43d ("netvsc: use ERR_PTR to avoid dereference issues") CC: stephen hemminger Signed-off-by: Fengguang Wu Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index a164981c15f7..9a9e269a25ae 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -734,7 +734,7 @@ static int netvsc_set_queues(struct net_device *net, struct hv_device *dev, return ret; net_device = rndis_filter_device_add(dev, &device_info); - return IS_ERR(net_device) ? PTR_ERR(net_device) : 0; + return PTR_ERR_OR_ZERO(net_device); } static int netvsc_set_channels(struct net_device *net, -- cgit v1.2.3 From ba3fb1022154d93fe71ee78e28e195207d511bc0 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 23 Jul 2017 10:44:52 -0400 Subject: liquidio: fix implicit irq include causing build failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To fix In file included from drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c:24:0: drivers/net/ethernet/cavium/liquidio/octeon_device.h:216:2: error: expected specifier-qualifier-list before ‘irqreturn_t’ irqreturn_t (*process_interrupt_regs)(void *); ^ as seen on arm64 allmodconfig builds. Cc: Derek Chickles Cc: Satanand Burla Cc: Felix Manlunas Cc: Raghu Vatsavayi Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_device.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index c90ed48ae8ab..ad464788c923 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -22,6 +22,8 @@ #ifndef _OCTEON_DEVICE_H_ #define _OCTEON_DEVICE_H_ +#include + /** PCI VendorId Device Id */ #define OCTEON_CN68XX_PCIID 0x91177d #define OCTEON_CN66XX_PCIID 0x92177d -- cgit v1.2.3 From f3eebe88195478b1bfbe8510d58a8c226ea759a8 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Sun, 23 Jul 2017 23:02:53 -0400 Subject: mlx4_en: remove unnecessary returned value The function mlx4_en_arm_cq always returns zero. So change the return type of the function mlx4_en_arm_cq to void. CC: Joe Jin CC: Junxiao Bi Signed-off-by: Zhu Yanjun Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_cq.c | 4 +--- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 85fe17e4dcfb..87d1f4d2a77b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -208,12 +208,10 @@ int mlx4_en_set_cq_moder(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) cq->moder_cnt, cq->moder_time); } -int mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) +void mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) { mlx4_cq_arm(&cq->mcq, MLX4_CQ_DB_REQ_NOT, priv->mdev->uar_map, &priv->mdev->uar_lock); - - return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index d350b2158104..fdb3ad0cbe54 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -685,7 +685,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, int cq_idx); void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); int mlx4_en_set_cq_moder(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); -int mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); +void mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); void mlx4_en_tx_irq(struct mlx4_cq *mcq); u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb, -- cgit v1.2.3 From b5f3e0d430122a551cbc2b88068dae8b2c2c0031 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 24 Jul 2017 09:56:00 +0200 Subject: mlxsw: spectrum_router: Fix build when IPv6 isn't enabled When IPv6 isn't enabled the following error is generated: ERROR: "nd_tbl" [drivers/net/ethernet/mellanox/mlxsw/mlxsw_spectrum.ko] undefined! Fix it by replacing 'arp_tbl' and 'nd_tbl' with 'tbl->family' wherever possible and reference 'nd_tbl' only when IPV6 is enabled. Fixes: d5eb89cf68d6 ("mlxsw: spectrum_router: Reflect IPv6 neighbours to the device") Signed-off-by: Ido Schimmel Reported-by: kbuild test robot Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 548552ccec3d..8bf076d22fb6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -949,9 +950,13 @@ mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp) { unsigned long interval; +#if IS_ENABLED(CONFIG_IPV6) interval = min_t(unsigned long, NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME), NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME)); +#else + interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME); +#endif mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval); } @@ -986,6 +991,7 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp, neigh_release(n); } +#if IS_ENABLED(IPV6) static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp, char *rauhtd_pl, int rec_index) @@ -1015,6 +1021,13 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp, neigh_event_send(n, NULL); neigh_release(n); } +#else +static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + int rec_index) +{ +} +#endif static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp, char *rauhtd_pl, @@ -1260,10 +1273,10 @@ mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp, if (!adding && !neigh_entry->connected) return; neigh_entry->connected = adding; - if (neigh_entry->key.n->tbl == &arp_tbl) { + if (neigh_entry->key.n->tbl->family == AF_INET) { mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry, mlxsw_sp_rauht_op(adding)); - } else if (neigh_entry->key.n->tbl == &nd_tbl) { + } else if (neigh_entry->key.n->tbl->family == AF_INET6) { if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n)) return; mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry, @@ -1339,7 +1352,8 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused, p = ptr; /* We don't care about changes in the default table. */ - if (!p->dev || (p->tbl != &arp_tbl && p->tbl != &nd_tbl)) + if (!p->dev || (p->tbl->family != AF_INET && + p->tbl->family != AF_INET6)) return NOTIFY_DONE; /* We are in atomic context and can't take RTNL mutex, @@ -1358,7 +1372,7 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused, case NETEVENT_NEIGH_UPDATE: n = ptr; - if (n->tbl != &arp_tbl && n->tbl != &nd_tbl) + if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6) return NOTIFY_DONE; mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev); -- cgit v1.2.3 From f575a02ee76cda1eff5e6949aff7cf5c386e3977 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Mon, 24 Jul 2017 04:22:44 -0400 Subject: mlx4_en: remove unnecessary error check The function mlx4_en_get_profile always return zero. So it is not necessary to check its return value. CC: Joe Jin CC: Junxiao Bi Signed-off-by: Zhu Yanjun Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_main.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index 2b0cbca4beb5..686e18de9a97 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -147,7 +147,7 @@ void mlx4_en_update_loopback_state(struct net_device *dev, mutex_unlock(&priv->mdev->state_lock); } -static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) +static void mlx4_en_get_profile(struct mlx4_en_dev *mdev) { struct mlx4_en_profile *params = &mdev->profile; int i; @@ -176,8 +176,6 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) params->prof[i].rss_rings = 0; params->prof[i].inline_thold = inline_thold; } - - return 0; } static void *mlx4_en_get_netdev(struct mlx4_dev *dev, void *ctx, u8 port) @@ -309,10 +307,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev) } /* Build device profile according to supplied module parameters */ - if (mlx4_en_get_profile(mdev)) { - mlx4_err(mdev, "Bad module parameters, aborting\n"); - goto err_mr; - } + mlx4_en_get_profile(mdev); /* Configure which ports to start according to module parameters */ mdev->port_cnt = 0; -- cgit v1.2.3 From acb2005463612930b07723e852b2483d669ff856 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 24 Jul 2017 12:34:20 -0400 Subject: bnxt_en: Update firmware interface spec to 1.8.0. VF representors and PTP are added features in the new firmware spec. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 +- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 8 +- drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h | 322 +++++++++++++++++++++--- drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 9 +- 4 files changed, 308 insertions(+), 39 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index e7c8539cbddf..2103f1412359 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5646,7 +5646,7 @@ static int bnxt_hwrm_phy_qcaps(struct bnxt *bp) if (rc) goto hwrm_phy_qcaps_exit; - if (resp->eee_supported & PORT_PHY_QCAPS_RESP_EEE_SUPPORTED) { + if (resp->flags & PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED) { struct ethtool_eee *eee = &bp->eee; u16 fw_speeds = le16_to_cpu(resp->supported_speeds_eee_mode); @@ -5686,13 +5686,15 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state) memcpy(&link_info->phy_qcfg_resp, resp, sizeof(*resp)); link_info->phy_link_status = resp->link; - link_info->duplex = resp->duplex; + link_info->duplex = resp->duplex_cfg; + if (bp->hwrm_spec_code >= 0x10800) + link_info->duplex = resp->duplex_state; link_info->pause = resp->pause; link_info->auto_mode = resp->auto_mode; link_info->auto_pause_setting = resp->auto_pause; link_info->lp_pause = resp->link_partner_adv_pause; link_info->force_pause_setting = resp->force_pause; - link_info->duplex_setting = resp->duplex; + link_info->duplex_setting = resp->duplex_cfg; if (link_info->phy_link_status == BNXT_LINK_LINK) link_info->link_speed = le16_to_cpu(resp->link_speed); else diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index f34691f85602..505691a33a7c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -12,10 +12,10 @@ #define BNXT_H #define DRV_MODULE_NAME "bnxt_en" -#define DRV_MODULE_VERSION "1.7.0" +#define DRV_MODULE_VERSION "1.8.0" #define DRV_VER_MAJ 1 -#define DRV_VER_MIN 7 +#define DRV_VER_MIN 8 #define DRV_VER_UPD 0 #include @@ -825,8 +825,8 @@ struct bnxt_link_info { u8 loop_back; u8 link_up; u8 duplex; -#define BNXT_LINK_DUPLEX_HALF PORT_PHY_QCFG_RESP_DUPLEX_HALF -#define BNXT_LINK_DUPLEX_FULL PORT_PHY_QCFG_RESP_DUPLEX_FULL +#define BNXT_LINK_DUPLEX_HALF PORT_PHY_QCFG_RESP_DUPLEX_STATE_HALF +#define BNXT_LINK_DUPLEX_FULL PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL u8 pause; #define BNXT_LINK_PAUSE_TX PORT_PHY_QCFG_RESP_PAUSE_TX #define BNXT_LINK_PAUSE_RX PORT_PHY_QCFG_RESP_PAUSE_RX diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 7dc71bb95837..3ba22e8ee914 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -11,14 +11,14 @@ #ifndef BNXT_HSI_H #define BNXT_HSI_H -/* HSI and HWRM Specification 1.7.6 */ +/* HSI and HWRM Specification 1.8.0 */ #define HWRM_VERSION_MAJOR 1 -#define HWRM_VERSION_MINOR 7 -#define HWRM_VERSION_UPDATE 6 +#define HWRM_VERSION_MINOR 8 +#define HWRM_VERSION_UPDATE 0 -#define HWRM_VERSION_RSVD 2 /* non-zero means beta version */ +#define HWRM_VERSION_RSVD 0 /* non-zero means beta version */ -#define HWRM_VERSION_STR "1.7.6.2" +#define HWRM_VERSION_STR "1.8.0.0" /* * Following is the signature for HWRM message field that indicates not * applicable (All F's). Need to cast it the size of the field if needed. @@ -813,7 +813,7 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED 0x4UL #define FUNC_QCFG_RESP_FLAGS_STD_TX_RING_MODE_ENABLED 0x8UL #define FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED 0x10UL - #define FUNC_QCFG_RESP_FLAGS_MULTI_HOST 0x20UL + #define FUNC_QCFG_RESP_FLAGS_MULTI_HOST 0x20UL u8 mac_address[6]; __le16 pci_id; __le16 alloc_rsscos_ctx; @@ -835,9 +835,8 @@ struct hwrm_func_qcfg_output { u8 port_pf_cnt; #define FUNC_QCFG_RESP_PORT_PF_CNT_UNAVAIL 0x0UL __le16 dflt_vnic_id; - u8 host_cnt; - #define FUNC_QCFG_RESP_HOST_CNT_UNAVAIL 0x0UL u8 unused_0; + u8 unused_1; __le32 min_bw; #define FUNC_QCFG_RESP_MIN_BW_BW_VALUE_MASK 0xfffffffUL #define FUNC_QCFG_RESP_MIN_BW_BW_VALUE_SFT 0 @@ -874,12 +873,56 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_EVB_MODE_NO_EVB 0x0UL #define FUNC_QCFG_RESP_EVB_MODE_VEB 0x1UL #define FUNC_QCFG_RESP_EVB_MODE_VEPA 0x2UL - u8 unused_1; + u8 unused_2; __le16 alloc_vfs; __le32 alloc_mcast_filters; __le32 alloc_hw_ring_grps; __le16 alloc_sp_tx_rings; + u8 unused_3; + u8 valid; +}; + +/* hwrm_func_vlan_cfg */ +/* Input (48 bytes) */ +struct hwrm_func_vlan_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 fid; + u8 unused_0; + u8 unused_1; + __le32 enables; + #define FUNC_VLAN_CFG_REQ_ENABLES_STAG_VID 0x1UL + #define FUNC_VLAN_CFG_REQ_ENABLES_CTAG_VID 0x2UL + #define FUNC_VLAN_CFG_REQ_ENABLES_STAG_PCP 0x4UL + #define FUNC_VLAN_CFG_REQ_ENABLES_CTAG_PCP 0x8UL + #define FUNC_VLAN_CFG_REQ_ENABLES_STAG_TPID 0x10UL + #define FUNC_VLAN_CFG_REQ_ENABLES_CTAG_TPID 0x20UL + __le16 stag_vid; + u8 stag_pcp; u8 unused_2; + __be16 stag_tpid; + __le16 ctag_vid; + u8 ctag_pcp; + u8 unused_3; + __be16 ctag_tpid; + __le32 rsvd1; + __le32 rsvd2; + __le32 unused_4; +}; + +/* Output (16 bytes) */ +struct hwrm_func_vlan_cfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; u8 valid; }; @@ -902,6 +945,7 @@ struct hwrm_func_cfg_input { #define FUNC_CFG_REQ_FLAGS_STD_TX_RING_MODE_ENABLE 0x200UL #define FUNC_CFG_REQ_FLAGS_STD_TX_RING_MODE_DISABLE 0x400UL #define FUNC_CFG_REQ_FLAGS_VIRT_MAC_PERSIST 0x800UL + #define FUNC_CFG_REQ_FLAGS_NO_AUTOCLEAR_STATISTIC 0x1000UL __le32 enables; #define FUNC_CFG_REQ_ENABLES_MTU 0x1UL #define FUNC_CFG_REQ_ENABLES_MRU 0x2UL @@ -1456,9 +1500,9 @@ struct hwrm_port_phy_qcfg_output { #define PORT_PHY_QCFG_RESP_LINK_SPEED_50GB 0x1f4UL #define PORT_PHY_QCFG_RESP_LINK_SPEED_100GB 0x3e8UL #define PORT_PHY_QCFG_RESP_LINK_SPEED_10MB 0xffffUL - u8 duplex; - #define PORT_PHY_QCFG_RESP_DUPLEX_HALF 0x0UL - #define PORT_PHY_QCFG_RESP_DUPLEX_FULL 0x1UL + u8 duplex_cfg; + #define PORT_PHY_QCFG_RESP_DUPLEX_CFG_HALF 0x0UL + #define PORT_PHY_QCFG_RESP_DUPLEX_CFG_FULL 0x1UL u8 pause; #define PORT_PHY_QCFG_RESP_PAUSE_TX 0x1UL #define PORT_PHY_QCFG_RESP_PAUSE_RX 0x2UL @@ -1573,6 +1617,9 @@ struct hwrm_port_phy_qcfg_output { #define PORT_PHY_QCFG_RESP_PHY_TYPE_40G_BASELR4 0x16UL #define PORT_PHY_QCFG_RESP_PHY_TYPE_40G_BASEER4 0x17UL #define PORT_PHY_QCFG_RESP_PHY_TYPE_40G_ACTIVE_CABLE 0x18UL + #define PORT_PHY_QCFG_RESP_PHY_TYPE_1G_BASET 0x19UL + #define PORT_PHY_QCFG_RESP_PHY_TYPE_1G_BASESX 0x1aUL + #define PORT_PHY_QCFG_RESP_PHY_TYPE_1G_BASECX 0x1bUL u8 media_type; #define PORT_PHY_QCFG_RESP_MEDIA_TYPE_UNKNOWN 0x0UL #define PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP 0x1UL @@ -1651,14 +1698,16 @@ struct hwrm_port_phy_qcfg_output { #define PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE74_ENABLED 0x10UL #define PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE91_SUPPORTED 0x20UL #define PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE91_ENABLED 0x40UL + u8 duplex_state; + #define PORT_PHY_QCFG_RESP_DUPLEX_STATE_HALF 0x0UL + #define PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL 0x1UL u8 unused_1; - u8 unused_2; char phy_vendor_name[16]; char phy_vendor_partnumber[16]; - __le32 unused_3; + __le32 unused_2; + u8 unused_3; u8 unused_4; u8 unused_5; - u8 unused_6; u8 valid; }; @@ -1744,6 +1793,51 @@ struct hwrm_port_mac_cfg_output { u8 valid; }; +/* hwrm_port_mac_ptp_qcfg */ +/* Input (24 bytes) */ +struct hwrm_port_mac_ptp_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 port_id; + __le16 unused_0[3]; +}; + +/* Output (80 bytes) */ +struct hwrm_port_mac_ptp_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 flags; + #define PORT_MAC_PTP_QCFG_RESP_FLAGS_DIRECT_ACCESS 0x1UL + #define PORT_MAC_PTP_QCFG_RESP_FLAGS_HWRM_ACCESS 0x2UL + u8 unused_0; + __le16 unused_1; + __le32 rx_ts_reg_off_lower; + __le32 rx_ts_reg_off_upper; + __le32 rx_ts_reg_off_seq_id; + __le32 rx_ts_reg_off_src_id_0; + __le32 rx_ts_reg_off_src_id_1; + __le32 rx_ts_reg_off_src_id_2; + __le32 rx_ts_reg_off_domain_id; + __le32 rx_ts_reg_off_fifo; + __le32 rx_ts_reg_off_fifo_adv; + __le32 rx_ts_reg_off_granularity; + __le32 tx_ts_reg_off_lower; + __le32 tx_ts_reg_off_upper; + __le32 tx_ts_reg_off_seq_id; + __le32 tx_ts_reg_off_fifo; + __le32 tx_ts_reg_off_granularity; + __le32 unused_2; + u8 unused_3; + u8 unused_4; + u8 unused_5; + u8 valid; +}; + /* hwrm_port_qstats */ /* Input (40 bytes) */ struct hwrm_port_qstats_input { @@ -1874,10 +1968,10 @@ struct hwrm_port_phy_qcaps_output { __le16 req_type; __le16 seq_id; __le16 resp_len; - u8 eee_supported; - #define PORT_PHY_QCAPS_RESP_EEE_SUPPORTED 0x1UL - #define PORT_PHY_QCAPS_RESP_RSVD1_MASK 0xfeUL - #define PORT_PHY_QCAPS_RESP_RSVD1_SFT 1 + u8 flags; + #define PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED 0x1UL + #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_MASK 0xfeUL + #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_SFT 1 u8 unused_0; __le16 supported_speeds_force_mode; #define PORT_PHY_QCAPS_RESP_SUPPORTED_SPEEDS_FORCE_MODE_100MBHD 0x1UL @@ -3152,6 +3246,95 @@ struct hwrm_queue_cos2bw_cfg_output { u8 valid; }; +/* hwrm_queue_dscp_qcaps */ +/* Input (24 bytes) */ +struct hwrm_queue_dscp_qcaps_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + u8 port_id; + u8 unused_0[7]; +}; + +/* Output (16 bytes) */ +struct hwrm_queue_dscp_qcaps_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 num_dscp_bits; + u8 unused_0; + __le16 max_entries; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_queue_dscp2pri_qcfg */ +/* Input (32 bytes) */ +struct hwrm_queue_dscp2pri_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 dest_data_addr; + u8 port_id; + u8 unused_0; + __le16 dest_data_buffer_size; + __le32 unused_1; +}; + +/* Output (16 bytes) */ +struct hwrm_queue_dscp2pri_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 entry_cnt; + u8 default_pri; + u8 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_queue_dscp2pri_cfg */ +/* Input (40 bytes) */ +struct hwrm_queue_dscp2pri_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 src_data_addr; + __le32 flags; + #define QUEUE_DSCP2PRI_CFG_REQ_FLAGS_USE_HW_DEFAULT_PRI 0x1UL + __le32 enables; + #define QUEUE_DSCP2PRI_CFG_REQ_ENABLES_DEFAULT_PRI 0x1UL + u8 port_id; + u8 default_pri; + __le16 entry_cnt; + __le32 unused_0; +}; + +/* Output (16 bytes) */ +struct hwrm_queue_dscp2pri_cfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + /* hwrm_vnic_alloc */ /* Input (24 bytes) */ struct hwrm_vnic_alloc_input { @@ -4038,7 +4221,7 @@ struct hwrm_cfa_encap_record_alloc_input { #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_IPGRE 0x8UL u8 unused_0; __le16 unused_1; - __le32 encap_data[16]; + __le32 encap_data[20]; }; /* Output (16 bytes) */ @@ -4120,8 +4303,8 @@ struct hwrm_cfa_ntuple_filter_alloc_input { #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6 0x6UL u8 ip_protocol; #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UNKNOWN 0x0UL - #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP 0x6UL - #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP 0x11UL + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP 0x6UL + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP 0x11UL __le16 dst_id; __le16 mirror_vnic_id; u8 tunnel_type; @@ -4224,6 +4407,58 @@ struct hwrm_cfa_ntuple_filter_cfg_output { u8 valid; }; +/* hwrm_cfa_vfr_alloc */ +/* Input (32 bytes) */ +struct hwrm_cfa_vfr_alloc_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 vf_id; + __le16 reserved; + __le32 unused_0; + char vfr_name[32]; +}; + +/* Output (16 bytes) */ +struct hwrm_cfa_vfr_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 rx_cfa_code; + __le16 tx_cfa_action; + u8 unused_0; + u8 unused_1; + u8 unused_2; + u8 valid; +}; + +/* hwrm_cfa_vfr_free */ +/* Input (24 bytes) */ +struct hwrm_cfa_vfr_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + char vfr_name[32]; +}; + +/* Output (16 bytes) */ +struct hwrm_cfa_vfr_free_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + /* hwrm_tunnel_dst_port_query */ /* Input (24 bytes) */ struct hwrm_tunnel_dst_port_query_input { @@ -4448,12 +4683,13 @@ struct hwrm_fw_reset_input { #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT 0x1UL #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL 0x2UL #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE 0x3UL - #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_RSVD 0x4UL + #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_HOST 0x4UL u8 selfrst_status; #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE 0x0UL #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP 0x1UL #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST 0x2UL - __le16 unused_0[3]; + u8 host_idx; + u8 unused_0[5]; }; /* Output (16 bytes) */ @@ -4487,7 +4723,7 @@ struct hwrm_fw_qstatus_input { #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_MGMT 0x1UL #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_NETCTRL 0x2UL #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_ROCE 0x3UL - #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_RSVD 0x4UL + #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_HOST 0x4UL u8 unused_0[7]; }; @@ -4572,6 +4808,16 @@ struct hwrm_fw_set_structured_data_output { u8 valid; }; +/* Command specific Error Codes (8 bytes) */ +struct hwrm_fw_set_structured_data_cmd_err { + u8 code; + #define FW_SET_STRUCTURED_DATA_CMD_ERR_CODE_UNKNOWN 0x0UL + #define FW_SET_STRUCTURED_DATA_CMD_ERR_CODE_BAD_HDR_CNT 0x1UL + #define FW_SET_STRUCTURED_DATA_CMD_ERR_CODE_BAD_FMT 0x2UL + #define FW_SET_STRUCTURED_DATA_CMD_ERR_CODE_BAD_ID 0x3UL + u8 unused_0[7]; +}; + /* hwrm_fw_get_structured_data */ /* Input (32 bytes) */ struct hwrm_fw_get_structured_data_input { @@ -4611,6 +4857,14 @@ struct hwrm_fw_get_structured_data_output { u8 valid; }; +/* Command specific Error Codes (8 bytes) */ +struct hwrm_fw_get_structured_data_cmd_err { + u8 code; + #define FW_GET_STRUCTURED_DATA_CMD_ERR_CODE_UNKNOWN 0x0UL + #define FW_GET_STRUCTURED_DATA_CMD_ERR_CODE_BAD_ID 0x3UL + u8 unused_0[7]; +}; + /* hwrm_exec_fwd_resp */ /* Input (128 bytes) */ struct hwrm_exec_fwd_resp_input { @@ -5411,7 +5665,7 @@ struct cmd_nums { #define HWRM_PORT_LPBK_CLR_STATS (0x26UL) #define HWRM_PORT_PHY_QCFG (0x27UL) #define HWRM_PORT_MAC_QCFG (0x28UL) - #define RESERVED7 (0x29UL) + #define HWRM_PORT_MAC_PTP_QCFG (0x29UL) #define HWRM_PORT_PHY_QCAPS (0x2aUL) #define HWRM_PORT_PHY_I2C_WRITE (0x2bUL) #define HWRM_PORT_PHY_I2C_READ (0x2cUL) @@ -5421,14 +5675,17 @@ struct cmd_nums { #define HWRM_QUEUE_QPORTCFG (0x30UL) #define HWRM_QUEUE_QCFG (0x31UL) #define HWRM_QUEUE_CFG (0x32UL) - #define RESERVED2 (0x33UL) - #define RESERVED3 (0x34UL) + #define HWRM_FUNC_VLAN_CFG (0x33UL) + #define HWRM_FUNC_VLAN_QCFG (0x34UL) #define HWRM_QUEUE_PFCENABLE_QCFG (0x35UL) #define HWRM_QUEUE_PFCENABLE_CFG (0x36UL) #define HWRM_QUEUE_PRI2COS_QCFG (0x37UL) #define HWRM_QUEUE_PRI2COS_CFG (0x38UL) #define HWRM_QUEUE_COS2BW_QCFG (0x39UL) #define HWRM_QUEUE_COS2BW_CFG (0x3aUL) + #define HWRM_QUEUE_DSCP_QCAPS (0x3bUL) + #define HWRM_QUEUE_DSCP2PRI_QCFG (0x3cUL) + #define HWRM_QUEUE_DSCP2PRI_CFG (0x3dUL) #define HWRM_VNIC_ALLOC (0x40UL) #define HWRM_VNIC_FREE (0x41UL) #define HWRM_VNIC_CFG (0x42UL) @@ -5455,7 +5712,7 @@ struct cmd_nums { #define HWRM_CFA_L2_FILTER_FREE (0x91UL) #define HWRM_CFA_L2_FILTER_CFG (0x92UL) #define HWRM_CFA_L2_SET_RX_MASK (0x93UL) - #define RESERVED4 (0x94UL) + #define HWRM_CFA_VLAN_ANTISPOOF_CFG (0x94UL) #define HWRM_CFA_TUNNEL_FILTER_ALLOC (0x95UL) #define HWRM_CFA_TUNNEL_FILTER_FREE (0x96UL) #define HWRM_CFA_ENCAP_RECORD_ALLOC (0x97UL) @@ -5494,6 +5751,8 @@ struct cmd_nums { #define HWRM_CFA_METER_PROFILE_CFG (0xf7UL) #define HWRM_CFA_METER_INSTANCE_ALLOC (0xf8UL) #define HWRM_CFA_METER_INSTANCE_FREE (0xf9UL) + #define HWRM_CFA_VFR_ALLOC (0xfdUL) + #define HWRM_CFA_VFR_FREE (0xfeUL) #define HWRM_CFA_VF_PAIR_ALLOC (0x100UL) #define HWRM_CFA_VF_PAIR_FREE (0x101UL) #define HWRM_CFA_VF_PAIR_INFO (0x102UL) @@ -5502,6 +5761,9 @@ struct cmd_nums { #define HWRM_CFA_FLOW_FLUSH (0x105UL) #define HWRM_CFA_FLOW_STATS (0x106UL) #define HWRM_CFA_FLOW_INFO (0x107UL) + #define HWRM_CFA_DECAP_FILTER_ALLOC (0x108UL) + #define HWRM_CFA_DECAP_FILTER_FREE (0x109UL) + #define HWRM_CFA_VLAN_ANTISPOOF_QCFG (0x10aUL) #define HWRM_SELFTEST_QLIST (0x200UL) #define HWRM_SELFTEST_EXEC (0x201UL) #define HWRM_SELFTEST_IRQ (0x202UL) @@ -5510,6 +5772,8 @@ struct cmd_nums { #define HWRM_DBG_WRITE_DIRECT (0xff12UL) #define HWRM_DBG_WRITE_INDIRECT (0xff13UL) #define HWRM_DBG_DUMP (0xff14UL) + #define HWRM_DBG_ERASE_NVM (0xff15UL) + #define HWRM_DBG_CFG (0xff16UL) #define HWRM_NVM_FACTORY_DEFAULTS (0xffeeUL) #define HWRM_NVM_VALIDATE_OPTION (0xffefUL) #define HWRM_NVM_FLUSH (0xfff0UL) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index b8e7248294d9..fde7256cad1b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -794,8 +794,10 @@ static int bnxt_vf_set_link(struct bnxt *bp, struct bnxt_vf_info *vf) PORT_PHY_QCFG_RESP_LINK_LINK; phy_qcfg_resp.link_speed = cpu_to_le16( PORT_PHY_QCFG_RESP_LINK_SPEED_10GB); - phy_qcfg_resp.duplex = - PORT_PHY_QCFG_RESP_DUPLEX_FULL; + phy_qcfg_resp.duplex_cfg = + PORT_PHY_QCFG_RESP_DUPLEX_CFG_FULL; + phy_qcfg_resp.duplex_state = + PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL; phy_qcfg_resp.pause = (PORT_PHY_QCFG_RESP_PAUSE_TX | PORT_PHY_QCFG_RESP_PAUSE_RX); @@ -804,7 +806,8 @@ static int bnxt_vf_set_link(struct bnxt *bp, struct bnxt_vf_info *vf) /* force link down */ phy_qcfg_resp.link = PORT_PHY_QCFG_RESP_LINK_NO_LINK; phy_qcfg_resp.link_speed = 0; - phy_qcfg_resp.duplex = PORT_PHY_QCFG_RESP_DUPLEX_HALF; + phy_qcfg_resp.duplex_state = + PORT_PHY_QCFG_RESP_DUPLEX_STATE_HALF; phy_qcfg_resp.pause = 0; } rc = bnxt_hwrm_fwd_resp(bp, vf, &phy_qcfg_resp, -- cgit v1.2.3 From 32e8239c9138a050bc1feeea7cf41f27d79e6664 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 24 Jul 2017 12:34:21 -0400 Subject: bnxt_en: Retrieve the hardware bridge mode from the firmware. Retrieve and store the hardware bridge mode, so that we can implement ndo_bridge_{get|set)link methods in the next patch. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 ++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 9 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 2103f1412359..ec8a1958ba12 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -4610,6 +4611,13 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) bp->port_partition_type = resp->port_partition_type; break; } + if (bp->hwrm_spec_code < 0x10707 || + resp->evb_mode == FUNC_QCFG_RESP_EVB_MODE_VEB) + bp->br_mode = BRIDGE_MODE_VEB; + else if (resp->evb_mode == FUNC_QCFG_RESP_EVB_MODE_VEPA) + bp->br_mode = BRIDGE_MODE_VEPA; + else + bp->br_mode = BRIDGE_MODE_UNDEF; func_qcfg_exit: mutex_unlock(&bp->hwrm_cmd_lock); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 505691a33a7c..15d5a10122ed 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1164,6 +1164,7 @@ struct bnxt { u8 nge_port_cnt; __le16 nge_fw_dst_port_id; u8 port_partition_type; + u16 br_mode; u16 rx_coal_ticks; u16 rx_coal_ticks_irq; -- cgit v1.2.3 From 39d8ba2e71fbdde686d7e31ad141a01994dc0793 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 24 Jul 2017 12:34:22 -0400 Subject: bnxt_en: Implement ndo_bridge_{get|set}link methods. To allow users to set the hardware bridging mode to VEB or VEPA. Only single function PF can change the bridging mode. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index ec8a1958ba12..4acaeaf87f18 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4919,6 +4919,26 @@ static void bnxt_hwrm_resource_free(struct bnxt *bp, bool close_path, } } +static int bnxt_hwrm_set_br_mode(struct bnxt *bp, u16 br_mode) +{ + struct hwrm_func_cfg_input req = {0}; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1); + req.fid = cpu_to_le16(0xffff); + req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_EVB_MODE); + if (br_mode == BRIDGE_MODE_VEB) + req.evb_mode = FUNC_CFG_REQ_EVB_MODE_VEB; + else if (br_mode == BRIDGE_MODE_VEPA) + req.evb_mode = FUNC_CFG_REQ_EVB_MODE_VEPA; + else + return -EINVAL; + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + rc = -EIO; + return rc; +} + static int bnxt_setup_vnic(struct bnxt *bp, u16 vnic_id) { struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id]; @@ -7432,6 +7452,51 @@ static void bnxt_udp_tunnel_del(struct net_device *dev, schedule_work(&bp->sp_task); } +static int bnxt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev, u32 filter_mask, + int nlflags) +{ + struct bnxt *bp = netdev_priv(dev); + + return ndo_dflt_bridge_getlink(skb, pid, seq, dev, bp->br_mode, 0, 0, + nlflags, filter_mask, NULL); +} + +static int bnxt_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, + u16 flags) +{ + struct bnxt *bp = netdev_priv(dev); + struct nlattr *attr, *br_spec; + int rem, rc = 0; + + if (bp->hwrm_spec_code < 0x10708 || !BNXT_SINGLE_PF(bp)) + return -EOPNOTSUPP; + + br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); + if (!br_spec) + return -EINVAL; + + nla_for_each_nested(attr, br_spec, rem) { + u16 mode; + + if (nla_type(attr) != IFLA_BRIDGE_MODE) + continue; + + if (nla_len(attr) < sizeof(mode)) + return -EINVAL; + + mode = nla_get_u16(attr); + if (mode == bp->br_mode) + break; + + rc = bnxt_hwrm_set_br_mode(bp, mode); + if (!rc) + bp->br_mode = mode; + break; + } + return rc; +} + static const struct net_device_ops bnxt_netdev_ops = { .ndo_open = bnxt_open, .ndo_start_xmit = bnxt_start_xmit, @@ -7463,6 +7528,8 @@ static const struct net_device_ops bnxt_netdev_ops = { .ndo_udp_tunnel_add = bnxt_udp_tunnel_add, .ndo_udp_tunnel_del = bnxt_udp_tunnel_del, .ndo_xdp = bnxt_xdp, + .ndo_bridge_getlink = bnxt_bridge_getlink, + .ndo_bridge_setlink = bnxt_bridge_setlink, }; static void bnxt_remove_one(struct pci_dev *pdev) -- cgit v1.2.3 From 5c8227d0d3b1eb1ad8f98d0b6dc619d70f2cfa04 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 24 Jul 2017 12:34:23 -0400 Subject: bnxt_en: Add bnxt_get_num_stats() to centrally get the number of ethtool stats. Instead of duplicating the logic multiple times. Also, it is unnecessary to zero the buffer in .get_ethtool_stats() because it is already zeroed by the caller. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 25 ++++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index be6acadcb202..4661b1185790 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -198,19 +198,23 @@ static const struct { #define BNXT_NUM_PORT_STATS ARRAY_SIZE(bnxt_port_stats_arr) +static int bnxt_get_num_stats(struct bnxt *bp) +{ + int num_stats = BNXT_NUM_STATS * bp->cp_nr_rings; + + if (bp->flags & BNXT_FLAG_PORT_STATS) + num_stats += BNXT_NUM_PORT_STATS; + + return num_stats; +} + static int bnxt_get_sset_count(struct net_device *dev, int sset) { struct bnxt *bp = netdev_priv(dev); switch (sset) { - case ETH_SS_STATS: { - int num_stats = BNXT_NUM_STATS * bp->cp_nr_rings; - - if (bp->flags & BNXT_FLAG_PORT_STATS) - num_stats += BNXT_NUM_PORT_STATS; - - return num_stats; - } + case ETH_SS_STATS: + return bnxt_get_num_stats(bp); case ETH_SS_TEST: if (!bp->num_tests) return -EOPNOTSUPP; @@ -225,11 +229,8 @@ static void bnxt_get_ethtool_stats(struct net_device *dev, { u32 i, j = 0; struct bnxt *bp = netdev_priv(dev); - u32 buf_size = sizeof(struct ctx_hw_stats) * bp->cp_nr_rings; u32 stat_fields = sizeof(struct ctx_hw_stats) / 8; - memset(buf, 0, buf_size); - if (!bp->bnapi) return; @@ -835,7 +836,7 @@ static void bnxt_get_drvinfo(struct net_device *dev, strlcpy(info->fw_version, bp->fw_ver_str, sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); - info->n_stats = BNXT_NUM_STATS * bp->cp_nr_rings; + info->n_stats = bnxt_get_num_stats(bp); info->testinfo_len = bp->num_tests; /* TODO CHIMP_FW: eeprom dump details */ info->eedump_len = 0; -- cgit v1.2.3 From adcc331e42e639ea44ac3c746db6c7207c3f69c0 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 24 Jul 2017 12:34:24 -0400 Subject: bnxt_en: Allow the user to set ethtool stats-block-usecs to 0. For debugging purpose, it is sometimes useful to disable periodic port statistics updates, so that the firmware logs will not be filled with statistics update messages. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 3 ++- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4acaeaf87f18..5df967037d10 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6843,7 +6843,8 @@ static void bnxt_timer(unsigned long data) if (atomic_read(&bp->intr_sem) != 0) goto bnxt_restart_timer; - if (bp->link_info.link_up && (bp->flags & BNXT_FLAG_PORT_STATS)) { + if (bp->link_info.link_up && (bp->flags & BNXT_FLAG_PORT_STATS) && + bp->stats_coal_ticks) { set_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event); schedule_work(&bp->sp_task); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 4661b1185790..140e76904af9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -86,9 +86,11 @@ static int bnxt_set_coalesce(struct net_device *dev, if (bp->stats_coal_ticks != coal->stats_block_coalesce_usecs) { u32 stats_ticks = coal->stats_block_coalesce_usecs; - stats_ticks = clamp_t(u32, stats_ticks, - BNXT_MIN_STATS_COAL_TICKS, - BNXT_MAX_STATS_COAL_TICKS); + /* Allow 0, which means disable. */ + if (stats_ticks) + stats_ticks = clamp_t(u32, stats_ticks, + BNXT_MIN_STATS_COAL_TICKS, + BNXT_MAX_STATS_COAL_TICKS); stats_ticks = rounddown(stats_ticks, BNXT_MIN_STATS_COAL_TICKS); bp->stats_coal_ticks = stats_ticks; update_stats = true; -- cgit v1.2.3 From 9315edca9b1d0daf41f81e1f5d4fb995d3cbc634 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 24 Jul 2017 12:34:25 -0400 Subject: bnxt_en: Report firmware DCBX agent. Report DCB_CAP_DCBX_LLD_MANAGED only if the firmware DCBX agent is enabled and running for PF or VF. Otherwise, if both LLDP and DCBX agents are disabled in firmware, we report DCB_CAP_DCBX_LLD_HOST and allow host IEEE DCB settings. This patch refines the current logic in the driver. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 19 ++++++++++--------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c | 11 ++++++++--- 3 files changed, 19 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 5df967037d10..95fea2622d64 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4578,6 +4578,7 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) { struct hwrm_func_qcfg_input req = {0}; struct hwrm_func_qcfg_output *resp = bp->hwrm_cmd_resp_addr; + u16 flags; int rc; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_QCFG, -1, -1); @@ -4594,15 +4595,15 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) vf->vlan = le16_to_cpu(resp->vlan) & VLAN_VID_MASK; } #endif - if (BNXT_PF(bp)) { - u16 flags = le16_to_cpu(resp->flags); - - if (flags & (FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED | - FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED)) - bp->flags |= BNXT_FLAG_FW_LLDP_AGENT; - if (flags & FUNC_QCFG_RESP_FLAGS_MULTI_HOST) - bp->flags |= BNXT_FLAG_MULTI_HOST; - } + flags = le16_to_cpu(resp->flags); + if (flags & (FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED | + FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED)) { + bp->flags |= BNXT_FLAG_FW_LLDP_AGENT; + if (flags & FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED) + bp->flags |= BNXT_FLAG_FW_DCBX_AGENT; + } + if (BNXT_PF(bp) && (flags & FUNC_QCFG_RESP_FLAGS_MULTI_HOST)) + bp->flags |= BNXT_FLAG_MULTI_HOST; switch (resp->port_partition_type) { case FUNC_QCFG_RESP_PORT_PARTITION_TYPE_NPAR1_0: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 15d5a10122ed..6b781be66722 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1027,6 +1027,7 @@ struct bnxt { #define BNXT_FLAG_MULTI_HOST 0x100000 #define BNXT_FLAG_SHORT_CMD 0x200000 #define BNXT_FLAG_DOUBLE_DB 0x400000 + #define BNXT_FLAG_FW_DCBX_AGENT 0x800000 #define BNXT_FLAG_CHIP_NITRO_A0 0x1000000 #define BNXT_FLAG_ALL_CONFIG_FEATS (BNXT_FLAG_TPA | \ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c index 5c6dd0ce209f..c0145898f64c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c @@ -549,13 +549,18 @@ static u8 bnxt_dcbnl_setdcbx(struct net_device *dev, u8 mode) { struct bnxt *bp = netdev_priv(dev); - /* only support IEEE */ - if ((mode & DCB_CAP_DCBX_VER_CEE) || !(mode & DCB_CAP_DCBX_VER_IEEE)) + /* All firmware DCBX settings are set in NVRAM */ + if (bp->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) return 1; if (mode & DCB_CAP_DCBX_HOST) { if (BNXT_VF(bp) || (bp->flags & BNXT_FLAG_FW_LLDP_AGENT)) return 1; + + /* only support IEEE */ + if ((mode & DCB_CAP_DCBX_VER_CEE) || + !(mode & DCB_CAP_DCBX_VER_IEEE)) + return 1; } if (mode == bp->dcbx_cap) @@ -584,7 +589,7 @@ void bnxt_dcb_init(struct bnxt *bp) bp->dcbx_cap = DCB_CAP_DCBX_VER_IEEE; if (BNXT_PF(bp) && !(bp->flags & BNXT_FLAG_FW_LLDP_AGENT)) bp->dcbx_cap |= DCB_CAP_DCBX_HOST; - else + else if (bp->flags & BNXT_FLAG_FW_DCBX_AGENT) bp->dcbx_cap |= DCB_CAP_DCBX_LLD_MANAGED; bp->dev->dcbnl_ops = &dcbnl_ops; } -- cgit v1.2.3 From 70098a47bbf131b65c64ca935c2480e64c9c7c51 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 24 Jul 2017 12:34:26 -0400 Subject: bnxt_en: Set ETS min_bw parameter for older firmware. In addition to the ETS weight, older firmware also requires the min_bw parameter to be set for it to work properly. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c | 6 ++++++ drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h | 1 + 2 files changed, 7 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c index c0145898f64c..aa1f3a2c7a78 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c @@ -93,6 +93,12 @@ static int bnxt_hwrm_queue_cos2bw_cfg(struct bnxt *bp, struct ieee_ets *ets, cos2bw.tsa = QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_ETS; cos2bw.bw_weight = ets->tc_tx_bw[i]; + /* older firmware requires min_bw to be set to the + * same weight value in percent. + */ + cos2bw.min_bw = + cpu_to_le32((ets->tc_tx_bw[i] * 100) | + BW_VALUE_UNIT_PERCENT1_100); } memcpy(data, &cos2bw.queue_id, sizeof(cos2bw) - 4); if (i == 0) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h index ecd0a5e46a49..d2e0af960bf5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h @@ -26,6 +26,7 @@ struct bnxt_cos2bw_cfg { u8 queue_id; __le32 min_bw; __le32 max_bw; +#define BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) u8 tsa; u8 pri_lvl; u8 bw_weight; -- cgit v1.2.3 From 4ab0c6a8ffd7d25475dd9eb06614eec1ae53a443 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Mon, 24 Jul 2017 12:34:27 -0400 Subject: bnxt_en: add support to enable VF-representors This patch is a part of a patch-set that introduces support for VF-reps in the bnxt_en driver. The driver registers eswitch mode get/set methods with the devlink interface that allow a user to enable SRIOV switchdev mode. When enabled, the driver registers a VF-rep netdev object for each VF with the stack. This can essentially bring the VFs unders the management perview of the hypervisor and applications such as OVS. The next patch in the series, adds the RX/TX routines and a slim netdev implementation for the VF-reps. Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/Kconfig | 1 + drivers/net/ethernet/broadcom/bnxt/Makefile | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 9 +- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 32 ++++ drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 6 + drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 244 ++++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h | 38 ++++ 7 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 96413808c726..285f8bc25682 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -193,6 +193,7 @@ config SYSTEMPORT config BNXT tristate "Broadcom NetXtreme-C/E support" depends on PCI + depends on MAY_USE_DEVLINK select FW_LOADER select LIBCRC32C ---help--- diff --git a/drivers/net/ethernet/broadcom/bnxt/Makefile b/drivers/net/ethernet/broadcom/bnxt/Makefile index a7ca45b251cb..d141a22ac50b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/Makefile +++ b/drivers/net/ethernet/broadcom/bnxt/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_BNXT) += bnxt_en.o -bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o +bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o bnxt_vfr.o diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 95fea2622d64..ebdeeb4a5756 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -57,6 +57,7 @@ #include "bnxt_ethtool.h" #include "bnxt_dcb.h" #include "bnxt_xdp.h" +#include "bnxt_vfr.h" #define BNXT_TX_TIMEOUT (5 * HZ) @@ -7539,8 +7540,10 @@ static void bnxt_remove_one(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct bnxt *bp = netdev_priv(dev); - if (BNXT_PF(bp)) + if (BNXT_PF(bp)) { bnxt_sriov_disable(bp); + bnxt_dl_unregister(bp); + } pci_disable_pcie_error_reporting(pdev); unregister_netdev(dev); @@ -7843,6 +7846,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #ifdef CONFIG_BNXT_SRIOV init_waitqueue_head(&bp->sriov_cfg_wait); + mutex_init(&bp->sriov_lock); #endif bp->gro_func = bnxt_gro_func_5730x; if (BNXT_CHIP_P4_PLUS(bp)) @@ -7934,6 +7938,9 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err_clr_int; + if (BNXT_PF(bp)) + bnxt_dl_register(bp); + netdev_info(dev, "%s found at mem %lx, node addr %pM\n", board_info[ent->driver_data].name, (long)pci_resource_start(pdev, 0), dev->dev_addr); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 6b781be66722..a7d5f42fb6a3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -19,6 +19,7 @@ #define DRV_VER_UPD 0 #include +#include struct tx_bd { __le32 tx_bd_len_flags_type; @@ -618,6 +619,8 @@ struct bnxt_tpa_info { #define BNXT_TPA_OUTER_L3_OFF(hdr_info) \ ((hdr_info) & 0x1ff) + + u16 cfa_code; /* cfa_code in TPA start compl */ }; struct bnxt_rx_ring_info { @@ -928,6 +931,23 @@ struct bnxt_test_info { #define BNXT_CAG_REG_LEGACY_INT_STATUS 0x4014 #define BNXT_CAG_REG_BASE 0x300000 +struct bnxt_vf_rep_stats { + u64 packets; + u64 bytes; + u64 dropped; +}; + +struct bnxt_vf_rep { + struct bnxt *bp; + struct net_device *dev; + u16 vf_idx; + u16 tx_cfa_action; + u16 rx_cfa_code; + + struct bnxt_vf_rep_stats rx_stats; + struct bnxt_vf_rep_stats tx_stats; +}; + struct bnxt { void __iomem *bar0; void __iomem *bar1; @@ -1208,6 +1228,12 @@ struct bnxt { wait_queue_head_t sriov_cfg_wait; bool sriov_cfg; #define BNXT_SRIOV_CFG_WAIT_TMO msecs_to_jiffies(10000) + + /* lock to protect VF-rep creation/cleanup via + * multiple paths such as ->sriov_configure() and + * devlink ->eswitch_mode_set() + */ + struct mutex sriov_lock; #endif #define BNXT_NTP_FLTR_MAX_FLTR 4096 @@ -1234,6 +1260,12 @@ struct bnxt { struct bnxt_led_info leds[BNXT_MAX_LED]; struct bpf_prog *xdp_prog; + + /* devlink interface and vf-rep structs */ + struct devlink *dl; + enum devlink_eswitch_mode eswitch_mode; + struct bnxt_vf_rep **vf_reps; /* array of vf-rep ptrs */ + u16 *cfa_code_map; /* cfa_code -> vf_idx map */ }; #define BNXT_RX_STATS_OFFSET(counter) \ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index fde7256cad1b..d37925a8a65b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -18,6 +18,7 @@ #include "bnxt.h" #include "bnxt_ulp.h" #include "bnxt_sriov.h" +#include "bnxt_vfr.h" #include "bnxt_ethtool.h" #ifdef CONFIG_BNXT_SRIOV @@ -587,6 +588,10 @@ void bnxt_sriov_disable(struct bnxt *bp) if (!num_vfs) return; + /* synchronize VF and VF-rep create and destroy */ + mutex_lock(&bp->sriov_lock); + bnxt_vf_reps_destroy(bp); + if (pci_vfs_assigned(bp->pdev)) { bnxt_hwrm_fwd_async_event_cmpl( bp, NULL, ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD); @@ -597,6 +602,7 @@ void bnxt_sriov_disable(struct bnxt *bp) /* Free the HW resources reserved for various VF's */ bnxt_hwrm_func_vf_resource_free(bp, num_vfs); } + mutex_unlock(&bp->sriov_lock); bnxt_free_vf_resources(bp); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c new file mode 100644 index 000000000000..eab358c2ac97 --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -0,0 +1,244 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2016-2017 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include "bnxt_hsi.h" +#include "bnxt.h" +#include "bnxt_vfr.h" + +#define CFA_HANDLE_INVALID 0xffff + +static void __bnxt_vf_reps_destroy(struct bnxt *bp) +{ + u16 num_vfs = pci_num_vf(bp->pdev); + struct bnxt_vf_rep *vf_rep; + int i; + + for (i = 0; i < num_vfs; i++) { + vf_rep = bp->vf_reps[i]; + if (vf_rep) { + if (vf_rep->dev) { + /* if register_netdev failed, then netdev_ops + * would have been set to NULL + */ + if (vf_rep->dev->netdev_ops) + unregister_netdev(vf_rep->dev); + free_netdev(vf_rep->dev); + } + } + } + + kfree(bp->vf_reps); + bp->vf_reps = NULL; +} + +void bnxt_vf_reps_destroy(struct bnxt *bp) +{ + bool closed = false; + + if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) + return; + + if (!bp->vf_reps) + return; + + /* Ensure that parent PF's and VF-reps' RX/TX has been quiesced + * before proceeding with VF-rep cleanup. + */ + rtnl_lock(); + if (netif_running(bp->dev)) { + bnxt_close_nic(bp, false, false); + closed = true; + } + bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; + + if (closed) + bnxt_open_nic(bp, false, false); + rtnl_unlock(); + + /* Need to call vf_reps_destroy() outside of rntl_lock + * as unregister_netdev takes rtnl_lock + */ + __bnxt_vf_reps_destroy(bp); +} + +/* Use the OUI of the PF's perm addr and report the same mac addr + * for the same VF-rep each time + */ +static void bnxt_vf_rep_eth_addr_gen(u8 *src_mac, u16 vf_idx, u8 *mac) +{ + u32 addr; + + ether_addr_copy(mac, src_mac); + + addr = jhash(src_mac, ETH_ALEN, 0) + vf_idx; + mac[3] = (u8)(addr & 0xFF); + mac[4] = (u8)((addr >> 8) & 0xFF); + mac[5] = (u8)((addr >> 16) & 0xFF); +} + +static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, + struct net_device *dev) +{ + struct net_device *pf_dev = bp->dev; + + /* Just inherit all the featues of the parent PF as the VF-R + * uses the RX/TX rings of the parent PF + */ + dev->hw_features = pf_dev->hw_features; + dev->gso_partial_features = pf_dev->gso_partial_features; + dev->vlan_features = pf_dev->vlan_features; + dev->hw_enc_features = pf_dev->hw_enc_features; + dev->features |= pf_dev->features; + bnxt_vf_rep_eth_addr_gen(bp->pf.mac_addr, vf_rep->vf_idx, + dev->perm_addr); + ether_addr_copy(dev->dev_addr, dev->perm_addr); +} + +static int bnxt_vf_reps_create(struct bnxt *bp) +{ + u16 num_vfs = pci_num_vf(bp->pdev); + struct bnxt_vf_rep *vf_rep; + struct net_device *dev; + int rc, i; + + bp->vf_reps = kcalloc(num_vfs, sizeof(vf_rep), GFP_KERNEL); + if (!bp->vf_reps) + return -ENOMEM; + + for (i = 0; i < num_vfs; i++) { + dev = alloc_etherdev(sizeof(*vf_rep)); + if (!dev) { + rc = -ENOMEM; + goto err; + } + + vf_rep = netdev_priv(dev); + bp->vf_reps[i] = vf_rep; + vf_rep->dev = dev; + vf_rep->bp = bp; + vf_rep->vf_idx = i; + vf_rep->tx_cfa_action = CFA_HANDLE_INVALID; + + bnxt_vf_rep_netdev_init(bp, vf_rep, dev); + rc = register_netdev(dev); + if (rc) { + /* no need for unregister_netdev in cleanup */ + dev->netdev_ops = NULL; + goto err; + } + } + + bp->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; + return 0; + +err: + netdev_info(bp->dev, "%s error=%d", __func__, rc); + __bnxt_vf_reps_destroy(bp); + return rc; +} + +/* Devlink related routines */ +static int bnxt_dl_eswitch_mode_get(struct devlink *devlink, u16 *mode) +{ + struct bnxt *bp = bnxt_get_bp_from_dl(devlink); + + *mode = bp->eswitch_mode; + return 0; +} + +static int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode) +{ + struct bnxt *bp = bnxt_get_bp_from_dl(devlink); + int rc = 0; + + mutex_lock(&bp->sriov_lock); + if (bp->eswitch_mode == mode) { + netdev_info(bp->dev, "already in %s eswitch mode", + mode == DEVLINK_ESWITCH_MODE_LEGACY ? + "legacy" : "switchdev"); + rc = -EINVAL; + goto done; + } + + switch (mode) { + case DEVLINK_ESWITCH_MODE_LEGACY: + bnxt_vf_reps_destroy(bp); + break; + + case DEVLINK_ESWITCH_MODE_SWITCHDEV: + if (pci_num_vf(bp->pdev) == 0) { + netdev_info(bp->dev, + "Enable VFs before setting swtichdev mode"); + rc = -EPERM; + goto done; + } + rc = bnxt_vf_reps_create(bp); + break; + + default: + rc = -EINVAL; + goto done; + } +done: + mutex_unlock(&bp->sriov_lock); + return rc; +} + +static const struct devlink_ops bnxt_dl_ops = { + .eswitch_mode_set = bnxt_dl_eswitch_mode_set, + .eswitch_mode_get = bnxt_dl_eswitch_mode_get +}; + +int bnxt_dl_register(struct bnxt *bp) +{ + struct devlink *dl; + int rc; + + if (!pci_find_ext_capability(bp->pdev, PCI_EXT_CAP_ID_SRIOV)) + return 0; + + if (bp->hwrm_spec_code < 0x10800) { + netdev_warn(bp->dev, "Firmware does not support SR-IOV E-Switch SWITCHDEV mode.\n"); + return -ENOTSUPP; + } + + dl = devlink_alloc(&bnxt_dl_ops, sizeof(struct bnxt_dl)); + if (!dl) { + netdev_warn(bp->dev, "devlink_alloc failed"); + return -ENOMEM; + } + + bnxt_link_bp_to_dl(dl, bp); + bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; + rc = devlink_register(dl, &bp->pdev->dev); + if (rc) { + bnxt_link_bp_to_dl(dl, NULL); + devlink_free(dl); + netdev_warn(bp->dev, "devlink_register failed. rc=%d", rc); + return rc; + } + + return 0; +} + +void bnxt_dl_unregister(struct bnxt *bp) +{ + struct devlink *dl = bp->dl; + + if (!dl) + return; + + devlink_unregister(dl); + devlink_free(dl); +} diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h new file mode 100644 index 000000000000..310c9c567152 --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h @@ -0,0 +1,38 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2016-2017 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#ifndef BNXT_VFR_H +#define BNXT_VFR_H + +#define MAX_CFA_CODE 65536 + +/* Struct to hold housekeeping info needed by devlink interface */ +struct bnxt_dl { + struct bnxt *bp; /* back ptr to the controlling dev */ +}; + +static inline struct bnxt *bnxt_get_bp_from_dl(struct devlink *dl) +{ + return ((struct bnxt_dl *)devlink_priv(dl))->bp; +} + +static inline void bnxt_link_bp_to_dl(struct devlink *dl, struct bnxt *bp) +{ + struct bnxt_dl *bp_dl = devlink_priv(dl); + + bp_dl->bp = bp; + if (bp) + bp->dl = dl; +} + +int bnxt_dl_register(struct bnxt *bp); +void bnxt_dl_unregister(struct bnxt *bp); +void bnxt_vf_reps_destroy(struct bnxt *bp); + +#endif /* BNXT_VFR_H */ -- cgit v1.2.3 From ee5c7fb3404724b9e25fe24c81fbcda60f3f2659 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Mon, 24 Jul 2017 12:34:28 -0400 Subject: bnxt_en: add vf-rep RX/TX and netdev implementation This patch introduces the RX/TX and a simple netdev implementation for VF-reps. The VF-reps use the RX/TX rings of the PF. For each VF-rep the PF driver issues a VFR_ALLOC FW cmd that returns "cfa_code" and "cfa_action" values. The FW sets up the filter tables in such a way that VF traffic by default (in absence of other rules) gets punted to the parent PF. The cfa_code value in the RX-compl informs the driver of the source VF. For traffic being transmitted from the VF-rep, the TX BD is tagged with a cfa_action value that informs the HW to punt it to the corresponding VF. Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 68 ++++++-- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 10 ++ drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 218 +++++++++++++++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h | 4 + 4 files changed, 288 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index ebdeeb4a5756..f262fe6092d7 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -245,6 +245,16 @@ const u16 bnxt_lhint_arr[] = { TX_BD_FLAGS_LHINT_2048_AND_LARGER, }; +static u16 bnxt_xmit_get_cfa_action(struct sk_buff *skb) +{ + struct metadata_dst *md_dst = skb_metadata_dst(skb); + + if (!md_dst || md_dst->type != METADATA_HW_PORT_MUX) + return 0; + + return md_dst->u.port_info.port_id; +} + static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct bnxt *bp = netdev_priv(dev); @@ -289,7 +299,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_buf->nr_frags = last_frag; vlan_tag_flags = 0; - cfa_action = 0; + cfa_action = bnxt_xmit_get_cfa_action(skb); if (skb_vlan_tag_present(skb)) { vlan_tag_flags = TX_BD_CFA_META_KEY_VLAN | skb_vlan_tag_get(skb); @@ -324,7 +334,8 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_push1->tx_bd_hsize_lflags = 0; tx_push1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags); - tx_push1->tx_bd_cfa_action = cpu_to_le32(cfa_action); + tx_push1->tx_bd_cfa_action = + cpu_to_le32(cfa_action << TX_BD_CFA_ACTION_SHIFT); end = pdata + length; end = PTR_ALIGN(end, 8) - 1; @@ -429,7 +440,8 @@ normal_tx: txbd->tx_bd_len_flags_type = cpu_to_le32(flags); txbd1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags); - txbd1->tx_bd_cfa_action = cpu_to_le32(cfa_action); + txbd1->tx_bd_cfa_action = + cpu_to_le32(cfa_action << TX_BD_CFA_ACTION_SHIFT); for (i = 0; i < last_frag; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; @@ -1034,7 +1046,10 @@ static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, bnxt_sched_reset(bp, rxr); return; } - + /* Store cfa_code in tpa_info to use in tpa_end + * completion processing. + */ + tpa_info->cfa_code = TPA_START_CFA_CODE(tpa_start1); prod_rx_buf->data = tpa_info->data; prod_rx_buf->data_ptr = tpa_info->data_ptr; @@ -1269,6 +1284,17 @@ static inline struct sk_buff *bnxt_gro_skb(struct bnxt *bp, return skb; } +/* Given the cfa_code of a received packet determine which + * netdev (vf-rep or PF) the packet is destined to. + */ +static struct net_device *bnxt_get_pkt_dev(struct bnxt *bp, u16 cfa_code) +{ + struct net_device *dev = bnxt_get_vf_rep(bp, cfa_code); + + /* if vf-rep dev is NULL, the must belongs to the PF */ + return dev ? dev : bp->dev; +} + static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, @@ -1362,7 +1388,9 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp, return NULL; } } - skb->protocol = eth_type_trans(skb, bp->dev); + + skb->protocol = + eth_type_trans(skb, bnxt_get_pkt_dev(bp, tpa_info->cfa_code)); if (tpa_info->hash_type != PKT_HASH_TYPE_NONE) skb_set_hash(skb, tpa_info->rss_hash, tpa_info->hash_type); @@ -1389,6 +1417,18 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp, return skb; } +static void bnxt_deliver_skb(struct bnxt *bp, struct bnxt_napi *bnapi, + struct sk_buff *skb) +{ + if (skb->dev != bp->dev) { + /* this packet belongs to a vf-rep */ + bnxt_vf_rep_rx(bp, skb); + return; + } + skb_record_rx_queue(skb, bnapi->index); + napi_gro_receive(&bnapi->napi, skb); +} + /* returns the following: * 1 - 1 packet successfully received * 0 - successful TPA_START, packet not completed yet @@ -1405,7 +1445,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, struct rx_cmp *rxcmp; struct rx_cmp_ext *rxcmp1; u32 tmp_raw_cons = *raw_cons; - u16 cons, prod, cp_cons = RING_CMP(tmp_raw_cons); + u16 cfa_code, cons, prod, cp_cons = RING_CMP(tmp_raw_cons); struct bnxt_sw_rx_bd *rx_buf; unsigned int len; u8 *data_ptr, agg_bufs, cmp_type; @@ -1447,8 +1487,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, rc = -ENOMEM; if (likely(skb)) { - skb_record_rx_queue(skb, bnapi->index); - napi_gro_receive(&bnapi->napi, skb); + bnxt_deliver_skb(bp, bnapi, skb); rc = 1; } *event |= BNXT_RX_EVENT; @@ -1537,7 +1576,8 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, skb_set_hash(skb, le32_to_cpu(rxcmp->rx_cmp_rss_hash), type); } - skb->protocol = eth_type_trans(skb, dev); + cfa_code = RX_CMP_CFA_CODE(rxcmp1); + skb->protocol = eth_type_trans(skb, bnxt_get_pkt_dev(bp, cfa_code)); if ((rxcmp1->rx_cmp_flags2 & cpu_to_le32(RX_CMP_FLAGS2_META_FORMAT_VLAN)) && @@ -1562,8 +1602,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, } } - skb_record_rx_queue(skb, bnapi->index); - napi_gro_receive(&bnapi->napi, skb); + bnxt_deliver_skb(bp, bnapi, skb); rc = 1; next_rx: @@ -6246,6 +6285,9 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) /* Poll link status and check for SFP+ module status */ bnxt_get_port_module_status(bp); + /* VF-reps may need to be re-opened after the PF is re-opened */ + if (BNXT_PF(bp)) + bnxt_vf_reps_open(bp); return 0; open_err: @@ -6334,6 +6376,10 @@ int bnxt_close_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) if (rc) netdev_warn(bp->dev, "timeout waiting for SRIOV config operation to complete!\n"); } + + /* Close the VF-reps before closing PF */ + if (BNXT_PF(bp)) + bnxt_vf_reps_close(bp); #endif /* Change device state to avoid TX queue wake up's */ bnxt_tx_disable(bp); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index a7d5f42fb6a3..63756f0389d7 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -20,6 +20,7 @@ #include #include +#include struct tx_bd { __le32 tx_bd_len_flags_type; @@ -243,6 +244,10 @@ struct rx_cmp_ext { ((le32_to_cpu((rxcmp1)->rx_cmp_flags2) & \ RX_CMP_FLAGS2_T_L4_CS_CALC) >> 3) +#define RX_CMP_CFA_CODE(rxcmpl1) \ + ((le32_to_cpu((rxcmpl1)->rx_cmp_cfa_code_errors_v2) & \ + RX_CMPL_CFA_CODE_MASK) >> RX_CMPL_CFA_CODE_SFT) + struct rx_agg_cmp { __le32 rx_agg_cmp_len_flags_type; #define RX_AGG_CMP_TYPE (0x3f << 0) @@ -312,6 +317,10 @@ struct rx_tpa_start_cmp_ext { __le32 rx_tpa_start_cmp_hdr_info; }; +#define TPA_START_CFA_CODE(rx_tpa_start) \ + ((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_cfa_code_v2) & \ + RX_TPA_START_CMP_CFA_CODE) >> RX_TPA_START_CMPL_CFA_CODE_SHIFT) + struct rx_tpa_end_cmp { __le32 rx_tpa_end_cmp_len_flags_type; #define RX_TPA_END_CMP_TYPE (0x3f << 0) @@ -940,6 +949,7 @@ struct bnxt_vf_rep_stats { struct bnxt_vf_rep { struct bnxt *bp; struct net_device *dev; + struct metadata_dst *dst; u16 vf_idx; u16 tx_cfa_action; u16 rx_cfa_code; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index eab358c2ac97..60bdb181358e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -17,6 +17,178 @@ #include "bnxt_vfr.h" #define CFA_HANDLE_INVALID 0xffff +#define VF_IDX_INVALID 0xffff + +static int hwrm_cfa_vfr_alloc(struct bnxt *bp, u16 vf_idx, + u16 *tx_cfa_action, u16 *rx_cfa_code) +{ + struct hwrm_cfa_vfr_alloc_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_cfa_vfr_alloc_input req = { 0 }; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_VFR_ALLOC, -1, -1); + req.vf_id = cpu_to_le16(vf_idx); + sprintf(req.vfr_name, "vfr%d", vf_idx); + + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) { + *tx_cfa_action = le16_to_cpu(resp->tx_cfa_action); + *rx_cfa_code = le16_to_cpu(resp->rx_cfa_code); + netdev_dbg(bp->dev, "tx_cfa_action=0x%x, rx_cfa_code=0x%x", + *tx_cfa_action, *rx_cfa_code); + } else { + netdev_info(bp->dev, "%s error rc=%d", __func__, rc); + } + + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + +static int hwrm_cfa_vfr_free(struct bnxt *bp, u16 vf_idx) +{ + struct hwrm_cfa_vfr_free_input req = { 0 }; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_VFR_FREE, -1, -1); + sprintf(req.vfr_name, "vfr%d", vf_idx); + + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + netdev_info(bp->dev, "%s error rc=%d", __func__, rc); + return rc; +} + +static int bnxt_vf_rep_open(struct net_device *dev) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + struct bnxt *bp = vf_rep->bp; + + /* Enable link and TX only if the parent PF is open. */ + if (netif_running(bp->dev)) { + netif_carrier_on(dev); + netif_tx_start_all_queues(dev); + } + return 0; +} + +static int bnxt_vf_rep_close(struct net_device *dev) +{ + netif_carrier_off(dev); + netif_tx_disable(dev); + + return 0; +} + +static netdev_tx_t bnxt_vf_rep_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + int rc, len = skb->len; + + skb_dst_drop(skb); + dst_hold((struct dst_entry *)vf_rep->dst); + skb_dst_set(skb, (struct dst_entry *)vf_rep->dst); + skb->dev = vf_rep->dst->u.port_info.lower_dev; + + rc = dev_queue_xmit(skb); + if (!rc) { + vf_rep->tx_stats.packets++; + vf_rep->tx_stats.bytes += len; + } + return rc; +} + +static void +bnxt_vf_rep_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + + stats->rx_packets = vf_rep->rx_stats.packets; + stats->rx_bytes = vf_rep->rx_stats.bytes; + stats->tx_packets = vf_rep->tx_stats.packets; + stats->tx_bytes = vf_rep->tx_stats.bytes; +} + +struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code) +{ + u16 vf_idx; + + if (cfa_code && bp->cfa_code_map && BNXT_PF(bp)) { + vf_idx = bp->cfa_code_map[cfa_code]; + if (vf_idx != VF_IDX_INVALID) + return bp->vf_reps[vf_idx]->dev; + } + return NULL; +} + +void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(skb->dev); + struct bnxt_vf_rep_stats *rx_stats; + + rx_stats = &vf_rep->rx_stats; + vf_rep->rx_stats.bytes += skb->len; + vf_rep->rx_stats.packets++; + + netif_receive_skb(skb); +} + +static void bnxt_vf_rep_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); +} + +static const struct ethtool_ops bnxt_vf_rep_ethtool_ops = { + .get_drvinfo = bnxt_vf_rep_get_drvinfo +}; + +static const struct net_device_ops bnxt_vf_rep_netdev_ops = { + .ndo_open = bnxt_vf_rep_open, + .ndo_stop = bnxt_vf_rep_close, + .ndo_start_xmit = bnxt_vf_rep_xmit, + .ndo_get_stats64 = bnxt_vf_rep_get_stats64 +}; + +/* Called when the parent PF interface is closed: + * As the mode transition from SWITCHDEV to LEGACY + * happens under the rtnl_lock() this routine is safe + * under the rtnl_lock() + */ +void bnxt_vf_reps_close(struct bnxt *bp) +{ + struct bnxt_vf_rep *vf_rep; + u16 num_vfs, i; + + if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) + return; + + num_vfs = pci_num_vf(bp->pdev); + for (i = 0; i < num_vfs; i++) { + vf_rep = bp->vf_reps[i]; + if (netif_running(vf_rep->dev)) + bnxt_vf_rep_close(vf_rep->dev); + } +} + +/* Called when the parent PF interface is opened (re-opened): + * As the mode transition from SWITCHDEV to LEGACY + * happen under the rtnl_lock() this routine is safe + * under the rtnl_lock() + */ +void bnxt_vf_reps_open(struct bnxt *bp) +{ + int i; + + if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) + return; + + for (i = 0; i < pci_num_vf(bp->pdev); i++) + bnxt_vf_rep_open(bp->vf_reps[i]->dev); +} static void __bnxt_vf_reps_destroy(struct bnxt *bp) { @@ -27,6 +199,11 @@ static void __bnxt_vf_reps_destroy(struct bnxt *bp) for (i = 0; i < num_vfs; i++) { vf_rep = bp->vf_reps[i]; if (vf_rep) { + dst_release((struct dst_entry *)vf_rep->dst); + + if (vf_rep->tx_cfa_action != CFA_HANDLE_INVALID) + hwrm_cfa_vfr_free(bp, vf_rep->vf_idx); + if (vf_rep->dev) { /* if register_netdev failed, then netdev_ops * would have been set to NULL @@ -60,6 +237,9 @@ void bnxt_vf_reps_destroy(struct bnxt *bp) bnxt_close_nic(bp, false, false); closed = true; } + /* un-publish cfa_code_map so that RX path can't see it anymore */ + kfree(bp->cfa_code_map); + bp->cfa_code_map = NULL; bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; if (closed) @@ -92,6 +272,8 @@ static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, { struct net_device *pf_dev = bp->dev; + dev->netdev_ops = &bnxt_vf_rep_netdev_ops; + dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops; /* Just inherit all the featues of the parent PF as the VF-R * uses the RX/TX rings of the parent PF */ @@ -107,7 +289,7 @@ static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, static int bnxt_vf_reps_create(struct bnxt *bp) { - u16 num_vfs = pci_num_vf(bp->pdev); + u16 *cfa_code_map = NULL, num_vfs = pci_num_vf(bp->pdev); struct bnxt_vf_rep *vf_rep; struct net_device *dev; int rc, i; @@ -116,6 +298,16 @@ static int bnxt_vf_reps_create(struct bnxt *bp) if (!bp->vf_reps) return -ENOMEM; + /* storage for cfa_code to vf-idx mapping */ + cfa_code_map = kmalloc(sizeof(*bp->cfa_code_map) * MAX_CFA_CODE, + GFP_KERNEL); + if (!cfa_code_map) { + rc = -ENOMEM; + goto err; + } + for (i = 0; i < MAX_CFA_CODE; i++) + cfa_code_map[i] = VF_IDX_INVALID; + for (i = 0; i < num_vfs; i++) { dev = alloc_etherdev(sizeof(*vf_rep)); if (!dev) { @@ -130,6 +322,26 @@ static int bnxt_vf_reps_create(struct bnxt *bp) vf_rep->vf_idx = i; vf_rep->tx_cfa_action = CFA_HANDLE_INVALID; + /* get cfa handles from FW */ + rc = hwrm_cfa_vfr_alloc(bp, vf_rep->vf_idx, + &vf_rep->tx_cfa_action, + &vf_rep->rx_cfa_code); + if (rc) { + rc = -ENOLINK; + goto err; + } + cfa_code_map[vf_rep->rx_cfa_code] = vf_rep->vf_idx; + + vf_rep->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, + GFP_KERNEL); + if (!vf_rep->dst) { + rc = -ENOMEM; + goto err; + } + /* only cfa_action is needed to mux a packet while TXing */ + vf_rep->dst->u.port_info.port_id = vf_rep->tx_cfa_action; + vf_rep->dst->u.port_info.lower_dev = bp->dev; + bnxt_vf_rep_netdev_init(bp, vf_rep, dev); rc = register_netdev(dev); if (rc) { @@ -139,11 +351,15 @@ static int bnxt_vf_reps_create(struct bnxt *bp) } } + /* publish cfa_code_map only after all VF-reps have been initialized */ + bp->cfa_code_map = cfa_code_map; bp->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; + netif_keep_dst(bp->dev); return 0; err: netdev_info(bp->dev, "%s error=%d", __func__, rc); + kfree(cfa_code_map); __bnxt_vf_reps_destroy(bp); return rc; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h index 310c9c567152..c6cd55afbb89 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h @@ -34,5 +34,9 @@ static inline void bnxt_link_bp_to_dl(struct devlink *dl, struct bnxt *bp) int bnxt_dl_register(struct bnxt *bp); void bnxt_dl_unregister(struct bnxt *bp); void bnxt_vf_reps_destroy(struct bnxt *bp); +void bnxt_vf_reps_close(struct bnxt *bp); +void bnxt_vf_reps_open(struct bnxt *bp); +void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb); +struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code); #endif /* BNXT_VFR_H */ -- cgit v1.2.3 From c124a62ff2dde9eaa9e8083de8206a142535c04e Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Mon, 24 Jul 2017 12:34:29 -0400 Subject: bnxt_en: add support for port_attr_get and and get_phys_port_name This patch adds support for the switchdev_port_attr_get() and ndo_get_phys_port_name() methods for the PF and the VF-reps. Using this support a user application can deduce that the PF (when in the ESWITCH_SWDEV mode) and it's VF-reps form a switch. Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 57 +++++++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 + drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 31 ++++++++++++++- 3 files changed, 89 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index f262fe6092d7..82cbe1804821 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7546,6 +7546,61 @@ static int bnxt_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, return rc; } +static int bnxt_get_phys_port_name(struct net_device *dev, char *buf, + size_t len) +{ + struct bnxt *bp = netdev_priv(dev); + int rc; + + /* The PF and it's VF-reps only support the switchdev framework */ + if (!BNXT_PF(bp)) + return -EOPNOTSUPP; + + /* The switch-id that the pf belongs to is exported by + * the switchdev ndo. This name is just to distinguish from the + * vf-rep ports. + */ + rc = snprintf(buf, len, "pf%d", bp->pf.port_id); + + if (rc >= len) + return -EOPNOTSUPP; + return 0; +} + +int bnxt_port_attr_get(struct bnxt *bp, struct switchdev_attr *attr) +{ + if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) + return -EOPNOTSUPP; + + /* The PF and it's VF-reps only support the switchdev framework */ + if (!BNXT_PF(bp)) + return -EOPNOTSUPP; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: + /* In SRIOV each PF-pool (PF + child VFs) serves as a + * switching domain, the PF's perm mac-addr can be used + * as the unique parent-id + */ + attr->u.ppid.id_len = ETH_ALEN; + ether_addr_copy(attr->u.ppid.id, bp->pf.mac_addr); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int bnxt_swdev_port_attr_get(struct net_device *dev, + struct switchdev_attr *attr) +{ + return bnxt_port_attr_get(netdev_priv(dev), attr); +} + +static const struct switchdev_ops bnxt_switchdev_ops = { + .switchdev_port_attr_get = bnxt_swdev_port_attr_get +}; + static const struct net_device_ops bnxt_netdev_ops = { .ndo_open = bnxt_open, .ndo_start_xmit = bnxt_start_xmit, @@ -7579,6 +7634,7 @@ static const struct net_device_ops bnxt_netdev_ops = { .ndo_xdp = bnxt_xdp, .ndo_bridge_getlink = bnxt_bridge_getlink, .ndo_bridge_setlink = bnxt_bridge_setlink, + .ndo_get_phys_port_name = bnxt_get_phys_port_name }; static void bnxt_remove_one(struct pci_dev *pdev) @@ -7838,6 +7894,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->netdev_ops = &bnxt_netdev_ops; dev->watchdog_timeo = BNXT_TX_TIMEOUT; dev->ethtool_ops = &bnxt_ethtool_ops; + dev->switchdev_ops = &bnxt_switchdev_ops; pci_set_drvdata(pdev, dev); rc = bnxt_alloc_hwrm_resources(bp); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 63756f0389d7..2d84d5719b70 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -21,6 +21,7 @@ #include #include #include +#include struct tx_bd { __le32 tx_bd_len_flags_type; @@ -1350,4 +1351,5 @@ int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, int bnxt_setup_mq_tc(struct net_device *dev, u8 tc); int bnxt_get_max_rings(struct bnxt *, int *, int *, bool); void bnxt_restore_pf_fw_resources(struct bnxt *bp); +int bnxt_port_attr_get(struct bnxt *bp, struct switchdev_attr *attr); #endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index 60bdb181358e..83478e912ee5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -135,6 +135,18 @@ void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb) netif_receive_skb(skb); } +static int bnxt_vf_rep_get_phys_port_name(struct net_device *dev, char *buf, + size_t len) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + int rc; + + rc = snprintf(buf, len, "vfr%d", vf_rep->vf_idx); + if (rc >= len) + return -EOPNOTSUPP; + return 0; +} + static void bnxt_vf_rep_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { @@ -142,6 +154,21 @@ static void bnxt_vf_rep_get_drvinfo(struct net_device *dev, strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } +static int bnxt_vf_rep_port_attr_get(struct net_device *dev, + struct switchdev_attr *attr) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + + /* as only PORT_PARENT_ID is supported currently use common code + * between PF and VF-rep for now. + */ + return bnxt_port_attr_get(vf_rep->bp, attr); +} + +static const struct switchdev_ops bnxt_vf_rep_switchdev_ops = { + .switchdev_port_attr_get = bnxt_vf_rep_port_attr_get +}; + static const struct ethtool_ops bnxt_vf_rep_ethtool_ops = { .get_drvinfo = bnxt_vf_rep_get_drvinfo }; @@ -150,7 +177,8 @@ static const struct net_device_ops bnxt_vf_rep_netdev_ops = { .ndo_open = bnxt_vf_rep_open, .ndo_stop = bnxt_vf_rep_close, .ndo_start_xmit = bnxt_vf_rep_xmit, - .ndo_get_stats64 = bnxt_vf_rep_get_stats64 + .ndo_get_stats64 = bnxt_vf_rep_get_stats64, + .ndo_get_phys_port_name = bnxt_vf_rep_get_phys_port_name }; /* Called when the parent PF interface is closed: @@ -274,6 +302,7 @@ static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, dev->netdev_ops = &bnxt_vf_rep_netdev_ops; dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops; + dev->switchdev_ops = &bnxt_vf_rep_switchdev_ops; /* Just inherit all the featues of the parent PF as the VF-R * uses the RX/TX rings of the parent PF */ -- cgit v1.2.3 From fd763ad96ae5a4aa76ce00b4090b6044c63c18ed Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:57:25 -0700 Subject: netvsc: remove bogus rtnl_unlock Remove accidental rtnl_unlock from earlier testing. Fixes: 3962981f4822 ("netvsc: add rtnl annotations in rndis") Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 9a9e269a25ae..70b7cc37103c 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1548,7 +1548,6 @@ static int netvsc_probe(struct hv_device *dev, netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); - rtnl_unlock(); netdev_lockdep_set_classes(net); -- cgit v1.2.3 From 37b9dfa0d833227bc65353eec9dd0b00e1545a00 Mon Sep 17 00:00:00 2001 From: Mohammed Gamal Date: Mon, 24 Jul 2017 10:57:26 -0700 Subject: netvsc: Remove redundant use of ipv6_hdr() This condition already uses an object of type ipv6hdr in the line above. Use the object directly instead of calling ipv6_hdr Signed-off-by: Mohammed Gamal Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 70b7cc37103c..c266d427f934 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -339,7 +339,7 @@ static u32 net_checksum_info(struct sk_buff *skb) if (ip6->nexthdr == IPPROTO_TCP) return TRANSPORT_INFO_IPV6_TCP; - else if (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP) + else if (ip6->nexthdr == IPPROTO_UDP) return TRANSPORT_INFO_IPV6_UDP; } -- cgit v1.2.3 From 43bf99ce009de710b68473480a611f3b5ddd11d5 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:57:27 -0700 Subject: netvsc: prefetch the first incoming ring element In interrupt handler, prefetch the first incoming ring element so that it is in cache by the time NAPI poll gets to it. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0a9d9feedc3f..06f39a99da7c 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -1265,10 +1266,15 @@ int netvsc_poll(struct napi_struct *napi, int budget) void netvsc_channel_cb(void *context) { struct netvsc_channel *nvchan = context; + struct vmbus_channel *channel = nvchan->channel; + struct hv_ring_buffer_info *rbi = &channel->inbound; + + /* preload first vmpacket descriptor */ + prefetch(hv_get_ring_buffer(rbi) + rbi->priv_read_index); if (napi_schedule_prep(&nvchan->napi)) { /* disable interupts from host */ - hv_begin_read(&nvchan->channel->inbound); + hv_begin_read(rbi); __napi_schedule(&nvchan->napi); } -- cgit v1.2.3 From 7ca4593338e19595cad48fa8dcd1da28c81352d6 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:57:28 -0700 Subject: netvsc: fix netvsc_set_channels The number of channels returned by rndis_filter_device_add maybe less than the number requested. Therefore set correct real number of queues. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 52 +++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index c266d427f934..e212cbeb6333 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -713,39 +713,16 @@ static void netvsc_get_channels(struct net_device *net, } } -static int netvsc_set_queues(struct net_device *net, struct hv_device *dev, - u32 num_chn) -{ - struct netvsc_device_info device_info; - struct netvsc_device *net_device; - int ret; - - memset(&device_info, 0, sizeof(device_info)); - device_info.num_chn = num_chn; - device_info.ring_size = ring_size; - device_info.max_num_vrss_chns = num_chn; - - ret = netif_set_real_num_tx_queues(net, num_chn); - if (ret) - return ret; - - ret = netif_set_real_num_rx_queues(net, num_chn); - if (ret) - return ret; - - net_device = rndis_filter_device_add(dev, &device_info); - return PTR_ERR_OR_ZERO(net_device); -} - static int netvsc_set_channels(struct net_device *net, struct ethtool_channels *channels) { struct net_device_context *net_device_ctx = netdev_priv(net); struct hv_device *dev = net_device_ctx->device_ctx; struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); - unsigned int count = channels->combined_count; + unsigned int orig, count = channels->combined_count; + struct netvsc_device_info device_info; bool was_opened; - int ret; + int ret = 0; /* We do not support separate count for rx, tx, or other */ if (count == 0 || @@ -764,19 +741,29 @@ static int netvsc_set_channels(struct net_device *net, if (count > nvdev->max_chn) return -EINVAL; + orig = nvdev->num_chn; was_opened = rndis_filter_opened(nvdev); if (was_opened) rndis_filter_close(nvdev); rndis_filter_device_remove(dev, nvdev); - ret = netvsc_set_queues(net, dev, count); - if (ret == 0) - nvdev->num_chn = count; - else - netvsc_set_queues(net, dev, nvdev->num_chn); + memset(&device_info, 0, sizeof(device_info)); + device_info.num_chn = count; + device_info.ring_size = ring_size; + device_info.max_num_vrss_chns = count; + + nvdev = rndis_filter_device_add(dev, &device_info); + if (!IS_ERR(nvdev)) { + netif_set_real_num_tx_queues(net, nvdev->num_chn); + netif_set_real_num_rx_queues(net, nvdev->num_chn); + ret = PTR_ERR(nvdev); + } else { + device_info.num_chn = orig; + device_info.max_num_vrss_chns = count; + rndis_filter_device_add(dev, &device_info); + } - nvdev = rtnl_dereference(net_device_ctx->nvdev); if (was_opened) rndis_filter_open(nvdev); @@ -863,7 +850,6 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; device_info.num_chn = nvdev->num_chn; - device_info.max_num_vrss_chns = nvdev->num_chn; rndis_filter_device_remove(hdev, nvdev); -- cgit v1.2.3 From 27f5aa92ccafbe1bbc695307e3dee41a0e924c28 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:57:29 -0700 Subject: netvsc: include rtnetlink.h Since these files use rtnl_derefernce make sure and include rtnetlink.h Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 2 ++ drivers/net/hyperv/rndis_filter.c | 1 + 2 files changed, 3 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index e212cbeb6333..a94fd545a650 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -33,6 +33,8 @@ #include #include #include +#include + #include #include #include diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index e439886f72c1..eaa3f0d5682a 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "hyperv_net.h" -- cgit v1.2.3 From 658677f17c5cbe84ec24fd7be69b4da1ed580596 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:57:30 -0700 Subject: netvsc: remove no longer used max_num_rss queues This value has been calculated in rndis_device_attach since 4.11. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 - drivers/net/hyperv/netvsc_drv.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index afb65f753574..4e7ff348327e 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -147,7 +147,6 @@ struct hv_netvsc_packet { struct netvsc_device_info { unsigned char mac_adr[ETH_ALEN]; int ring_size; - u32 max_num_vrss_chns; u32 num_chn; }; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index a94fd545a650..262486ce8e2a 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -753,7 +753,6 @@ static int netvsc_set_channels(struct net_device *net, memset(&device_info, 0, sizeof(device_info)); device_info.num_chn = count; device_info.ring_size = ring_size; - device_info.max_num_vrss_chns = count; nvdev = rndis_filter_device_add(dev, &device_info); if (!IS_ERR(nvdev)) { @@ -762,7 +761,6 @@ static int netvsc_set_channels(struct net_device *net, ret = PTR_ERR(nvdev); } else { device_info.num_chn = orig; - device_info.max_num_vrss_chns = count; rndis_filter_device_add(dev, &device_info); } -- cgit v1.2.3 From a248878d7a1d35ea3bb874891997144ad16d7c27 Mon Sep 17 00:00:00 2001 From: John Allen Date: Mon, 24 Jul 2017 13:26:06 -0500 Subject: ibmvnic: Check for transport event on driver resume On resume, the ibmvnic driver will fail to resume normal operation. The main crq gets closed on suspend by the vnic server and doesn't get reopened again as the interrupt for the transport event that would reset the main crq comes in after the driver has been suspended. This patch resolves the issue by removing the calls to kick the receive interrupts handlers and instead directly invoking the main crq interrupt handler. This will ensure that we see the transport event necessary to properly resume the driver. Signed-off-by: John Allen Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index a3e694679635..9d8af464dc44 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -3851,10 +3851,7 @@ static int ibmvnic_resume(struct device *dev) if (adapter->state != VNIC_OPEN) return 0; - /* kick the interrupt handlers just in case we lost an interrupt */ - for (i = 0; i < adapter->req_rx_queues; i++) - ibmvnic_interrupt_rx(adapter->rx_scrq[i]->irq, - adapter->rx_scrq[i]); + tasklet_schedule(&adapter->tasklet); return 0; } -- cgit v1.2.3 From bc88055ab72c0eaa080926c888628b77d2055513 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 24 Jul 2017 21:20:16 -0700 Subject: bnxt_en: Use SWITCHDEV_SET_OPS(). Suggested by Jakub Kicinski. Fixes: c124a62ff2dd ("bnxt_en: add support for port_attr_get and and get_phys_port_name") Reported-by: kbuild test robot Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 82cbe1804821..badbc3550338 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7894,7 +7894,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->netdev_ops = &bnxt_netdev_ops; dev->watchdog_timeo = BNXT_TX_TIMEOUT; dev->ethtool_ops = &bnxt_ethtool_ops; - dev->switchdev_ops = &bnxt_switchdev_ops; + SWITCHDEV_SET_OPS(dev, &bnxt_switchdev_ops); pci_set_drvdata(pdev, dev); rc = bnxt_alloc_hwrm_resources(bp); -- cgit v1.2.3 From 98b5798499b6614963e693dff5262de8d33b2cd0 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 8 Jul 2017 10:40:13 +0200 Subject: mrf24j40: Fix en error handling path in 'mrf24j40_probe()' If this check fails, we must release some resources as done everywhere else in this function before returning an error code. Signed-off-by: Christophe JAILLET Acked-by: Alan Ott Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/mrf24j40.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 7d334963dc08..ee7084b2d52d 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -1330,7 +1330,8 @@ static int mrf24j40_probe(struct spi_device *spi) if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) { dev_warn(&spi->dev, "spi clock above possible maximum: %d", MAX_SPI_SPEED_HZ); - return -EINVAL; + ret = -EINVAL; + goto err_register_device; } ret = mrf24j40_hw_init(devrec); -- cgit v1.2.3 From 81fc9b5ccf84674ad70b1cdf29abd36397097d66 Mon Sep 17 00:00:00 2001 From: Tonghao Zhang Date: Tue, 25 Jul 2017 00:00:26 -0700 Subject: drivers/net: Fix ptr_ret.cocci warnings. we can use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR 1. drivers/net/appletalk/ipddp.c 2. drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c Generated by: scripts/coccinelle/api/ptr_ret.cocci Signed-off-by: Tonghao Zhang Signed-off-by: David S. Miller --- drivers/net/appletalk/ipddp.c | 4 +--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index a306de4318d7..9375cef22420 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c @@ -311,9 +311,7 @@ module_param(ipddp_mode, int, 0); static int __init ipddp_init_module(void) { dev_ipddp = ipddp_init(); - if (IS_ERR(dev_ipddp)) - return PTR_ERR(dev_ipddp); - return 0; + return PTR_ERR_OR_ZERO(dev_ipddp); } static void __exit ipddp_cleanup_module(void) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c index 1447a8352383..2d3e5e263a32 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c @@ -78,10 +78,7 @@ int brcmf_debug_attach(struct brcmf_pub *drvr) return -ENODEV; drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder); - if (IS_ERR(drvr->dbgfs_dir)) - return PTR_ERR(drvr->dbgfs_dir); - - return 0; + return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); } void brcmf_debug_detach(struct brcmf_pub *drvr) -- cgit v1.2.3 From 447e9ebfc19950559ebb9aa08302a7f11ab1508c Mon Sep 17 00:00:00 2001 From: Dirk van der Merwe Date: Tue, 25 Jul 2017 00:51:08 -0700 Subject: nfp: set config bit (ifup/ifdown) on netdev open/close When a netdev (PF netdev or representor) is opened or closed, set the physical port config bit appropriately - which powers UP/DOWN the PHY module for the physical interface. The PHY is powered first in the HW/FW configuration step when opening the netdev and again last in the HW/FW configuration step when closing the netdev. This is only applicable when there is a physical port associated with the netdev and if the NSP support this. Otherwise we silently ignore this step. The 'nfp_eth_set_configured' can actually return positive values - updated the function documentation appropriately. Signed-off-by: Dirk van der Merwe Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 10 +++++++- drivers/net/ethernet/netronome/nfp/nfp_net_repr.c | 23 ++++++++++++++++-- drivers/net/ethernet/netronome/nfp/nfp_port.c | 27 ++++++++++++++++++++++ drivers/net/ethernet/netronome/nfp/nfp_port.h | 1 + .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 18 +++++++++++++-- 5 files changed, 74 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 18750ff0ede6..ea471604450e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2658,6 +2658,7 @@ static int nfp_net_netdev_close(struct net_device *netdev) /* Step 2: Tell NFP */ nfp_net_clear_config_and_disable(nn); + nfp_port_configure(netdev, false); /* Step 3: Free resources */ @@ -2775,16 +2776,21 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_free_all; /* Step 2: Configure the NFP + * - Ifup the physical interface if it exists * - Enable rings from 0 to tx_rings/rx_rings - 1. * - Write MAC address (in case it changed) * - Set the MTU * - Set the Freelist buffer size * - Enable the FW */ - err = nfp_net_set_config_and_enable(nn); + err = nfp_port_configure(netdev, true); if (err) goto err_free_all; + err = nfp_net_set_config_and_enable(nn); + if (err) + goto err_port_disable; + /* Step 3: Enable for kernel * - put some freelist descriptors on each RX ring * - enable NAPI on each ring @@ -2795,6 +2801,8 @@ static int nfp_net_netdev_open(struct net_device *netdev) return 0; +err_port_disable: + nfp_port_configure(netdev, false); err_free_all: nfp_net_close_free_all(nn); return err; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 8ec5474f4b18..47daad30756c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -239,15 +239,34 @@ static netdev_tx_t nfp_repr_xmit(struct sk_buff *skb, struct net_device *netdev) static int nfp_repr_stop(struct net_device *netdev) { struct nfp_repr *repr = netdev_priv(netdev); + int err; + + err = nfp_app_repr_stop(repr->app, repr); + if (err) + return err; - return nfp_app_repr_stop(repr->app, repr); + nfp_port_configure(netdev, false); + return 0; } static int nfp_repr_open(struct net_device *netdev) { struct nfp_repr *repr = netdev_priv(netdev); + int err; + + err = nfp_port_configure(netdev, true); + if (err) + return err; + + err = nfp_app_repr_open(repr->app, repr); + if (err) + goto err_port_disable; - return nfp_app_repr_open(repr->app, repr); + return 0; + +err_port_disable: + nfp_port_configure(netdev, false); + return err; } const struct net_device_ops nfp_repr_netdev_ops = { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index e42644dbb865..d16a7b78ba9b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -181,6 +181,33 @@ nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len) return 0; } +/** + * nfp_port_configure() - helper to set the interface configured bit + * @netdev: net_device instance + * @configed: Desired state + * + * Helper to set the ifup/ifdown state on the PHY only if there is a physical + * interface associated with the netdev. + * + * Return: + * 0 - configuration successful (or no change); + * -ERRNO - configuration failed. + */ +int nfp_port_configure(struct net_device *netdev, bool configed) +{ + struct nfp_eth_table_port *eth_port; + struct nfp_port *port; + int err; + + port = nfp_port_from_netdev(netdev); + eth_port = __nfp_port_get_eth_port(port); + if (!eth_port) + return 0; + + err = nfp_eth_set_configured(port->app->cpp, eth_port->index, configed); + return err < 0 && err != -EOPNOTSUPP ? err : 0; +} + int nfp_port_init_phy_port(struct nfp_pf *pf, struct nfp_app *app, struct nfp_port *port, unsigned int id) { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index a33d22e18f94..56c76926c82a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -120,6 +120,7 @@ struct nfp_eth_table_port *nfp_port_get_eth_port(struct nfp_port *port); int nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len); +int nfp_port_configure(struct net_device *netdev, bool configed); struct nfp_port * nfp_port_alloc(struct nfp_app *app, enum nfp_port_type type, diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index c2bc36e8649f..f6f7c085f8e0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -391,7 +391,10 @@ int nfp_eth_config_commit_end(struct nfp_nsp *nsp) * Enable or disable PHY module (this usually means setting the TX lanes * disable bits). * - * Return: 0 or -ERRNO. + * Return: + * 0 - configuration successful; + * 1 - no changes were needed; + * -ERRNO - configuration failed. */ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) { @@ -427,7 +430,10 @@ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) * * Set the ifup/ifdown state on the PHY. * - * Return: 0 or -ERRNO. + * Return: + * 0 - configuration successful; + * 1 - no changes were needed; + * -ERRNO - configuration failed. */ int nfp_eth_set_configured(struct nfp_cpp *cpp, unsigned int idx, bool configed) { @@ -439,6 +445,14 @@ int nfp_eth_set_configured(struct nfp_cpp *cpp, unsigned int idx, bool configed) if (IS_ERR(nsp)) return PTR_ERR(nsp); + /* Older ABI versions did support this feature, however this has only + * been reliable since ABI 20. + */ + if (nfp_nsp_get_abi_ver_minor(nsp) < 20) { + nfp_eth_config_cleanup_end(nsp); + return -EOPNOTSUPP; + } + entries = nfp_nsp_config_entries(nsp); /* Check if we are already in requested state */ -- cgit v1.2.3 From b721cfaf03bcaac0a3abf702c4240326eed9e4b1 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:25:18 -0700 Subject: bnxt: fix unsigned comparsion with 0 Fixes warning because location is u32 and can never be netative warning: comparison of unsigned expression < 0 is always false [-Wtype-limits] Signed-off-by: Stephen Hemminger Acked-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 140e76904af9..08b870d7d466 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -523,7 +523,7 @@ static int bnxt_grxclsrule(struct bnxt *bp, struct ethtool_rxnfc *cmd) struct flow_keys *fkeys; int i, rc = -EINVAL; - if (fs->location < 0 || fs->location >= BNXT_NTP_FLTR_MAX_FLTR) + if (fs->location >= BNXT_NTP_FLTR_MAX_FLTR) return rc; for (i = 0; i < BNXT_NTP_FLTR_HASH_SIZE; i++) { -- cgit v1.2.3 From 351bac30613378c4684d4673aac0c7917980a652 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:25:19 -0700 Subject: bnxt: fix unused variable warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a couple of warnings where variable ‘txq’ set but not used Signed-off-by: Stephen Hemminger Acked-by: Michael Chan v, i); Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index badbc3550338..9835ddf1685b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5628,12 +5628,10 @@ void bnxt_tx_disable(struct bnxt *bp) { int i; struct bnxt_tx_ring_info *txr; - struct netdev_queue *txq; if (bp->tx_ring) { for (i = 0; i < bp->tx_nr_rings; i++) { txr = &bp->tx_ring[i]; - txq = netdev_get_tx_queue(bp->dev, i); txr->dev_state = BNXT_DEV_STATE_CLOSING; } } @@ -5646,11 +5644,9 @@ void bnxt_tx_enable(struct bnxt *bp) { int i; struct bnxt_tx_ring_info *txr; - struct netdev_queue *txq; for (i = 0; i < bp->tx_nr_rings; i++) { txr = &bp->tx_ring[i]; - txq = netdev_get_tx_queue(bp->dev, i); txr->dev_state = 0; } netif_tx_wake_all_queues(bp->dev); -- cgit v1.2.3 From 8916829366c17a12c996a10da43c7f9d2e5e7039 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:25:20 -0700 Subject: benet: fix set but not used warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: variable ‘netdev’ set but not used Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_roce.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/emulex/benet/be_roce.c b/drivers/net/ethernet/emulex/benet/be_roce.c index 2b62841c4c63..05989aafaf32 100644 --- a/drivers/net/ethernet/emulex/benet/be_roce.c +++ b/drivers/net/ethernet/emulex/benet/be_roce.c @@ -139,10 +139,7 @@ int be_roce_register_driver(struct ocrdma_driver *drv) } ocrdma_drv = drv; list_for_each_entry(dev, &be_adapter_list, entry) { - struct net_device *netdev; - _be_roce_dev_add(dev); - netdev = dev->netdev; } mutex_unlock(&be_adapter_list_lock); return 0; -- cgit v1.2.3 From d3e3becedc43adc8b8fb12e7507dd4e5aae4d17d Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 25 Jul 2017 13:28:39 -0400 Subject: bnxt_en: include bnxt_vfr.c code under CONFIG_BNXT_SRIOV switch And define empty functions in bnxt_vfr.h when CONFIG_BNXT_SRIOV is not defined. This fixes build error when CONFIG_BNXT_SRIOV is switched off: >> drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c:165:16: error: 'struct >> bnxt' has no member named 'sriov_lock' Reported-by: kbuild test robot Fixes: 4ab0c6a8ffd7 ("bnxt_en: add support to enable VF-representors") Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 4 ++++ drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h | 30 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index 83478e912ee5..a52e292be052 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -16,6 +16,8 @@ #include "bnxt.h" #include "bnxt_vfr.h" +#ifdef CONFIG_BNXT_SRIOV + #define CFA_HANDLE_INVALID 0xffff #define VF_IDX_INVALID 0xffff @@ -487,3 +489,5 @@ void bnxt_dl_unregister(struct bnxt *bp) devlink_unregister(dl); devlink_free(dl); } + +#endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h index c6cd55afbb89..e55a3b693e20 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h @@ -10,6 +10,8 @@ #ifndef BNXT_VFR_H #define BNXT_VFR_H +#ifdef CONFIG_BNXT_SRIOV + #define MAX_CFA_CODE 65536 /* Struct to hold housekeeping info needed by devlink interface */ @@ -39,4 +41,32 @@ void bnxt_vf_reps_open(struct bnxt *bp); void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb); struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code); +#else + +static inline int bnxt_dl_register(struct bnxt *bp) +{ + return 0; +} + +static inline void bnxt_dl_unregister(struct bnxt *bp) +{ +} + +static inline void bnxt_vf_reps_close(struct bnxt *bp) +{ +} + +static inline void bnxt_vf_reps_open(struct bnxt *bp) +{ +} + +static inline void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb) +{ +} + +static inline struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code) +{ + return NULL; +} +#endif /* CONFIG_BNXT_SRIOV */ #endif /* BNXT_VFR_H */ -- cgit v1.2.3 From e408ebdc41aa53f0aa552132384daaa5f5c6301d Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 25 Jul 2017 13:28:40 -0400 Subject: bnxt_en: use SWITCHDEV_SET_OPS() for setting vf_rep_switchdev_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the build error: ‘struct net_device’ has no member named ‘switchdev_ops’ Reported-by: kbuild test robot Fixes: c124a62ff2dd ("bnxt_en: add support for port_attr_get and and get_phys_port_name") Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index a52e292be052..c00352a4c1f0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -304,7 +304,7 @@ static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, dev->netdev_ops = &bnxt_vf_rep_netdev_ops; dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops; - dev->switchdev_ops = &bnxt_vf_rep_switchdev_ops; + SWITCHDEV_SET_OPS(dev, &bnxt_vf_rep_switchdev_ops); /* Just inherit all the featues of the parent PF as the VF-R * uses the RX/TX rings of the parent PF */ -- cgit v1.2.3 From 53f70b8b5aa06db53eb06f092342e6073891729a Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 25 Jul 2017 13:28:41 -0400 Subject: bnxt_en: fix switchdev port naming for external-port-rep and vf-reps Fix the phys_port_name for the external physical port to be in "pA" format and that of VF-rep to be in "pCvfD" format as suggested by Jakub Kicinski. Fixes: c124a62ff2dd ("bnxt_en: add support for port_attr_get and get_phys_port_name") Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 6 +----- drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 4 +++- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 9835ddf1685b..156fb374522b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7552,11 +7552,7 @@ static int bnxt_get_phys_port_name(struct net_device *dev, char *buf, if (!BNXT_PF(bp)) return -EOPNOTSUPP; - /* The switch-id that the pf belongs to is exported by - * the switchdev ndo. This name is just to distinguish from the - * vf-rep ports. - */ - rc = snprintf(buf, len, "pf%d", bp->pf.port_id); + rc = snprintf(buf, len, "p%d", bp->pf.port_id); if (rc >= len) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index c00352a4c1f0..b05c5d0ee3f9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -141,9 +141,11 @@ static int bnxt_vf_rep_get_phys_port_name(struct net_device *dev, char *buf, size_t len) { struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + struct pci_dev *pf_pdev = vf_rep->bp->pdev; int rc; - rc = snprintf(buf, len, "vfr%d", vf_rep->vf_idx); + rc = snprintf(buf, len, "pf%dvf%d", PCI_FUNC(pf_pdev->devfn), + vf_rep->vf_idx); if (rc >= len) return -EOPNOTSUPP; return 0; -- cgit v1.2.3 From 0e1ff3061cb529a70f03f63988a48f9fda8ed419 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Wed, 19 Jul 2017 15:00:26 -0700 Subject: ixgbe: Ensure MAC filter was added before setting MACVLAN This patch adds a check to ensure that adding the MAC filter was successful before setting the MACVLAN. If it was unsuccessful, propagate the error. Signed-off-by: Tony Nguyen Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 0760bd7eeb01..112d24c6c9ce 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -679,8 +679,9 @@ update_vlvfb: static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter, int vf, int index, unsigned char *mac_addr) { - struct list_head *pos; struct vf_macvlans *entry; + struct list_head *pos; + int retval = 0; if (index <= 1) { list_for_each(pos, &adapter->vf_mvs.l) { @@ -721,13 +722,15 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter, if (!entry || !entry->free) return -ENOSPC; + retval = ixgbe_add_mac_filter(adapter, mac_addr, vf); + if (retval < 0) + return retval; + entry->free = false; entry->is_macvlan = true; entry->vf = vf; memcpy(entry->vf_macvlan, mac_addr, ETH_ALEN); - ixgbe_add_mac_filter(adapter, mac_addr, vf); - return 0; } -- cgit v1.2.3 From 72f740b1013783c81da928cfe2ac82dd767c74f0 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Wed, 7 Jun 2017 14:36:18 -0700 Subject: ixgbe: Enable LASI interrupts for X552 devices Enable LASI interrupts on X552 devices in order to receive notifications of link configurations of the external PHY and support the configuration of the internal iXFI link since iXFI does not support auto-negotiation. This is not required for X553 devices; add a check to avoid enabling LASI interrupts for X553 devices. Signed-off-by: Tony Nguyen Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 31 +++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 72d84a065e34..aa34e0b131bb 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -2404,17 +2404,30 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) status = ixgbe_get_lasi_ext_t_x550em(hw, &lsc); /* Enable link status change alarm */ - status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, - MDIO_MMD_AN, ®); - if (status) - return status; - reg |= IXGBE_MDIO_PMA_TX_VEN_LASI_INT_EN; + /* Enable the LASI interrupts on X552 devices to receive notifications + * of the link configurations of the external PHY and correspondingly + * support the configuration of the internal iXFI link, since iXFI does + * not support auto-negotiation. This is not required for X553 devices + * having KR support, which performs auto-negotiations and which is used + * as the internal link to the external PHY. Hence adding a check here + * to avoid enabling LASI interrupts for X553 devices. + */ + if (hw->mac.type != ixgbe_mac_x550em_a) { + status = hw->phy.ops.read_reg(hw, + IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, + MDIO_MMD_AN, ®); + if (status) + return status; + + reg |= IXGBE_MDIO_PMA_TX_VEN_LASI_INT_EN; - status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, - MDIO_MMD_AN, reg); - if (status) - return status; + status = hw->phy.ops.write_reg(hw, + IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, + MDIO_MMD_AN, reg); + if (status) + return status; + } /* Enable high temperature failure and global fault alarms */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK, -- cgit v1.2.3 From 48301cf22fa7d70db3ae777e374edfd4119fc826 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Wed, 7 Jun 2017 14:36:19 -0700 Subject: ixgbe: Update NW_MNG_IF_SEL support for X553 The MAC register NW_MNG_IF_SEL fields have been redefined for X553. These changes impact the iXFI driver code flow. Since iXFI is only supported in X552, add MAC checks for iXFI flows. Signed-off-by: Tony Nguyen Signed-off-by: Paul Greenwalt Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 4 ++-- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 14 +++++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 0f867dcda65f..96606e3eb965 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -386,7 +386,7 @@ u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg) if (ixgbe_removed(reg_addr)) return IXGBE_FAILED_READ_REG; if (unlikely(hw->phy.nw_mng_if_sel & - IXGBE_NW_MNG_IF_SEL_ENABLE_10_100M)) { + IXGBE_NW_MNG_IF_SEL_SGMII_ENABLE)) { struct ixgbe_adapter *adapter; int i; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 9c2460c5ef1b..ffa0ee5cd0f5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3778,8 +3778,8 @@ struct ixgbe_info { #define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_1G BIT(19) #define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_2_5G BIT(20) #define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_10G BIT(21) -#define IXGBE_NW_MNG_IF_SEL_ENABLE_10_100M BIT(23) -#define IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE BIT(24) +#define IXGBE_NW_MNG_IF_SEL_SGMII_ENABLE BIT(25) +#define IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE BIT(24) /* X552 only */ #define IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT 3 #define IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD \ (0x1F << IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index aa34e0b131bb..95adbda36235 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1555,9 +1555,14 @@ static s32 ixgbe_restart_an_internal_phy_x550em(struct ixgbe_hw *hw) **/ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) { + struct ixgbe_mac_info *mac = &hw->mac; s32 status; u32 reg_val; + /* iXFI is only supported with X552 */ + if (mac->type != ixgbe_mac_X550EM_x) + return IXGBE_ERR_LINK_SETUP; + /* Disable AN and force speed to 10G Serial. */ status = ixgbe_read_iosf_sb_reg_x550(hw, IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), @@ -1874,8 +1879,10 @@ static s32 ixgbe_setup_mac_link_t_X550em(struct ixgbe_hw *hw, else force_speed = IXGBE_LINK_SPEED_1GB_FULL; - /* If internal link mode is XFI, then setup XFI internal link. */ - if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) { + /* If X552 and internal link mode is XFI, then setup XFI internal link. + */ + if (hw->mac.type == ixgbe_mac_X550EM_x && + !(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) { status = ixgbe_setup_ixfi_x550em(hw, &force_speed); if (status) @@ -2628,7 +2635,8 @@ static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw) if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper) return IXGBE_ERR_CONFIG; - if (hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) { + if (!(hw->mac.type == ixgbe_mac_X550EM_x && + !(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE))) { speed = IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL; return ixgbe_setup_kr_speed_x550em(hw, speed); -- cgit v1.2.3 From ae84dbf7ff485b3b59740c6ea69df0613f6cd4f7 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Wed, 7 Jun 2017 14:36:20 -0700 Subject: ixgbe: Do not support flow control autonegotiation for X553 Flow control autonegotiation is not supported for fiber on X553. Add device ID checks in ixgbe_device_supports_autoneg_fc() to return the appropriate value. Signed-off-by: Tony Nguyen Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 4e35e7017f3d..40ae7db468ea 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -79,13 +79,22 @@ bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw) switch (hw->phy.media_type) { case ixgbe_media_type_fiber: - hw->mac.ops.check_link(hw, &speed, &link_up, false); - /* if link is down, assume supported */ - if (link_up) - supported = speed == IXGBE_LINK_SPEED_1GB_FULL ? + /* flow control autoneg black list */ + switch (hw->device_id) { + case IXGBE_DEV_ID_X550EM_A_SFP: + case IXGBE_DEV_ID_X550EM_A_SFP_N: + supported = false; + break; + default: + hw->mac.ops.check_link(hw, &speed, &link_up, false); + /* if link is down, assume supported */ + if (link_up) + supported = speed == IXGBE_LINK_SPEED_1GB_FULL ? true : false; - else - supported = true; + else + supported = true; + } + break; case ixgbe_media_type_backplane: supported = true; @@ -111,6 +120,10 @@ bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw) break; } + if (!supported) + hw_dbg(hw, "Device %x does not support flow control autoneg\n", + hw->device_id); + return supported; } -- cgit v1.2.3 From 7adbccbbb5beabe14f3a02ee41abdaa1801395b8 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Wed, 7 Jun 2017 14:36:21 -0700 Subject: ixgbe: Disable flow control for XFI Flow control autonegotiation is not supported for XFI. Make sure that ixgbe_device_supports_autoneg_fc() returns false and hw->fc.disable_fc_autoneg is set to true to avoid running the fc_autoneg function for that device. Signed-off-by: Tony Nguyen Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 5 ++- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 57 ++++++++++++++----------- 2 files changed, 35 insertions(+), 27 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 40ae7db468ea..2c19070d2a0b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -97,7 +97,10 @@ bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw) break; case ixgbe_media_type_backplane: - supported = true; + if (hw->device_id == IXGBE_DEV_ID_X550EM_X_XFI) + supported = false; + else + supported = true; break; case ixgbe_media_type_copper: /* only some copper devices support flow control autoneg */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 95adbda36235..19fbb2f28ea4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -2843,7 +2843,7 @@ static s32 ixgbe_setup_fc_x550em(struct ixgbe_hw *hw) { bool pause, asm_dir; u32 reg_val; - s32 rc; + s32 rc = 0; /* Validate the requested mode */ if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) { @@ -2886,32 +2886,37 @@ static s32 ixgbe_setup_fc_x550em(struct ixgbe_hw *hw) return IXGBE_ERR_CONFIG; } - if (hw->device_id != IXGBE_DEV_ID_X550EM_X_KR && - hw->device_id != IXGBE_DEV_ID_X550EM_A_KR && - hw->device_id != IXGBE_DEV_ID_X550EM_A_KR_L) - return 0; - - rc = hw->mac.ops.read_iosf_sb_reg(hw, - IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, - ®_val); - if (rc) - return rc; - - reg_val &= ~(IXGBE_KRM_AN_CNTL_1_SYM_PAUSE | - IXGBE_KRM_AN_CNTL_1_ASM_PAUSE); - if (pause) - reg_val |= IXGBE_KRM_AN_CNTL_1_SYM_PAUSE; - if (asm_dir) - reg_val |= IXGBE_KRM_AN_CNTL_1_ASM_PAUSE; - rc = hw->mac.ops.write_iosf_sb_reg(hw, - IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, - reg_val); - - /* This device does not fully support AN. */ - hw->fc.disable_fc_autoneg = true; + switch (hw->device_id) { + case IXGBE_DEV_ID_X550EM_X_KR: + case IXGBE_DEV_ID_X550EM_A_KR: + case IXGBE_DEV_ID_X550EM_A_KR_L: + rc = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, + ®_val); + if (rc) + return rc; + reg_val &= ~(IXGBE_KRM_AN_CNTL_1_SYM_PAUSE | + IXGBE_KRM_AN_CNTL_1_ASM_PAUSE); + if (pause) + reg_val |= IXGBE_KRM_AN_CNTL_1_SYM_PAUSE; + if (asm_dir) + reg_val |= IXGBE_KRM_AN_CNTL_1_ASM_PAUSE; + rc = hw->mac.ops.write_iosf_sb_reg(hw, + IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, + reg_val); + + /* This device does not fully support AN. */ + hw->fc.disable_fc_autoneg = true; + break; + case IXGBE_DEV_ID_X550EM_X_XFI: + hw->fc.disable_fc_autoneg = true; + break; + default: + break; + } return rc; } -- cgit v1.2.3 From 67a75194bce07669bf11e14a24a95f64ebde8b47 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 25 Jul 2017 17:35:50 +0200 Subject: virtio-net: mark PM functions as __maybe_unused After removing the reset function, the freeze and restore functions are now unused when CONFIG_PM_SLEEP is disabled: drivers/net/virtio_net.c:1881:12: error: 'virtnet_restore_up' defined but not used [-Werror=unused-function] static int virtnet_restore_up(struct virtio_device *vdev) drivers/net/virtio_net.c:1859:13: error: 'virtnet_freeze_down' defined but not used [-Werror=unused-function] static void virtnet_freeze_down(struct virtio_device *vdev) A more robust way to do this is to remove the #ifdef around the callers and instead mark them as __maybe_unused. The compiler will now just silently drop the unused code. Fixes: 4941d472bf95 ("virtio-net: do not reset during XDP set") Signed-off-by: Arnd Bergmann Acked-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index d4751ce23b4f..1902701e15a9 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2702,8 +2702,7 @@ static void virtnet_remove(struct virtio_device *vdev) free_netdev(vi->dev); } -#ifdef CONFIG_PM_SLEEP -static int virtnet_freeze(struct virtio_device *vdev) +static __maybe_unused int virtnet_freeze(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; @@ -2714,7 +2713,7 @@ static int virtnet_freeze(struct virtio_device *vdev) return 0; } -static int virtnet_restore(struct virtio_device *vdev) +static __maybe_unused int virtnet_restore(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; int err; @@ -2730,7 +2729,6 @@ static int virtnet_restore(struct virtio_device *vdev) return 0; } -#endif static struct virtio_device_id id_table[] = { { VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID }, -- cgit v1.2.3 From db1a8f8e833037a8ed0f333243d0c90d18acb62f Mon Sep 17 00:00:00 2001 From: Gustavo A R Silva Date: Wed, 14 Jun 2017 21:38:26 -0500 Subject: i40e: fix incorrect variable assignment Fix incorrect variable assignment. Based on line 1511: aq_ret = I40_ERR_PARAM; the correct variable to be used in this instance is aq_ret instead of ret. Also, variable ret is updated at line 1602 just before return, so assigning a value to this variable in this code block is useless. Addresses-Coverity-ID: 1397693 Signed-off-by: Gustavo A R Silva Acked-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index ecbe40ea8ffe..ba327e90f32a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1567,7 +1567,7 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) dev_err(&pf->pdev->dev, "VF %d requested polling mode: this feature is supported only when the device is running in single function per port (SFP) mode\n", vf->vf_id); - ret = I40E_ERR_PARAM; + aq_ret = I40E_ERR_PARAM; goto err; } vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING; -- cgit v1.2.3 From 7c9ae7f053e9e896c24fd23595ba369a5fe322e1 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Tue, 20 Jun 2017 15:16:53 -0700 Subject: i40e: Fix for trace found with S4 state This patch fixes a problem found in systems when entering S4 state. This patch fixes the problem by ensuring that the misc vector's IRQ is disabled as well. Without this patch a stack trace can be seen upon entering S4 state. Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2db93d3f6d23..933b8e357ee4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -12089,7 +12089,10 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); i40e_stop_misc_vector(pf); - + if (pf->msix_entries) { + synchronize_irq(pf->msix_entries[0].vector); + free_irq(pf->msix_entries[0].vector, pf); + } retval = pci_save_state(pdev); if (retval) return retval; @@ -12129,6 +12132,15 @@ static int i40e_resume(struct pci_dev *pdev) /* handling the reset will rebuild the device state */ if (test_and_clear_bit(__I40E_SUSPENDED, pf->state)) { clear_bit(__I40E_DOWN, pf->state); + if (pf->msix_entries) { + err = request_irq(pf->msix_entries[0].vector, + i40e_intr, 0, pf->int_name, pf); + if (err) { + dev_err(&pf->pdev->dev, + "request_irq for %s failed: %d\n", + pf->int_name, err); + } + } i40e_reset_and_rebuild(pf, false, false); } -- cgit v1.2.3 From 4d5957cbdecdbb77d24c1465caadd801c07afa4a Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 20 Jun 2017 15:16:54 -0700 Subject: i40e: remove WQ_UNBOUND and the task limit of our workqueue During certain events such as a CORER, multiple devices will run a work task to handle some cleanup. This can cause issues due to a single-threaded workqueue which can mean that a device doesn't cleanup in time. Prevent this by removing the single-threaded restriction on the module workqueue. This avoids the need to add more complex yielding logic in our service task routine. This is also similar to what other drivers such as fm10k do. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 933b8e357ee4..22e60841cb22 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -12180,12 +12180,14 @@ static int __init i40e_init_module(void) i40e_driver_string, i40e_driver_version_str); pr_info("%s: %s\n", i40e_driver_name, i40e_copyright); - /* we will see if single thread per module is enough for now, - * it can't be any worse than using the system workqueue which - * was already single threaded + /* There is no need to throttle the number of active tasks because + * each device limits its own task using a state bit for scheduling + * the service task, and the device tasks do not interfere with each + * other, so we don't set a max task limit. We must set WQ_MEM_RECLAIM + * since we need to be able to guarantee forward progress even under + * memory pressure. */ - i40e_wq = alloc_workqueue("%s", WQ_UNBOUND | WQ_MEM_RECLAIM, 1, - i40e_driver_name); + i40e_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, i40e_driver_name); if (!i40e_wq) { pr_err("%s: Failed to create workqueue\n", i40e_driver_name); return -ENOMEM; -- cgit v1.2.3 From 981e25c32bc22bcaa429420c92bfec860008a1eb Mon Sep 17 00:00:00 2001 From: Paul M Stillwell Jr Date: Tue, 20 Jun 2017 15:16:55 -0700 Subject: i40e: Handle admin Q timeout when releasing NVM There are some rare cases where the release resource call will return an admin Q timeout. In these cases the code needs to try to release the resource again until it succeeds or it times out. Signed-off-by: Paul M Stillwell Jr Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_nvm.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 800bd55d0159..6fdecd70dcbc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -134,8 +134,25 @@ i40e_i40e_acquire_nvm_exit: **/ void i40e_release_nvm(struct i40e_hw *hw) { - if (!hw->nvm.blank_nvm_mode) - i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL); + i40e_status ret_code = I40E_SUCCESS; + u32 total_delay = 0; + + if (hw->nvm.blank_nvm_mode) + return; + + ret_code = i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL); + + /* there are some rare cases when trying to release the resource + * results in an admin Q timeout, so handle them correctly + */ + while ((ret_code == I40E_ERR_ADMIN_QUEUE_TIMEOUT) && + (total_delay < hw->aq.asq_cmd_timeout)) { + usleep_range(1000, 2000); + ret_code = i40e_aq_release_resource(hw, + I40E_NVM_RESOURCE_ID, + 0, NULL); + total_delay++; + } } /** -- cgit v1.2.3 From 0ac30ce433232944e702876c1288c0d50eee3151 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Tue, 20 Jun 2017 15:16:56 -0700 Subject: i40e: fix up 32 bit timespec references As it turns out there was only a small set of errors on 32 bit, and we just needed to be using the right calls for dealing with timespec64 variables. Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 1a0be835fa06..0129ed3b78ec 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -158,13 +158,12 @@ static int i40e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) static int i40e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); - struct timespec64 now, then; + struct timespec64 now; - then = ns_to_timespec64(delta); mutex_lock(&pf->tmreg_lock); i40e_ptp_read(pf, &now); - now = timespec64_add(now, then); + timespec64_add_ns(&now, delta); i40e_ptp_write(pf, (const struct timespec64 *)&now); mutex_unlock(&pf->tmreg_lock); -- cgit v1.2.3 From 4d433084dd3b8b9ce656c35505e7dc5bd1d929b5 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Tue, 20 Jun 2017 15:16:57 -0700 Subject: i40e: fix odd formatting and indent The compiler warned on an oddly indented bit of code, and when investigating that, noted that the functions themselves had an odd flow. The if condition was checked, and would exclude a call to AQ, but then the aq_ret would be checked unconditionally which just looks really weird, and is likely to cause objections. Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index ba327e90f32a..2e261bb59d10 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1741,16 +1741,14 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, NULL); } else if (i40e_getnum_vf_vsi_vlan_filters(vsi)) { hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { - aq_ret = 0; - if (f->vlan >= 0 && f->vlan <= I40E_MAX_VLANID) { - aq_ret = - i40e_aq_set_vsi_uc_promisc_on_vlan(hw, - vsi->seid, - alluni, - f->vlan, - NULL); - aq_err = pf->hw.aq.asq_last_status; - } + if (f->vlan < 0 || f->vlan > I40E_MAX_VLANID) + continue; + aq_ret = i40e_aq_set_vsi_uc_promisc_on_vlan(hw, + vsi->seid, + alluni, + f->vlan, + NULL); + aq_err = pf->hw.aq.asq_last_status; if (aq_ret) dev_err(&pf->pdev->dev, "Could not add VLAN %d to Unicast promiscuous domain err %s aq_err %s\n", -- cgit v1.2.3 From 601a2e7ac5acd4a1681ba7ca6cefe5f9897a3c28 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Tue, 20 Jun 2017 15:16:58 -0700 Subject: i40e/i40evf: make IPv6 ATR code clearer This just reorders some local vars and makes the code flow clearer. Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index b936febc315a..c9a149678926 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2451,9 +2451,15 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, hlen = (hdr.network[0] & 0x0F) << 2; l4_proto = hdr.ipv4->protocol; } else { - hlen = hdr.network - skb->data; - l4_proto = ipv6_find_hdr(skb, &hlen, IPPROTO_TCP, NULL, NULL); - hlen -= hdr.network - skb->data; + /* find the start of the innermost ipv6 header */ + unsigned int inner_hlen = hdr.network - skb->data; + unsigned int h_offset = inner_hlen; + + /* this function updates h_offset to the end of the header */ + l4_proto = + ipv6_find_hdr(skb, &h_offset, IPPROTO_TCP, NULL, NULL); + /* hlen will contain our best estimate of the tcp header */ + hlen = h_offset - inner_hlen; } if (l4_proto != IPPROTO_TCP) -- cgit v1.2.3 From b85c94b617c0004d1f2bd6ca32baa9132a6c2fe5 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Tue, 20 Jun 2017 15:16:59 -0700 Subject: i40e/i40evf: remove mismatched type warnings Compiler reported several places where driver compared signed and unsigned types. Cast or change the types to remove the warnings. Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 6 +++--- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 6 +++--- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 6 +++--- drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 9692a5294fa3..1d29152256fe 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1091,7 +1091,7 @@ static void i40e_get_regs(struct net_device *netdev, struct ethtool_regs *regs, struct i40e_pf *pf = np->vsi->back; struct i40e_hw *hw = &pf->hw; u32 *reg_buf = p; - int i, j, ri; + unsigned int i, j, ri; u32 reg; /* Tell ethtool which driver-version-specific regs output we have. @@ -1550,9 +1550,9 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, struct i40e_ring *tx_ring, *rx_ring; struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; + unsigned int j; int i = 0; char *p; - int j; struct rtnl_link_stats64 *net_stats = i40e_get_vsi_stats_struct(vsi); unsigned int start; @@ -1637,7 +1637,7 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; char *p = (char *)data; - int i; + unsigned int i; switch (stringset) { case ETH_SS_TEST: diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 22e60841cb22..a2d665161def 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4773,7 +4773,7 @@ static void i40e_detect_recover_hung(struct i40e_pf *pf) { struct net_device *netdev; struct i40e_vsi *vsi; - int i; + unsigned int i; /* Only for LAN VSI */ vsi = pf->vsi[pf->lan_vsi]; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index c9a149678926..d464fceb300f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -860,7 +860,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, netdev_tx_completed_queue(txring_txq(tx_ring), total_packets, total_bytes); -#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) +#define TX_WAKE_THRESHOLD ((s16)(DESC_NEEDED * 2)) if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) && (I40E_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) { /* Make sure that anybody stopping the queue after this @@ -2063,7 +2063,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); bool failure = false, xdp_xmit = false; - while (likely(total_rx_packets < budget)) { + while (likely(total_rx_packets < (unsigned int)budget)) { struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; struct xdp_buff xdp; @@ -2196,7 +2196,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_ring->q_vector->rx.total_bytes += total_rx_bytes; /* guarantee a trip back through this routine if there was a failure */ - return failure ? budget : total_rx_packets; + return failure ? budget : (int)total_rx_packets; } static u32 i40e_buildreg_itr(const int type, const u16 itr) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 12b02e530503..d91676ccf125 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -275,7 +275,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, netdev_tx_completed_queue(txring_txq(tx_ring), total_packets, total_bytes); -#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) +#define TX_WAKE_THRESHOLD ((s16)(DESC_NEEDED * 2)) if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) && (I40E_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) { /* Make sure that anybody stopping the queue after this @@ -1299,7 +1299,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); bool failure = false; - while (likely(total_rx_packets < budget)) { + while (likely(total_rx_packets < (unsigned int)budget)) { struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; unsigned int size; @@ -1406,7 +1406,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_ring->q_vector->rx.total_bytes += total_rx_bytes; /* guarantee a trip back through this routine if there was a failure */ - return failure ? budget : total_rx_packets; + return failure ? budget : (int)total_rx_packets; } static u32 i40e_buildreg_itr(const int type, const u16 itr) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index 9bb2cc7dd4e4..76fd89c1dbb2 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -165,7 +165,7 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct i40evf_adapter *adapter = netdev_priv(netdev); - int i, j; + unsigned int i, j; char *p; for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) { @@ -197,7 +197,7 @@ static void i40evf_get_strings(struct net_device *netdev, u32 sset, u8 *data) int i; if (sset == ETH_SS_STATS) { - for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) { + for (i = 0; i < (int)I40EVF_GLOBAL_STATS_LEN; i++) { memcpy(p, i40evf_gstrings_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; -- cgit v1.2.3 From d8b2c700a3a07c7108d9031dc88a6298c101e05d Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 20 Jun 2017 15:17:00 -0700 Subject: i40e: display correct UDP tunnel type name The i40e driver attempts to display the UDP tunnel name by doing a check against the type, where for non-zero types we use "vxlan" and for zero type we use "geneve". This is not future proof, because if new tunnel types get added, we'll incorrectly label them. It also depends on the value of UDP_TUNNEL_TYPE_GENEVE == 0, which is brittle. Instead, replace this with a function that can return a constant string depending on the type. For now we'll use "unknown" for types we don't know about, and we can expand this in the future if new types get added. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index a2d665161def..2b115b0c5296 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7520,6 +7520,18 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) i40e_flush(hw); } +static const char *i40e_tunnel_name(struct i40e_udp_port_config *port) +{ + switch (port->type) { + case UDP_TUNNEL_TYPE_VXLAN: + return "vxlan"; + case UDP_TUNNEL_TYPE_GENEVE: + return "geneve"; + default: + return "unknown"; + } +} + /** * i40e_sync_udp_filters - Trigger a sync event for existing UDP filters * @pf: board private structure @@ -7565,14 +7577,14 @@ static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf) ret = i40e_aq_del_udp_tunnel(hw, i, NULL); if (ret) { - dev_dbg(&pf->pdev->dev, - "%s %s port %d, index %d failed, err %s aq_err %s\n", - pf->udp_ports[i].type ? "vxlan" : "geneve", - port ? "add" : "delete", - port, i, - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, - pf->hw.aq.asq_last_status)); + dev_info(&pf->pdev->dev, + "%s %s port %d, index %d failed, err %s aq_err %s\n", + i40e_tunnel_name(&pf->udp_ports[i]), + port ? "add" : "delete", + port, i, + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); pf->udp_ports[i].port = 0; } } -- cgit v1.2.3 From 8d9ee66ac0e5212be37204949c8c86eabbadb24d Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Tue, 20 Jun 2017 15:17:01 -0700 Subject: i40evf: add some missing includes These includes were all being used in the driver, but weren't being directly included. Since the current advised method is to directly include anything that you need, this implements that. Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 6cc92089fecb..7901cc85cbe5 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -39,6 +39,17 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -- cgit v1.2.3 From eb23039f6c22a6b240aef7d7ebccceeb52a8452a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 24 Jun 2017 21:13:52 +0200 Subject: i40e: report BPF prog id during XDP_QUERY_PROG Fill the XDP prog_id with the id just like we do in other XDP enabled drivers such as ixgbe. This is needed so that on dump we can retrieve the attached program based on the id, and dump BPF insns, opcodes, etc back to user space. Only XDP driver missing this is currently i40e. Signed-off-by: Daniel Borkmann Acked-by: John Fastabend Acked-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2b115b0c5296..4104944ea367 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9601,6 +9601,7 @@ static int i40e_xdp(struct net_device *dev, return i40e_xdp_setup(vsi, xdp->prog); case XDP_QUERY_PROG: xdp->prog_attached = i40e_enabled_xdp_vsi(vsi); + xdp->prog_id = vsi->xdp_prog ? vsi->xdp_prog->aux->id : 0; return 0; default: return -EINVAL; -- cgit v1.2.3 From c969ef4ed9cbf3662fc8709e2581427c191504fb Mon Sep 17 00:00:00 2001 From: Tushar Dave Date: Thu, 22 Jun 2017 09:44:32 -0700 Subject: i40evf: Use le32_to_cpu before evaluating HW desc fields i40e hardware descriptor fields are in little-endian format. Driver must use le32_to_cpu while evaluating these fields otherwise on big-endian arch we end up evaluating incorrect values, cause errors like: i40evf 0000:03:0a.0: Expected response 24 from PF, received 402653184 i40evf 0000:03:0a.1: Expected response 7 from PF, received 117440512 Signed-off-by: Tushar Dave Reviewed-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 7c213a347909..93536b9fc629 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1957,8 +1957,8 @@ static void i40evf_adminq_task(struct work_struct *work) container_of(work, struct i40evf_adapter, adminq_task); struct i40e_hw *hw = &adapter->hw; struct i40e_arq_event_info event; - struct virtchnl_msg *v_msg; - i40e_status ret; + enum virtchnl_ops v_op; + i40e_status ret, v_ret; u32 val, oldval; u16 pending; @@ -1970,15 +1970,15 @@ static void i40evf_adminq_task(struct work_struct *work) if (!event.msg_buf) goto out; - v_msg = (struct virtchnl_msg *)&event.desc; do { ret = i40evf_clean_arq_element(hw, &event, &pending); - if (ret || !v_msg->v_opcode) + v_op = (enum virtchnl_ops)le32_to_cpu(event.desc.cookie_high); + v_ret = (i40e_status)le32_to_cpu(event.desc.cookie_low); + + if (ret || !v_op) break; /* No event to process or error cleaning ARQ */ - i40evf_virtchnl_completion(adapter, v_msg->v_opcode, - (i40e_status)v_msg->v_retval, - event.msg_buf, + i40evf_virtchnl_completion(adapter, v_op, v_ret, event.msg_buf, event.msg_len); if (pending != 0) memset(event.msg_buf, 0, I40EVF_MAX_AQ_BUF_SIZE); -- cgit v1.2.3 From 07c357f34834a94b8cb4274f69b75905034dc30e Mon Sep 17 00:00:00 2001 From: Tushar Dave Date: Thu, 22 Jun 2017 10:12:11 -0700 Subject: i40evf: remove unnecessary __packed This is similar to 'commit 9588397d24eec ("i40e: remove unnecessary __packed")' to avoid unaligned access. Signed-off-by: Tushar Dave Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40e_osdep.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40evf/i40e_osdep.h b/drivers/net/ethernet/intel/i40evf/i40e_osdep.h index 5e314fd3c016..a90737786c34 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_osdep.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_osdep.h @@ -54,7 +54,7 @@ struct i40e_dma_mem { void *va; dma_addr_t pa; u32 size; -} __packed; +}; #define i40e_allocate_dma_mem(h, m, unused, s, a) \ i40evf_allocate_dma_mem_d(h, m, s, a) @@ -63,7 +63,7 @@ struct i40e_dma_mem { struct i40e_virt_mem { void *va; u32 size; -} __packed; +}; #define i40e_allocate_virt_mem(h, m, s) i40evf_allocate_virt_mem_d(h, m, s) #define i40e_free_virt_mem(h, m) i40evf_free_virt_mem_d(h, m) -- cgit v1.2.3 From 2f1d86e44c9dac948a79ee7543426e00230564ab Mon Sep 17 00:00:00 2001 From: Stefan Assmann Date: Fri, 23 Jun 2017 09:46:24 +0200 Subject: i40e: handle setting administratively set MAC address back to zero When an administratively set MAC was previously set and should now be switched back to 00:00:00:00:00:00 the pf_set_mac flag did not get toggled back to false. As a result VFs were still treated as if an administratively set MAC was present. Signed-off-by: Stefan Assmann Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 2e261bb59d10..979110d59f67 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2762,7 +2762,6 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) spin_unlock_bh(&vsi->mac_filter_hash_lock); - dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id); /* program mac filter */ if (i40e_sync_vsi_filters(vsi)) { dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); @@ -2770,7 +2769,16 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) goto error_param; } ether_addr_copy(vf->default_lan_addr.addr, mac); - vf->pf_set_mac = true; + + if (is_zero_ether_addr(mac)) { + vf->pf_set_mac = false; + dev_info(&pf->pdev->dev, "Removing MAC on VF %d\n", vf_id); + } else { + vf->pf_set_mac = true; + dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", + mac, vf_id); + } + /* Force the VF driver stop so it has to reload with new MAC address */ i40e_vc_disable_vf(pf, vf); dev_info(&pf->pdev->dev, "Reload the VF driver to make this change effective.\n"); -- cgit v1.2.3 From 9877e1058aaf166b578ed44c8e0fc78fe6e67152 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 26 Jul 2017 09:55:33 +0200 Subject: hamradio: dmascc: avoid -Wformat-overflow warning gcc warns that the device name might overflow: drivers/net/hamradio/dmascc.c: In function 'dmascc_init': drivers/net/hamradio/dmascc.c:584:22: error: 'sprintf' may write a terminating nul past the end of the destination [-Werror=format-overflow=] sprintf(dev->name, "dmascc%i", 2 * n + i); drivers/net/hamradio/dmascc.c:584:3: note: 'sprintf' output between 8 and 17 bytes into a destination of size 16 sprintf(dev->name, "dmascc%i", 2 * n + i); >From the static data in this file, I can tell that the index is strictly limited to 16, so it won't overflow. This simply changes the sprintf() to snprintf(), which is a good idea in general, and shuts up this warning. Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/hamradio/dmascc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index dec6b76bc0fb..cde41200f40a 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -581,7 +581,7 @@ static int __init setup_adapter(int card_base, int type, int n) priv->param.dma = -1; INIT_WORK(&priv->rx_work, rx_bh); dev->ml_priv = priv; - sprintf(dev->name, "dmascc%i", 2 * n + i); + snprintf(dev->name, sizeof(dev->name), "dmascc%i", 2 * n + i); dev->base_addr = card_base; dev->irq = irq; dev->netdev_ops = &scc_netdev_ops; -- cgit v1.2.3 From ec9b8dbd825dd3a0667003b5ab56386214f9c648 Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Wed, 26 Jul 2017 06:07:09 -0700 Subject: qede: Add getter APIs support for RX flow classification This patch adds support for ethtool getter APIs to query RX flow classification rules. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 10 +- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 22 ++++- drivers/net/ethernet/qlogic/qede/qede_filter.c | 120 +++++++++++++++++++++++- 3 files changed, 144 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 4dfb238221f9..0a2475b65978 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -160,6 +160,8 @@ struct qede_rdma_dev { struct qede_ptp; +#define QEDE_RFS_MAX_FLTR 256 + struct qede_dev { struct qed_dev *cdev; struct net_device *ndev; @@ -241,9 +243,7 @@ struct qede_dev { u16 vxlan_dst_port; u16 geneve_dst_port; -#ifdef CONFIG_RFS_ACCEL struct qede_arfs *arfs; -#endif bool wol_enabled; struct qede_rdma_dev rdma_info; @@ -455,9 +455,13 @@ int qede_alloc_arfs(struct qede_dev *edev); #define QEDE_SP_ARFS_CONFIG 4 #define QEDE_SP_TASK_POLL_DELAY (5 * HZ) -#define QEDE_RFS_MAX_FLTR 256 #endif +int qede_get_cls_rule_entry(struct qede_dev *edev, struct ethtool_rxnfc *cmd); +int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info, + u32 *rule_locs); +int qede_get_arfs_filter_count(struct qede_dev *edev); + struct qede_reload_args { void (*func)(struct qede_dev *edev, struct qede_reload_args *args); union { diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 6a03d3e66cff..dd39dec62650 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -1045,20 +1045,34 @@ static int qede_get_rss_flags(struct qede_dev *edev, struct ethtool_rxnfc *info) } static int qede_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, - u32 *rules __always_unused) + u32 *rule_locs) { struct qede_dev *edev = netdev_priv(dev); + int rc = 0; switch (info->cmd) { case ETHTOOL_GRXRINGS: info->data = QEDE_RSS_COUNT(edev); - return 0; + break; case ETHTOOL_GRXFH: - return qede_get_rss_flags(edev, info); + rc = qede_get_rss_flags(edev, info); + break; + case ETHTOOL_GRXCLSRLCNT: + info->rule_cnt = qede_get_arfs_filter_count(edev); + info->data = QEDE_RFS_MAX_FLTR; + break; + case ETHTOOL_GRXCLSRULE: + rc = qede_get_cls_rule_entry(edev, info); + break; + case ETHTOOL_GRXCLSRLALL: + rc = qede_get_cls_rule_all(edev, info, rule_locs); + break; default: DP_ERR(edev, "Command parameters not supported\n"); - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; } + + return rc; } static int qede_set_rss_flags(struct qede_dev *edev, struct ethtool_rxnfc *info) diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index f939db5bac5f..a5e5d328e730 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -38,7 +38,6 @@ #include #include "qede.h" -#ifdef CONFIG_RFS_ACCEL struct qede_arfs_tuple { union { __be32 src_ipv4; @@ -80,6 +79,7 @@ struct qede_arfs_fltr_node { }; struct qede_arfs { +#define QEDE_ARFS_BUCKET_HEAD(edev, idx) (&(edev)->arfs->arfs_hl_head[idx]) #define QEDE_ARFS_POLL_COUNT 100 #define QEDE_RFS_FLW_BITSHIFT (4) #define QEDE_RFS_FLW_MASK ((1 << QEDE_RFS_FLW_BITSHIFT) - 1) @@ -92,6 +92,7 @@ struct qede_arfs { bool enable; }; +#ifdef CONFIG_RFS_ACCEL static void qede_configure_arfs_fltr(struct qede_dev *edev, struct qede_arfs_fltr_node *n, u16 rxq_id, bool add_fltr) @@ -1263,3 +1264,120 @@ void qede_config_rx_mode(struct net_device *ndev) out: kfree(uc_macs); } + +static struct qede_arfs_fltr_node * +qede_get_arfs_fltr_by_loc(struct hlist_head *head, u32 location) +{ + struct qede_arfs_fltr_node *fltr; + + hlist_for_each_entry(fltr, head, node) + if (location == fltr->sw_id) + return fltr; + + return NULL; +} + +int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info, + u32 *rule_locs) +{ + struct qede_arfs_fltr_node *fltr; + struct hlist_head *head; + int cnt = 0, rc = 0; + + info->data = QEDE_RFS_MAX_FLTR; + + __qede_lock(edev); + + if (!edev->arfs) { + rc = -EPERM; + goto unlock; + } + + head = QEDE_ARFS_BUCKET_HEAD(edev, 0); + + hlist_for_each_entry(fltr, head, node) { + if (cnt == info->rule_cnt) { + rc = -EMSGSIZE; + goto unlock; + } + + rule_locs[cnt] = fltr->sw_id; + cnt++; + } + + info->rule_cnt = cnt; + +unlock: + __qede_unlock(edev); + return rc; +} + +int qede_get_cls_rule_entry(struct qede_dev *edev, struct ethtool_rxnfc *cmd) +{ + struct ethtool_rx_flow_spec *fsp = &cmd->fs; + struct qede_arfs_fltr_node *fltr = NULL; + int rc = 0; + + cmd->data = QEDE_RFS_MAX_FLTR; + + __qede_lock(edev); + + if (!edev->arfs) { + rc = -EPERM; + goto unlock; + } + + fltr = qede_get_arfs_fltr_by_loc(QEDE_ARFS_BUCKET_HEAD(edev, 0), + fsp->location); + if (!fltr) { + DP_NOTICE(edev, "Rule not found - location=0x%x\n", + fsp->location); + rc = -EINVAL; + goto unlock; + } + + if (fltr->tuple.eth_proto == htons(ETH_P_IP)) { + if (fltr->tuple.ip_proto == IPPROTO_TCP) + fsp->flow_type = TCP_V4_FLOW; + else + fsp->flow_type = UDP_V4_FLOW; + + fsp->h_u.tcp_ip4_spec.psrc = fltr->tuple.src_port; + fsp->h_u.tcp_ip4_spec.pdst = fltr->tuple.dst_port; + fsp->h_u.tcp_ip4_spec.ip4src = fltr->tuple.src_ipv4; + fsp->h_u.tcp_ip4_spec.ip4dst = fltr->tuple.dst_ipv4; + } else { + if (fltr->tuple.ip_proto == IPPROTO_TCP) + fsp->flow_type = TCP_V6_FLOW; + else + fsp->flow_type = UDP_V6_FLOW; + fsp->h_u.tcp_ip6_spec.psrc = fltr->tuple.src_port; + fsp->h_u.tcp_ip6_spec.pdst = fltr->tuple.dst_port; + memcpy(&fsp->h_u.tcp_ip6_spec.ip6src, + &fltr->tuple.src_ipv6, sizeof(struct in6_addr)); + memcpy(&fsp->h_u.tcp_ip6_spec.ip6dst, + &fltr->tuple.dst_ipv6, sizeof(struct in6_addr)); + } + + fsp->ring_cookie = fltr->rxq_id; + +unlock: + __qede_unlock(edev); + return rc; +} + +int qede_get_arfs_filter_count(struct qede_dev *edev) +{ + int count = 0; + + __qede_lock(edev); + + if (!edev->arfs) + goto unlock; + + count = edev->arfs->filter_count; + +unlock: + __qede_unlock(edev); + return count; +} -- cgit v1.2.3 From 3f2a2b8b7aaadd731e688a23cbd23f7eb085c7fb Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Wed, 26 Jul 2017 06:07:10 -0700 Subject: qed/qede: Add setter APIs support for RX flow classification This patch adds support for adding and deleting rx flow classification rules. Using this user can classify RX flow constituting of TCP/UDP 4-tuples [src_ip/dst_ip and src_port/dst_port] to be steered on a given RX queue Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_main.c | 8 - drivers/net/ethernet/qlogic/qede/qede.h | 11 +- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 14 +- drivers/net/ethernet/qlogic/qede/qede_filter.c | 365 +++++++++++++++++++++--- drivers/net/ethernet/qlogic/qede/qede_main.c | 9 +- 5 files changed, 347 insertions(+), 60 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index b11399606990..1bddf9372fc9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -954,9 +954,7 @@ static int qed_slowpath_start(struct qed_dev *cdev, struct qed_tunnel_info tunn_info; const u8 *data = NULL; struct qed_hwfn *hwfn; -#ifdef CONFIG_RFS_ACCEL struct qed_ptt *p_ptt; -#endif int rc = -EINVAL; if (qed_iov_wq_start(cdev)) @@ -972,7 +970,6 @@ static int qed_slowpath_start(struct qed_dev *cdev, goto err; } -#ifdef CONFIG_RFS_ACCEL if (cdev->num_hwfns == 1) { p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); if (p_ptt) { @@ -983,7 +980,6 @@ static int qed_slowpath_start(struct qed_dev *cdev, goto err; } } -#endif } cdev->rx_coalesce_usecs = QED_DEFAULT_RX_USECS; @@ -1091,12 +1087,10 @@ err: if (IS_PF(cdev)) release_firmware(cdev->firmware); -#ifdef CONFIG_RFS_ACCEL if (IS_PF(cdev) && (cdev->num_hwfns == 1) && QED_LEADING_HWFN(cdev)->p_arfs_ptt) qed_ptt_release(QED_LEADING_HWFN(cdev), QED_LEADING_HWFN(cdev)->p_arfs_ptt); -#endif qed_iov_wq_stop(cdev, false); @@ -1111,11 +1105,9 @@ static int qed_slowpath_stop(struct qed_dev *cdev) qed_ll2_dealloc_if(cdev); if (IS_PF(cdev)) { -#ifdef CONFIG_RFS_ACCEL if (cdev->num_hwfns == 1) qed_ptt_release(QED_LEADING_HWFN(cdev), QED_LEADING_HWFN(cdev)->p_arfs_ptt); -#endif qed_free_stream_mem(cdev); if (IS_QED_ETH_IF(cdev)) qed_sriov_disable(cdev, true); diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 0a2475b65978..adb700512baa 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -447,16 +447,17 @@ struct qede_fastpath { #ifdef CONFIG_RFS_ACCEL int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, u16 rxq_index, u32 flow_id); +#define QEDE_SP_ARFS_CONFIG 4 +#define QEDE_SP_TASK_POLL_DELAY (5 * HZ) +#endif + void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr); void qede_poll_for_freeing_arfs_filters(struct qede_dev *edev); void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc); void qede_free_arfs(struct qede_dev *edev); int qede_alloc_arfs(struct qede_dev *edev); - -#define QEDE_SP_ARFS_CONFIG 4 -#define QEDE_SP_TASK_POLL_DELAY (5 * HZ) -#endif - +int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info); +int qede_del_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info); int qede_get_cls_rule_entry(struct qede_dev *edev, struct ethtool_rxnfc *cmd); int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info, u32 *rule_locs); diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index dd39dec62650..e31266df8fdd 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -1182,14 +1182,24 @@ static int qede_set_rss_flags(struct qede_dev *edev, struct ethtool_rxnfc *info) static int qede_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info) { struct qede_dev *edev = netdev_priv(dev); + int rc; switch (info->cmd) { case ETHTOOL_SRXFH: - return qede_set_rss_flags(edev, info); + rc = qede_set_rss_flags(edev, info); + break; + case ETHTOOL_SRXCLSRLINS: + rc = qede_add_cls_rule(edev, info); + break; + case ETHTOOL_SRXCLSRLDEL: + rc = qede_del_cls_rule(edev, info); + break; default: DP_INFO(edev, "Command parameters not supported\n"); - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; } + + return rc; } static u32 qede_get_rxfh_indir_size(struct net_device *dev) diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index a5e5d328e730..f79e36e4060a 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -75,6 +75,7 @@ struct qede_arfs_fltr_node { u16 next_rxq_id; bool filter_op; bool used; + u8 fw_rc; struct hlist_node node; }; @@ -92,7 +93,6 @@ struct qede_arfs { bool enable; }; -#ifdef CONFIG_RFS_ACCEL static void qede_configure_arfs_fltr(struct qede_dev *edev, struct qede_arfs_fltr_node *n, u16 rxq_id, bool add_fltr) @@ -122,11 +122,56 @@ qede_free_arfs_filter(struct qede_dev *edev, struct qede_arfs_fltr_node *fltr) kfree(fltr); } +static int +qede_enqueue_fltr_and_config_searcher(struct qede_dev *edev, + struct qede_arfs_fltr_node *fltr, + u16 bucket_idx) +{ + fltr->mapping = dma_map_single(&edev->pdev->dev, fltr->data, + fltr->buf_len, DMA_TO_DEVICE); + if (dma_mapping_error(&edev->pdev->dev, fltr->mapping)) { + DP_NOTICE(edev, "Failed to map DMA memory for rule\n"); + qede_free_arfs_filter(edev, fltr); + return -ENOMEM; + } + + INIT_HLIST_NODE(&fltr->node); + hlist_add_head(&fltr->node, + QEDE_ARFS_BUCKET_HEAD(edev, bucket_idx)); + edev->arfs->filter_count++; + + if (edev->arfs->filter_count == 1 && !edev->arfs->enable) { + edev->ops->configure_arfs_searcher(edev->cdev, true); + edev->arfs->enable = true; + } + + return 0; +} + +static void +qede_dequeue_fltr_and_config_searcher(struct qede_dev *edev, + struct qede_arfs_fltr_node *fltr) +{ + hlist_del(&fltr->node); + dma_unmap_single(&edev->pdev->dev, fltr->mapping, + fltr->buf_len, DMA_TO_DEVICE); + + qede_free_arfs_filter(edev, fltr); + edev->arfs->filter_count--; + + if (!edev->arfs->filter_count && edev->arfs->enable) { + edev->arfs->enable = false; + edev->ops->configure_arfs_searcher(edev->cdev, false); + } +} + void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc) { struct qede_arfs_fltr_node *fltr = filter; struct qede_dev *edev = dev; + fltr->fw_rc = fw_rc; + if (fw_rc) { DP_NOTICE(edev, "Failed arfs filter configuration fw_rc=%d, flow_id=%d, sw_id=%d, src_port=%d, dst_port=%d, rxq=%d\n", @@ -186,18 +231,17 @@ void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr) if ((!test_bit(QEDE_FLTR_VALID, &fltr->state) && !fltr->used) || free_fltr) { - hlist_del(&fltr->node); - dma_unmap_single(&edev->pdev->dev, - fltr->mapping, - fltr->buf_len, DMA_TO_DEVICE); - qede_free_arfs_filter(edev, fltr); - edev->arfs->filter_count--; + qede_dequeue_fltr_and_config_searcher(edev, + fltr); } else { - if ((rps_may_expire_flow(edev->ndev, - fltr->rxq_id, - fltr->flow_id, - fltr->sw_id) || del) && - !free_fltr) + bool flow_exp = false; +#ifdef CONFIG_RFS_ACCEL + flow_exp = rps_may_expire_flow(edev->ndev, + fltr->rxq_id, + fltr->flow_id, + fltr->sw_id); +#endif + if ((flow_exp || del) && !free_fltr) qede_configure_arfs_fltr(edev, fltr, fltr->rxq_id, false); @@ -214,10 +258,12 @@ void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr) edev->arfs->enable = false; edev->ops->configure_arfs_searcher(edev->cdev, false); } +#ifdef CONFIG_RFS_ACCEL } else { set_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags); schedule_delayed_work(&edev->sp_task, QEDE_SP_TASK_POLL_DELAY); +#endif } spin_unlock_bh(&edev->arfs->arfs_list_lock); @@ -259,25 +305,26 @@ int qede_alloc_arfs(struct qede_dev *edev) spin_lock_init(&edev->arfs->arfs_list_lock); for (i = 0; i <= QEDE_RFS_FLW_MASK; i++) - INIT_HLIST_HEAD(&edev->arfs->arfs_hl_head[i]); + INIT_HLIST_HEAD(QEDE_ARFS_BUCKET_HEAD(edev, i)); - edev->ndev->rx_cpu_rmap = alloc_irq_cpu_rmap(QEDE_RSS_COUNT(edev)); - if (!edev->ndev->rx_cpu_rmap) { + edev->arfs->arfs_fltr_bmap = vzalloc(BITS_TO_LONGS(QEDE_RFS_MAX_FLTR) * + sizeof(long)); + if (!edev->arfs->arfs_fltr_bmap) { vfree(edev->arfs); edev->arfs = NULL; return -ENOMEM; } - edev->arfs->arfs_fltr_bmap = vzalloc(BITS_TO_LONGS(QEDE_RFS_MAX_FLTR) * - sizeof(long)); - if (!edev->arfs->arfs_fltr_bmap) { - free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); - edev->ndev->rx_cpu_rmap = NULL; +#ifdef CONFIG_RFS_ACCEL + edev->ndev->rx_cpu_rmap = alloc_irq_cpu_rmap(QEDE_RSS_COUNT(edev)); + if (!edev->ndev->rx_cpu_rmap) { + vfree(edev->arfs->arfs_fltr_bmap); + edev->arfs->arfs_fltr_bmap = NULL; vfree(edev->arfs); edev->arfs = NULL; return -ENOMEM; } - +#endif return 0; } @@ -286,16 +333,19 @@ void qede_free_arfs(struct qede_dev *edev) if (!edev->arfs) return; +#ifdef CONFIG_RFS_ACCEL if (edev->ndev->rx_cpu_rmap) free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); edev->ndev->rx_cpu_rmap = NULL; +#endif vfree(edev->arfs->arfs_fltr_bmap); edev->arfs->arfs_fltr_bmap = NULL; vfree(edev->arfs); edev->arfs = NULL; } +#ifdef CONFIG_RFS_ACCEL static bool qede_compare_ip_addr(struct qede_arfs_fltr_node *tpos, const struct sk_buff *skb) { @@ -395,9 +445,8 @@ int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, spin_lock_bh(&edev->arfs->arfs_list_lock); - n = qede_arfs_htbl_key_search(&edev->arfs->arfs_hl_head[tbl_idx], + n = qede_arfs_htbl_key_search(QEDE_ARFS_BUCKET_HEAD(edev, tbl_idx), skb, ports[0], ports[1], ip_proto); - if (n) { /* Filter match */ n->next_rxq_id = rxq_index; @@ -449,23 +498,9 @@ int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, n->tuple.ip_proto = ip_proto; memcpy(n->data + ETH_HLEN, skb->data, skb_headlen(skb)); - n->mapping = dma_map_single(&edev->pdev->dev, n->data, - n->buf_len, DMA_TO_DEVICE); - if (dma_mapping_error(&edev->pdev->dev, n->mapping)) { - DP_NOTICE(edev, "Failed to map DMA memory for arfs\n"); - qede_free_arfs_filter(edev, n); - rc = -ENOMEM; + rc = qede_enqueue_fltr_and_config_searcher(edev, n, tbl_idx); + if (rc) goto ret_unlock; - } - - INIT_HLIST_NODE(&n->node); - hlist_add_head(&n->node, &edev->arfs->arfs_hl_head[tbl_idx]); - edev->arfs->filter_count++; - - if (edev->arfs->filter_count == 1 && !edev->arfs->enable) { - edev->ops->configure_arfs_searcher(edev->cdev, true); - edev->arfs->enable = true; - } qede_configure_arfs_fltr(edev, n, n->rxq_id, true); @@ -473,6 +508,7 @@ int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, set_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags); schedule_delayed_work(&edev->sp_task, 0); + return n->sw_id; ret_unlock: @@ -1277,6 +1313,38 @@ qede_get_arfs_fltr_by_loc(struct hlist_head *head, u32 location) return NULL; } +static bool +qede_compare_user_flow_ips(struct qede_arfs_fltr_node *tpos, + struct ethtool_rx_flow_spec *fsp, + __be16 proto) +{ + if (proto == htons(ETH_P_IP)) { + struct ethtool_tcpip4_spec *ip; + + ip = &fsp->h_u.tcp_ip4_spec; + + if (tpos->tuple.src_ipv4 == ip->ip4src && + tpos->tuple.dst_ipv4 == ip->ip4dst) + return true; + else + return false; + } else { + struct ethtool_tcpip6_spec *ip6; + struct in6_addr *src; + + ip6 = &fsp->h_u.tcp_ip6_spec; + src = &tpos->tuple.src_ipv6; + + if (!memcmp(src, &ip6->ip6src, sizeof(struct in6_addr)) && + !memcmp(&tpos->tuple.dst_ipv6, &ip6->ip6dst, + sizeof(struct in6_addr))) + return true; + else + return false; + } + return false; +} + int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info, u32 *rule_locs) { @@ -1366,6 +1434,225 @@ unlock: return rc; } +static int +qede_validate_and_check_flow_exist(struct qede_dev *edev, + struct ethtool_rx_flow_spec *fsp, + int *min_hlen) +{ + __be16 src_port = 0x0, dst_port = 0x0; + struct qede_arfs_fltr_node *fltr; + struct hlist_node *temp; + struct hlist_head *head; + __be16 eth_proto; + u8 ip_proto; + + if (fsp->location >= QEDE_RFS_MAX_FLTR || + fsp->ring_cookie >= QEDE_RSS_COUNT(edev)) + return -EINVAL; + + if (fsp->flow_type == TCP_V4_FLOW) { + *min_hlen += sizeof(struct iphdr) + + sizeof(struct tcphdr); + eth_proto = htons(ETH_P_IP); + ip_proto = IPPROTO_TCP; + } else if (fsp->flow_type == UDP_V4_FLOW) { + *min_hlen += sizeof(struct iphdr) + + sizeof(struct udphdr); + eth_proto = htons(ETH_P_IP); + ip_proto = IPPROTO_UDP; + } else if (fsp->flow_type == TCP_V6_FLOW) { + *min_hlen += sizeof(struct ipv6hdr) + + sizeof(struct tcphdr); + eth_proto = htons(ETH_P_IPV6); + ip_proto = IPPROTO_TCP; + } else if (fsp->flow_type == UDP_V6_FLOW) { + *min_hlen += sizeof(struct ipv6hdr) + + sizeof(struct udphdr); + eth_proto = htons(ETH_P_IPV6); + ip_proto = IPPROTO_UDP; + } else { + DP_NOTICE(edev, "Unsupported flow type = 0x%x\n", + fsp->flow_type); + return -EPROTONOSUPPORT; + } + + if (eth_proto == htons(ETH_P_IP)) { + src_port = fsp->h_u.tcp_ip4_spec.psrc; + dst_port = fsp->h_u.tcp_ip4_spec.pdst; + } else { + src_port = fsp->h_u.tcp_ip6_spec.psrc; + dst_port = fsp->h_u.tcp_ip6_spec.pdst; + } + + head = QEDE_ARFS_BUCKET_HEAD(edev, 0); + hlist_for_each_entry_safe(fltr, temp, head, node) { + if ((fltr->tuple.ip_proto == ip_proto && + fltr->tuple.eth_proto == eth_proto && + qede_compare_user_flow_ips(fltr, fsp, eth_proto) && + fltr->tuple.src_port == src_port && + fltr->tuple.dst_port == dst_port) || + fltr->sw_id == fsp->location) + return -EEXIST; + } + + return 0; +} + +static int +qede_poll_arfs_filter_config(struct qede_dev *edev, + struct qede_arfs_fltr_node *fltr) +{ + int count = QEDE_ARFS_POLL_COUNT; + + while (fltr->used && count) { + msleep(20); + count--; + } + + if (count == 0 || fltr->fw_rc) { + qede_dequeue_fltr_and_config_searcher(edev, fltr); + return -EIO; + } + + return fltr->fw_rc; +} + +int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info) +{ + struct ethtool_rx_flow_spec *fsp = &info->fs; + struct qede_arfs_fltr_node *n; + int min_hlen = ETH_HLEN, rc; + struct ethhdr *eth; + struct iphdr *ip; + __be16 *ports; + + __qede_lock(edev); + + if (!edev->arfs) { + rc = -EPERM; + goto unlock; + } + + rc = qede_validate_and_check_flow_exist(edev, fsp, &min_hlen); + if (rc) + goto unlock; + + n = kzalloc(sizeof(*n), GFP_KERNEL); + if (!n) { + rc = -ENOMEM; + goto unlock; + } + + n->data = kzalloc(min_hlen, GFP_KERNEL); + if (!n->data) { + kfree(n); + rc = -ENOMEM; + goto unlock; + } + + n->sw_id = fsp->location; + set_bit(n->sw_id, edev->arfs->arfs_fltr_bmap); + n->buf_len = min_hlen; + n->rxq_id = fsp->ring_cookie; + n->next_rxq_id = n->rxq_id; + eth = (struct ethhdr *)n->data; + + if (info->fs.flow_type == TCP_V4_FLOW || + info->fs.flow_type == UDP_V4_FLOW) { + ports = (__be16 *)(n->data + ETH_HLEN + + sizeof(struct iphdr)); + eth->h_proto = htons(ETH_P_IP); + n->tuple.eth_proto = htons(ETH_P_IP); + n->tuple.src_ipv4 = info->fs.h_u.tcp_ip4_spec.ip4src; + n->tuple.dst_ipv4 = info->fs.h_u.tcp_ip4_spec.ip4dst; + n->tuple.src_port = info->fs.h_u.tcp_ip4_spec.psrc; + n->tuple.dst_port = info->fs.h_u.tcp_ip4_spec.pdst; + ports[0] = n->tuple.src_port; + ports[1] = n->tuple.dst_port; + ip = (struct iphdr *)(n->data + ETH_HLEN); + ip->saddr = info->fs.h_u.tcp_ip4_spec.ip4src; + ip->daddr = info->fs.h_u.tcp_ip4_spec.ip4dst; + ip->version = 0x4; + ip->ihl = 0x5; + + if (info->fs.flow_type == TCP_V4_FLOW) { + n->tuple.ip_proto = IPPROTO_TCP; + ip->protocol = IPPROTO_TCP; + } else { + n->tuple.ip_proto = IPPROTO_UDP; + ip->protocol = IPPROTO_UDP; + } + ip->tot_len = cpu_to_be16(min_hlen - ETH_HLEN); + } else { + struct ipv6hdr *ip6; + + ip6 = (struct ipv6hdr *)(n->data + ETH_HLEN); + ports = (__be16 *)(n->data + ETH_HLEN + + sizeof(struct ipv6hdr)); + eth->h_proto = htons(ETH_P_IPV6); + n->tuple.eth_proto = htons(ETH_P_IPV6); + memcpy(&n->tuple.src_ipv6, &info->fs.h_u.tcp_ip6_spec.ip6src, + sizeof(struct in6_addr)); + memcpy(&n->tuple.dst_ipv6, &info->fs.h_u.tcp_ip6_spec.ip6dst, + sizeof(struct in6_addr)); + n->tuple.src_port = info->fs.h_u.tcp_ip6_spec.psrc; + n->tuple.dst_port = info->fs.h_u.tcp_ip6_spec.pdst; + ports[0] = n->tuple.src_port; + ports[1] = n->tuple.dst_port; + memcpy(&ip6->saddr, &n->tuple.src_ipv6, + sizeof(struct in6_addr)); + memcpy(&ip6->daddr, &n->tuple.dst_ipv6, + sizeof(struct in6_addr)); + ip6->version = 0x6; + + if (info->fs.flow_type == TCP_V6_FLOW) { + n->tuple.ip_proto = IPPROTO_TCP; + ip6->nexthdr = NEXTHDR_TCP; + ip6->payload_len = cpu_to_be16(sizeof(struct tcphdr)); + } else { + n->tuple.ip_proto = IPPROTO_UDP; + ip6->nexthdr = NEXTHDR_UDP; + ip6->payload_len = cpu_to_be16(sizeof(struct udphdr)); + } + } + + rc = qede_enqueue_fltr_and_config_searcher(edev, n, 0); + if (rc) + goto unlock; + + qede_configure_arfs_fltr(edev, n, n->rxq_id, true); + rc = qede_poll_arfs_filter_config(edev, n); +unlock: + __qede_unlock(edev); + return rc; +} + +int qede_del_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info) +{ + struct ethtool_rx_flow_spec *fsp = &info->fs; + struct qede_arfs_fltr_node *fltr = NULL; + int rc = -EPERM; + + __qede_lock(edev); + if (!edev->arfs) + goto unlock; + + fltr = qede_get_arfs_fltr_by_loc(QEDE_ARFS_BUCKET_HEAD(edev, 0), + fsp->location); + if (!fltr) + goto unlock; + + qede_configure_arfs_fltr(edev, fltr, fltr->rxq_id, false); + + rc = qede_poll_arfs_filter_config(edev, fltr); + if (rc == 0) + qede_dequeue_fltr_and_config_searcher(edev, fltr); + +unlock: + __qede_unlock(edev); + return rc; +} + int qede_get_arfs_filter_count(struct qede_dev *edev) { int count = 0; diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 06ca13dd9ddb..e5ee9f274a71 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -873,9 +873,7 @@ static void qede_update_pf_params(struct qed_dev *cdev) */ pf_params.eth_pf_params.num_vf_cons = 48; -#ifdef CONFIG_RFS_ACCEL pf_params.eth_pf_params.num_arfs_filters = QEDE_RFS_MAX_FLTR; -#endif qed_ops->common->update_pf_params(cdev, &pf_params); } @@ -1984,12 +1982,12 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode, qede_vlan_mark_nonconfigured(edev); edev->ops->fastpath_stop(edev->cdev); -#ifdef CONFIG_RFS_ACCEL + if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) { qede_poll_for_freeing_arfs_filters(edev); qede_free_arfs(edev); } -#endif + /* Release the interrupts */ qede_sync_free_irqs(edev); edev->ops->common->set_fp_int(edev->cdev, 0); @@ -2041,13 +2039,12 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode, if (rc) goto err2; -#ifdef CONFIG_RFS_ACCEL if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) { rc = qede_alloc_arfs(edev); if (rc) DP_NOTICE(edev, "aRFS memory allocation failed\n"); } -#endif + qede_napi_add_enable(edev); DP_INFO(edev, "Napi added and enabled\n"); -- cgit v1.2.3 From 645874e5807ae5ffa09fba2ccd23f01e4eb16d58 Mon Sep 17 00:00:00 2001 From: Sudarsana Reddy Kalluru Date: Wed, 26 Jul 2017 06:07:11 -0700 Subject: qed: Add support for Energy efficient ethernet. The patch adds required driver support for reading/configuring the Energy Efficient Ethernet (EEE) parameters. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 72 ++++++++++++++++++++++++++++-- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 49 ++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_main.c | 19 ++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.c | 66 +++++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.h | 37 +++++++++++++-- include/linux/qed/qed_if.h | 20 +++++++++ 6 files changed, 256 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 6c87bed13bd2..f545607100e4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1684,6 +1684,8 @@ int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params) "Load request was sent. Load code: 0x%x\n", load_code); + qed_mcp_set_capabilities(p_hwfn, p_hwfn->p_main_ptt); + qed_reset_mb_shadow(p_hwfn, p_hwfn->p_main_ptt); p_hwfn->first_on_engine = (load_code == @@ -2472,6 +2474,7 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { u32 port_cfg_addr, link_temp, nvm_cfg_addr, device_capabilities; u32 nvm_cfg1_offset, mf_mode, addr, generic_cont0, core_cfg; + struct qed_mcp_link_capabilities *p_caps; struct qed_mcp_link_params *link; /* Read global nvm_cfg address */ @@ -2534,6 +2537,7 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) /* Read default link configuration */ link = &p_hwfn->mcp_info->link_input; + p_caps = &p_hwfn->mcp_info->link_capabilities; port_cfg_addr = MCP_REG_SCRATCH + nvm_cfg1_offset + offsetof(struct nvm_cfg1, port[MFW_PORT(p_hwfn)]); link_temp = qed_rd(p_hwfn, p_ptt, @@ -2588,10 +2592,45 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) NVM_CFG1_PORT_DRV_FLOW_CONTROL_TX); link->loopback_mode = 0; - DP_VERBOSE(p_hwfn, NETIF_MSG_LINK, - "Read default link: Speed 0x%08x, Adv. Speed 0x%08x, AN: 0x%02x, PAUSE AN: 0x%02x\n", - link->speed.forced_speed, link->speed.advertised_speeds, - link->speed.autoneg, link->pause.autoneg); + if (p_hwfn->mcp_info->capabilities & FW_MB_PARAM_FEATURE_SUPPORT_EEE) { + link_temp = qed_rd(p_hwfn, p_ptt, port_cfg_addr + + offsetof(struct nvm_cfg1_port, ext_phy)); + link_temp &= NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_MASK; + link_temp >>= NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_OFFSET; + p_caps->default_eee = QED_MCP_EEE_ENABLED; + link->eee.enable = true; + switch (link_temp) { + case NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_DISABLED: + p_caps->default_eee = QED_MCP_EEE_DISABLED; + link->eee.enable = false; + break; + case NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_BALANCED: + p_caps->eee_lpi_timer = EEE_TX_TIMER_USEC_BALANCED_TIME; + break; + case NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_AGGRESSIVE: + p_caps->eee_lpi_timer = + EEE_TX_TIMER_USEC_AGGRESSIVE_TIME; + break; + case NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_LOW_LATENCY: + p_caps->eee_lpi_timer = EEE_TX_TIMER_USEC_LATENCY_TIME; + break; + } + + link->eee.tx_lpi_timer = p_caps->eee_lpi_timer; + link->eee.tx_lpi_enable = link->eee.enable; + link->eee.adv_caps = QED_EEE_1G_ADV | QED_EEE_10G_ADV; + } else { + p_caps->default_eee = QED_MCP_EEE_UNSUPPORTED; + } + + DP_VERBOSE(p_hwfn, + NETIF_MSG_LINK, + "Read default link: Speed 0x%08x, Adv. Speed 0x%08x, AN: 0x%02x, PAUSE AN: 0x%02x EEE: %02x [%08x usec]\n", + link->speed.forced_speed, + link->speed.advertised_speeds, + link->speed.autoneg, + link->pause.autoneg, + p_caps->default_eee, p_caps->eee_lpi_timer); /* Read Multi-function information from shmem */ addr = MCP_REG_SCRATCH + nvm_cfg1_offset + @@ -2751,6 +2790,27 @@ static void qed_hw_info_port_num(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) qed_hw_info_port_num_ah(p_hwfn, p_ptt); } +static void qed_get_eee_caps(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_mcp_link_capabilities *p_caps; + u32 eee_status; + + p_caps = &p_hwfn->mcp_info->link_capabilities; + if (p_caps->default_eee == QED_MCP_EEE_UNSUPPORTED) + return; + + p_caps->eee_speed_caps = 0; + eee_status = qed_rd(p_hwfn, p_ptt, p_hwfn->mcp_info->port_addr + + offsetof(struct public_port, eee_status)); + eee_status = (eee_status & EEE_SUPPORTED_SPEED_MASK) >> + EEE_SUPPORTED_SPEED_OFFSET; + + if (eee_status & EEE_1G_SUPPORTED) + p_caps->eee_speed_caps |= QED_EEE_1G_ADV; + if (eee_status & EEE_10G_ADV) + p_caps->eee_speed_caps |= QED_EEE_10G_ADV; +} + static int qed_get_hw_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, @@ -2767,6 +2827,8 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, qed_hw_info_port_num(p_hwfn, p_ptt); + qed_mcp_get_capabilities(p_hwfn, p_ptt); + qed_hw_get_nvm_info(p_hwfn, p_ptt); rc = qed_int_igu_read_cam(p_hwfn, p_ptt); @@ -2785,6 +2847,8 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, p_hwfn->mcp_info->func_info.ovlan; qed_mcp_cmd_port_init(p_hwfn, p_ptt); + + qed_get_eee_caps(p_hwfn, p_ptt); } if (qed_mcp_is_init(p_hwfn)) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 31fb0bffa098..3427fe7049b5 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -10825,6 +10825,17 @@ struct eth_phy_cfg { #define ETH_LOOPBACK_EXT (3) #define ETH_LOOPBACK_MAC (4) + u32 eee_cfg; +#define EEE_CFG_EEE_ENABLED BIT(0) +#define EEE_CFG_TX_LPI BIT(1) +#define EEE_CFG_ADV_SPEED_1G BIT(2) +#define EEE_CFG_ADV_SPEED_10G BIT(3) +#define EEE_TX_TIMER_USEC_MASK (0xfffffff0) +#define EEE_TX_TIMER_USEC_OFFSET 4 +#define EEE_TX_TIMER_USEC_BALANCED_TIME (0xa00) +#define EEE_TX_TIMER_USEC_AGGRESSIVE_TIME (0x100) +#define EEE_TX_TIMER_USEC_LATENCY_TIME (0x6000) + u32 feature_config_flags; #define ETH_EEE_MODE_ADV_LPI (1 << 0) }; @@ -11242,6 +11253,25 @@ struct public_port { u32 wol_pkt_len; u32 wol_pkt_details; struct dcb_dscp_map dcb_dscp_map; + + u32 eee_status; +#define EEE_ACTIVE_BIT BIT(0) +#define EEE_LD_ADV_STATUS_MASK 0x000000f0 +#define EEE_LD_ADV_STATUS_OFFSET 4 +#define EEE_1G_ADV BIT(1) +#define EEE_10G_ADV BIT(2) +#define EEE_LP_ADV_STATUS_MASK 0x00000f00 +#define EEE_LP_ADV_STATUS_OFFSET 8 +#define EEE_SUPPORTED_SPEED_MASK 0x0000f000 +#define EEE_SUPPORTED_SPEED_OFFSET 12 +#define EEE_1G_SUPPORTED BIT(1) +#define EEE_10G_SUPPORTED BIT(2) + + u32 eee_remote; +#define EEE_REMOTE_TW_TX_MASK 0x0000ffff +#define EEE_REMOTE_TW_TX_OFFSET 0 +#define EEE_REMOTE_TW_RX_MASK 0xffff0000 +#define EEE_REMOTE_TW_RX_OFFSET 16 }; struct public_func { @@ -11570,6 +11600,9 @@ struct public_drv_mb { #define DRV_MSG_CODE_GET_PF_RDMA_PROTOCOL 0x002b0000 #define DRV_MSG_CODE_OS_WOL 0x002e0000 +#define DRV_MSG_CODE_FEATURE_SUPPORT 0x00300000 +#define DRV_MSG_CODE_GET_MFW_FEATURE_SUPPORT 0x00310000 + #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 drv_mb_param; @@ -11653,6 +11686,10 @@ struct public_drv_mb { #define DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_SHIFT 8 #define DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_MASK 0x0000FF00 +#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_MASK 0x0000FFFF +#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_OFFSET 0 +#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_EEE 0x00000002 + u32 fw_mb_header; #define FW_MSG_CODE_MASK 0xffff0000 #define FW_MSG_CODE_UNSUPPORTED 0x00000000 @@ -11696,6 +11733,9 @@ struct public_drv_mb { #define FW_MB_PARAM_GET_PF_RDMA_IWARP 0x2 #define FW_MB_PARAM_GET_PF_RDMA_BOTH 0x3 +/* get MFW feature support response */ +#define FW_MB_PARAM_FEATURE_SUPPORT_EEE 0x00000002 + #define FW_MB_PARAM_LOAD_DONE_DID_EFUSE_ERROR (1 << 0) u32 drv_pulse_mb; @@ -11891,7 +11931,16 @@ struct nvm_cfg1_port { #define NVM_CFG1_PORT_DRV_FLOW_CONTROL_TX 0x4 u32 phy_cfg; u32 mgmt_traffic; + u32 ext_phy; + /* EEE power saving mode */ +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_MASK 0x00FF0000 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_OFFSET 16 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_DISABLED 0x0 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_BALANCED 0x1 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_AGGRESSIVE 0x2 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_LOW_LATENCY 0x3 + u32 mba_cfg1; u32 mba_cfg2; u32 vf_cfg; diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 1bddf9372fc9..0a06683abfa0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -1297,6 +1297,10 @@ static int qed_set_link(struct qed_dev *cdev, struct qed_link_params *params) } } + if (params->override_flags & QED_LINK_OVERRIDE_EEE_CONFIG) + memcpy(&link_params->eee, ¶ms->eee, + sizeof(link_params->eee)); + rc = qed_mcp_set_link(hwfn, ptt, params->link_up); qed_ptt_release(hwfn, ptt); @@ -1483,6 +1487,21 @@ static void qed_fill_link(struct qed_hwfn *hwfn, if (link.partner_adv_pause == QED_LINK_PARTNER_ASYMMETRIC_PAUSE || link.partner_adv_pause == QED_LINK_PARTNER_BOTH_PAUSE) if_link->lp_caps |= QED_LM_Asym_Pause_BIT; + + if (link_caps.default_eee == QED_MCP_EEE_UNSUPPORTED) { + if_link->eee_supported = false; + } else { + if_link->eee_supported = true; + if_link->eee_active = link.eee_active; + if_link->sup_caps = link_caps.eee_speed_caps; + /* MFW clears adv_caps on eee disable; use configured value */ + if_link->eee.adv_caps = link.eee_adv_caps ? link.eee_adv_caps : + params.eee.adv_caps; + if_link->eee.lp_adv_caps = link.eee_lp_adv_caps; + if_link->eee.enable = params.eee.enable; + if_link->eee.tx_lpi_enable = params.eee.tx_lpi_enable; + if_link->eee.tx_lpi_timer = params.eee.tx_lpi_timer; + } } static void qed_get_current_link(struct qed_dev *cdev, diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 9da91045d167..c1ecce6b9141 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -1097,6 +1097,31 @@ static void qed_mcp_handle_transceiver_change(struct qed_hwfn *p_hwfn, DP_NOTICE(p_hwfn, "Transceiver is unplugged.\n"); } +static void qed_mcp_read_eee_config(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_mcp_link_state *p_link) +{ + u32 eee_status, val; + + p_link->eee_adv_caps = 0; + p_link->eee_lp_adv_caps = 0; + eee_status = qed_rd(p_hwfn, + p_ptt, + p_hwfn->mcp_info->port_addr + + offsetof(struct public_port, eee_status)); + p_link->eee_active = !!(eee_status & EEE_ACTIVE_BIT); + val = (eee_status & EEE_LD_ADV_STATUS_MASK) >> EEE_LD_ADV_STATUS_OFFSET; + if (val & EEE_1G_ADV) + p_link->eee_adv_caps |= QED_EEE_1G_ADV; + if (val & EEE_10G_ADV) + p_link->eee_adv_caps |= QED_EEE_10G_ADV; + val = (eee_status & EEE_LP_ADV_STATUS_MASK) >> EEE_LP_ADV_STATUS_OFFSET; + if (val & EEE_1G_ADV) + p_link->eee_lp_adv_caps |= QED_EEE_1G_ADV; + if (val & EEE_10G_ADV) + p_link->eee_lp_adv_caps |= QED_EEE_10G_ADV; +} + static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, bool b_reset) { @@ -1228,6 +1253,9 @@ static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn, p_link->sfp_tx_fault = !!(status & LINK_STATUS_SFP_TX_FAULT); + if (p_hwfn->mcp_info->capabilities & FW_MB_PARAM_FEATURE_SUPPORT_EEE) + qed_mcp_read_eee_config(p_hwfn, p_ptt, p_link); + qed_link_update(p_hwfn); out: spin_unlock_bh(&p_hwfn->mcp_info->link_lock); @@ -1251,6 +1279,19 @@ int qed_mcp_set_link(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, bool b_up) phy_cfg.pause |= (params->pause.forced_tx) ? ETH_PAUSE_TX : 0; phy_cfg.adv_speed = params->speed.advertised_speeds; phy_cfg.loopback_mode = params->loopback_mode; + if (p_hwfn->mcp_info->capabilities & FW_MB_PARAM_FEATURE_SUPPORT_EEE) { + if (params->eee.enable) + phy_cfg.eee_cfg |= EEE_CFG_EEE_ENABLED; + if (params->eee.tx_lpi_enable) + phy_cfg.eee_cfg |= EEE_CFG_TX_LPI; + if (params->eee.adv_caps & QED_EEE_1G_ADV) + phy_cfg.eee_cfg |= EEE_CFG_ADV_SPEED_1G; + if (params->eee.adv_caps & QED_EEE_10G_ADV) + phy_cfg.eee_cfg |= EEE_CFG_ADV_SPEED_10G; + phy_cfg.eee_cfg |= (params->eee.tx_lpi_timer << + EEE_TX_TIMER_USEC_OFFSET) & + EEE_TX_TIMER_USEC_MASK; + } p_hwfn->b_drv_link_init = b_up; @@ -2822,3 +2863,28 @@ void qed_mcp_resc_lock_default_init(struct qed_resc_lock_params *p_lock, p_unlock->resource = resource; } } + +int qed_mcp_get_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + u32 mcp_resp; + int rc; + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_GET_MFW_FEATURE_SUPPORT, + 0, &mcp_resp, &p_hwfn->mcp_info->capabilities); + if (!rc) + DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_PROBE), + "MFW supported features: %08x\n", + p_hwfn->mcp_info->capabilities); + + return rc; +} + +int qed_mcp_set_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + u32 mcp_resp, mcp_param, features; + + features = DRV_MB_PARAM_FEATURE_SUPPORT_PORT_EEE; + + return qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_FEATURE_SUPPORT, + features, &mcp_resp, &mcp_param); +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index af03b3651411..c7ec2395d1ce 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -53,15 +53,25 @@ struct qed_mcp_link_pause_params { bool forced_tx; }; +enum qed_mcp_eee_mode { + QED_MCP_EEE_DISABLED, + QED_MCP_EEE_ENABLED, + QED_MCP_EEE_UNSUPPORTED +}; + struct qed_mcp_link_params { - struct qed_mcp_link_speed_params speed; - struct qed_mcp_link_pause_params pause; - u32 loopback_mode; + struct qed_mcp_link_speed_params speed; + struct qed_mcp_link_pause_params pause; + u32 loopback_mode; + struct qed_link_eee_params eee; }; struct qed_mcp_link_capabilities { u32 speed_capabilities; bool default_speed_autoneg; + enum qed_mcp_eee_mode default_eee; + u32 eee_lpi_timer; + u8 eee_speed_caps; }; struct qed_mcp_link_state { @@ -102,6 +112,9 @@ struct qed_mcp_link_state { u8 partner_adv_pause; bool sfp_tx_fault; + bool eee_active; + u8 eee_adv_caps; + u8 eee_lp_adv_caps; }; struct qed_mcp_function_info { @@ -546,6 +559,9 @@ struct qed_mcp_info { u8 *mfw_mb_shadow; u16 mfw_mb_length; u32 mcp_hist; + + /* Capabilties negotiated with the MFW */ + u32 capabilities; }; struct qed_mcp_mb_params { @@ -925,5 +941,20 @@ void qed_mcp_resc_lock_default_init(struct qed_resc_lock_params *p_lock, struct qed_resc_unlock_params *p_unlock, enum qed_resc_lock resource, bool b_is_permanent); +/** + * @brief Learn of supported MFW features; To be done during early init + * + * @param p_hwfn + * @param p_ptt + */ +int qed_mcp_get_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); +/** + * @brief Inform MFW of set of features supported by driver. Should be done + * inside the content of the LOAD_REQ. + * + * @param p_hwfn + * @param p_ptt + */ +int qed_mcp_set_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); #endif diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index ef39c7f40ae6..9f3276271b02 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -161,6 +161,18 @@ enum qed_nvm_images { QED_NVM_IMAGE_FCOE_CFG, }; +struct qed_link_eee_params { + u32 tx_lpi_timer; +#define QED_EEE_1G_ADV BIT(0) +#define QED_EEE_10G_ADV BIT(1) + + /* Capabilities are represented using QED_EEE_*_ADV values */ + u8 adv_caps; + u8 lp_adv_caps; + bool enable; + bool tx_lpi_enable; +}; + enum qed_led_mode { QED_LED_MODE_OFF, QED_LED_MODE_ON, @@ -408,6 +420,7 @@ struct qed_link_params { #define QED_LINK_OVERRIDE_SPEED_FORCED_SPEED BIT(2) #define QED_LINK_OVERRIDE_PAUSE_CONFIG BIT(3) #define QED_LINK_OVERRIDE_LOOPBACK_MODE BIT(4) +#define QED_LINK_OVERRIDE_EEE_CONFIG BIT(5) u32 override_flags; bool autoneg; u32 adv_speeds; @@ -422,6 +435,7 @@ struct qed_link_params { #define QED_LINK_LOOPBACK_EXT BIT(3) #define QED_LINK_LOOPBACK_MAC BIT(4) u32 loopback_mode; + struct qed_link_eee_params eee; }; struct qed_link_output { @@ -437,6 +451,12 @@ struct qed_link_output { u8 port; /* In PORT defs */ bool autoneg; u32 pause_config; + + /* EEE - capability & param */ + bool eee_supported; + bool eee_active; + u8 sup_caps; + struct qed_link_eee_params eee; }; struct qed_probe_params { -- cgit v1.2.3 From c3dc48f78638a4810678e64ddebf0839de8ea07e Mon Sep 17 00:00:00 2001 From: Sudarsana Reddy Kalluru Date: Wed, 26 Jul 2017 06:07:12 -0700 Subject: qede: Add ethtool support for Energy efficient ethernet. The patch adds ethtool callback implementations for querying/configuring the Energy Efficient Ethernet (EEE) parameters. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 84 +++++++++++++++++++++++++ 1 file changed, 84 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index e31266df8fdd..55fa2ef19d8a 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -1631,6 +1631,87 @@ static int qede_get_tunable(struct net_device *dev, return 0; } +static int qede_get_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct qede_dev *edev = netdev_priv(dev); + struct qed_link_output current_link; + + memset(¤t_link, 0, sizeof(current_link)); + edev->ops->common->get_link(edev->cdev, ¤t_link); + + if (!current_link.eee_supported) { + DP_INFO(edev, "EEE is not supported\n"); + return -EOPNOTSUPP; + } + + if (current_link.eee.adv_caps & QED_EEE_1G_ADV) + edata->advertised = ADVERTISED_1000baseT_Full; + if (current_link.eee.adv_caps & QED_EEE_10G_ADV) + edata->advertised |= ADVERTISED_10000baseT_Full; + if (current_link.sup_caps & QED_EEE_1G_ADV) + edata->supported = ADVERTISED_1000baseT_Full; + if (current_link.sup_caps & QED_EEE_10G_ADV) + edata->supported |= ADVERTISED_10000baseT_Full; + if (current_link.eee.lp_adv_caps & QED_EEE_1G_ADV) + edata->lp_advertised = ADVERTISED_1000baseT_Full; + if (current_link.eee.lp_adv_caps & QED_EEE_10G_ADV) + edata->lp_advertised |= ADVERTISED_10000baseT_Full; + + edata->tx_lpi_timer = current_link.eee.tx_lpi_timer; + edata->eee_enabled = current_link.eee.enable; + edata->tx_lpi_enabled = current_link.eee.tx_lpi_enable; + edata->eee_active = current_link.eee_active; + + return 0; +} + +static int qede_set_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct qede_dev *edev = netdev_priv(dev); + struct qed_link_output current_link; + struct qed_link_params params; + + if (!edev->ops->common->can_link_change(edev->cdev)) { + DP_INFO(edev, "Link settings are not allowed to be changed\n"); + return -EOPNOTSUPP; + } + + memset(¤t_link, 0, sizeof(current_link)); + edev->ops->common->get_link(edev->cdev, ¤t_link); + + if (!current_link.eee_supported) { + DP_INFO(edev, "EEE is not supported\n"); + return -EOPNOTSUPP; + } + + memset(¶ms, 0, sizeof(params)); + params.override_flags |= QED_LINK_OVERRIDE_EEE_CONFIG; + + if (!(edata->advertised & (ADVERTISED_1000baseT_Full | + ADVERTISED_10000baseT_Full)) || + ((edata->advertised & (ADVERTISED_1000baseT_Full | + ADVERTISED_10000baseT_Full)) != + edata->advertised)) { + DP_VERBOSE(edev, QED_MSG_DEBUG, + "Invalid advertised capabilities %d\n", + edata->advertised); + return -EINVAL; + } + + if (edata->advertised & ADVERTISED_1000baseT_Full) + params.eee.adv_caps = QED_EEE_1G_ADV; + if (edata->advertised & ADVERTISED_10000baseT_Full) + params.eee.adv_caps |= QED_EEE_10G_ADV; + params.eee.enable = edata->eee_enabled; + params.eee.tx_lpi_enable = edata->tx_lpi_enabled; + params.eee.tx_lpi_timer = edata->tx_lpi_timer; + + params.link_up = true; + edev->ops->common->set_link(edev->cdev, ¶ms); + + return 0; +} + static const struct ethtool_ops qede_ethtool_ops = { .get_link_ksettings = qede_get_link_ksettings, .set_link_ksettings = qede_set_link_ksettings, @@ -1664,6 +1745,9 @@ static const struct ethtool_ops qede_ethtool_ops = { .get_channels = qede_get_channels, .set_channels = qede_set_channels, .self_test = qede_self_test, + .get_eee = qede_get_eee, + .set_eee = qede_set_eee, + .get_tunable = qede_get_tunable, .set_tunable = qede_set_tunable, }; -- cgit v1.2.3 From 477f2d1460a636abd08f03eafabe0c51366fa5de Mon Sep 17 00:00:00 2001 From: Rahul Verma Date: Wed, 26 Jul 2017 06:07:13 -0700 Subject: qed: Add support for vf coalesce configuration. This patch add the ethtool support to set RX/Tx coalesce value to the VF associated Rx/Tx queues. Signed-off-by: Rahul Verma Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 73 +++++++++++++++------- drivers/net/ethernet/qlogic/qed/qed_dev_api.h | 41 ++++-------- drivers/net/ethernet/qlogic/qed/qed_l2.h | 7 +++ drivers/net/ethernet/qlogic/qed/qed_main.c | 24 +------ drivers/net/ethernet/qlogic/qed/qed_sriov.c | 83 +++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_vf.c | 44 +++++++++++++ drivers/net/ethernet/qlogic/qed/qed_vf.h | 24 ++++++- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 38 ++++++++--- include/linux/qed/qed_if.h | 4 +- 9 files changed, 251 insertions(+), 87 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index f545607100e4..58a689fb04db 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -3694,7 +3694,7 @@ static int qed_set_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, } p_coal_timeset = p_eth_qzone; - memset(p_coal_timeset, 0, eth_qzone_size); + memset(p_eth_qzone, 0, eth_qzone_size); SET_FIELD(p_coal_timeset->value, COALESCING_TIMESET_TIMESET, timeset); SET_FIELD(p_coal_timeset->value, COALESCING_TIMESET_VALID, 1); qed_memcpy_to(p_hwfn, p_ptt, hw_addr, p_eth_qzone, eth_qzone_size); @@ -3702,12 +3702,46 @@ static int qed_set_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, return 0; } -int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u16 coalesce, u16 qid, u16 sb_id) +int qed_set_queue_coalesce(u16 rx_coal, u16 tx_coal, void *p_handle) +{ + struct qed_queue_cid *p_cid = p_handle; + struct qed_hwfn *p_hwfn; + struct qed_ptt *p_ptt; + int rc = 0; + + p_hwfn = p_cid->p_owner; + + if (IS_VF(p_hwfn->cdev)) + return qed_vf_pf_set_coalesce(p_hwfn, rx_coal, tx_coal, p_cid); + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EAGAIN; + + if (rx_coal) { + rc = qed_set_rxq_coalesce(p_hwfn, p_ptt, rx_coal, p_cid); + if (rc) + goto out; + p_hwfn->cdev->rx_coalesce_usecs = rx_coal; + } + + if (tx_coal) { + rc = qed_set_txq_coalesce(p_hwfn, p_ptt, tx_coal, p_cid); + if (rc) + goto out; + p_hwfn->cdev->tx_coalesce_usecs = tx_coal; + } +out: + qed_ptt_release(p_hwfn, p_ptt); + return rc; +} + +int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 coalesce, struct qed_queue_cid *p_cid) { struct ustorm_eth_queue_zone eth_qzone; u8 timeset, timer_res; - u16 fw_qid = 0; u32 address; int rc; @@ -3724,32 +3758,29 @@ int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, } timeset = (u8)(coalesce >> timer_res); - rc = qed_fw_l2_queue(p_hwfn, qid, &fw_qid); - if (rc) - return rc; - - rc = qed_int_set_timer_res(p_hwfn, p_ptt, timer_res, sb_id, false); + rc = qed_int_set_timer_res(p_hwfn, p_ptt, timer_res, + p_cid->sb_igu_id, false); if (rc) goto out; - address = BAR0_MAP_REG_USDM_RAM + USTORM_ETH_QUEUE_ZONE_OFFSET(fw_qid); + address = BAR0_MAP_REG_USDM_RAM + + USTORM_ETH_QUEUE_ZONE_OFFSET(p_cid->abs.queue_id); rc = qed_set_coalesce(p_hwfn, p_ptt, address, ð_qzone, sizeof(struct ustorm_eth_queue_zone), timeset); if (rc) goto out; - p_hwfn->cdev->rx_coalesce_usecs = coalesce; out: return rc; } -int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u16 coalesce, u16 qid, u16 sb_id) +int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 coalesce, struct qed_queue_cid *p_cid) { struct xstorm_eth_queue_zone eth_qzone; u8 timeset, timer_res; - u16 fw_qid = 0; u32 address; int rc; @@ -3766,22 +3797,16 @@ int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, } timeset = (u8)(coalesce >> timer_res); - rc = qed_fw_l2_queue(p_hwfn, qid, &fw_qid); - if (rc) - return rc; - - rc = qed_int_set_timer_res(p_hwfn, p_ptt, timer_res, sb_id, true); + rc = qed_int_set_timer_res(p_hwfn, p_ptt, timer_res, + p_cid->sb_igu_id, true); if (rc) goto out; - address = BAR0_MAP_REG_XSDM_RAM + XSTORM_ETH_QUEUE_ZONE_OFFSET(fw_qid); + address = BAR0_MAP_REG_XSDM_RAM + + XSTORM_ETH_QUEUE_ZONE_OFFSET(p_cid->abs.queue_id); rc = qed_set_coalesce(p_hwfn, p_ptt, address, ð_qzone, sizeof(struct xstorm_eth_queue_zone), timeset); - if (rc) - goto out; - - p_hwfn->cdev->tx_coalesce_usecs = coalesce; out: return rc; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index 1f1df1bf127c..e6b3c83c5db8 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -443,38 +443,23 @@ int qed_final_cleanup(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u16 id, bool is_vf); /** - * @brief qed_set_rxq_coalesce - Configure coalesce parameters for an Rx queue - * The fact that we can configure coalescing to up to 511, but on varying - * accuracy [the bigger the value the less accurate] up to a mistake of 3usec - * for the highest values. - * - * @param p_hwfn - * @param p_ptt - * @param coalesce - Coalesce value in micro seconds. - * @param qid - Queue index. - * @param qid - SB Id - * - * @return int - */ -int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u16 coalesce, u16 qid, u16 sb_id); - -/** - * @brief qed_set_txq_coalesce - Configure coalesce parameters for a Tx queue - * While the API allows setting coalescing per-qid, all tx queues sharing a - * SB should be in same range [i.e., either 0-0x7f, 0x80-0xff or 0x100-0x1ff] + * @brief qed_set_queue_coalesce - Configure coalesce parameters for Rx or + * Tx queue. We can configure coalescing to up to 511, but on + * varying accuracy [the bigger the value the less accurate] up to a mistake + * of 3usec for the highest values. + * While the API allows setting coalescing per-qid, all queues sharing a SB + * should be in same range [i.e., either 0-0x7f, 0x80-0xff or 0x100-0x1ff] * otherwise configuration would break. * - * @param p_hwfn - * @param p_ptt - * @param coalesce - Coalesce value in micro seconds. - * @param qid - Queue index. - * @param qid - SB Id + * @param rx_coal - Rx Coalesce value in micro seconds. + * @param tx_coal - TX Coalesce value in micro seconds. + * @param p_handle * * @return int - */ -int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u16 coalesce, u16 qid, u16 sb_id); + **/ +int +qed_set_queue_coalesce(u16 rx_coal, u16 tx_coal, void *p_handle); + const char *qed_hw_get_resc_name(enum qed_resources res_id); #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.h b/drivers/net/ethernet/qlogic/qed/qed_l2.h index f8f09aadced7..60ea72ce3e2c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.h +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.h @@ -400,4 +400,11 @@ qed_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, u8 qed_mcast_bin_from_mac(u8 *mac); +int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 coalesce, struct qed_queue_cid *p_cid); + +int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 coalesce, struct qed_queue_cid *p_cid); #endif /* _QED_L2_H */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 0a06683abfa0..448810a235b8 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -1575,29 +1575,9 @@ static void qed_get_coalesce(struct qed_dev *cdev, u16 *rx_coal, u16 *tx_coal) } static int qed_set_coalesce(struct qed_dev *cdev, u16 rx_coal, u16 tx_coal, - u16 qid, u16 sb_id) + void *handle) { - struct qed_hwfn *hwfn; - struct qed_ptt *ptt; - int hwfn_index; - int status = 0; - - hwfn_index = qid % cdev->num_hwfns; - hwfn = &cdev->hwfns[hwfn_index]; - ptt = qed_ptt_acquire(hwfn); - if (!ptt) - return -EAGAIN; - - status = qed_set_rxq_coalesce(hwfn, ptt, rx_coal, - qid / cdev->num_hwfns, sb_id); - if (status) - goto out; - status = qed_set_txq_coalesce(hwfn, ptt, tx_coal, - qid / cdev->num_hwfns, sb_id); -out: - qed_ptt_release(hwfn, ptt); - - return status; + return qed_set_queue_coalesce(rx_coal, tx_coal, handle); } static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 2cfd3bd9a031..5feef783623b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3400,6 +3400,86 @@ static void qed_iov_vf_mbx_release(struct qed_hwfn *p_hwfn, length, status); } +static void qed_iov_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + struct vfpf_update_coalesce *req; + u8 status = PFVF_STATUS_FAILURE; + struct qed_queue_cid *p_cid; + u16 rx_coal, tx_coal; + int rc = 0, i; + u16 qid; + + req = &mbx->req_virt->update_coalesce; + + rx_coal = req->rx_coal; + tx_coal = req->tx_coal; + qid = req->qid; + + if (!qed_iov_validate_rxq(p_hwfn, vf, qid, + QED_IOV_VALIDATE_Q_ENABLE) && rx_coal) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d]: Invalid Rx queue_id = %d\n", + vf->abs_vf_id, qid); + goto out; + } + + if (!qed_iov_validate_txq(p_hwfn, vf, qid, + QED_IOV_VALIDATE_Q_ENABLE) && tx_coal) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d]: Invalid Tx queue_id = %d\n", + vf->abs_vf_id, qid); + goto out; + } + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d]: Setting coalesce for VF rx_coal = %d, tx_coal = %d at queue = %d\n", + vf->abs_vf_id, rx_coal, tx_coal, qid); + + if (rx_coal) { + p_cid = qed_iov_get_vf_rx_queue_cid(&vf->vf_queues[qid]); + + rc = qed_set_rxq_coalesce(p_hwfn, p_ptt, rx_coal, p_cid); + if (rc) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d]: Unable to set rx queue = %d coalesce\n", + vf->abs_vf_id, vf->vf_queues[qid].fw_rx_qid); + goto out; + } + } + + if (tx_coal) { + struct qed_vf_queue *p_queue = &vf->vf_queues[qid]; + + for (i = 0; i < MAX_QUEUES_PER_QZONE; i++) { + if (!p_queue->cids[i].p_cid) + continue; + + if (!p_queue->cids[i].b_is_tx) + continue; + + rc = qed_set_txq_coalesce(p_hwfn, p_ptt, tx_coal, + p_queue->cids[i].p_cid); + + if (rc) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d]: Unable to set tx queue coalesce\n", + vf->abs_vf_id); + goto out; + } + } + } + + status = PFVF_STATUS_SUCCESS; +out: + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_COALESCE_UPDATE, + sizeof(struct pfvf_def_resp_tlv), status); +} static int qed_iov_vf_flr_poll_dorq(struct qed_hwfn *p_hwfn, struct qed_vf_info *p_vf, struct qed_ptt *p_ptt) @@ -3725,6 +3805,9 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, case CHANNEL_TLV_UPDATE_TUNN_PARAM: qed_iov_vf_mbx_update_tunn_param(p_hwfn, p_ptt, p_vf); break; + case CHANNEL_TLV_COALESCE_UPDATE: + qed_iov_vf_pf_set_coalesce(p_hwfn, p_ptt, p_vf); + break; } } else if (qed_iov_tlv_supported(mbx->first_tlv.tl.type)) { DP_VERBOSE(p_hwfn, QED_MSG_IOV, diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index 1926d1ed439f..0a7bbc0f19b0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -1343,6 +1343,50 @@ exit: return rc; } +int +qed_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, + u16 rx_coal, u16 tx_coal, struct qed_queue_cid *p_cid) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct vfpf_update_coalesce *req; + struct pfvf_def_resp_tlv *resp; + int rc; + + /* clear mailbox and prep header tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_COALESCE_UPDATE, sizeof(*req)); + + req->rx_coal = rx_coal; + req->tx_coal = tx_coal; + req->qid = p_cid->rel.queue_id; + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "Setting coalesce rx_coal = %d, tx_coal = %d at queue = %d\n", + rx_coal, tx_coal, req->qid); + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->default_resp; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + goto exit; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + goto exit; + + if (rx_coal) + p_hwfn->cdev->rx_coalesce_usecs = rx_coal; + + if (tx_coal) + p_hwfn->cdev->tx_coalesce_usecs = tx_coal; + +exit: + qed_vf_pf_req_end(p_hwfn, rc); + return rc; +} + u16 qed_vf_get_igu_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id) { struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h index 34d9b882a780..2d9fdd62f56d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.h +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -497,6 +497,13 @@ struct tlv_buffer_size { u8 tlv_buffer[TLV_BUFFER_SIZE]; }; +struct vfpf_update_coalesce { + struct vfpf_first_tlv first_tlv; + u16 rx_coal; + u16 tx_coal; + u16 qid; + u8 padding[2]; +}; union vfpf_tlvs { struct vfpf_first_tlv first_tlv; struct vfpf_acquire_tlv acquire; @@ -509,6 +516,7 @@ union vfpf_tlvs { struct vfpf_vport_update_tlv vport_update; struct vfpf_ucast_filter_tlv ucast_filter; struct vfpf_update_tunn_param_tlv tunn_param_update; + struct vfpf_update_coalesce update_coalesce; struct channel_list_end_tlv list_end; struct tlv_buffer_size tlv_buf_size; }; @@ -624,7 +632,7 @@ enum { CHANNEL_TLV_VPORT_UPDATE_ACCEPT_ANY_VLAN, CHANNEL_TLV_VPORT_UPDATE_SGE_TPA, CHANNEL_TLV_UPDATE_TUNN_PARAM, - CHANNEL_TLV_RESERVED, + CHANNEL_TLV_COALESCE_UPDATE, CHANNEL_TLV_QID, CHANNEL_TLV_MAX, @@ -677,6 +685,20 @@ struct qed_vf_iov { bool b_doorbell_bar; }; +/** + * @brief VF - Set Rx/Tx coalesce per VF's relative queue. + * Coalesce value '0' will omit the configuration. + * + * @param p_hwfn + * @param rx_coal - coalesce value in micro second for rx queue + * @param tx_coal - coalesce value in micro second for tx queue + * @param p_cid - queue cid + * + **/ +int qed_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, + u16 rx_coal, + u16 tx_coal, struct qed_queue_cid *p_cid); + #ifdef CONFIG_QED_SRIOV /** * @brief Read the VF bulletin and act on it if needed diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 55fa2ef19d8a..76e0b132e8cc 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -718,8 +718,9 @@ static int qede_set_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) { struct qede_dev *edev = netdev_priv(dev); + struct qede_fastpath *fp; int i, rc = 0; - u16 rxc, txc, sb_id; + u16 rxc, txc; if (!netif_running(dev)) { DP_INFO(edev, "Interface is down\n"); @@ -730,21 +731,36 @@ static int qede_set_coalesce(struct net_device *dev, coal->tx_coalesce_usecs > QED_COALESCE_MAX) { DP_INFO(edev, "Can't support requested %s coalesce value [max supported value %d]\n", - coal->rx_coalesce_usecs > QED_COALESCE_MAX ? "rx" - : "tx", - QED_COALESCE_MAX); + coal->rx_coalesce_usecs > QED_COALESCE_MAX ? "rx" : + "tx", QED_COALESCE_MAX); return -EINVAL; } rxc = (u16)coal->rx_coalesce_usecs; txc = (u16)coal->tx_coalesce_usecs; for_each_queue(i) { - sb_id = edev->fp_array[i].sb_info->igu_sb_id; - rc = edev->ops->common->set_coalesce(edev->cdev, rxc, txc, - (u16)i, sb_id); - if (rc) { - DP_INFO(edev, "Set coalesce error, rc = %d\n", rc); - return rc; + fp = &edev->fp_array[i]; + + if (edev->fp_array[i].type & QEDE_FASTPATH_RX) { + rc = edev->ops->common->set_coalesce(edev->cdev, + rxc, 0, + fp->rxq->handle); + if (rc) { + DP_INFO(edev, + "Set RX coalesce error, rc = %d\n", rc); + return rc; + } + } + + if (edev->fp_array[i].type & QEDE_FASTPATH_TX) { + rc = edev->ops->common->set_coalesce(edev->cdev, + 0, txc, + fp->txq->handle); + if (rc) { + DP_INFO(edev, + "Set TX coalesce error, rc = %d\n", rc); + return rc; + } } } @@ -1758,6 +1774,8 @@ static const struct ethtool_ops qede_vf_ethtool_ops = { .get_msglevel = qede_get_msglevel, .set_msglevel = qede_set_msglevel, .get_link = qede_get_link, + .get_coalesce = qede_get_coalesce, + .set_coalesce = qede_set_coalesce, .get_ringparam = qede_get_ringparam, .set_ringparam = qede_set_ringparam, .get_strings = qede_get_strings, diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index 9f3276271b02..4d59ca16134c 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -694,8 +694,8 @@ struct qed_common_ops { * * @return 0 on success, error otherwise. */ - int (*set_coalesce)(struct qed_dev *cdev, u16 rx_coal, u16 tx_coal, - u16 qid, u16 sb_id); + int (*set_coalesce)(struct qed_dev *cdev, + u16 rx_coal, u16 tx_coal, void *handle); /** * @brief set_led - Configure LED mode -- cgit v1.2.3 From bf5a94bfe26a9fcd4af91ae6bccd4f3d600d2262 Mon Sep 17 00:00:00 2001 From: Rahul Verma Date: Wed, 26 Jul 2017 06:07:14 -0700 Subject: qed: Read per queue coalesce from hardware Retrieve the actual coalesce value from hardware for every Rx/Tx queue, instead of Rx/Tx coalesce value cached during set coalesce. Signed-off-by: Rahul Verma Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev_api.h | 26 ++++-- drivers/net/ethernet/qlogic/qed/qed_l2.c | 115 ++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_l2.h | 11 ++- drivers/net/ethernet/qlogic/qed/qed_main.c | 7 -- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 74 +++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_sriov.h | 3 + drivers/net/ethernet/qlogic/qed/qed_vf.c | 31 +++++++ drivers/net/ethernet/qlogic/qed/qed_vf.h | 29 +++++- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 47 ++++++++-- include/linux/qed/qed_eth_if.h | 1 + include/linux/qed/qed_if.h | 11 +-- 11 files changed, 324 insertions(+), 31 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index e6b3c83c5db8..defdda1ffaa2 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -443,13 +443,25 @@ int qed_final_cleanup(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u16 id, bool is_vf); /** - * @brief qed_set_queue_coalesce - Configure coalesce parameters for Rx or - * Tx queue. We can configure coalescing to up to 511, but on - * varying accuracy [the bigger the value the less accurate] up to a mistake - * of 3usec for the highest values. - * While the API allows setting coalescing per-qid, all queues sharing a SB - * should be in same range [i.e., either 0-0x7f, 0x80-0xff or 0x100-0x1ff] - * otherwise configuration would break. + * @brief qed_get_queue_coalesce - Retrieve coalesce value for a given queue. + * + * @param p_hwfn + * @param p_coal - store coalesce value read from the hardware. + * @param p_handle + * + * @return int + **/ +int qed_get_queue_coalesce(struct qed_hwfn *p_hwfn, u16 *coal, void *handle); + +/** + * @brief qed_set_queue_coalesce - Configure coalesce parameters for Rx and + * Tx queue. The fact that we can configure coalescing to up to 511, but on + * varying accuracy [the bigger the value the less accurate] up to a mistake + * of 3usec for the highest values. + * While the API allows setting coalescing per-qid, all queues sharing a SB + * should be in same range [i.e., either 0-0x7f, 0x80-0xff or 0x100-0x1ff] + * otherwise configuration would break. + * * * @param rx_coal - Rx Coalesce value in micro seconds. * @param tx_coal - TX Coalesce value in micro seconds. diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 0ba5ec8a9814..9a1645852015 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -2047,6 +2047,106 @@ qed_configure_rfs_ntuple_filter(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, return qed_spq_post(p_hwfn, p_ent, NULL); } +int qed_get_rxq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_queue_cid *p_cid, u16 *p_rx_coal) +{ + u32 coalesce, address, is_valid; + struct cau_sb_entry sb_entry; + u8 timer_res; + int rc; + + rc = qed_dmae_grc2host(p_hwfn, p_ptt, CAU_REG_SB_VAR_MEMORY + + p_cid->sb_igu_id * sizeof(u64), + (u64)(uintptr_t)&sb_entry, 2, 0); + if (rc) { + DP_ERR(p_hwfn, "dmae_grc2host failed %d\n", rc); + return rc; + } + + timer_res = GET_FIELD(sb_entry.params, CAU_SB_ENTRY_TIMER_RES0); + + address = BAR0_MAP_REG_USDM_RAM + + USTORM_ETH_QUEUE_ZONE_OFFSET(p_cid->abs.queue_id); + coalesce = qed_rd(p_hwfn, p_ptt, address); + + is_valid = GET_FIELD(coalesce, COALESCING_TIMESET_VALID); + if (!is_valid) + return -EINVAL; + + coalesce = GET_FIELD(coalesce, COALESCING_TIMESET_TIMESET); + *p_rx_coal = (u16)(coalesce << timer_res); + + return 0; +} + +int qed_get_txq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_queue_cid *p_cid, u16 *p_tx_coal) +{ + u32 coalesce, address, is_valid; + struct cau_sb_entry sb_entry; + u8 timer_res; + int rc; + + rc = qed_dmae_grc2host(p_hwfn, p_ptt, CAU_REG_SB_VAR_MEMORY + + p_cid->sb_igu_id * sizeof(u64), + (u64)(uintptr_t)&sb_entry, 2, 0); + if (rc) { + DP_ERR(p_hwfn, "dmae_grc2host failed %d\n", rc); + return rc; + } + + timer_res = GET_FIELD(sb_entry.params, CAU_SB_ENTRY_TIMER_RES1); + + address = BAR0_MAP_REG_XSDM_RAM + + XSTORM_ETH_QUEUE_ZONE_OFFSET(p_cid->abs.queue_id); + coalesce = qed_rd(p_hwfn, p_ptt, address); + + is_valid = GET_FIELD(coalesce, COALESCING_TIMESET_VALID); + if (!is_valid) + return -EINVAL; + + coalesce = GET_FIELD(coalesce, COALESCING_TIMESET_TIMESET); + *p_tx_coal = (u16)(coalesce << timer_res); + + return 0; +} + +int qed_get_queue_coalesce(struct qed_hwfn *p_hwfn, u16 *p_coal, void *handle) +{ + struct qed_queue_cid *p_cid = handle; + struct qed_ptt *p_ptt; + int rc = 0; + + if (IS_VF(p_hwfn->cdev)) { + rc = qed_vf_pf_get_coalesce(p_hwfn, p_coal, p_cid); + if (rc) + DP_NOTICE(p_hwfn, "Unable to read queue coalescing\n"); + + return rc; + } + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EAGAIN; + + if (p_cid->b_is_rx) { + rc = qed_get_rxq_coalesce(p_hwfn, p_ptt, p_cid, p_coal); + if (rc) + goto out; + } else { + rc = qed_get_txq_coalesce(p_hwfn, p_ptt, p_cid, p_coal); + if (rc) + goto out; + } + +out: + qed_ptt_release(p_hwfn, p_ptt); + + return rc; +} + static int qed_fill_eth_dev_info(struct qed_dev *cdev, struct qed_dev_eth_info *info) { @@ -2696,6 +2796,20 @@ static int qed_ntuple_arfs_filter_config(struct qed_dev *cdev, void *cookie, return rc; } +static int qed_get_coalesce(struct qed_dev *cdev, u16 *coal, void *handle) +{ + struct qed_queue_cid *p_cid = handle; + struct qed_hwfn *p_hwfn; + int rc; + + p_hwfn = p_cid->p_owner; + rc = qed_get_queue_coalesce(p_hwfn, coal, handle); + if (rc) + DP_NOTICE(p_hwfn, "Unable to read queue calescing\n"); + + return rc; +} + static int qed_fp_cqe_completion(struct qed_dev *dev, u8 rss_id, struct eth_slow_path_rx_cqe *cqe) { @@ -2739,6 +2853,7 @@ static const struct qed_eth_ops qed_eth_ops_pass = { .tunn_config = &qed_tunn_configure, .ntuple_filter_config = &qed_ntuple_arfs_filter_config, .configure_arfs_searcher = &qed_configure_arfs_searcher, + .get_coalesce = &qed_get_coalesce, }; const struct qed_eth_ops *qed_get_eth_ops(void) diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.h b/drivers/net/ethernet/qlogic/qed/qed_l2.h index 60ea72ce3e2c..cc1f248551c9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.h +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.h @@ -407,4 +407,13 @@ int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u16 coalesce, struct qed_queue_cid *p_cid); -#endif /* _QED_L2_H */ + +int qed_get_rxq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_queue_cid *p_cid, u16 *p_hw_coal); + +int qed_get_txq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_queue_cid *p_cid, u16 *p_hw_coal); + +#endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 448810a235b8..27832885a87f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -1568,12 +1568,6 @@ static int qed_nvm_get_image(struct qed_dev *cdev, enum qed_nvm_images type, return rc; } -static void qed_get_coalesce(struct qed_dev *cdev, u16 *rx_coal, u16 *tx_coal) -{ - *rx_coal = cdev->rx_coalesce_usecs; - *tx_coal = cdev->tx_coalesce_usecs; -} - static int qed_set_coalesce(struct qed_dev *cdev, u16 rx_coal, u16 tx_coal, void *handle) { @@ -1726,7 +1720,6 @@ const struct qed_common_ops qed_common_ops_pass = { .chain_alloc = &qed_chain_alloc, .chain_free = &qed_chain_free, .nvm_get_image = &qed_nvm_get_image, - .get_coalesce = &qed_get_coalesce, .set_coalesce = &qed_set_coalesce, .set_led = &qed_set_led, .update_drv_state = &qed_update_drv_state, diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 5feef783623b..3f40b1de7957 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3400,6 +3400,75 @@ static void qed_iov_vf_mbx_release(struct qed_hwfn *p_hwfn, length, status); } +static void qed_iov_vf_pf_get_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *p_vf) +{ + struct qed_iov_vf_mbx *mbx = &p_vf->vf_mbx; + struct pfvf_read_coal_resp_tlv *p_resp; + struct vfpf_read_coal_req_tlv *req; + u8 status = PFVF_STATUS_FAILURE; + struct qed_vf_queue *p_queue; + struct qed_queue_cid *p_cid; + u16 coal = 0, qid, i; + bool b_is_rx; + int rc = 0; + + mbx->offset = (u8 *)mbx->reply_virt; + req = &mbx->req_virt->read_coal_req; + + qid = req->qid; + b_is_rx = req->is_rx ? true : false; + + if (b_is_rx) { + if (!qed_iov_validate_rxq(p_hwfn, p_vf, qid, + QED_IOV_VALIDATE_Q_ENABLE)) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d]: Invalid Rx queue_id = %d\n", + p_vf->abs_vf_id, qid); + goto send_resp; + } + + p_cid = qed_iov_get_vf_rx_queue_cid(&p_vf->vf_queues[qid]); + rc = qed_get_rxq_coalesce(p_hwfn, p_ptt, p_cid, &coal); + if (rc) + goto send_resp; + } else { + if (!qed_iov_validate_txq(p_hwfn, p_vf, qid, + QED_IOV_VALIDATE_Q_ENABLE)) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d]: Invalid Tx queue_id = %d\n", + p_vf->abs_vf_id, qid); + goto send_resp; + } + for (i = 0; i < MAX_QUEUES_PER_QZONE; i++) { + p_queue = &p_vf->vf_queues[qid]; + if ((!p_queue->cids[i].p_cid) || + (!p_queue->cids[i].b_is_tx)) + continue; + + p_cid = p_queue->cids[i].p_cid; + + rc = qed_get_txq_coalesce(p_hwfn, p_ptt, p_cid, &coal); + if (rc) + goto send_resp; + break; + } + } + + status = PFVF_STATUS_SUCCESS; + +send_resp: + p_resp = qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_COALESCE_READ, + sizeof(*p_resp)); + p_resp->coal = coal; + + qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + + qed_iov_send_response(p_hwfn, p_ptt, p_vf, sizeof(*p_resp), status); +} + static void qed_iov_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_vf_info *vf) @@ -3450,6 +3519,7 @@ static void qed_iov_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, vf->abs_vf_id, vf->vf_queues[qid].fw_rx_qid); goto out; } + vf->rx_coal = rx_coal; } if (tx_coal) { @@ -3473,6 +3543,7 @@ static void qed_iov_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, goto out; } } + vf->tx_coal = tx_coal; } status = PFVF_STATUS_SUCCESS; @@ -3808,6 +3879,9 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, case CHANNEL_TLV_COALESCE_UPDATE: qed_iov_vf_pf_set_coalesce(p_hwfn, p_ptt, p_vf); break; + case CHANNEL_TLV_COALESCE_READ: + qed_iov_vf_pf_get_coalesce(p_hwfn, p_ptt, p_vf); + break; } } else if (qed_iov_tlv_supported(mbx->first_tlv.tl.type)) { DP_VERBOSE(p_hwfn, QED_MSG_IOV, diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.h b/drivers/net/ethernet/qlogic/qed/qed_sriov.h index c2e44bce398c..3955929ba892 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.h @@ -217,6 +217,9 @@ struct qed_vf_info { u8 num_rxqs; u8 num_txqs; + u16 rx_coal; + u16 tx_coal; + u8 num_sbs; u8 num_mac_filters; diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index 0a7bbc0f19b0..91b5e9f02a62 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -1343,6 +1343,37 @@ exit: return rc; } +int qed_vf_pf_get_coalesce(struct qed_hwfn *p_hwfn, + u16 *p_coal, struct qed_queue_cid *p_cid) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_read_coal_resp_tlv *resp; + struct vfpf_read_coal_req_tlv *req; + int rc; + + /* clear mailbox and prep header tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_COALESCE_READ, sizeof(*req)); + req->qid = p_cid->rel.queue_id; + req->is_rx = p_cid->b_is_rx ? 1 : 0; + + qed_add_tlv(p_hwfn, &p_iov->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + resp = &p_iov->pf2vf_reply->read_coal_resp; + + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + goto exit; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + goto exit; + + *p_coal = resp->coal; +exit: + qed_vf_pf_req_end(p_hwfn, rc); + + return rc; +} + int qed_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, u16 rx_coal, u16 tx_coal, struct qed_queue_cid *p_cid) diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h index 2d9fdd62f56d..97d44dfb38ca 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.h +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -504,6 +504,20 @@ struct vfpf_update_coalesce { u16 qid; u8 padding[2]; }; + +struct vfpf_read_coal_req_tlv { + struct vfpf_first_tlv first_tlv; + u16 qid; + u8 is_rx; + u8 padding[5]; +}; + +struct pfvf_read_coal_resp_tlv { + struct pfvf_tlv hdr; + u16 coal; + u8 padding[6]; +}; + union vfpf_tlvs { struct vfpf_first_tlv first_tlv; struct vfpf_acquire_tlv acquire; @@ -517,7 +531,7 @@ union vfpf_tlvs { struct vfpf_ucast_filter_tlv ucast_filter; struct vfpf_update_tunn_param_tlv tunn_param_update; struct vfpf_update_coalesce update_coalesce; - struct channel_list_end_tlv list_end; + struct vfpf_read_coal_req_tlv read_coal_req; struct tlv_buffer_size tlv_buf_size; }; @@ -527,6 +541,7 @@ union pfvf_tlvs { struct tlv_buffer_size tlv_buf_size; struct pfvf_start_queue_resp_tlv queue_start; struct pfvf_update_tunn_param_tlv tunn_param_resp; + struct pfvf_read_coal_resp_tlv read_coal_resp; }; enum qed_bulletin_bit { @@ -634,6 +649,7 @@ enum { CHANNEL_TLV_UPDATE_TUNN_PARAM, CHANNEL_TLV_COALESCE_UPDATE, CHANNEL_TLV_QID, + CHANNEL_TLV_COALESCE_READ, CHANNEL_TLV_MAX, /* Required for iterating over vport-update tlvs. @@ -699,6 +715,17 @@ int qed_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, u16 rx_coal, u16 tx_coal, struct qed_queue_cid *p_cid); +/** + * @brief VF - Get coalesce per VF's relative queue. + * + * @param p_hwfn + * @param p_coal - coalesce value in micro second for VF queues. + * @param p_cid - queue cid + * + **/ +int qed_vf_pf_get_coalesce(struct qed_hwfn *p_hwfn, + u16 *p_coal, struct qed_queue_cid *p_cid); + #ifdef CONFIG_QED_SRIOV /** * @brief Read the VF bulletin and act on it if needed diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 76e0b132e8cc..dae741270022 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -702,16 +702,53 @@ static u32 qede_get_link(struct net_device *dev) static int qede_get_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) { + void *rx_handle = NULL, *tx_handle = NULL; struct qede_dev *edev = netdev_priv(dev); - u16 rxc, txc; + u16 rx_coal, tx_coal, i, rc = 0; + struct qede_fastpath *fp; + + rx_coal = QED_DEFAULT_RX_USECS; + tx_coal = QED_DEFAULT_TX_USECS; memset(coal, 0, sizeof(struct ethtool_coalesce)); - edev->ops->common->get_coalesce(edev->cdev, &rxc, &txc); - coal->rx_coalesce_usecs = rxc; - coal->tx_coalesce_usecs = txc; + __qede_lock(edev); + if (edev->state == QEDE_STATE_OPEN) { + for_each_queue(i) { + fp = &edev->fp_array[i]; + + if (fp->type & QEDE_FASTPATH_RX) { + rx_handle = fp->rxq->handle; + break; + } + } - return 0; + rc = edev->ops->get_coalesce(edev->cdev, &rx_coal, rx_handle); + if (rc) { + DP_INFO(edev, "Read Rx coalesce error\n"); + goto out; + } + + for_each_queue(i) { + fp = &edev->fp_array[i]; + if (fp->type & QEDE_FASTPATH_TX) { + tx_handle = fp->txq->handle; + break; + } + } + + rc = edev->ops->get_coalesce(edev->cdev, &tx_coal, tx_handle); + if (rc) + DP_INFO(edev, "Read Tx coalesce error\n"); + } + +out: + __qede_unlock(edev); + + coal->rx_coalesce_usecs = rx_coal; + coal->tx_coalesce_usecs = tx_coal; + + return rc; } static int qede_set_coalesce(struct net_device *dev, diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 0eef0a2b1901..d60de4a39810 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -323,6 +323,7 @@ struct qed_eth_ops { int (*configure_arfs_searcher)(struct qed_dev *cdev, bool en_searcher); + int (*get_coalesce)(struct qed_dev *cdev, u16 *coal, void *handle); }; const struct qed_eth_ops *qed_get_eth_ops(void); diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index 4d59ca16134c..2b4720bb8b40 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -186,6 +186,7 @@ enum qed_led_mode { #define QED_COALESCE_MAX 0xFF #define QED_DEFAULT_RX_USECS 12 +#define QED_DEFAULT_TX_USECS 48 /* forward */ struct qed_dev; @@ -673,16 +674,6 @@ struct qed_common_ops { int (*nvm_get_image)(struct qed_dev *cdev, enum qed_nvm_images type, u8 *buf, u16 len); -/** - * @brief get_coalesce - Get coalesce parameters in usec - * - * @param cdev - * @param rx_coal - Rx coalesce value in usec - * @param tx_coal - Tx coalesce value in usec - * - */ - void (*get_coalesce)(struct qed_dev *cdev, u16 *rx_coal, u16 *tx_coal); - /** * @brief set_coalesce - Configure Rx coalesce value in usec * -- cgit v1.2.3 From 4b5dde2d6234ff5bc68e97e6901d1f2a0a7f3749 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 29 Jun 2017 18:23:54 -0700 Subject: mwifiex: correct channel stat buffer overflows mwifiex records information about various channels as it receives scan information. It does this by appending to a buffer that was sized to the max number of supported channels on any band, but there are numerous problems: (a) scans can return info from more than one band (e.g., both 2.4 and 5 GHz), so the determined "max" is not large enough (b) some firmware appears to return multiple results for a given channel, so the max *really* isn't large enough (c) there is no bounds checking when stashing these stats, so problems (a) and (b) can easily lead to buffer overflows Let's patch this by setting a slightly-more-correct max (that accounts for a combination of both 2.4G and 5G bands) and adding a bounds check when writing to our statistics buffer. Due to problem (b), we still might not properly report all known survey information (e.g., with "iw survey dump"), since duplicate results (or otherwise "larger than expected" results) will cause some truncation. But that's a problem for a future bugfix. (And because of this known deficiency, only log the excess at the WARN level, since that isn't visible by default in this driver and would otherwise be a bit too noisy.) Fixes: bf35443314ac ("mwifiex: channel statistics support for mwifiex") Cc: Cc: Avinash Patil Cc: Xinming Hu Signed-off-by: Brian Norris Reviewed-by: Dmitry Torokhov Reviewed-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 +- drivers/net/wireless/marvell/mwifiex/scan.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 06ad2d50f9b0..fdfdf2371986 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -4215,7 +4215,7 @@ int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter) if (adapter->config_bands & BAND_A) n_channels_a = mwifiex_band_5ghz.n_channels; - adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a); + adapter->num_in_chan_stats = n_channels_bg + n_channels_a; adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) * adapter->num_in_chan_stats); diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index ae9630b49342..9900855746ac 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -2492,6 +2492,12 @@ mwifiex_update_chan_statistics(struct mwifiex_private *priv, sizeof(struct mwifiex_chan_stats); for (i = 0 ; i < num_chan; i++) { + if (adapter->survey_idx >= adapter->num_in_chan_stats) { + mwifiex_dbg(adapter, WARN, + "FW reported too many channel results (max %d)\n", + adapter->num_in_chan_stats); + return; + } chan_stats.chan_num = fw_chan_stats->chan_num; chan_stats.bandcfg = fw_chan_stats->bandcfg; chan_stats.flags = fw_chan_stats->flags; -- cgit v1.2.3 From d0116f6f7b306bc2d1bfc98d7c7c80fe5f468c20 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 30 Jun 2017 11:08:43 +0100 Subject: rtlwifi: kfree entry until after entry->bssid has been accessed The current code kfree's entry and then dereferences it by accessing entry->bssid. Avoid the dereference-after-free by moving the kfree after the access to entry->bssid. Detected by CoverityScan, CID#1448600 ("Read from pointer after free") Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index e36ee592c660..208f56297a75 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1735,12 +1735,12 @@ void rtl_scan_list_expire(struct ieee80211_hw *hw) continue; list_del(&entry->list); - kfree(entry); rtlpriv->scan_list.num--; RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, "BSSID=%pM is expire in scan list (total=%d)\n", entry->bssid, rtlpriv->scan_list.num); + kfree(entry); } spin_unlock_irqrestore(&rtlpriv->locks.scan_list_lock, flags); -- cgit v1.2.3 From 6e9aae179f290f1a44fce7ef8e9a8e2dd68ed1e4 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Thu, 6 Jul 2017 15:00:37 -0700 Subject: wl1251: add a missing spin_lock_init() This fixes the following kernel warning: [ 5668.771453] BUG: spinlock bad magic on CPU#0, kworker/u2:3/9745 [ 5668.771850] lock: 0xce63ef20, .magic: 00000000, .owner: /-1, .owner_cpu: 0 [ 5668.772277] CPU: 0 PID: 9745 Comm: kworker/u2:3 Tainted: G W 4.12.0-03002-gec979a4-dirty #40 [ 5668.772796] Hardware name: Nokia RX-51 board [ 5668.773071] Workqueue: phy1 wl1251_irq_work [ 5668.773345] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 5668.773803] [] (show_stack) from [] (do_raw_spin_lock+0x6c/0xa0) [ 5668.774230] [] (do_raw_spin_lock) from [] (_raw_spin_lock_irqsave+0x10/0x18) [ 5668.774658] [] (_raw_spin_lock_irqsave) from [] (wl1251_op_tx+0x38/0x5c) [ 5668.775115] [] (wl1251_op_tx) from [] (ieee80211_tx_frags+0x188/0x1c0) [ 5668.775543] [] (ieee80211_tx_frags) from [] (__ieee80211_tx+0x6c/0x130) [ 5668.775970] [] (__ieee80211_tx) from [] (ieee80211_tx+0xdc/0x104) [ 5668.776367] [] (ieee80211_tx) from [] (__ieee80211_subif_start_xmit+0x454/0x8c8) [ 5668.776824] [] (__ieee80211_subif_start_xmit) from [] (ieee80211_subif_start_xmit+0x30/0x2fc) [ 5668.777343] [] (ieee80211_subif_start_xmit) from [] (dev_hard_start_xmit+0x80/0x118) ... by adding the missing spin_lock_init(). Reported-by: Pavel Machek Cc: Kalle Valo Signed-off-by: Cong Wang Acked-by: Pavel Machek Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wl1251/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 08f0477f78d9..9915d83a4a30 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -1571,6 +1571,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) wl->state = WL1251_STATE_OFF; mutex_init(&wl->mutex); + spin_lock_init(&wl->wl_lock); wl->tx_mgmt_frm_rate = DEFAULT_HW_GEN_TX_RATE; wl->tx_mgmt_frm_mod = DEFAULT_HW_GEN_MODULATION_TYPE; -- cgit v1.2.3 From 26a8985fa52e3c3087794d1329cfdafceae8510c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 26 Jul 2017 11:09:46 -0700 Subject: nfp: remove the probe deferral when FW not present We use a hack to defer probe when firmware was not pre-loaded or found on disk. This helps in case users forgot to include firmware in initramfs, the driver will most likely get another shot at probing after real root is mounted. This is not for what EPROBE_DEFER is supposed to be used, and when FW is completely missing every time new device is probed NFP will reprobe spamming kernel logs. Remove this hack, users will now have to make sure the right firmware image is present in initramfs if nfp.ko is placed there or built in. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 5797dbf2b507..d5e2361f0e86 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -704,7 +704,7 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (!pf->rtbl) { nfp_err(pf->cpp, "No %s, giving up.\n", pf->fw_loaded ? "symbol table" : "firmware found"); - return -EPROBE_DEFER; + return -EINVAL; } mutex_lock(&pf->lock); -- cgit v1.2.3 From 9511f2980d9367713c65991edeb7608c9d78dbeb Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 26 Jul 2017 11:09:47 -0700 Subject: nfp: look for firmware image by device serial number and PCI name We generally look up firmware by card type, but that doesn't allow users who have more than one card of the same type in their system to select firmware per adapter. Unfortunately user space firmware helper seems fraught with difficulties and to be on its way out. In particular support for handling firmware uevents have been dropped from systemd and most distributions don't enable the FW fallback by default any more. To allow users selecting firmware for a particular device look up firmware names by serial and pci_name(). Use the direct lookup to disable generating uevents when enabled in Kconfig and not print any warnings to logs if adapter-specific files are missing. Users can place in /lib/firmware/netronome files named: pci-${pci_name}.nffw serial-${serial}.nffw to target a specific card. E.g.: pci-0000:04:00.0.nffw pci-0000:82:00.0.nffw serial-00-aa-bb-11-22-33-10-ff.nffw We use the full serial number including the interface id, as it appears in lspci output (bytes separated by '-'). Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index d67969d3e484..13d056da0765 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -188,9 +188,27 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf) struct nfp_eth_table_port *port; const char *fw_model; char fw_name[256]; + const u8 *serial; int spc, err = 0; + u16 interface; int i, j; + /* First try to find a firmware image specific for this device */ + interface = nfp_cpp_interface(pf->cpp); + nfp_cpp_serial(pf->cpp, &serial); + sprintf(fw_name, "netronome/serial-%pMF-%02hhx-%02hhx.nffw", + serial, interface >> 8, interface & 0xff); + err = request_firmware_direct(&fw, fw_name, &pdev->dev); + if (!err) + goto done; + + /* Then try the PCI name */ + sprintf(fw_name, "netronome/pci-%s.nffw", pci_name(pdev)); + err = request_firmware_direct(&fw, fw_name, &pdev->dev); + if (!err) + goto done; + + /* Finally try the card type and media */ if (!pf->eth_tbl) { dev_err(&pdev->dev, "Error: can't identify media config\n"); return NULL; @@ -226,7 +244,7 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf) err = request_firmware(&fw, fw_name, &pdev->dev); if (err) return NULL; - +done: dev_info(&pdev->dev, "Loading FW image: %s\n", fw_name); return fw; -- cgit v1.2.3 From 1680a3705b00e90c1e1de91a9fec421b23cef719 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 26 Jul 2017 11:09:48 -0700 Subject: nfp: only use direct firmware requests request_firmware() will fallback to user space helper and may cause long delays when driver is loaded if udev doesn't correctly handle FW requests. Since we never really made use of the user space helper functionality switch to the simpler request_firmware_direct() call. The side effect of this change is that no warning will be printed when the FW image does not exists. To help users figure out which FW file is missing print a info message when we request each file. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.c | 42 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index 13d056da0765..dd769eceb33d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -174,6 +174,21 @@ static int nfp_pcie_sriov_configure(struct pci_dev *pdev, int num_vfs) return nfp_pcie_sriov_enable(pdev, num_vfs); } +static const struct firmware * +nfp_net_fw_request(struct pci_dev *pdev, struct nfp_pf *pf, const char *name) +{ + const struct firmware *fw = NULL; + int err; + + err = request_firmware_direct(&fw, name, &pdev->dev); + nfp_info(pf->cpp, " %s: %s\n", + name, err ? "not found" : "found, loading..."); + if (err) + return NULL; + + return fw; +} + /** * nfp_net_fw_find() - Find the correct firmware image for netdev mode * @pdev: PCI Device structure @@ -184,29 +199,30 @@ static int nfp_pcie_sriov_configure(struct pci_dev *pdev, int num_vfs) static const struct firmware * nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf) { - const struct firmware *fw = NULL; struct nfp_eth_table_port *port; + const struct firmware *fw; const char *fw_model; char fw_name[256]; const u8 *serial; - int spc, err = 0; u16 interface; - int i, j; + int spc, i, j; + + nfp_info(pf->cpp, "Looking for firmware file in order of priority:\n"); /* First try to find a firmware image specific for this device */ interface = nfp_cpp_interface(pf->cpp); nfp_cpp_serial(pf->cpp, &serial); sprintf(fw_name, "netronome/serial-%pMF-%02hhx-%02hhx.nffw", serial, interface >> 8, interface & 0xff); - err = request_firmware_direct(&fw, fw_name, &pdev->dev); - if (!err) - goto done; + fw = nfp_net_fw_request(pdev, pf, fw_name); + if (fw) + return fw; /* Then try the PCI name */ sprintf(fw_name, "netronome/pci-%s.nffw", pci_name(pdev)); - err = request_firmware_direct(&fw, fw_name, &pdev->dev); - if (!err) - goto done; + fw = nfp_net_fw_request(pdev, pf, fw_name); + if (fw) + return fw; /* Finally try the card type and media */ if (!pf->eth_tbl) { @@ -241,13 +257,7 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf) if (spc <= 0) return NULL; - err = request_firmware(&fw, fw_name, &pdev->dev); - if (err) - return NULL; -done: - dev_info(&pdev->dev, "Loading FW image: %s\n", fw_name); - - return fw; + return nfp_net_fw_request(pdev, pf, fw_name); } /** -- cgit v1.2.3 From fb5e7606b100668293f7b0c4a719f7a73233cbb1 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 26 Jul 2017 12:05:38 -0700 Subject: net: phy: Remove stale comments referencing timer Since commit a390d1f379cf ("phylib: convert state_queue work to delayed_work"), the PHYLIB state machine was converted to use delayed workqueues, yet some functions were still referencing the PHY library timer in their comments, fix that and remove the now unused linux/timer.h include. Signed-off-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d0626bf5c540..ac1dcf0289fa 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -705,8 +704,8 @@ EXPORT_SYMBOL(phy_start_aneg); * * Description: The PHY infrastructure can run a state machine * which tracks whether the PHY is starting up, negotiating, - * etc. This function starts the timer which tracks the state - * of the PHY. If you want to maintain your own state machine, + * etc. This function starts the delayed workqueue which tracks + * the state of the PHY. If you want to maintain your own state machine, * do not call this function. */ void phy_start_machine(struct phy_device *phydev) @@ -737,9 +736,9 @@ void phy_trigger_machine(struct phy_device *phydev, bool sync) * phy_stop_machine - stop the PHY state machine tracking * @phydev: target phy_device struct * - * Description: Stops the state machine timer, sets the state to UP - * (unless it wasn't up yet). This function must be called BEFORE - * phy_detach. + * Description: Stops the state machine delayed workqueue, sets the + * state to UP (unless it wasn't up yet). This function must be + * called BEFORE phy_detach. */ void phy_stop_machine(struct phy_device *phydev) { -- cgit v1.2.3 From ade0a79ab11dc18f9ef377472e262ec13597d7ca Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Wed, 26 Jul 2017 12:10:48 -0700 Subject: liquidio: standardization: use min_t instead of custom macro Signed-off-by: Rick Farrington Signed-off-by: Derek Chickles Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_console.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index e08f7600f986..501ad95171fe 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -42,7 +42,6 @@ module_param(console_bitmask, int, 0644); MODULE_PARM_DESC(console_bitmask, "Bitmask indicating which consoles have debug output redirected to syslog."); -#define MIN(a, b) min((a), (b)) #define CAST_ULL(v) ((u64)(v)) #define BOOTLOADER_PCI_READ_BUFFER_DATA_ADDR 0x0006c008 @@ -704,7 +703,7 @@ static int octeon_console_read(struct octeon_device *oct, u32 console_num, if (bytes_to_read <= 0) return bytes_to_read; - bytes_to_read = MIN(bytes_to_read, (s32)buf_size); + bytes_to_read = min_t(s32, bytes_to_read, buf_size); /* Check to see if what we want to read is not contiguous, and limit * ourselves to the contiguous block -- cgit v1.2.3 From 19d5c35950ad222e7f0cb076b428f0888f3f15f4 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Wed, 26 Jul 2017 12:11:09 -0700 Subject: liquidio: cleanup: removed cryptic and misleading macro Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_console.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index 501ad95171fe..15ad1ab2c0c7 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -42,8 +42,6 @@ module_param(console_bitmask, int, 0644); MODULE_PARM_DESC(console_bitmask, "Bitmask indicating which consoles have debug output redirected to syslog."); -#define CAST_ULL(v) ((u64)(v)) - #define BOOTLOADER_PCI_READ_BUFFER_DATA_ADDR 0x0006c008 #define BOOTLOADER_PCI_READ_BUFFER_LEN_ADDR 0x0006c004 #define BOOTLOADER_PCI_READ_BUFFER_OWNER_ADDR 0x0006c000 @@ -233,7 +231,7 @@ static int __cvmx_bootmem_check_version(struct octeon_device *oct, (exact_match && major_version != exact_match)) { dev_err(&oct->pci_dev->dev, "bootmem ver mismatch %d.%d addr:0x%llx\n", major_version, minor_version, - CAST_ULL(oct->bootmem_desc_addr)); + (long long)oct->bootmem_desc_addr); return -1; } else { return 0; -- cgit v1.2.3 From e0d0ae8a4304ce6be488570d085bef278fe941dd Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 6 Jul 2017 20:07:04 +0530 Subject: rsi: use BUILD_BUG_ON check for fsm_state Whenever new fsm_state enum element is added, fsm_state array also needs to be updated. If this change is missed, we may end up doing invalid access in array. BUILD_BUG_ON check will help to avoid this problem. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_debugfs.c | 2 ++ drivers/net/wireless/rsi/rsi_main.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_debugfs.c b/drivers/net/wireless/rsi/rsi_91x_debugfs.c index 4c0a493bd44e..f3b91b656351 100644 --- a/drivers/net/wireless/rsi/rsi_91x_debugfs.c +++ b/drivers/net/wireless/rsi/rsi_91x_debugfs.c @@ -138,6 +138,8 @@ static int rsi_stats_read(struct seq_file *seq, void *data) seq_puts(seq, "==> RSI STA DRIVER STATUS <==\n"); seq_puts(seq, "DRIVER_FSM_STATE: "); + BUILD_BUG_ON(ARRAY_SIZE(fsm_state) != NUM_FSM_STATES); + if (common->fsm_state <= FSM_MAC_INIT_DONE) seq_printf(seq, "%s", fsm_state[common->fsm_state]); diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index f3985250b593..72675ebcd454 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -40,7 +40,9 @@ enum RSI_FSM_STATES { FSM_RESET_MAC_SENT, FSM_RADIO_CAPS_SENT, FSM_BB_RF_PROG_SENT, - FSM_MAC_INIT_DONE + FSM_MAC_INIT_DONE, + + NUM_FSM_STATES }; extern u32 rsi_zone_enabled; -- cgit v1.2.3 From 09cfb41f35799af91372105a40c0d173e4fef6e5 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Thu, 6 Jul 2017 20:07:05 +0530 Subject: rsi: changes in eeprom read frame EEPROM read frame is sent during device initialization to read mac address. The format of the frame is modified in firmware to include eeprom length and offset. This frame does not return firmware version now. Also same frame is sent again to read rf type and band information. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_debugfs.c | 1 + drivers/net/wireless/rsi/rsi_91x_mgmt.c | 90 ++++++++++++++++++++---------- drivers/net/wireless/rsi/rsi_main.h | 16 ++++++ drivers/net/wireless/rsi/rsi_mgmt.h | 19 ++++++- 4 files changed, 96 insertions(+), 30 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_debugfs.c b/drivers/net/wireless/rsi/rsi_91x_debugfs.c index f3b91b656351..e98eb55c26cc 100644 --- a/drivers/net/wireless/rsi/rsi_91x_debugfs.c +++ b/drivers/net/wireless/rsi/rsi_91x_debugfs.c @@ -130,6 +130,7 @@ static int rsi_stats_read(struct seq_file *seq, void *data) "FSM_COMMON_DEV_PARAMS_SENT", "FSM_BOOT_PARAMS_SENT", "FSM_EEPROM_READ_MAC_ADDR", + "FSM_EEPROM_READ_RF_TYPE", "FSM_RESET_MAC_SENT", "FSM_RADIO_CAPS_SENT", "FSM_BB_RF_PROG_SENT", diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index d4d365b5d2d6..ebd1e5647f03 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1276,7 +1276,8 @@ void rsi_inform_bss_status(struct rsi_common *common, */ static int rsi_eeprom_read(struct rsi_common *common) { - struct rsi_mac_frame *mgmt_frame; + struct rsi_eeprom_read_frame *mgmt_frame; + struct rsi_hw *adapter = common->priv; struct sk_buff *skb; rsi_dbg(MGMT_TX_ZONE, "%s: Sending EEPROM read req frame\n", __func__); @@ -1289,18 +1290,21 @@ static int rsi_eeprom_read(struct rsi_common *common) } memset(skb->data, 0, FRAME_DESC_SZ); - mgmt_frame = (struct rsi_mac_frame *)skb->data; + mgmt_frame = (struct rsi_eeprom_read_frame *)skb->data; /* FrameType */ - mgmt_frame->desc_word[1] = cpu_to_le16(EEPROM_READ_TYPE); - mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + rsi_set_len_qno(&mgmt_frame->len_qno, 0, RSI_WIFI_MGMT_Q); + mgmt_frame->pkt_type = EEPROM_READ; + /* Number of bytes to read */ - mgmt_frame->desc_word[3] = cpu_to_le16(ETH_ALEN + - WLAN_MAC_MAGIC_WORD_LEN + - WLAN_HOST_MODE_LEN + - WLAN_FW_VERSION_LEN); + mgmt_frame->pkt_info = + cpu_to_le32((adapter->eeprom.length << RSI_EEPROM_LEN_OFFSET) & + RSI_EEPROM_LEN_MASK); + mgmt_frame->pkt_info |= cpu_to_le32((3 << RSI_EEPROM_HDR_SIZE_OFFSET) & + RSI_EEPROM_HDR_SIZE_MASK); + /* Address to read */ - mgmt_frame->desc_word[4] = cpu_to_le16(WLAN_MAC_EEPROM_ADDR); + mgmt_frame->eeprom_offset = cpu_to_le32(adapter->eeprom.offset); skb_put(skb, FRAME_DESC_SZ); @@ -1426,19 +1430,25 @@ int rsi_set_antenna(struct rsi_common *common, u8 antenna) static int rsi_handle_ta_confirm_type(struct rsi_common *common, u8 *msg) { + struct rsi_hw *adapter = common->priv; u8 sub_type = (msg[15] & 0xff); + u16 msg_len = ((u16 *)msg)[0] & 0xfff; + u8 offset; switch (sub_type) { case BOOTUP_PARAMS_REQUEST: rsi_dbg(FSM_ZONE, "%s: Boot up params confirm received\n", __func__); if (common->fsm_state == FSM_BOOT_PARAMS_SENT) { + adapter->eeprom.length = (IEEE80211_ADDR_LEN + + WLAN_MAC_MAGIC_WORD_LEN + + WLAN_HOST_MODE_LEN); + adapter->eeprom.offset = WLAN_MAC_EEPROM_ADDR; if (rsi_eeprom_read(common)) { common->fsm_state = FSM_CARD_NOT_READY; goto out; - } else { - common->fsm_state = FSM_EEPROM_READ_MAC_ADDR; } + common->fsm_state = FSM_EEPROM_READ_MAC_ADDR; } else { rsi_dbg(INFO_ZONE, "%s: Received bootup params cfm in %d state\n", @@ -1447,30 +1457,52 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common, } break; - case EEPROM_READ_TYPE: + case EEPROM_READ: + rsi_dbg(FSM_ZONE, "EEPROM READ confirm received\n"); + if (msg_len <= 0) { + rsi_dbg(FSM_ZONE, + "%s: [EEPROM_READ] Invalid len %d\n", + __func__, msg_len); + goto out; + } + if (msg[16] != MAGIC_WORD) { + rsi_dbg(FSM_ZONE, + "%s: [EEPROM_READ] Invalid token\n", __func__); + common->fsm_state = FSM_CARD_NOT_READY; + goto out; + } if (common->fsm_state == FSM_EEPROM_READ_MAC_ADDR) { - if (msg[16] == MAGIC_WORD) { - u8 offset = (FRAME_DESC_SZ + WLAN_HOST_MODE_LEN - + WLAN_MAC_MAGIC_WORD_LEN); - memcpy(common->mac_addr, - &msg[offset], - ETH_ALEN); - memcpy(&common->fw_ver, - &msg[offset + ETH_ALEN], - sizeof(struct version_info)); - - } else { + offset = (FRAME_DESC_SZ + WLAN_HOST_MODE_LEN + + WLAN_MAC_MAGIC_WORD_LEN); + memcpy(common->mac_addr, &msg[offset], ETH_ALEN); + adapter->eeprom.length = + ((WLAN_MAC_MAGIC_WORD_LEN + 3) & (~3)); + adapter->eeprom.offset = WLAN_EEPROM_RFTYPE_ADDR; + if (rsi_eeprom_read(common)) { + rsi_dbg(ERR_ZONE, + "%s: Failed reading RF band\n", + __func__); common->fsm_state = FSM_CARD_NOT_READY; - break; + goto out; + } + common->fsm_state = FSM_EEPROM_READ_RF_TYPE; + } else if (common->fsm_state == FSM_EEPROM_READ_RF_TYPE) { + if ((msg[17] & 0x3) == 0x3) { + rsi_dbg(INIT_ZONE, "Dual band supported\n"); + common->band = NL80211_BAND_5GHZ; + common->num_supp_bands = 2; + } else if ((msg[17] & 0x3) == 0x1) { + rsi_dbg(INIT_ZONE, + "Only 2.4Ghz band supported\n"); + common->band = NL80211_BAND_2GHZ; + common->num_supp_bands = 1; } if (rsi_send_reset_mac(common)) goto out; - else - common->fsm_state = FSM_RESET_MAC_SENT; + common->fsm_state = FSM_RESET_MAC_SENT; } else { - rsi_dbg(ERR_ZONE, - "%s: Received eeprom mac addr in %d state\n", - __func__, common->fsm_state); + rsi_dbg(ERR_ZONE, "%s: Invalid EEPROM read type\n", + __func__); return 0; } break; diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 72675ebcd454..cbf29c36f4f4 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -37,6 +37,7 @@ enum RSI_FSM_STATES { FSM_COMMON_DEV_PARAMS_SENT, FSM_BOOT_PARAMS_SENT, FSM_EEPROM_READ_MAC_ADDR, + FSM_EEPROM_READ_RF_TYPE, FSM_RESET_MAC_SENT, FSM_RADIO_CAPS_SENT, FSM_BB_RF_PROG_SENT, @@ -177,6 +178,7 @@ struct rsi_common { /* Channel/band related */ u8 band; + u8 num_supp_bands; u8 channel_width; u16 rts_threshold; @@ -230,6 +232,19 @@ enum host_intf { RSI_HOST_INTF_USB }; +struct eepromrw_info { + u32 offset; + u32 length; + u8 write; + u16 eeprom_erase; + u8 data[480]; +}; + +struct eeprom_read { + u16 length; + u16 off_set; +}; + struct rsi_hw { struct rsi_common *priv; u8 device_model; @@ -252,6 +267,7 @@ struct rsi_hw { struct timer_list bl_cmd_timer; bool blcmd_timer_expired; u32 flash_capacity; + struct eepromrw_info eeprom; u8 dfs_region; void *rsi_dev; struct rsi_host_intf_ops *host_intf_ops; diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index dcb6db728cbd..47926c9f2027 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -43,6 +43,7 @@ #define WLAN_HOST_MODE_LEN 0x04 #define WLAN_FW_VERSION_LEN 0x08 #define MAGIC_WORD 0x5A +#define WLAN_EEPROM_RFTYPE_ADDR 424 /* Receive Frame Types */ #define TA_CONFIRM_TYPE 0x01 @@ -192,7 +193,7 @@ enum cmd_frame_type { AUTO_RATE_IND, BOOTUP_PARAMS_REQUEST, VAP_CAPABILITIES, - EEPROM_READ_TYPE , + EEPROM_READ, EEPROM_WRITE, GPIO_PIN_CONFIG , SET_RX_FILTER, @@ -353,6 +354,22 @@ struct rsi_config_vals { u8 reserved2[16]; } __packed; +/* Packet info flags */ +#define RSI_EEPROM_HDR_SIZE_OFFSET 8 +#define RSI_EEPROM_HDR_SIZE_MASK 0x300 +#define RSI_EEPROM_LEN_OFFSET 20 +#define RSI_EEPROM_LEN_MASK 0xFFF00000 + +struct rsi_eeprom_read_frame { + __le16 len_qno; + u8 pkt_type; + u8 misc_flags; + __le32 pkt_info; + __le32 eeprom_offset; + __le16 delay_ms; + __le16 reserved3; +} __packed; + static inline u32 rsi_get_queueno(u8 *addr, u16 offset) { return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12; -- cgit v1.2.3 From f95bbd979df76319f8f87b96049efa41092d99cc Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:06 +0530 Subject: rsi: fix sdio card reset problem card reset is not working with recent kernels. Using host->card->ocr instead of host->ocr_avail solved the problem. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_sdio.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index e5ea99bb2dd8..b5ac50394e2b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -219,26 +219,18 @@ static void rsi_reset_card(struct sdio_func *pfunction) if (err) rsi_dbg(ERR_ZONE, "%s: CMD0 failed : %d\n", __func__, err); - if (!host->ocr_avail) { - /* Issue CMD5, arg = 0 */ - err = rsi_issue_sdiocommand(pfunction, - SD_IO_SEND_OP_COND, - 0, - (MMC_RSP_R4 | MMC_CMD_BCR), - &resp); - if (err) - rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n", - __func__, err); - host->ocr_avail = resp; - } + /* Issue CMD5, arg = 0 */ + err = rsi_issue_sdiocommand(pfunction, SD_IO_SEND_OP_COND, 0, + (MMC_RSP_R4 | MMC_CMD_BCR), &resp); + if (err) + rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n", __func__, err); + card->ocr = resp; /* Issue CMD5, arg = ocr. Wait till card is ready */ for (i = 0; i < 100; i++) { - err = rsi_issue_sdiocommand(pfunction, - SD_IO_SEND_OP_COND, - host->ocr_avail, - (MMC_RSP_R4 | MMC_CMD_BCR), - &resp); + err = rsi_issue_sdiocommand(pfunction, SD_IO_SEND_OP_COND, + card->ocr, + (MMC_RSP_R4 | MMC_CMD_BCR), &resp); if (err) { rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n", __func__, err); -- cgit v1.2.3 From 49ddac0d4b804aed1bba4ffdc7ed20815430cd70 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:07 +0530 Subject: rsi: chip reset for SDIO interface We need to reset the chip in teardown path so that it can work next time when driver is loaded. This patch adds support for this reset configuration for SDIO. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_sdio.c | 80 ++++++++++++++++++++++++++++++++- drivers/net/wireless/rsi/rsi_hal.h | 33 ++++++++++++++ drivers/net/wireless/rsi/rsi_sdio.h | 1 + 3 files changed, 113 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index b5ac50394e2b..ebfd29cafbbe 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -933,6 +933,84 @@ fail: return 1; } +static void ulp_read_write(struct rsi_hw *adapter, u16 addr, u32 data, + u16 len_in_bits) +{ + rsi_sdio_master_reg_write(adapter, RSI_GSPI_DATA_REG1, + ((addr << 6) | ((data >> 16) & 0xffff)), 2); + rsi_sdio_master_reg_write(adapter, RSI_GSPI_DATA_REG0, + (data & 0xffff), 2); + rsi_sdio_master_reg_write(adapter, RSI_GSPI_CTRL_REG0, + RSI_GSPI_CTRL_REG0_VALUE, 2); + rsi_sdio_master_reg_write(adapter, RSI_GSPI_CTRL_REG1, + ((len_in_bits - 1) | RSI_GSPI_TRIG), 2); + msleep(20); +} + +/*This function resets and re-initializes the chip.*/ +static void rsi_reset_chip(struct rsi_hw *adapter) +{ + __le32 data; + u8 sdio_interrupt_status = 0; + u8 request = 1; + int ret; + + rsi_dbg(INFO_ZONE, "Writing disable to wakeup register\n"); + ret = rsi_sdio_write_register(adapter, 0, SDIO_WAKEUP_REG, &request); + if (ret < 0) { + rsi_dbg(ERR_ZONE, + "%s: Failed to write SDIO wakeup register\n", __func__); + return; + } + msleep(20); + ret = rsi_sdio_read_register(adapter, RSI_FN1_INT_REGISTER, + &sdio_interrupt_status); + if (ret < 0) { + rsi_dbg(ERR_ZONE, "%s: Failed to Read Intr Status Register\n", + __func__); + return; + } + rsi_dbg(INFO_ZONE, "%s: Intr Status Register value = %d\n", + __func__, sdio_interrupt_status); + + /* Put Thread-Arch processor on hold */ + if (rsi_sdio_master_access_msword(adapter, TA_BASE_ADDR)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to set ms word to common reg\n", + __func__); + return; + } + + data = TA_HOLD_THREAD_VALUE; + if (rsi_sdio_write_register_multiple(adapter, TA_HOLD_THREAD_REG | + RSI_SD_REQUEST_MASTER, + (u8 *)&data, 4)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to hold Thread-Arch processor threads\n", + __func__); + return; + } + + /* This msleep will ensure Thread-Arch processor to go to hold + * and any pending dma transfers to rf spi in device to finish. + */ + msleep(100); + + ulp_read_write(adapter, RSI_ULP_RESET_REG, RSI_ULP_WRITE_0, 32); + ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_1, RSI_ULP_WRITE_2, 32); + ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_2, RSI_ULP_WRITE_0, 32); + ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_1, RSI_ULP_WRITE_50, + 32); + ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_2, RSI_ULP_WRITE_0, + 32); + ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_ENABLE, + RSI_ULP_TIMER_ENABLE, 32); + /* This msleep will be sufficient for the ulp + * read write operations to complete for chip reset. + */ + msleep(500); +} + /** * rsi_disconnect() - This function performs the reverse of the probe function. * @pfunction: Pointer to the sdio_func structure. @@ -956,7 +1034,7 @@ static void rsi_disconnect(struct sdio_func *pfunction) sdio_release_irq(pfunction); sdio_disable_func(pfunction); rsi_91x_deinit(adapter); - /* Resetting to take care of the case, where-in driver is re-loaded */ + rsi_reset_chip(adapter); rsi_reset_card(pfunction); sdio_release_host(pfunction); } diff --git a/drivers/net/wireless/rsi/rsi_hal.h b/drivers/net/wireless/rsi/rsi_hal.h index 902dc540849c..3179e8606b7e 100644 --- a/drivers/net/wireless/rsi/rsi_hal.h +++ b/drivers/net/wireless/rsi/rsi_hal.h @@ -52,6 +52,39 @@ #define FW_LOADING_SUCCESSFUL 'S' #define LOADING_INITIATED '1' +#define RSI_ULP_RESET_REG 0x161 +#define RSI_WATCH_DOG_TIMER_1 0x16c +#define RSI_WATCH_DOG_TIMER_2 0x16d +#define RSI_WATCH_DOG_DELAY_TIMER_1 0x16e +#define RSI_WATCH_DOG_DELAY_TIMER_2 0x16f +#define RSI_WATCH_DOG_TIMER_ENABLE 0x170 + +#define RSI_ULP_WRITE_0 00 +#define RSI_ULP_WRITE_2 02 +#define RSI_ULP_WRITE_50 50 + +#define RSI_RESTART_WDT BIT(11) +#define RSI_BYPASS_ULP_ON_WDT BIT(1) + +#define RSI_ULP_TIMER_ENABLE ((0xaa000) | RSI_RESTART_WDT | \ + RSI_BYPASS_ULP_ON_WDT) +#define RSI_RF_SPI_PROG_REG_BASE_ADDR 0x40080000 + +#define RSI_GSPI_CTRL_REG0 (RSI_RF_SPI_PROG_REG_BASE_ADDR) +#define RSI_GSPI_CTRL_REG1 (RSI_RF_SPI_PROG_REG_BASE_ADDR + 0x2) +#define RSI_GSPI_DATA_REG0 (RSI_RF_SPI_PROG_REG_BASE_ADDR + 0x4) +#define RSI_GSPI_DATA_REG1 (RSI_RF_SPI_PROG_REG_BASE_ADDR + 0x6) +#define RSI_GSPI_DATA_REG2 (RSI_RF_SPI_PROG_REG_BASE_ADDR + 0x8) + +#define RSI_GSPI_CTRL_REG0_VALUE 0x340 + +#define RSI_GSPI_DMA_MODE BIT(13) + +#define RSI_GSPI_2_ULP BIT(12) +#define RSI_GSPI_TRIG BIT(7) +#define RSI_GSPI_READ BIT(6) +#define RSI_GSPI_RF_SPI_ACTIVE BIT(8) + /* Boot loader commands */ #define SEND_RPS_FILE '2' diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h index 9fb73f68282a..f11f8189e0b6 100644 --- a/drivers/net/wireless/rsi/rsi_sdio.h +++ b/drivers/net/wireless/rsi/rsi_sdio.h @@ -58,6 +58,7 @@ enum sdio_interrupt_type { #define SDIO_READ_START_LVL 0x000FC #define SDIO_READ_FIFO_CTL 0x000FD #define SDIO_WRITE_FIFO_CTL 0x000FE +#define SDIO_WAKEUP_REG 0x000FF #define SDIO_FUN1_INTR_CLR_REG 0x0008 #define SDIO_REG_HIGH_SPEED 0x0013 -- cgit v1.2.3 From f746606a51edf85de3a11f545e506be8f4dba219 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:08 +0530 Subject: rsi: correct SDIO disconnect path handling Sometimes it's observed that we get interrupt/Rx frame when device is already detached from mac80211. In this case couple of error messages are displayed in dmesg log. This patch corrects the order so that disconnection will happen cleanly Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_sdio.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index ebfd29cafbbe..f1ba8ac3783d 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -1026,17 +1026,26 @@ static void rsi_disconnect(struct sdio_func *pfunction) return; dev = (struct rsi_91x_sdiodev *)adapter->rsi_dev; + sdio_claim_host(pfunction); + sdio_release_irq(pfunction); + sdio_release_host(pfunction); + mdelay(10); - dev->write_fail = 2; rsi_mac80211_detach(adapter); + mdelay(10); - sdio_claim_host(pfunction); - sdio_release_irq(pfunction); - sdio_disable_func(pfunction); - rsi_91x_deinit(adapter); + /* Reset Chip */ rsi_reset_chip(adapter); + + /* Resetting to take care of the case, where-in driver is re-loaded */ + sdio_claim_host(pfunction); rsi_reset_card(pfunction); + sdio_disable_func(pfunction); sdio_release_host(pfunction); + dev->write_fail = 2; + rsi_91x_deinit(adapter); + rsi_dbg(ERR_ZONE, "##### RSI SDIO device disconnected #####\n"); + } #ifdef CONFIG_PM -- cgit v1.2.3 From ea0676c470852dea84ea74658e8ddee36ebc685a Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:09 +0530 Subject: rsi: card reset for USB interface We need to reset the chip in teardown path so that it can work next time when driver is loaded. This patch adds support for this reset configuration for USB. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 72 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/rsi/rsi_usb.h | 1 + 2 files changed, 73 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index bcd7f454ef30..1d7bb9d9d09b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -558,6 +558,77 @@ fail_tx: return status; } +static int usb_ulp_read_write(struct rsi_hw *adapter, u16 addr, u32 data, + u16 len_in_bits) +{ + int ret; + + ret = rsi_usb_master_reg_write + (adapter, RSI_GSPI_DATA_REG1, + ((addr << 6) | ((data >> 16) & 0xffff)), 2); + if (ret < 0) + return ret; + + ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_DATA_REG0, + (data & 0xffff), 2); + if (ret < 0) + return ret; + + /* Initializing GSPI for ULP read/writes */ + rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG0, + RSI_GSPI_CTRL_REG0_VALUE, 2); + + ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG1, + ((len_in_bits - 1) | RSI_GSPI_TRIG), 2); + if (ret < 0) + return ret; + + msleep(20); + + return 0; +} + +static int rsi_reset_card(struct rsi_hw *adapter) +{ + int ret; + + rsi_dbg(INFO_ZONE, "Resetting Card...\n"); + rsi_usb_master_reg_write(adapter, RSI_TA_HOLD_REG, 0xE, 4); + + /* This msleep will ensure Thread-Arch processor to go to hold + * and any pending dma transfers to rf in device to finish. + */ + msleep(100); + + ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_1, + RSI_ULP_WRITE_2, 32); + if (ret < 0) + goto fail; + ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_2, + RSI_ULP_WRITE_0, 32); + if (ret < 0) + goto fail; + ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_1, + RSI_ULP_WRITE_50, 32); + if (ret < 0) + goto fail; + ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_2, + RSI_ULP_WRITE_0, 32); + if (ret < 0) + goto fail; + ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_ENABLE, + RSI_ULP_TIMER_ENABLE, 32); + if (ret < 0) + goto fail; + + rsi_dbg(INFO_ZONE, "Reset card done\n"); + return ret; + +fail: + rsi_dbg(ERR_ZONE, "Reset card failed\n"); + return ret; +} + /** * rsi_probe() - This function is called by kernel when the driver provided * Vendor and device IDs are matched. All the initialization @@ -641,6 +712,7 @@ static void rsi_disconnect(struct usb_interface *pfunction) return; rsi_mac80211_detach(adapter); + rsi_reset_card(adapter); rsi_deinit_usb_interface(adapter); rsi_91x_deinit(adapter); diff --git a/drivers/net/wireless/rsi/rsi_usb.h b/drivers/net/wireless/rsi/rsi_usb.h index 59513ac61fb3..2c5c0e7a979a 100644 --- a/drivers/net/wireless/rsi/rsi_usb.h +++ b/drivers/net/wireless/rsi/rsi_usb.h @@ -25,6 +25,7 @@ #define USB_INTERNAL_REG_1 0x25000 #define RSI_USB_READY_MAGIC_NUM 0xab #define FW_STATUS_REG 0x41050012 +#define RSI_TA_HOLD_REG 0x22000844 #define USB_VENDOR_REGISTER_READ 0x15 #define USB_VENDOR_REGISTER_WRITE 0x16 -- cgit v1.2.3 From ed833be6faa00168466c37e763dee43895b2623b Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:10 +0530 Subject: rsi: USB tx headroom cleanup USB headroom is added while submitting the data to URB as per firmware's requirement. This logic is moved to rsi_usb_card_write() so that caller need not worry about it. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 41 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 1d7bb9d9d09b..25f619506b58 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -29,19 +29,24 @@ * Return: status: 0 on success, a negative error code on failure. */ static int rsi_usb_card_write(struct rsi_hw *adapter, - void *buf, + u8 *buf, u16 len, u8 endpoint) { struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; int status; - s32 transfer; + u8 *seg = dev->tx_buffer; + int transfer; + int ep = dev->bulkout_endpoint_addr[endpoint - 1]; + memset(seg, 0, len + RSI_USB_TX_HEAD_ROOM); + memcpy(seg + RSI_USB_TX_HEAD_ROOM, buf, len); + len += RSI_USB_TX_HEAD_ROOM; + transfer = len; status = usb_bulk_msg(dev->usbdev, - usb_sndbulkpipe(dev->usbdev, - dev->bulkout_endpoint_addr[endpoint - 1]), - buf, - len, + usb_sndbulkpipe(dev->usbdev, ep), + (void *)seg, + (int)len, &transfer, HZ * 5); @@ -68,23 +73,19 @@ static int rsi_write_multiple(struct rsi_hw *adapter, u8 *data, u32 count) { - struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; - u8 *seg = dev->tx_buffer; + struct rsi_91x_usbdev *dev = + (struct rsi_91x_usbdev *)adapter->rsi_dev; - if (dev->write_fail) - return 0; + if (!adapter) + return -ENODEV; - if (endpoint == MGMT_EP) { - memset(seg, 0, RSI_USB_TX_HEAD_ROOM); - memcpy(seg + RSI_USB_TX_HEAD_ROOM, data, count); - } else { - seg = ((u8 *)data - RSI_USB_TX_HEAD_ROOM); - } + if (endpoint == 0) + return -EINVAL; + + if (dev->write_fail) + return -ENETDOWN; - return rsi_usb_card_write(adapter, - seg, - count + RSI_USB_TX_HEAD_ROOM, - endpoint); + return rsi_usb_card_write(adapter, data, count, endpoint); } /** -- cgit v1.2.3 From ac6107caa0d871e59a63f69e3617e47f1ed15c01 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 6 Jul 2017 20:07:11 +0530 Subject: rsi: correct the logic of deriving queue number Maximum valid queue number is 0x5. So anding with 0x7 should be ok here. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 25f619506b58..dc0a0b2e9afc 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -381,7 +381,7 @@ static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter, u8 *pkt, u32 len) { - u32 queueno = ((pkt[1] >> 4) & 0xf); + u32 queueno = ((pkt[1] >> 4) & 0x7); u8 endpoint; endpoint = ((queueno == RSI_WIFI_MGMT_Q) ? MGMT_EP : DATA_EP); -- cgit v1.2.3 From 5d16a1c1ae1193fdc8379fc4fb0b49b8ff9386db Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:12 +0530 Subject: rsi: rename USB endpoint macros These endpoints are for WLAN and BT protocols. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 2 +- drivers/net/wireless/rsi/rsi_usb.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index dc0a0b2e9afc..99967ce94a39 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -384,7 +384,7 @@ static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter, u32 queueno = ((pkt[1] >> 4) & 0x7); u8 endpoint; - endpoint = ((queueno == RSI_WIFI_MGMT_Q) ? MGMT_EP : DATA_EP); + endpoint = ((queueno == RSI_WIFI_MGMT_Q) ? WLAN_EP : BT_EP); return rsi_write_multiple(adapter, endpoint, diff --git a/drivers/net/wireless/rsi/rsi_usb.h b/drivers/net/wireless/rsi/rsi_usb.h index 2c5c0e7a979a..3babf81f5a39 100644 --- a/drivers/net/wireless/rsi/rsi_usb.h +++ b/drivers/net/wireless/rsi/rsi_usb.h @@ -33,8 +33,8 @@ #define MAX_RX_URBS 1 #define MAX_BULK_EP 8 -#define MGMT_EP 1 -#define DATA_EP 2 +#define WLAN_EP 1 +#define BT_EP 2 #define RSI_USB_BUF_SIZE 4096 -- cgit v1.2.3 From d1f69e418f663e67472580aecceff755ec0b5914 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:13 +0530 Subject: rsi: choose correct endpoint based on queue. Till now only management packets were handled. Let's enhance the logic for choosing endpoint to accommodate other packets. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 99967ce94a39..3febf24d619b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -384,7 +384,8 @@ static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter, u32 queueno = ((pkt[1] >> 4) & 0x7); u8 endpoint; - endpoint = ((queueno == RSI_WIFI_MGMT_Q) ? WLAN_EP : BT_EP); + endpoint = ((queueno == RSI_WIFI_MGMT_Q || queueno == RSI_WIFI_DATA_Q || + queueno == RSI_COEX_Q) ? WLAN_EP : BT_EP); return rsi_write_multiple(adapter, endpoint, -- cgit v1.2.3 From 015240018b0a6c4b9e394055a4fc7d27bdd2e104 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:14 +0530 Subject: rsi: set immediate wakeup bit immediate wakeup bit is set while sending internal management frame to the firmware. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_hal.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index c2303599c12e..7c9224fa53a3 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -149,6 +149,7 @@ int rsi_send_mgmt_pkt(struct rsi_common *common, extnd_size = ((uintptr_t)skb->data & 0x3); if (tx_params->flags & INTERNAL_MGMT_PKT) { + skb->data[1] |= BIT(7); /* Immediate Wakeup bit*/ if ((extnd_size) > skb_headroom(skb)) { rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); dev_kfree_skb(skb); -- cgit v1.2.3 From de2dea16ec9f6b483c13f9e1709821c76699fb6b Mon Sep 17 00:00:00 2001 From: Pavani Muthyala Date: Thu, 6 Jul 2017 20:07:15 +0530 Subject: rsi: management frame descriptor preparation cleanup Currently this descriptor is prepared with the help of __le16 pointer. This patch makes use of a structure to prepare the descriptor in a cleaner way. Signed-off-by: Pavani Muthyala Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_hal.c | 37 +++++++++++++++++----------------- drivers/net/wireless/rsi/rsi_hal.h | 17 ++++++++++++++++ 2 files changed, 35 insertions(+), 19 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index 7c9224fa53a3..9eaa0a265c77 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -138,9 +138,9 @@ int rsi_send_mgmt_pkt(struct rsi_common *common, struct ieee80211_bss_conf *bss; struct ieee80211_hw *hw = adapter->hw; struct ieee80211_conf *conf = &hw->conf; + struct rsi_mgmt_desc *mgmt_desc; struct skb_info *tx_params; int status = -E2BIG; - __le16 *msg; u8 extnd_size; u8 vap_id = 0; @@ -176,44 +176,43 @@ int rsi_send_mgmt_pkt(struct rsi_common *common, skb_push(skb, FRAME_DESC_SZ); memset(skb->data, 0, FRAME_DESC_SZ); - msg = (__le16 *)skb->data; + mgmt_desc = (struct rsi_mgmt_desc *)skb->data; if (skb->len > MAX_MGMT_PKT_SIZE) { rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); goto err; } - msg[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) | - (RSI_WIFI_MGMT_Q << 12)); - msg[1] = cpu_to_le16(TX_DOT11_MGMT); - msg[2] = cpu_to_le16(MIN_802_11_HDR_LEN << 8); - msg[3] = cpu_to_le16(RATE_INFO_ENABLE); - msg[6] = cpu_to_le16(le16_to_cpu(wh->seq_ctrl) >> 4); + rsi_set_len_qno(&mgmt_desc->len_qno, (skb->len - FRAME_DESC_SZ), + RSI_WIFI_MGMT_Q); + mgmt_desc->frame_type = TX_DOT11_MGMT; + mgmt_desc->header_len = MIN_802_11_HDR_LEN; + mgmt_desc->info_cap |= cpu_to_le16(RATE_INFO_ENABLE); + mgmt_desc->seq_ctrl = cpu_to_le16(le16_to_cpu(wh->seq_ctrl) >> 4); if (wh->addr1[0] & BIT(0)) - msg[3] |= cpu_to_le16(RSI_BROADCAST_PKT); + mgmt_desc->info_cap |= cpu_to_le16(RSI_BROADCAST_PKT); if (common->band == NL80211_BAND_2GHZ) - msg[4] = cpu_to_le16(RSI_11B_MODE); + mgmt_desc->rate_info = RSI_11B_MODE; else - msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE); + mgmt_desc->rate_info = (RSI_RATE_6 & 0x0f) | RSI_11G_MODE; if (conf_is_ht40(conf)) { - msg[4] = cpu_to_le16(0xB | RSI_11G_MODE); - msg[5] = cpu_to_le16(0x6); + mgmt_desc->rate_info = 0xB | RSI_11G_MODE; + mgmt_desc->bbp_info = BBP_INFO_40MHZ; } /* Indicate to firmware to give cfm */ if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) { - msg[1] |= cpu_to_le16(BIT(10)); - msg[7] = cpu_to_le16(PROBEREQ_CONFIRM); + mgmt_desc->misc_flags |= BIT(2); + mgmt_desc->cfm_frame_type = PROBEREQ_CONFIRM; common->mgmt_q_block = true; } + mgmt_desc->vap_info = vap_id << 8; - msg[7] |= cpu_to_le16(vap_id << 8); - - status = adapter->host_intf_ops->write_pkt(common->priv, (u8 *)msg, - skb->len); + status = adapter->host_intf_ops->write_pkt(common->priv, + (u8 *)mgmt_desc, skb->len); if (status) rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__); diff --git a/drivers/net/wireless/rsi/rsi_hal.h b/drivers/net/wireless/rsi/rsi_hal.h index 3179e8606b7e..da115dd11bdd 100644 --- a/drivers/net/wireless/rsi/rsi_hal.h +++ b/drivers/net/wireless/rsi/rsi_hal.h @@ -99,6 +99,8 @@ #define RSI_DEV_OPMODE_WIFI_ALONE 1 #define RSI_DEV_COEX_MODE_WIFI_ALONE 1 +#define BBP_INFO_40MHZ 0x6 + struct bl_header { __le32 flags; __le32 image_no; @@ -112,6 +114,21 @@ struct ta_metadata { unsigned int address; }; +struct rsi_mgmt_desc { + __le16 len_qno; + u8 frame_type; + u8 misc_flags; + u8 reserved1; + u8 header_len; + __le16 info_cap; + u8 rate_info; + u8 reserved2; + u16 bbp_info; + __le16 seq_ctrl; + u8 cfm_frame_type; + u8 vap_info; +} __packed; + int rsi_hal_device_init(struct rsi_hw *adapter); #endif -- cgit v1.2.3 From 6507de6df9007b24d843287a6feba10c1dafffd6 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Thu, 6 Jul 2017 20:07:16 +0530 Subject: rsi: separate function for management packet descriptor Management descriptor preparation is move to a separate function as it will be called from a different context in upcoming patches. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_hal.c | 132 +++++++++++++++++++++------------ drivers/net/wireless/rsi/rsi_hal.h | 10 +-- drivers/net/wireless/rsi/rsi_main.h | 7 ++ drivers/net/wireless/rsi/rsi_mgmt.h | 1 + 4 files changed, 98 insertions(+), 52 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index 9eaa0a265c77..f8ccb1cb7c75 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -25,6 +25,89 @@ static struct ta_metadata metadata_flash_content[] = { {"rsi/rs9113_wlan_qspi.rps", 0x00010000}, }; +/*This function prepares descriptor for given management packet*/ + +static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) +{ + struct rsi_hw *adapter = common->priv; + struct ieee80211_hdr *wh = NULL; + struct ieee80211_tx_info *info; + struct ieee80211_conf *conf = &adapter->hw->conf; + struct ieee80211_vif *vif = NULL; + struct rsi_mgmt_desc *mgmt_desc; + struct skb_info *tx_params; + struct ieee80211_bss_conf *bss = NULL; + struct xtended_desc *xtend_desc = NULL; + u8 header_size; + u32 dword_align_bytes = 0; + + info = IEEE80211_SKB_CB(skb); + tx_params = (struct skb_info *)info->driver_data; + + /* Update header size */ + header_size = FRAME_DESC_SZ + sizeof(struct xtended_desc); + if (header_size > skb_headroom(skb)) { + rsi_dbg(ERR_ZONE, + "%s: Failed to add extended descriptor\n", + __func__); + return -ENOSPC; + } + skb_push(skb, header_size); + dword_align_bytes = ((unsigned long)skb->data & 0x3f); + if (dword_align_bytes > skb_headroom(skb)) { + rsi_dbg(ERR_ZONE, + "%s: Failed to add dword align\n", __func__); + return -ENOSPC; + } + skb_push(skb, dword_align_bytes); + header_size += dword_align_bytes; + + tx_params->internal_hdr_size = header_size; + memset(&skb->data[0], 0, header_size); + bss = &info->control.vif->bss_conf; + wh = (struct ieee80211_hdr *)&skb->data[header_size]; + vif = adapter->vifs[0]; + + mgmt_desc = (struct rsi_mgmt_desc *)skb->data; + xtend_desc = (struct xtended_desc *)&skb->data[FRAME_DESC_SZ]; + + if (skb->len > MAX_MGMT_PKT_SIZE) { + rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); + return -EINVAL; + } + rsi_set_len_qno(&mgmt_desc->len_qno, (skb->len - FRAME_DESC_SZ), + RSI_WIFI_MGMT_Q); + mgmt_desc->frame_type = TX_DOT11_MGMT; + mgmt_desc->header_len = MIN_802_11_HDR_LEN; + mgmt_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; + mgmt_desc->frame_info |= cpu_to_le16(RATE_INFO_ENABLE); + if (is_broadcast_ether_addr(wh->addr1)) + mgmt_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT); + + mgmt_desc->seq_ctrl = + cpu_to_le16(IEEE80211_SEQ_TO_SN(le16_to_cpu(wh->seq_ctrl))); + if (common->band == NL80211_BAND_2GHZ) + mgmt_desc->rate_info = RSI_RATE_1; + else + mgmt_desc->rate_info = RSI_RATE_6; + + if (conf_is_ht40(conf)) + mgmt_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE); + + if (ieee80211_is_probe_req(wh->frame_control)) { + if (!bss->assoc) { + rsi_dbg(INFO_ZONE, + "%s: blocking mgmt queue\n", __func__); + mgmt_desc->misc_flags = RSI_DESC_REQUIRE_CFM_TO_HOST; + xtend_desc->confirm_frame_type = PROBEREQ_CONFIRM; + common->mgmt_q_block = true; + rsi_dbg(INFO_ZONE, "Mgmt queue blocked\n"); + } + } + + return 0; +} + /** * rsi_send_data_pkt() - This function sends the recieved data packet from * driver to device. @@ -133,16 +216,10 @@ int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb) { struct rsi_hw *adapter = common->priv; - struct ieee80211_hdr *wh; struct ieee80211_tx_info *info; - struct ieee80211_bss_conf *bss; - struct ieee80211_hw *hw = adapter->hw; - struct ieee80211_conf *conf = &hw->conf; - struct rsi_mgmt_desc *mgmt_desc; struct skb_info *tx_params; int status = -E2BIG; u8 extnd_size; - u8 vap_id = 0; info = IEEE80211_SKB_CB(skb); tx_params = (struct skb_info *)info->driver_data; @@ -168,51 +245,12 @@ int rsi_send_mgmt_pkt(struct rsi_common *common, return status; } - bss = &info->control.vif->bss_conf; - wh = (struct ieee80211_hdr *)&skb->data[0]; - if (FRAME_DESC_SZ > skb_headroom(skb)) goto err; - skb_push(skb, FRAME_DESC_SZ); - memset(skb->data, 0, FRAME_DESC_SZ); - mgmt_desc = (struct rsi_mgmt_desc *)skb->data; - - if (skb->len > MAX_MGMT_PKT_SIZE) { - rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); - goto err; - } - - rsi_set_len_qno(&mgmt_desc->len_qno, (skb->len - FRAME_DESC_SZ), - RSI_WIFI_MGMT_Q); - mgmt_desc->frame_type = TX_DOT11_MGMT; - mgmt_desc->header_len = MIN_802_11_HDR_LEN; - mgmt_desc->info_cap |= cpu_to_le16(RATE_INFO_ENABLE); - mgmt_desc->seq_ctrl = cpu_to_le16(le16_to_cpu(wh->seq_ctrl) >> 4); - - if (wh->addr1[0] & BIT(0)) - mgmt_desc->info_cap |= cpu_to_le16(RSI_BROADCAST_PKT); - - if (common->band == NL80211_BAND_2GHZ) - mgmt_desc->rate_info = RSI_11B_MODE; - else - mgmt_desc->rate_info = (RSI_RATE_6 & 0x0f) | RSI_11G_MODE; - - if (conf_is_ht40(conf)) { - mgmt_desc->rate_info = 0xB | RSI_11G_MODE; - mgmt_desc->bbp_info = BBP_INFO_40MHZ; - } - - /* Indicate to firmware to give cfm */ - if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) { - mgmt_desc->misc_flags |= BIT(2); - mgmt_desc->cfm_frame_type = PROBEREQ_CONFIRM; - common->mgmt_q_block = true; - } - mgmt_desc->vap_info = vap_id << 8; - + rsi_prepare_mgmt_desc(common, skb); status = adapter->host_intf_ops->write_pkt(common->priv, - (u8 *)mgmt_desc, skb->len); + (u8 *)skb->data, skb->len); if (status) rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__); diff --git a/drivers/net/wireless/rsi/rsi_hal.h b/drivers/net/wireless/rsi/rsi_hal.h index da115dd11bdd..adbe54e10007 100644 --- a/drivers/net/wireless/rsi/rsi_hal.h +++ b/drivers/net/wireless/rsi/rsi_hal.h @@ -118,14 +118,14 @@ struct rsi_mgmt_desc { __le16 len_qno; u8 frame_type; u8 misc_flags; - u8 reserved1; + u8 xtend_desc_size; u8 header_len; - __le16 info_cap; + __le16 frame_info; u8 rate_info; - u8 reserved2; - u16 bbp_info; + u8 reserved1; + __le16 bbp_info; __le16 seq_ctrl; - u8 cfm_frame_type; + u8 reserved2; u8 vap_info; } __packed; diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index cbf29c36f4f4..699e9da1a87b 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -105,6 +105,7 @@ struct skb_info { u16 channel; s8 tid; s8 sta_id; + u8 internal_hdr_size; }; enum edca_queue { @@ -158,6 +159,12 @@ struct cqm_info { u32 rssi_hyst; }; +struct xtended_desc { + u8 confirm_frame_type; + u8 retry_cnt; + u16 reserved; +}; + struct rsi_hw; struct rsi_common { diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 47926c9f2027..8e05e0b222e0 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -63,6 +63,7 @@ #define RF_RESET_ENABLE BIT(3) #define RATE_INFO_ENABLE BIT(0) #define RSI_BROADCAST_PKT BIT(9) +#define RSI_DESC_REQUIRE_CFM_TO_HOST BIT(2) #define UPPER_20_ENABLE (0x2 << 12) #define LOWER_20_ENABLE (0x4 << 12) -- cgit v1.2.3 From af193097767819b72456800143cf577e453a9331 Mon Sep 17 00:00:00 2001 From: Pavani Muthyala Date: Thu, 6 Jul 2017 20:07:17 +0530 Subject: rsi: data packet descriptor code cleanup Currently this descriptor is prepared with the help of __le16 pointer. This patch makes use of a structure to prepare the descriptor in a cleaner way. Signed-off-by: Pavani Muthyala Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_hal.c | 41 +++++++++++++++++----------------- drivers/net/wireless/rsi/rsi_hal.h | 13 +++++++++++ drivers/net/wireless/rsi/rsi_mgmt.h | 3 +++ 3 files changed, 36 insertions(+), 21 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index f8ccb1cb7c75..9da2fc806a4d 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -123,10 +123,10 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) struct ieee80211_tx_info *info; struct skb_info *tx_params; struct ieee80211_bss_conf *bss; + struct rsi_data_desc *data_desc; int status; u8 ieee80211_size = MIN_802_11_HDR_LEN; u8 extnd_size; - __le16 *frame_desc; u16 seq_num; info = IEEE80211_SKB_CB(skb); @@ -150,12 +150,12 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) } skb_push(skb, (FRAME_DESC_SZ + extnd_size)); - frame_desc = (__le16 *)&skb->data[0]; - memset((u8 *)frame_desc, 0, FRAME_DESC_SZ); + data_desc = (struct rsi_data_desc *)skb->data; + memset(data_desc, 0, sizeof(*data_desc)); if (ieee80211_is_data_qos(tmp_hdr->frame_control)) { ieee80211_size += 2; - frame_desc[6] |= cpu_to_le16(BIT(12)); + data_desc->mac_flags |= cpu_to_le16(RSI_QOS_ENABLE); } if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) && @@ -164,35 +164,34 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) ieee80211_size += 4; else ieee80211_size += 8; - frame_desc[6] |= cpu_to_le16(BIT(15)); + data_desc->mac_flags |= cpu_to_le16(RSI_ENCRYPT_PKT); } + rsi_set_len_qno(&data_desc->len_qno, (skb->len - FRAME_DESC_SZ), + RSI_WIFI_DATA_Q); + data_desc->header_len = ieee80211_size; + data_desc->xtend_desc_size = extnd_size; - frame_desc[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) | - (RSI_WIFI_DATA_Q << 12)); - frame_desc[2] = cpu_to_le16((extnd_size) | (ieee80211_size) << 8); - - if (common->min_rate != 0xffff) { + if (common->min_rate != RSI_RATE_AUTO) { /* Send fixed rate */ - frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE); - frame_desc[4] = cpu_to_le16(common->min_rate); + data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); + data_desc->rate_info = cpu_to_le16(common->min_rate); if (conf_is_ht40(&common->priv->hw->conf)) - frame_desc[5] = cpu_to_le16(FULL40M_ENABLE); + data_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE); if (common->vif_info[0].sgi) { if (common->min_rate & 0x100) /* Only MCS rates */ - frame_desc[4] |= + data_desc->rate_info |= cpu_to_le16(ENABLE_SHORTGI_RATE); } - } - frame_desc[6] |= cpu_to_le16(seq_num & 0xfff); - frame_desc[7] = cpu_to_le16(((tx_params->tid & 0xf) << 4) | - (skb->priority & 0xf) | - (tx_params->sta_id << 8)); + data_desc->mac_flags = cpu_to_le16(seq_num & 0xfff); + data_desc->qid_tid = ((skb->priority & 0xf) | + ((tx_params->tid & 0xf) << 4)); + data_desc->sta_id = tx_params->sta_id; - status = adapter->host_intf_ops->write_pkt(common->priv, skb->data, + status = adapter->host_intf_ops->write_pkt(adapter, skb->data, skb->len); if (status) rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", @@ -200,7 +199,7 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) err: ++common->tx_stats.total_tx_pkt_freed[skb->priority]; - rsi_indicate_tx_status(common->priv, skb, status); + rsi_indicate_tx_status(adapter, skb, status); return status; } diff --git a/drivers/net/wireless/rsi/rsi_hal.h b/drivers/net/wireless/rsi/rsi_hal.h index adbe54e10007..2ae5863b9390 100644 --- a/drivers/net/wireless/rsi/rsi_hal.h +++ b/drivers/net/wireless/rsi/rsi_hal.h @@ -129,6 +129,19 @@ struct rsi_mgmt_desc { u8 vap_info; } __packed; +struct rsi_data_desc { + __le16 len_qno; + u16 reserved; + u8 xtend_desc_size; + u8 header_len; + __le16 frame_info; + __le16 rate_info; + __le16 bbp_info; + __le16 mac_flags; + u8 qid_tid; + u8 sta_id; +} __packed; + int rsi_hal_device_init(struct rsi_hw *adapter); #endif diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 8e05e0b222e0..058dfe539922 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -64,6 +64,8 @@ #define RATE_INFO_ENABLE BIT(0) #define RSI_BROADCAST_PKT BIT(9) #define RSI_DESC_REQUIRE_CFM_TO_HOST BIT(2) +#define RSI_QOS_ENABLE BIT(12) +#define RSI_ENCRYPT_PKT BIT(15) #define UPPER_20_ENABLE (0x2 << 12) #define LOWER_20_ENABLE (0x4 << 12) @@ -122,6 +124,7 @@ #define RSI_RATE_MCS6 0x106 #define RSI_RATE_MCS7 0x107 #define RSI_RATE_MCS7_SG 0x307 +#define RSI_RATE_AUTO 0xffff #define BW_20MHZ 0 #define BW_40MHZ 1 -- cgit v1.2.3 From 0eb42586cf876ebeea07c5952d7c95f3b81d685b Mon Sep 17 00:00:00 2001 From: Pavani Muthyala Date: Thu, 6 Jul 2017 20:07:18 +0530 Subject: rsi: data packet descriptor enhancements This patch covers some enhancements in data packet descriptor preparation especially for EAPOL, multicast/broadcast packets. Signed-off-by: Pavani Muthyala Signed-off-by: Amitkumar Karwar Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_hal.c | 64 ++++++++++++++++++++++++++-------- drivers/net/wireless/rsi/rsi_hal.h | 3 +- drivers/net/wireless/rsi/rsi_mgmt.h | 3 ++ 3 files changed, 55 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index 9da2fc806a4d..af7fe87940c2 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -119,14 +119,18 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) { struct rsi_hw *adapter = common->priv; - struct ieee80211_hdr *tmp_hdr; + struct ieee80211_hdr *wh = NULL; struct ieee80211_tx_info *info; + struct ieee80211_vif *vif = NULL; struct skb_info *tx_params; struct ieee80211_bss_conf *bss; struct rsi_data_desc *data_desc; + struct xtended_desc *xtend_desc; int status; u8 ieee80211_size = MIN_802_11_HDR_LEN; - u8 extnd_size; + u8 header_size; + u8 vap_id = 0; + u8 dword_align_bytes; u16 seq_num; info = IEEE80211_SKB_CB(skb); @@ -137,23 +141,34 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) status = -EINVAL; goto err; } - - tmp_hdr = (struct ieee80211_hdr *)&skb->data[0]; - seq_num = (le16_to_cpu(tmp_hdr->seq_ctrl) >> 4); - - extnd_size = ((uintptr_t)skb->data & 0x3); - - if ((FRAME_DESC_SZ + extnd_size) > skb_headroom(skb)) { + header_size = FRAME_DESC_SZ + sizeof(struct xtended_desc); + if (header_size > skb_headroom(skb)) { rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); status = -ENOSPC; goto err; } + skb_push(skb, header_size); + dword_align_bytes = ((unsigned long)skb->data & 0x3f); + if (header_size > skb_headroom(skb)) { + rsi_dbg(ERR_ZONE, "%s: Not enough headroom\n", __func__); + status = -ENOSPC; + goto err; + } + skb_push(skb, dword_align_bytes); + header_size += dword_align_bytes; - skb_push(skb, (FRAME_DESC_SZ + extnd_size)); + tx_params->internal_hdr_size = header_size; data_desc = (struct rsi_data_desc *)skb->data; - memset(data_desc, 0, sizeof(*data_desc)); + memset(data_desc, 0, header_size); + + xtend_desc = (struct xtended_desc *)&skb->data[FRAME_DESC_SZ]; + wh = (struct ieee80211_hdr *)&skb->data[header_size]; + seq_num = (le16_to_cpu(wh->seq_ctrl) >> 4); + vif = adapter->vifs[0]; - if (ieee80211_is_data_qos(tmp_hdr->frame_control)) { + data_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; + + if (ieee80211_is_data_qos(wh->frame_control)) { ieee80211_size += 2; data_desc->mac_flags |= cpu_to_le16(RSI_QOS_ENABLE); } @@ -169,7 +184,6 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) rsi_set_len_qno(&data_desc->len_qno, (skb->len - FRAME_DESC_SZ), RSI_WIFI_DATA_Q); data_desc->header_len = ieee80211_size; - data_desc->xtend_desc_size = extnd_size; if (common->min_rate != RSI_RATE_AUTO) { /* Send fixed rate */ @@ -184,6 +198,21 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) data_desc->rate_info |= cpu_to_le16(ENABLE_SHORTGI_RATE); } + + } + + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + rsi_dbg(INFO_ZONE, "*** Tx EAPOL ***\n"); + + data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); + if (common->band == NL80211_BAND_5GHZ) + data_desc->rate_info = cpu_to_le16(RSI_RATE_6); + else + data_desc->rate_info = cpu_to_le16(RSI_RATE_1); + data_desc->mac_flags |= cpu_to_le16(RSI_REKEY_PURPOSE); + data_desc->misc_flags |= RSI_FETCH_RETRY_CNT_FRM_HST; +#define EAPOL_RETRY_CNT 15 + xtend_desc->retry_cnt = EAPOL_RETRY_CNT; } data_desc->mac_flags = cpu_to_le16(seq_num & 0xfff); @@ -191,7 +220,14 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) ((tx_params->tid & 0xf) << 4)); data_desc->sta_id = tx_params->sta_id; - status = adapter->host_intf_ops->write_pkt(adapter, skb->data, + if ((is_broadcast_ether_addr(wh->addr1)) || + (is_multicast_ether_addr(wh->addr1))) { + data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); + data_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT); + data_desc->sta_id = vap_id; + } + + status = adapter->host_intf_ops->write_pkt(common->priv, skb->data, skb->len); if (status) rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", diff --git a/drivers/net/wireless/rsi/rsi_hal.h b/drivers/net/wireless/rsi/rsi_hal.h index 2ae5863b9390..00c6a0c5a891 100644 --- a/drivers/net/wireless/rsi/rsi_hal.h +++ b/drivers/net/wireless/rsi/rsi_hal.h @@ -131,7 +131,8 @@ struct rsi_mgmt_desc { struct rsi_data_desc { __le16 len_qno; - u16 reserved; + u8 cfm_frame_type; + u8 misc_flags; u8 xtend_desc_size; u8 header_len; __le16 frame_info; diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 058dfe539922..a8a195e8a91e 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -64,7 +64,10 @@ #define RATE_INFO_ENABLE BIT(0) #define RSI_BROADCAST_PKT BIT(9) #define RSI_DESC_REQUIRE_CFM_TO_HOST BIT(2) +#define RSI_ADD_DELTA_TSF_VAP_ID BIT(3) +#define RSI_FETCH_RETRY_CNT_FRM_HST BIT(4) #define RSI_QOS_ENABLE BIT(12) +#define RSI_REKEY_PURPOSE BIT(13) #define RSI_ENCRYPT_PKT BIT(15) #define UPPER_20_ENABLE (0x2 << 12) -- cgit v1.2.3 From ceb2e4eab9948a8ed14fd5b439ccb66dec4931e0 Mon Sep 17 00:00:00 2001 From: Pavani Muthyala Date: Thu, 6 Jul 2017 20:07:19 +0530 Subject: rsi: separate function for data packet descriptor Data packet descriptor preparation code is moved a separate function. Signed-off-by: Pavani Muthyala Signed-off-by: Amitkumar Karwar Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_hal.c | 51 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 23 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index af7fe87940c2..b0a7a1511aee 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -108,25 +108,15 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) return 0; } -/** - * rsi_send_data_pkt() - This function sends the recieved data packet from - * driver to device. - * @common: Pointer to the driver private structure. - * @skb: Pointer to the socket buffer structure. - * - * Return: status: 0 on success, -1 on failure. - */ -int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) +/* This function prepares descriptor for given data packet */ +static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) { - struct rsi_hw *adapter = common->priv; struct ieee80211_hdr *wh = NULL; struct ieee80211_tx_info *info; - struct ieee80211_vif *vif = NULL; struct skb_info *tx_params; struct ieee80211_bss_conf *bss; struct rsi_data_desc *data_desc; struct xtended_desc *xtend_desc; - int status; u8 ieee80211_size = MIN_802_11_HDR_LEN; u8 header_size; u8 vap_id = 0; @@ -137,22 +127,16 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) bss = &info->control.vif->bss_conf; tx_params = (struct skb_info *)info->driver_data; - if (!bss->assoc) { - status = -EINVAL; - goto err; - } header_size = FRAME_DESC_SZ + sizeof(struct xtended_desc); if (header_size > skb_headroom(skb)) { rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); - status = -ENOSPC; - goto err; + return -ENOSPC; } skb_push(skb, header_size); dword_align_bytes = ((unsigned long)skb->data & 0x3f); if (header_size > skb_headroom(skb)) { rsi_dbg(ERR_ZONE, "%s: Not enough headroom\n", __func__); - status = -ENOSPC; - goto err; + return -ENOSPC; } skb_push(skb, dword_align_bytes); header_size += dword_align_bytes; @@ -164,7 +148,6 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) xtend_desc = (struct xtended_desc *)&skb->data[FRAME_DESC_SZ]; wh = (struct ieee80211_hdr *)&skb->data[header_size]; seq_num = (le16_to_cpu(wh->seq_ctrl) >> 4); - vif = adapter->vifs[0]; data_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; @@ -227,11 +210,33 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) data_desc->sta_id = vap_id; } + return 0; +} + +/* This function sends received data packet from driver to device */ +int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) +{ + struct rsi_hw *adapter = common->priv; + struct ieee80211_tx_info *info; + struct ieee80211_bss_conf *bss; + int status = -EIO; + + info = IEEE80211_SKB_CB(skb); + bss = &info->control.vif->bss_conf; + + if (!bss->assoc) { + status = -EINVAL; + goto err; + } + + status = rsi_prepare_data_desc(common, skb); + if (status) + goto err; + status = adapter->host_intf_ops->write_pkt(common->priv, skb->data, skb->len); if (status) - rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", - __func__); + rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", __func__); err: ++common->tx_stats.total_tx_pkt_freed[skb->priority]; -- cgit v1.2.3 From 5f6ae7cae21120bd3236873872532b5864c44ebc Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:20 +0530 Subject: rsi: rename variable in_sdio_litefi_irq The variable in_sdio_litefi_irq is renamed to sdio_irq_task, as it is more relevant here. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_sdio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h index f11f8189e0b6..cbbc0448dc40 100644 --- a/drivers/net/wireless/rsi/rsi_sdio.h +++ b/drivers/net/wireless/rsi/rsi_sdio.h @@ -104,7 +104,7 @@ struct receive_info { struct rsi_91x_sdiodev { struct sdio_func *pfunction; - struct task_struct *in_sdio_litefi_irq; + struct task_struct *sdio_irq_task; struct receive_info rx_info; u32 next_read_delay; u32 sdio_high_speed_enable; -- cgit v1.2.3 From 72bccf51d459282e7bf66e2721d4a831a209e918 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:21 +0530 Subject: rsi: Optimise sdio claim and release host SDIO host is already claimed in our interrupt handler. Some lower level APIs claims host while performing SDIO read or write operations. Let's use sdio_irq_task variable to check if we are in interrupt context and claim/release the host accordingly. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_sdio.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index f1ba8ac3783d..42d558b61721 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -138,12 +138,15 @@ static int rsi_issue_sdiocommand(struct sdio_func *func, static void rsi_handle_interrupt(struct sdio_func *function) { struct rsi_hw *adapter = sdio_get_drvdata(function); + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; if (adapter->priv->fsm_state == FSM_FW_NOT_LOADED) return; - sdio_release_host(function); + + dev->sdio_irq_task = current; rsi_interrupt_handler(adapter); - sdio_claim_host(function); + dev->sdio_irq_task = NULL; } /** @@ -407,14 +410,16 @@ int rsi_sdio_read_register(struct rsi_hw *adapter, u8 fun_num = 0; int status; - sdio_claim_host(dev->pfunction); + if (likely(dev->sdio_irq_task != current)) + sdio_claim_host(dev->pfunction); if (fun_num == 0) *data = sdio_f0_readb(dev->pfunction, addr, &status); else *data = sdio_readb(dev->pfunction, addr, &status); - sdio_release_host(dev->pfunction); + if (likely(dev->sdio_irq_task != current)) + sdio_release_host(dev->pfunction); return status; } @@ -438,14 +443,16 @@ int rsi_sdio_write_register(struct rsi_hw *adapter, (struct rsi_91x_sdiodev *)adapter->rsi_dev; int status = 0; - sdio_claim_host(dev->pfunction); + if (likely(dev->sdio_irq_task != current)) + sdio_claim_host(dev->pfunction); if (function == 0) sdio_f0_writeb(dev->pfunction, *data, addr, &status); else sdio_writeb(dev->pfunction, *data, addr, &status); - sdio_release_host(dev->pfunction); + if (likely(dev->sdio_irq_task != current)) + sdio_release_host(dev->pfunction); return status; } @@ -490,11 +497,13 @@ static int rsi_sdio_read_register_multiple(struct rsi_hw *adapter, (struct rsi_91x_sdiodev *)adapter->rsi_dev; u32 status; - sdio_claim_host(dev->pfunction); + if (likely(dev->sdio_irq_task != current)) + sdio_claim_host(dev->pfunction); status = sdio_readsb(dev->pfunction, data, addr, count); - sdio_release_host(dev->pfunction); + if (likely(dev->sdio_irq_task != current)) + sdio_release_host(dev->pfunction); if (status != 0) rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 read failed\n", __func__); @@ -532,11 +541,13 @@ int rsi_sdio_write_register_multiple(struct rsi_hw *adapter, dev->write_fail++; } - sdio_claim_host(dev->pfunction); + if (likely(dev->sdio_irq_task != current)) + sdio_claim_host(dev->pfunction); status = sdio_writesb(dev->pfunction, addr, data, count); - sdio_release_host(dev->pfunction); + if (likely(dev->sdio_irq_task != current)) + sdio_release_host(dev->pfunction); if (status) { rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 write failed %d\n", -- cgit v1.2.3 From ebf084ea0ec749f6ec576a67762f0ec34c514111 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:22 +0530 Subject: rsi: SDIO Rx packet processing enhancement Newer firmware sends information about number of blocks through interrupt only. We don't need to read extra register for this. This patch adds needed driver changes for this enhancment. The change here is backward compatible Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_sdio_ops.c | 34 ++++++++++++++++++++++------- drivers/net/wireless/rsi/rsi_main.h | 1 + drivers/net/wireless/rsi/rsi_sdio.h | 1 + 3 files changed, 28 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index df2a63b1f15c..b6d0e2ae1412 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -69,20 +69,37 @@ int rsi_sdio_master_access_msword(struct rsi_hw *adapter, u16 ms_word) static int rsi_process_pkt(struct rsi_common *common) { struct rsi_hw *adapter = common->priv; + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; u8 num_blks = 0; u32 rcv_pkt_len = 0; int status = 0; + u8 value = 0; - status = rsi_sdio_read_register(adapter, - SDIO_RX_NUM_BLOCKS_REG, - &num_blks); + num_blks = ((adapter->interrupt_status & 1) | + ((adapter->interrupt_status >> RECV_NUM_BLOCKS) << 1)); - if (status) { - rsi_dbg(ERR_ZONE, - "%s: Failed to read pkt length from the card:\n", - __func__); - return status; + if (!num_blks) { + status = rsi_sdio_read_register(adapter, + SDIO_RX_NUM_BLOCKS_REG, + &value); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to read pkt length from the card:\n", + __func__); + return status; + } + num_blks = value & 0x1f; } + + if (dev->write_fail == 2) + rsi_sdio_ack_intr(common->priv, (1 << MSDU_PKT_PENDING)); + + if (unlikely(!num_blks)) { + dev->write_fail = 2; + return -1; + } + rcv_pkt_len = (num_blks * 256); common->rx_data_pkt = kmalloc(rcv_pkt_len, GFP_KERNEL); @@ -224,6 +241,7 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) mutex_unlock(&common->tx_rxlock); return; } + adapter->interrupt_status = isr_status; if (isr_status == 0) { rsi_set_event(&common->tx_thread.event); diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 699e9da1a87b..c2e1c1ce285c 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -275,6 +275,7 @@ struct rsi_hw { bool blcmd_timer_expired; u32 flash_capacity; struct eepromrw_info eeprom; + u32 interrupt_status; u8 dfs_region; void *rsi_dev; struct rsi_host_intf_ops *host_intf_ops; diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h index cbbc0448dc40..3cf67565feb1 100644 --- a/drivers/net/wireless/rsi/rsi_sdio.h +++ b/drivers/net/wireless/rsi/rsi_sdio.h @@ -41,6 +41,7 @@ enum sdio_interrupt_type { #define PKT_BUFF_FULL 1 #define PKT_MGMT_BUFF_FULL 2 #define MSDU_PKT_PENDING 3 +#define RECV_NUM_BLOCKS 4 /* Interrupt Bit Related Macros */ #define PKT_BUFF_AVAILABLE 1 #define FW_ASSERT_IND 2 -- cgit v1.2.3 From 6c409cad3d2b66cb4fd184d140fdf48e34890249 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:23 +0530 Subject: rsi: use separate mutex lock for receive thread Deadlock issue is observed during our stress tests. The root cause for the issue is same lock is used between tx and rx threads. This patch adds a separate mutex lock for rx thread to resolve the problem. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_main.c | 1 + drivers/net/wireless/rsi/rsi_91x_sdio_ops.c | 10 +++++----- drivers/net/wireless/rsi/rsi_91x_usb_ops.c | 6 +++--- drivers/net/wireless/rsi/rsi_main.h | 2 ++ 4 files changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c index f1cde0ca81f9..939f568dae9b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_main.c +++ b/drivers/net/wireless/rsi/rsi_91x_main.c @@ -221,6 +221,7 @@ struct rsi_hw *rsi_91x_init(void) rsi_init_event(&common->tx_thread.event); mutex_init(&common->mutex); mutex_init(&common->tx_rxlock); + mutex_init(&common->rx_lock); if (rsi_create_kthread(common, &common->tx_thread, diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index b6d0e2ae1412..b3f7adc9d085 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -230,7 +230,7 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) dev->rx_info.sdio_int_counter++; do { - mutex_lock(&common->tx_rxlock); + mutex_lock(&common->rx_lock); status = rsi_sdio_read_register(common->priv, RSI_FN1_INT_REGISTER, &isr_status); @@ -238,7 +238,7 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) rsi_dbg(ERR_ZONE, "%s: Failed to Read Intr Status Register\n", __func__); - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->rx_lock); return; } adapter->interrupt_status = isr_status; @@ -246,7 +246,7 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) if (isr_status == 0) { rsi_set_event(&common->tx_thread.event); dev->rx_info.sdio_intr_status_zero++; - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->rx_lock); return; } @@ -304,7 +304,7 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) rsi_dbg(ERR_ZONE, "%s: Failed to read pkt\n", __func__); - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->rx_lock); return; } break; @@ -319,7 +319,7 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) } isr_status ^= BIT(isr_type - 1); } while (isr_status); - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->rx_lock); } while (1); } diff --git a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c index d3e0a07604a6..465692b3c351 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c @@ -37,14 +37,14 @@ void rsi_usb_rx_thread(struct rsi_common *common) if (atomic_read(&dev->rx_thread.thread_done)) goto out; - mutex_lock(&common->tx_rxlock); + mutex_lock(&common->rx_lock); status = rsi_read_pkt(common, 0); if (status) { rsi_dbg(ERR_ZONE, "%s: Failed To read data", __func__); - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->rx_lock); return; } - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->rx_lock); rsi_reset_event(&dev->rx_thread.event); if (adapter->rx_urb_submit(adapter)) { rsi_dbg(ERR_ZONE, diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index c2e1c1ce285c..29bccb7079ec 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -181,6 +181,8 @@ struct rsi_common { struct mutex mutex; /* Mutex used between tx/rx threads */ struct mutex tx_rxlock; + /* Mutex used for rx thread */ + struct mutex rx_lock; u8 endpoint; /* Channel/band related */ -- cgit v1.2.3 From cb16453565f8d2d6ee9f098ee575ea030e5d71e9 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 6 Jul 2017 20:07:24 +0530 Subject: rsi: Rename mutex tx_rxlock to the tx_lock. We have now added separate lock for Rx. This lock is used to protect tx path only Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_core.c | 10 +++++----- drivers/net/wireless/rsi/rsi_91x_main.c | 2 +- drivers/net/wireless/rsi/rsi_main.h | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index 68f04a76769e..88a1a56a20ab 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -268,11 +268,11 @@ void rsi_core_qos_processor(struct rsi_common *common) break; } - mutex_lock(&common->tx_rxlock); + mutex_lock(&common->tx_lock); status = adapter->check_hw_queue_status(adapter, q_num); if ((status <= 0)) { - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->tx_lock); break; } @@ -287,7 +287,7 @@ void rsi_core_qos_processor(struct rsi_common *common) skb = rsi_core_dequeue_pkt(common, q_num); if (skb == NULL) { rsi_dbg(ERR_ZONE, "skb null\n"); - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->tx_lock); break; } @@ -297,14 +297,14 @@ void rsi_core_qos_processor(struct rsi_common *common) status = rsi_send_data_pkt(common, skb); if (status) { - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->tx_lock); break; } common->tx_stats.total_tx_pkt_send[q_num]++; tstamp_2 = jiffies; - mutex_unlock(&common->tx_rxlock); + mutex_unlock(&common->tx_lock); if (time_after(tstamp_2, tstamp_1 + (300 * HZ) / 1000)) schedule(); diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c index 939f568dae9b..bb0febb17be0 100644 --- a/drivers/net/wireless/rsi/rsi_91x_main.c +++ b/drivers/net/wireless/rsi/rsi_91x_main.c @@ -220,7 +220,7 @@ struct rsi_hw *rsi_91x_init(void) rsi_init_event(&common->tx_thread.event); mutex_init(&common->mutex); - mutex_init(&common->tx_rxlock); + mutex_init(&common->tx_lock); mutex_init(&common->rx_lock); if (rsi_create_kthread(common, diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 29bccb7079ec..709f767aa34b 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -179,8 +179,8 @@ struct rsi_common { struct sk_buff_head tx_queue[NUM_EDCA_QUEUES + 1]; /* Mutex declaration */ struct mutex mutex; - /* Mutex used between tx/rx threads */ - struct mutex tx_rxlock; + /* Mutex used for tx thread */ + struct mutex tx_lock; /* Mutex used for rx thread */ struct mutex rx_lock; u8 endpoint; -- cgit v1.2.3 From 6c8ab76d6ae9ba8027c985444299372b965ed2d2 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:32 +0530 Subject: rsi: add common structures needed for command packets All internal management packets (command packets) use some common fields and some packet specific fields for packet descriptors. This patch adds some common structures which are needed for all command packets. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_mgmt.h | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index a8a195e8a91e..63360c2be726 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -221,6 +221,44 @@ struct rsi_mac_frame { __le16 desc_word[8]; } __packed; +#define PWR_SAVE_WAKEUP_IND BIT(0) +#define TCP_CHECK_SUM_OFFLOAD BIT(1) +#define CONFIRM_REQUIRED_TO_HOST BIT(2) +#define ADD_DELTA_TSF BIT(3) +#define FETCH_RETRY_CNT_FROM_HOST_DESC BIT(4) +#define EOSP_INDICATION BIT(5) +#define REQUIRE_TSF_SYNC_CONFIRM BIT(6) +#define ENCAP_MGMT_PKT BIT(7) + +struct rsi_cmd_desc_dword0 { + __le16 len_qno; + u8 frame_type; + u8 misc_flags; +}; + +struct rsi_cmd_desc_dword1 { + u8 xtend_desc_size; + u8 reserved1; + __le16 reserved2; +}; + +struct rsi_cmd_desc_dword2 { + __le32 pkt_info; /* Packet specific data */ +}; + +struct rsi_cmd_desc_dword3 { + __le16 token; + u8 qid_tid; + u8 sta_id; +}; + +struct rsi_cmd_desc { + struct rsi_cmd_desc_dword0 desc_dword0; + struct rsi_cmd_desc_dword1 desc_dword1; + struct rsi_cmd_desc_dword2 desc_dword2; + struct rsi_cmd_desc_dword3 desc_dword3; +}; + struct rsi_boot_params { __le16 desc_word[8]; struct bootup_params bootup_params; -- cgit v1.2.3 From 9a629fafe7d8a486f8f02e2788c5368319f2dd0c Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:33 +0530 Subject: rsi: immediate wakeup bit and priority for TX command packets For all TX command packets immediate wakeup bit needs to be set in descriptor. This will make sure device will wakeup if it is in any sleep state. Priority of the packet is also set. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 3 +++ drivers/net/wireless/rsi/rsi_mgmt.h | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index ebd1e5647f03..68771b05797e 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -266,11 +266,14 @@ static int rsi_send_internal_mgmt_frame(struct rsi_common *common, struct sk_buff *skb) { struct skb_info *tx_params; + struct rsi_cmd_desc *desc; if (skb == NULL) { rsi_dbg(ERR_ZONE, "%s: Unable to allocate skb\n", __func__); return -ENOMEM; } + desc->desc_dword0.len_qno |= cpu_to_le16(DESC_IMMEDIATE_WAKEUP); + skb->priority = MGMT_SOFT_Q; tx_params = (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data; tx_params->flags |= INTERNAL_MGMT_PKT; skb_queue_tail(&common->tx_queue[MGMT_SOFT_Q], skb); diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 63360c2be726..6f7f181710f8 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -229,6 +229,7 @@ struct rsi_mac_frame { #define EOSP_INDICATION BIT(5) #define REQUIRE_TSF_SYNC_CONFIRM BIT(6) #define ENCAP_MGMT_PKT BIT(7) +#define DESC_IMMEDIATE_WAKEUP BIT(15) struct rsi_cmd_desc_dword0 { __le16 len_qno; -- cgit v1.2.3 From 6abdf2c19346b0f98437943215c6763e5f4847c9 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:34 +0530 Subject: rsi: Update in tx command frame radio capabilities Radio capabilities frame is updated to use common descriptor structure. Also, few changes to this frame is done like hardware queues are increase to 12 from 8, default channel number is included. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 56 ++++++++++++++++++--------------- drivers/net/wireless/rsi/rsi_main.h | 7 ++++- drivers/net/wireless/rsi/rsi_mgmt.h | 13 +++++++- 3 files changed, 48 insertions(+), 28 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 68771b05797e..65d2dd6b908a 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -301,10 +301,11 @@ static int rsi_load_radio_caps(struct rsi_common *common) 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0}; struct sk_buff *skb; + u16 frame_len = sizeof(struct rsi_radio_caps); rsi_dbg(INFO_ZONE, "%s: Sending rate symbol req frame\n", __func__); - skb = dev_alloc_skb(sizeof(struct rsi_radio_caps)); + skb = dev_alloc_skb(frame_len); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", @@ -312,37 +313,40 @@ static int rsi_load_radio_caps(struct rsi_common *common) return -ENOMEM; } - memset(skb->data, 0, sizeof(struct rsi_radio_caps)); + memset(skb->data, 0, frame_len); radio_caps = (struct rsi_radio_caps *)skb->data; - radio_caps->desc_word[1] = cpu_to_le16(RADIO_CAPABILITIES); - radio_caps->desc_word[4] = cpu_to_le16(RSI_RF_TYPE << 8); + radio_caps->desc_dword0.frame_type = RADIO_CAPABILITIES; + radio_caps->channel_num = common->channel; + radio_caps->rf_model = RSI_RF_TYPE; if (common->channel_width == BW_40MHZ) { - radio_caps->desc_word[7] |= cpu_to_le16(RSI_LMAC_CLOCK_80MHZ); - radio_caps->desc_word[7] |= cpu_to_le16(RSI_ENABLE_40MHZ); + radio_caps->radio_cfg_info = RSI_LMAC_CLOCK_80MHZ; + radio_caps->radio_cfg_info |= RSI_ENABLE_40MHZ; if (common->fsm_state == FSM_MAC_INIT_DONE) { struct ieee80211_hw *hw = adapter->hw; struct ieee80211_conf *conf = &hw->conf; + if (conf_is_ht40_plus(conf)) { - radio_caps->desc_word[5] = - cpu_to_le16(LOWER_20_ENABLE); - radio_caps->desc_word[5] |= - cpu_to_le16(LOWER_20_ENABLE >> 12); + radio_caps->radio_cfg_info = + RSI_CMDDESC_LOWER_20_ENABLE; + radio_caps->radio_info = + RSI_CMDDESC_LOWER_20_ENABLE; } else if (conf_is_ht40_minus(conf)) { - radio_caps->desc_word[5] = - cpu_to_le16(UPPER_20_ENABLE); - radio_caps->desc_word[5] |= - cpu_to_le16(UPPER_20_ENABLE >> 12); + radio_caps->radio_cfg_info = + RSI_CMDDESC_UPPER_20_ENABLE; + radio_caps->radio_info = + RSI_CMDDESC_UPPER_20_ENABLE; } else { - radio_caps->desc_word[5] = - cpu_to_le16(BW_40MHZ << 12); - radio_caps->desc_word[5] |= - cpu_to_le16(FULL40M_ENABLE); + radio_caps->radio_cfg_info = + RSI_CMDDESC_40MHZ; + radio_caps->radio_info = + RSI_CMDDESC_FULL_40_ENABLE; } } } + radio_caps->radio_info |= radio_id; radio_caps->sifs_tx_11n = cpu_to_le16(SIFS_TX_11N_VALUE); radio_caps->sifs_tx_11b = cpu_to_le16(SIFS_TX_11B_VALUE); @@ -351,8 +355,6 @@ static int rsi_load_radio_caps(struct rsi_common *common) radio_caps->cck_ack_tout = cpu_to_le16(CCK_ACK_TOUT_VALUE); radio_caps->preamble_type = cpu_to_le16(LONG_PREAMBLE); - radio_caps->desc_word[7] |= cpu_to_le16(radio_id << 8); - for (ii = 0; ii < MAX_HW_QUEUES; ii++) { radio_caps->qos_params[ii].cont_win_min_q = cpu_to_le16(3); radio_caps->qos_params[ii].cont_win_max_q = cpu_to_le16(0x3f); @@ -360,7 +362,7 @@ static int rsi_load_radio_caps(struct rsi_common *common) radio_caps->qos_params[ii].txop_q = 0; } - for (ii = 0; ii < MAX_HW_QUEUES - 4; ii++) { + for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) { radio_caps->qos_params[ii].cont_win_min_q = cpu_to_le16(common->edca_params[ii].cw_min); radio_caps->qos_params[ii].cont_win_max_q = @@ -371,17 +373,19 @@ static int rsi_load_radio_caps(struct rsi_common *common) cpu_to_le16(common->edca_params[ii].txop); } + radio_caps->qos_params[BROADCAST_HW_Q].txop_q = cpu_to_le16(0xffff); + radio_caps->qos_params[MGMT_HW_Q].txop_q = 0; + radio_caps->qos_params[BEACON_HW_Q].txop_q = cpu_to_le16(0xffff); + memcpy(&common->rate_pwr[0], &gc[0], 40); for (ii = 0; ii < 20; ii++) radio_caps->gcpd_per_rate[inx++] = cpu_to_le16(common->rate_pwr[ii] & 0x00FF); - radio_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_radio_caps) - - FRAME_DESC_SZ) | - (RSI_WIFI_MGMT_Q << 12)); - + rsi_set_len_qno(&radio_caps->desc_dword0.len_qno, + (frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q); - skb_put(skb, (sizeof(struct rsi_radio_caps))); + skb_put(skb, frame_len); return rsi_send_internal_mgmt_frame(common, skb); } diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 709f767aa34b..a567986c5b0b 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -61,11 +61,16 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...); #define MAC_80211_HDR_FRAME_CONTROL 0 #define WME_NUM_AC 4 #define NUM_SOFT_QUEUES 5 -#define MAX_HW_QUEUES 8 +#define MAX_HW_QUEUES 12 #define INVALID_QUEUE 0xff #define MAX_CONTINUOUS_VO_PKTS 8 #define MAX_CONTINUOUS_VI_PKTS 4 +/* Hardware queue info */ +#define BROADCAST_HW_Q 9 +#define MGMT_HW_Q 10 +#define BEACON_HW_Q 11 + /* Queue information */ #define RSI_COEX_Q 0x0 #define RSI_WIFI_MGMT_Q 0x4 diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 6f7f181710f8..3b4bd85e9c4e 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -70,6 +70,10 @@ #define RSI_REKEY_PURPOSE BIT(13) #define RSI_ENCRYPT_PKT BIT(15) +#define RSI_CMDDESC_40MHZ BIT(4) +#define RSI_CMDDESC_UPPER_20_ENABLE BIT(5) +#define RSI_CMDDESC_LOWER_20_ENABLE BIT(6) +#define RSI_CMDDESC_FULL_40_ENABLE (BIT(5) | BIT(6)) #define UPPER_20_ENABLE (0x2 << 12) #define LOWER_20_ENABLE (0x4 << 12) #define FULL40M_ENABLE 0x6 @@ -317,7 +321,14 @@ struct qos_params { } __packed; struct rsi_radio_caps { - __le16 desc_word[8]; + struct rsi_cmd_desc_dword0 desc_dword0; + struct rsi_cmd_desc_dword0 desc_dword1; + u8 channel_num; + u8 rf_model; + __le16 ppe_ack_rate; + __le16 mode_11j; + u8 radio_cfg_info; + u8 radio_info; struct qos_params qos_params[MAX_HW_QUEUES]; u8 num_11n_rates; u8 num_11ac_rates; -- cgit v1.2.3 From dff80fc5fe4fa14299ee333fb37a7a7991b67c01 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:35 +0530 Subject: rsi: remove unnecessary check for 802.11 management packet The function rsi_mgmt_pkt_to_core() is for passing the 802.11 management frames to mac80211. So, it is unnecessary to check again for the frame type 802.11 management in this function. It can be checked before passing to this function. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 54 +++++++++++++++------------------ 1 file changed, 24 insertions(+), 30 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 65d2dd6b908a..3724dd4c6377 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -401,8 +401,7 @@ static int rsi_load_radio_caps(struct rsi_common *common) */ static int rsi_mgmt_pkt_to_core(struct rsi_common *common, u8 *msg, - s32 msg_len, - u8 type) + s32 msg_len) { struct rsi_hw *adapter = common->priv; struct ieee80211_tx_info *info; @@ -410,37 +409,30 @@ static int rsi_mgmt_pkt_to_core(struct rsi_common *common, u8 pad_bytes = msg[4]; struct sk_buff *skb; - if (type == RX_DOT11_MGMT) { - if (!adapter->sc_nvifs) - return -ENOLINK; + if (!adapter->sc_nvifs) + return -ENOLINK; - msg_len -= pad_bytes; - if (msg_len <= 0) { - rsi_dbg(MGMT_RX_ZONE, - "%s: Invalid rx msg of len = %d\n", - __func__, msg_len); - return -EINVAL; - } + msg_len -= pad_bytes; + if (msg_len <= 0) { + rsi_dbg(MGMT_RX_ZONE, + "%s: Invalid rx msg of len = %d\n", + __func__, msg_len); + return -EINVAL; + } - skb = dev_alloc_skb(msg_len); - if (!skb) { - rsi_dbg(ERR_ZONE, "%s: Failed to allocate skb\n", - __func__); - return -ENOMEM; - } + skb = dev_alloc_skb(msg_len); + if (!skb) + return -ENOMEM; - skb_put_data(skb, - (u8 *)(msg + FRAME_DESC_SZ + pad_bytes), - msg_len); + skb_put_data(skb, + (u8 *)(msg + FRAME_DESC_SZ + pad_bytes), + msg_len); - info = IEEE80211_SKB_CB(skb); - rx_params = (struct skb_info *)info->driver_data; - rx_params->rssi = rsi_get_rssi(msg); - rx_params->channel = rsi_get_channel(msg); - rsi_indicate_pkt_to_os(common, skb); - } else { - rsi_dbg(MGMT_TX_ZONE, "%s: Internal Packet\n", __func__); - } + info = IEEE80211_SKB_CB(skb); + rx_params = (struct skb_info *)info->driver_data; + rx_params->rssi = rsi_get_rssi(msg); + rx_params->channel = rsi_get_channel(msg); + rsi_indicate_pkt_to_os(common, skb); return 0; } @@ -1641,8 +1633,10 @@ int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg) rsi_dbg(FSM_ZONE, "%s: Probe confirm received\n", __func__); } + } else if (msg_type == RX_DOT11_MGMT) { + return rsi_mgmt_pkt_to_core(common, msg, msg_len); } else { - return rsi_mgmt_pkt_to_core(common, msg, msg_len, msg_type); + rsi_dbg(INFO_ZONE, "Received packet type: 0x%x\n", msg_type); } return 0; } -- cgit v1.2.3 From 59e006dc77c4e2b48c4efbe9a46477c7e6f3ca6c Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:36 +0530 Subject: rsi: Update peer notify command frame TX command frame peer notify is updated to use common descriptor structure. MPDU density value added to the frame. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 19 ++++++++++--------- drivers/net/wireless/rsi/rsi_mgmt.h | 4 +++- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 3724dd4c6377..fd9bcd719a33 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -460,10 +460,11 @@ static int rsi_hal_send_sta_notify_frame(struct rsi_common *common, struct rsi_peer_notify *peer_notify; u16 vap_id = 0; int status; + u16 frame_len = sizeof(struct rsi_peer_notify); rsi_dbg(MGMT_TX_ZONE, "%s: Sending sta notify frame\n", __func__); - skb = dev_alloc_skb(sizeof(struct rsi_peer_notify)); + skb = dev_alloc_skb(frame_len); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", @@ -471,7 +472,7 @@ static int rsi_hal_send_sta_notify_frame(struct rsi_common *common, return -ENOMEM; } - memset(skb->data, 0, sizeof(struct rsi_peer_notify)); + memset(skb->data, 0, frame_len); peer_notify = (struct rsi_peer_notify *)skb->data; peer_notify->command = cpu_to_le16(opmode << 1); @@ -489,16 +490,16 @@ static int rsi_hal_send_sta_notify_frame(struct rsi_common *common, peer_notify->command |= cpu_to_le16((aid & 0xfff) << 4); ether_addr_copy(peer_notify->mac_addr, bssid); - + peer_notify->mpdu_density = cpu_to_le16(RSI_MPDU_DENSITY); peer_notify->sta_flags = cpu_to_le32((qos_enable) ? 1 : 0); - peer_notify->desc_word[0] = - cpu_to_le16((sizeof(struct rsi_peer_notify) - FRAME_DESC_SZ) | - (RSI_WIFI_MGMT_Q << 12)); - peer_notify->desc_word[1] = cpu_to_le16(PEER_NOTIFY); - peer_notify->desc_word[7] |= cpu_to_le16(vap_id << 8); + rsi_set_len_qno(&peer_notify->desc.desc_dword0.len_qno, + (frame_len - FRAME_DESC_SZ), + RSI_WIFI_MGMT_Q); + peer_notify->desc.desc_dword0.frame_type = PEER_NOTIFY; + peer_notify->desc.desc_dword3.sta_id = vap_id; - skb_put(skb, sizeof(struct rsi_peer_notify)); + skb_put(skb, frame_len); status = rsi_send_internal_mgmt_frame(common, skb); diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 3b4bd85e9c4e..d2fe9ee1e934 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -165,6 +165,8 @@ #define ALLOW_CONN_PEER_MGMT_WHILE_BUF_FULL BIT(5) #define DISALLOW_BROADCAST_DATA BIT(6) +#define RSI_MPDU_DENSITY 0x8 + enum opmode { STA_OPMODE = 1, AP_OPMODE = 2 @@ -270,7 +272,7 @@ struct rsi_boot_params { } __packed; struct rsi_peer_notify { - __le16 desc_word[8]; + struct rsi_cmd_desc desc; u8 mac_addr[6]; __le16 command; __le16 mpdu_density; -- cgit v1.2.3 From 3a9828c92ba16b8a51e451a5d074d7e4c567950d Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:37 +0530 Subject: rsi: Update aggregation parameters command frame AMPDU aggregation parameters frame configured to device is modified to use common descriptor structure. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 41 ++++++++++++++------------------- drivers/net/wireless/rsi/rsi_mgmt.h | 14 +++++++++++ 2 files changed, 31 insertions(+), 24 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index fd9bcd719a33..a1a60f7dd42f 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -528,10 +528,11 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, u8 event) { struct sk_buff *skb = NULL; - struct rsi_mac_frame *mgmt_frame; + struct rsi_aggr_params *aggr_params; u8 peer_id = 0; + u16 frame_len = sizeof(struct rsi_aggr_params); - skb = dev_alloc_skb(FRAME_DESC_SZ); + skb = dev_alloc_skb(frame_len); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", @@ -539,37 +540,29 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, return -ENOMEM; } - memset(skb->data, 0, FRAME_DESC_SZ); - mgmt_frame = (struct rsi_mac_frame *)skb->data; + memset(skb->data, 0, frame_len); + aggr_params = (struct rsi_aggr_params *)skb->data; rsi_dbg(MGMT_TX_ZONE, "%s: Sending AMPDU indication frame\n", __func__); - mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); - mgmt_frame->desc_word[1] = cpu_to_le16(AMPDU_IND); + rsi_set_len_qno(&aggr_params->desc_dword0.len_qno, 0, RSI_WIFI_MGMT_Q); + aggr_params->desc_dword0.frame_type = AMPDU_IND; + aggr_params->aggr_params = tid & RSI_AGGR_PARAMS_TID_MASK; + aggr_params->peer_id = peer_id; if (event == STA_TX_ADDBA_DONE) { - mgmt_frame->desc_word[4] = cpu_to_le16(ssn); - mgmt_frame->desc_word[5] = cpu_to_le16(buf_size); - mgmt_frame->desc_word[7] = - cpu_to_le16((tid | (START_AMPDU_AGGR << 4) | (peer_id << 8))); + aggr_params->seq_start = cpu_to_le16(ssn); + aggr_params->baw_size = cpu_to_le16(buf_size); + aggr_params->aggr_params |= RSI_AGGR_PARAMS_START; } else if (event == STA_RX_ADDBA_DONE) { - mgmt_frame->desc_word[4] = cpu_to_le16(ssn); - mgmt_frame->desc_word[7] = cpu_to_le16(tid | - (START_AMPDU_AGGR << 4) | - (RX_BA_INDICATION << 5) | - (peer_id << 8)); - } else if (event == STA_TX_DELBA) { - mgmt_frame->desc_word[7] = cpu_to_le16(tid | - (STOP_AMPDU_AGGR << 4) | - (peer_id << 8)); + aggr_params->seq_start = cpu_to_le16(ssn); + aggr_params->aggr_params |= (RSI_AGGR_PARAMS_START | + RSI_AGGR_PARAMS_RX_AGGR); } else if (event == STA_RX_DELBA) { - mgmt_frame->desc_word[7] = cpu_to_le16(tid | - (STOP_AMPDU_AGGR << 4) | - (RX_BA_INDICATION << 5) | - (peer_id << 8)); + aggr_params->aggr_params |= RSI_AGGR_PARAMS_RX_AGGR; } - skb_put(skb, FRAME_DESC_SZ); + skb_put(skb, frame_len); return rsi_send_internal_mgmt_frame(common, skb); } diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index d2fe9ee1e934..68863c814c82 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -280,6 +280,20 @@ struct rsi_peer_notify { __le32 sta_flags; } __packed; +/* Aggregation params flags */ +#define RSI_AGGR_PARAMS_TID_MASK 0xf +#define RSI_AGGR_PARAMS_START BIT(4) +#define RSI_AGGR_PARAMS_RX_AGGR BIT(5) +struct rsi_aggr_params { + struct rsi_cmd_desc_dword0 desc_dword0; + struct rsi_cmd_desc_dword0 desc_dword1; + __le16 seq_start; + __le16 baw_size; + __le16 token; + u8 aggr_params; + u8 peer_id; +} __packed; + struct rsi_vap_caps { __le16 desc_word[8]; u8 mac_addr[6]; -- cgit v1.2.3 From 5c7ca1bbf2007c767a8027d8a4ee1de8f93eb48d Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:38 +0530 Subject: rsi: Update baseband RF programming frame Baseband RF programming frame configured to device is modified to use common descriptor structure. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 24 +++++++++++++----------- drivers/net/wireless/rsi/rsi_mgmt.h | 12 ++++++++++++ 2 files changed, 25 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index a1a60f7dd42f..ead3573b027b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -577,34 +577,36 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, static int rsi_program_bb_rf(struct rsi_common *common) { struct sk_buff *skb; - struct rsi_mac_frame *mgmt_frame; + struct rsi_bb_rf_prog *bb_rf_prog; + u16 frame_len = sizeof(struct rsi_bb_rf_prog); rsi_dbg(MGMT_TX_ZONE, "%s: Sending program BB/RF frame\n", __func__); - skb = dev_alloc_skb(FRAME_DESC_SZ); + skb = dev_alloc_skb(frame_len); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", __func__); return -ENOMEM; } - memset(skb->data, 0, FRAME_DESC_SZ); - mgmt_frame = (struct rsi_mac_frame *)skb->data; + memset(skb->data, 0, frame_len); + bb_rf_prog = (struct rsi_bb_rf_prog *)skb->data; - mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); - mgmt_frame->desc_word[1] = cpu_to_le16(BBP_PROG_IN_TA); - mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint); + rsi_set_len_qno(&bb_rf_prog->desc_dword0.len_qno, 0, RSI_WIFI_MGMT_Q); + bb_rf_prog->desc_dword0.frame_type = BBP_PROG_IN_TA; + bb_rf_prog->endpoint = common->endpoint; + bb_rf_prog->rf_power_mode = common->wlan_rf_power_mode; if (common->rf_reset) { - mgmt_frame->desc_word[7] = cpu_to_le16(RF_RESET_ENABLE); + bb_rf_prog->flags = cpu_to_le16(RF_RESET_ENABLE); rsi_dbg(MGMT_TX_ZONE, "%s: ===> RF RESET REQUEST SENT <===\n", __func__); common->rf_reset = 0; } common->bb_rf_prog_count = 1; - mgmt_frame->desc_word[7] |= cpu_to_le16(PUT_BBP_RESET | - BBP_REG_WRITE | (RSI_RF_TYPE << 4)); - skb_put(skb, FRAME_DESC_SZ); + bb_rf_prog->flags |= cpu_to_le16(PUT_BBP_RESET | BBP_REG_WRITE | + (RSI_RF_TYPE << 4)); + skb_put(skb, frame_len); return rsi_send_internal_mgmt_frame(common, skb); } diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 68863c814c82..d44c79f74882 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -294,6 +294,18 @@ struct rsi_aggr_params { u8 peer_id; } __packed; +struct rsi_bb_rf_prog { + struct rsi_cmd_desc_dword0 desc_dword0; + __le16 reserved1; + u8 rf_power_mode; + u8 reserved2; + u8 endpoint; + u8 reserved3; + __le16 reserved4; + __le16 reserved5; + __le16 flags; +} __packed; + struct rsi_vap_caps { __le16 desc_word[8]; u8 mac_addr[6]; -- cgit v1.2.3 From f04854fa3c38286e0c1ba504a32fd5dc04d5cb69 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:39 +0530 Subject: rsi: update set_channel command frame TX command frame set_channel is modified to use common descriptor structure. Also DFS channel indication to firmware is added in the descriptor for dfs channels configuration. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 47 +++++++++++++++------------------ drivers/net/wireless/rsi/rsi_mgmt.h | 13 +++++++++ 2 files changed, 35 insertions(+), 25 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index ead3573b027b..4bcb84087874 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -965,12 +965,13 @@ int rsi_set_channel(struct rsi_common *common, struct ieee80211_channel *channel) { struct sk_buff *skb = NULL; - struct rsi_mac_frame *mgmt_frame; + struct rsi_chan_config *chan_cfg; + u16 frame_len = sizeof(struct rsi_chan_config); rsi_dbg(MGMT_TX_ZONE, "%s: Sending scan req frame\n", __func__); - skb = dev_alloc_skb(FRAME_DESC_SZ); + skb = dev_alloc_skb(frame_len); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", __func__); @@ -981,37 +982,33 @@ int rsi_set_channel(struct rsi_common *common, dev_kfree_skb(skb); return 0; } - memset(skb->data, 0, FRAME_DESC_SZ); - mgmt_frame = (struct rsi_mac_frame *)skb->data; - - mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); - mgmt_frame->desc_word[1] = cpu_to_le16(SCAN_REQUEST); - mgmt_frame->desc_word[4] = cpu_to_le16(channel->hw_value); - - mgmt_frame->desc_word[4] |= - cpu_to_le16(((char)(channel->max_antenna_gain)) << 8); - mgmt_frame->desc_word[5] = - cpu_to_le16((char)(channel->max_antenna_gain)); - - mgmt_frame->desc_word[7] = cpu_to_le16(PUT_BBP_RESET | - BBP_REG_WRITE | - (RSI_RF_TYPE << 4)); - - if (!(channel->flags & IEEE80211_CHAN_NO_IR) && - !(channel->flags & IEEE80211_CHAN_RADAR)) { + memset(skb->data, 0, frame_len); + chan_cfg = (struct rsi_chan_config *)skb->data; + + rsi_set_len_qno(&chan_cfg->desc_dword0.len_qno, 0, RSI_WIFI_MGMT_Q); + chan_cfg->desc_dword0.frame_type = SCAN_REQUEST; + chan_cfg->channel_number = channel->hw_value; + chan_cfg->antenna_gain_offset_2g = channel->max_antenna_gain; + chan_cfg->antenna_gain_offset_5g = channel->max_antenna_gain; + chan_cfg->region_rftype = (RSI_RF_TYPE & 0xf) << 4; + + if ((channel->flags & IEEE80211_CHAN_NO_IR) || + (channel->flags & IEEE80211_CHAN_RADAR)) { + chan_cfg->antenna_gain_offset_2g |= RSI_CHAN_RADAR; + } else { if (common->tx_power < channel->max_power) - mgmt_frame->desc_word[6] = cpu_to_le16(common->tx_power); + chan_cfg->tx_power = cpu_to_le16(common->tx_power); else - mgmt_frame->desc_word[6] = cpu_to_le16(channel->max_power); + chan_cfg->tx_power = cpu_to_le16(channel->max_power); } - mgmt_frame->desc_word[7] = cpu_to_le16(common->priv->dfs_region); + chan_cfg->region_rftype |= (common->priv->dfs_region & 0xf); if (common->channel_width == BW_40MHZ) - mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8); + chan_cfg->channel_width = 0x1; common->channel = channel->hw_value; - skb_put(skb, FRAME_DESC_SZ); + skb_put(skb, frame_len); return rsi_send_internal_mgmt_frame(common, skb); } diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index d44c79f74882..2f49e55650b0 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -166,6 +166,7 @@ #define DISALLOW_BROADCAST_DATA BIT(6) #define RSI_MPDU_DENSITY 0x8 +#define RSI_CHAN_RADAR BIT(7) enum opmode { STA_OPMODE = 1, @@ -306,6 +307,18 @@ struct rsi_bb_rf_prog { __le16 flags; } __packed; +struct rsi_chan_config { + struct rsi_cmd_desc_dword0 desc_dword0; + struct rsi_cmd_desc_dword1 desc_dword1; + u8 channel_number; + u8 antenna_gain_offset_2g; + u8 antenna_gain_offset_5g; + u8 channel_width; + __le16 tx_power; + u8 region_rftype; + u8 flags; +} __packed; + struct rsi_vap_caps { __le16 desc_word[8]; u8 mac_addr[6]; -- cgit v1.2.3 From de1d1813a8ca31e8c1229fe0b42eba0574f43e89 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:40 +0530 Subject: rsi: update vap capabilities command frame VAP capablities frame configured to device is modified to use common descriptor structure. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 56 ++++++++++++++++++--------------- drivers/net/wireless/rsi/rsi_main.h | 3 ++ drivers/net/wireless/rsi/rsi_mgmt.h | 19 +++++++++-- 3 files changed, 49 insertions(+), 29 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 4bcb84087874..3ce1f71cf537 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -230,6 +230,8 @@ static void rsi_set_default_parameters(struct rsi_common *common) common->rf_power_val = 0; /* Default 1.9V */ common->wlan_rf_power_mode = 0; common->obm_ant_sel_val = 2; + common->beacon_interval = RSI_BEACON_INTERVAL; + common->dtim_cnt = RSI_DTIM_COUNT; } /** @@ -627,59 +629,61 @@ int rsi_set_vap_capabilities(struct rsi_common *common, struct rsi_hw *adapter = common->priv; struct ieee80211_hw *hw = adapter->hw; struct ieee80211_conf *conf = &hw->conf; + u16 frame_len = sizeof(struct rsi_vap_caps); u16 vap_id = 0; rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__); - skb = dev_alloc_skb(sizeof(struct rsi_vap_caps)); + skb = dev_alloc_skb(frame_len); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", __func__); return -ENOMEM; } - memset(skb->data, 0, sizeof(struct rsi_vap_caps)); + memset(skb->data, 0, frame_len); vap_caps = (struct rsi_vap_caps *)skb->data; - vap_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_vap_caps) - - FRAME_DESC_SZ) | - (RSI_WIFI_MGMT_Q << 12)); - vap_caps->desc_word[1] = cpu_to_le16(VAP_CAPABILITIES); - vap_caps->desc_word[2] = cpu_to_le16(vap_status << 8); - vap_caps->desc_word[4] = cpu_to_le16(mode | - (common->channel_width << 8)); - vap_caps->desc_word[7] = cpu_to_le16((vap_id << 8) | - (common->mac_id << 4) | - common->radio_id); + rsi_set_len_qno(&vap_caps->desc_dword0.len_qno, + (frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q); + vap_caps->desc_dword0.frame_type = VAP_CAPABILITIES; + vap_caps->status = vap_status; + vap_caps->vif_type = mode; + vap_caps->channel_bw = common->channel_width; + vap_caps->vap_id = vap_id; + vap_caps->radioid_macid = ((common->mac_id & 0xf) << 4) | + (common->radio_id & 0xf); memcpy(vap_caps->mac_addr, common->mac_addr, IEEE80211_ADDR_LEN); vap_caps->keep_alive_period = cpu_to_le16(90); vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD); vap_caps->rts_threshold = cpu_to_le16(common->rts_threshold); - vap_caps->default_mgmt_rate = cpu_to_le32(RSI_RATE_6); if (common->band == NL80211_BAND_5GHZ) { - vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_6); - if (conf_is_ht40(&common->priv->hw->conf)) { - vap_caps->default_ctrl_rate |= - cpu_to_le32(FULL40M_ENABLE << 16); - } + vap_caps->default_ctrl_rate = cpu_to_le16(RSI_RATE_6); + vap_caps->default_mgmt_rate = cpu_to_le32(RSI_RATE_6); } else { - vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_1); + vap_caps->default_ctrl_rate = cpu_to_le16(RSI_RATE_1); + vap_caps->default_mgmt_rate = cpu_to_le32(RSI_RATE_1); + } + if (conf_is_ht40(conf)) { if (conf_is_ht40_minus(conf)) - vap_caps->default_ctrl_rate |= - cpu_to_le32(UPPER_20_ENABLE << 16); + vap_caps->ctrl_rate_flags = + cpu_to_le16(UPPER_20_ENABLE); else if (conf_is_ht40_plus(conf)) - vap_caps->default_ctrl_rate |= - cpu_to_le32(LOWER_20_ENABLE << 16); + vap_caps->ctrl_rate_flags = + cpu_to_le16(LOWER_20_ENABLE); + else + vap_caps->ctrl_rate_flags = + cpu_to_le16(FULL40M_ENABLE); } vap_caps->default_data_rate = 0; - vap_caps->beacon_interval = cpu_to_le16(200); - vap_caps->dtim_period = cpu_to_le16(4); + vap_caps->beacon_interval = cpu_to_le16(common->beacon_interval); + vap_caps->dtim_period = cpu_to_le16(common->dtim_cnt); - skb_put(skb, sizeof(*vap_caps)); + skb_put(skb, frame_len); return rsi_send_internal_mgmt_frame(common, skb); } diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index a567986c5b0b..485b97ab5779 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -239,6 +239,9 @@ struct rsi_common { u8 obm_ant_sel_val; int tx_power; u8 ant_in_use; + + u16 beacon_interval; + u8 dtim_cnt; }; enum host_intf { diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 2f49e55650b0..fcde44e966f1 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -167,6 +167,8 @@ #define RSI_MPDU_DENSITY 0x8 #define RSI_CHAN_RADAR BIT(7) +#define RSI_BEACON_INTERVAL 200 +#define RSI_DTIM_COUNT 2 enum opmode { STA_OPMODE = 1, @@ -320,19 +322,30 @@ struct rsi_chan_config { } __packed; struct rsi_vap_caps { - __le16 desc_word[8]; + struct rsi_cmd_desc_dword0 desc_dword0; + u8 reserved1; + u8 status; + __le16 reserved2; + u8 vif_type; + u8 channel_bw; + __le16 antenna_info; + u8 radioid_macid; + u8 vap_id; + __le16 reserved3; u8 mac_addr[6]; __le16 keep_alive_period; u8 bssid[6]; - __le16 reserved; + __le16 reserved4; __le32 flags; __le16 frag_threshold; __le16 rts_threshold; __le32 default_mgmt_rate; - __le32 default_ctrl_rate; + __le16 default_ctrl_rate; + __le16 ctrl_rate_flags; __le32 default_data_rate; __le16 beacon_interval; __le16 dtim_period; + __le16 beacon_miss_threshold; } __packed; struct rsi_set_key { -- cgit v1.2.3 From a84faab0fdb6a2e99c90fff6e3344bf5c50bd7cd Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:41 +0530 Subject: rsi: update set_key command frame TX command frame set_key is modified to use common descriptor structure. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 26 +++++++++++++------------- drivers/net/wireless/rsi/rsi_mgmt.h | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 3ce1f71cf537..9395d6deac15 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -709,39 +709,39 @@ int rsi_hal_load_key(struct rsi_common *common, struct sk_buff *skb = NULL; struct rsi_set_key *set_key; u16 key_descriptor = 0; + u16 frame_len = sizeof(struct rsi_set_key); rsi_dbg(MGMT_TX_ZONE, "%s: Sending load key frame\n", __func__); - skb = dev_alloc_skb(sizeof(struct rsi_set_key)); + skb = dev_alloc_skb(frame_len); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", __func__); return -ENOMEM; } - memset(skb->data, 0, sizeof(struct rsi_set_key)); + memset(skb->data, 0, frame_len); set_key = (struct rsi_set_key *)skb->data; if ((cipher == WLAN_CIPHER_SUITE_WEP40) || (cipher == WLAN_CIPHER_SUITE_WEP104)) { key_len += 1; - key_descriptor |= BIT(2); + key_descriptor |= RSI_WEP_KEY; if (key_len >= 13) - key_descriptor |= BIT(3); + key_descriptor |= RSI_WEP_KEY_104; } else if (cipher != KEY_TYPE_CLEAR) { - key_descriptor |= BIT(4); + key_descriptor |= RSI_CIPHER_WPA; if (key_type == RSI_PAIRWISE_KEY) key_id = 0; if (cipher == WLAN_CIPHER_SUITE_TKIP) - key_descriptor |= BIT(5); + key_descriptor |= RSI_CIPHER_TKIP; } - key_descriptor |= (key_type | BIT(13) | (key_id << 14)); + key_descriptor |= (key_type | RSI_PROTECT_DATA_FRAMES | (key_id << 14)); - set_key->desc_word[0] = cpu_to_le16((sizeof(struct rsi_set_key) - - FRAME_DESC_SZ) | - (RSI_WIFI_MGMT_Q << 12)); - set_key->desc_word[1] = cpu_to_le16(SET_KEY_REQ); - set_key->desc_word[4] = cpu_to_le16(key_descriptor); + rsi_set_len_qno(&set_key->desc_dword0.len_qno, + (frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q); + set_key->desc_dword0.frame_type = SET_KEY_REQ; + set_key->key_desc = cpu_to_le16(key_descriptor); if ((cipher == WLAN_CIPHER_SUITE_WEP40) || (cipher == WLAN_CIPHER_SUITE_WEP104)) { @@ -755,7 +755,7 @@ int rsi_hal_load_key(struct rsi_common *common, memcpy(set_key->tx_mic_key, &data[16], 8); memcpy(set_key->rx_mic_key, &data[24], 8); - skb_put(skb, sizeof(struct rsi_set_key)); + skb_put(skb, frame_len); return rsi_send_internal_mgmt_frame(common, skb); } diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index fcde44e966f1..5df64d3c67dc 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -348,8 +348,22 @@ struct rsi_vap_caps { __le16 beacon_miss_threshold; } __packed; +/* Key descriptor flags */ +#define RSI_KEY_TYPE_BROADCAST BIT(1) +#define RSI_WEP_KEY BIT(2) +#define RSI_WEP_KEY_104 BIT(3) +#define RSI_CIPHER_WPA BIT(4) +#define RSI_CIPHER_TKIP BIT(5) +#define RSI_PROTECT_DATA_FRAMES BIT(13) +#define RSI_KEY_ID_MASK 0xC0 +#define RSI_KEY_ID_OFFSET 14 struct rsi_set_key { - __le16 desc_word[8]; + struct rsi_cmd_desc_dword0 desc_dword0; + struct rsi_cmd_desc_dword1 desc_dword1; + __le16 key_desc; + __le32 bpn; + u8 sta_id; + u8 vap_id; u8 key[4][32]; u8 tx_mic_key[8]; u8 rx_mic_key[8]; -- cgit v1.2.3 From 2a58900bf59db2ea2212afec8487a143063075ef Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:42 +0530 Subject: rsi: set_key enhancements Broadcast bit to be set for broadcast and multicast packets. For remove_key, tx and rx mic need not be filled. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 9395d6deac15..3bffe310f8d9 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -723,38 +723,40 @@ int rsi_hal_load_key(struct rsi_common *common, memset(skb->data, 0, frame_len); set_key = (struct rsi_set_key *)skb->data; + if (key_type == RSI_GROUP_KEY) + key_descriptor = RSI_KEY_TYPE_BROADCAST; if ((cipher == WLAN_CIPHER_SUITE_WEP40) || (cipher == WLAN_CIPHER_SUITE_WEP104)) { - key_len += 1; + key_id = 0; key_descriptor |= RSI_WEP_KEY; if (key_len >= 13) key_descriptor |= RSI_WEP_KEY_104; } else if (cipher != KEY_TYPE_CLEAR) { key_descriptor |= RSI_CIPHER_WPA; - if (key_type == RSI_PAIRWISE_KEY) - key_id = 0; if (cipher == WLAN_CIPHER_SUITE_TKIP) key_descriptor |= RSI_CIPHER_TKIP; } - key_descriptor |= (key_type | RSI_PROTECT_DATA_FRAMES | (key_id << 14)); + key_descriptor |= RSI_PROTECT_DATA_FRAMES; + key_descriptor |= ((key_id << RSI_KEY_ID_OFFSET) & RSI_KEY_ID_MASK); rsi_set_len_qno(&set_key->desc_dword0.len_qno, (frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q); set_key->desc_dword0.frame_type = SET_KEY_REQ; set_key->key_desc = cpu_to_le16(key_descriptor); - if ((cipher == WLAN_CIPHER_SUITE_WEP40) || - (cipher == WLAN_CIPHER_SUITE_WEP104)) { - memcpy(&set_key->key[key_id][1], - data, - key_len * 2); + if (data) { + if ((cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104)) { + memcpy(&set_key->key[key_id][1], data, key_len * 2); + } else { + memcpy(&set_key->key[0][0], data, key_len); + } + memcpy(set_key->tx_mic_key, &data[16], 8); + memcpy(set_key->rx_mic_key, &data[24], 8); } else { - memcpy(&set_key->key[0][0], data, key_len); + memset(&set_key[FRAME_DESC_SZ], 0, frame_len - FRAME_DESC_SZ); } - memcpy(set_key->tx_mic_key, &data[16], 8); - memcpy(set_key->rx_mic_key, &data[24], 8); - skb_put(skb, frame_len); return rsi_send_internal_mgmt_frame(common, skb); -- cgit v1.2.3 From b3115e8a426733988cfe32d8c8c418c84c88e198 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:43 +0530 Subject: rsi: update autorate request command frame When Short Gaurd Interval is enabled bit 9 is set in rate. Otherwise it should not be set. Added missing 'else' case in this patch. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 3bffe310f8d9..77b675701b87 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1199,6 +1199,9 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) conf_is_ht40(&common->priv->hw->conf)) auto_rate->supported_rates[ii++] = cpu_to_le16(rsi_mcsrates[kk] | BIT(9)); + else + auto_rate->supported_rates[ii++] = + cpu_to_le16(rsi_mcsrates[kk]); auto_rate->supported_rates[ii] = cpu_to_le16(rsi_mcsrates[kk--]); } -- cgit v1.2.3 From bcb283d27f653597180fe125aef38f2a06e46bbf Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:44 +0530 Subject: rsi: block/unblock data queues as per connection status Data queues should be unblocked after station add notify frame is sent and should be blocked after station delete notify is sent. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 77b675701b87..64bbd0a6f8de 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1250,6 +1250,7 @@ void rsi_inform_bss_status(struct rsi_common *common, u16 aid) { if (status) { + common->hw_data_qs_blocked = true; rsi_hal_send_sta_notify_frame(common, RSI_IFTYPE_STATION, STA_CONNECTED, @@ -1258,13 +1259,17 @@ void rsi_inform_bss_status(struct rsi_common *common, aid); if (common->min_rate == 0xffff) rsi_send_auto_rate_request(common); + if (!rsi_send_block_unblock_frame(common, false)) + common->hw_data_qs_blocked = false; } else { + common->hw_data_qs_blocked = true; rsi_hal_send_sta_notify_frame(common, RSI_IFTYPE_STATION, STA_DISCONNECTED, bssid, qos_enable, aid); + rsi_send_block_unblock_frame(common, true); } } -- cgit v1.2.3 From 79e590d924de3f73f7469b3927c8aa7d086e4c82 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:45 +0530 Subject: rsi: update tx command frame block/unblock data TX command frame block/unblock data is modified to use common descriptor structure. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 16 +++++++++------- drivers/net/wireless/rsi/rsi_mgmt.h | 13 +++++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 64bbd0a6f8de..1fba7bba3a10 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1327,7 +1327,7 @@ static int rsi_eeprom_read(struct rsi_common *common) */ int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event) { - struct rsi_mac_frame *mgmt_frame; + struct rsi_block_unblock_data *mgmt_frame; struct sk_buff *skb; rsi_dbg(MGMT_TX_ZONE, "%s: Sending block/unblock frame\n", __func__); @@ -1340,23 +1340,25 @@ int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event) } memset(skb->data, 0, FRAME_DESC_SZ); - mgmt_frame = (struct rsi_mac_frame *)skb->data; + mgmt_frame = (struct rsi_block_unblock_data *)skb->data; - mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); - mgmt_frame->desc_word[1] = cpu_to_le16(BLOCK_HW_QUEUE); + rsi_set_len_qno(&mgmt_frame->desc_dword0.len_qno, 0, RSI_WIFI_MGMT_Q); + mgmt_frame->desc_dword0.frame_type = BLOCK_HW_QUEUE; + mgmt_frame->host_quiet_info = QUIET_INFO_VALID; if (block_event) { rsi_dbg(INFO_ZONE, "blocking the data qs\n"); - mgmt_frame->desc_word[4] = cpu_to_le16(0xf); + mgmt_frame->block_q_bitmap = cpu_to_le16(0xf); + mgmt_frame->block_q_bitmap |= cpu_to_le16(0xf << 4); } else { rsi_dbg(INFO_ZONE, "unblocking the data qs\n"); - mgmt_frame->desc_word[5] = cpu_to_le16(0xf); + mgmt_frame->unblock_q_bitmap = cpu_to_le16(0xf); + mgmt_frame->unblock_q_bitmap |= cpu_to_le16(0xf << 4); } skb_put(skb, FRAME_DESC_SZ); return rsi_send_internal_mgmt_frame(common, skb); - } /** diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 5df64d3c67dc..cb0b17ec48d0 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -381,6 +381,19 @@ struct rsi_auto_rate { __le16 supported_rates[40]; } __packed; +#define QUIET_INFO_VALID BIT(0) +#define QUIET_ENABLE BIT(1) +struct rsi_block_unblock_data { + struct rsi_cmd_desc_dword0 desc_dword0; + u8 xtend_desc_size; + u8 host_quiet_info; + __le16 reserved; + __le16 block_q_bitmap; + __le16 unblock_q_bitmap; + __le16 token; + __le16 flush_q_bitmap; +} __packed; + struct qos_params { __le16 cont_win_min_q; __le16 cont_win_max_q; -- cgit v1.2.3 From 5059afacee8815ebf387f79c55ff9e60a5ba8c6f Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:46 +0530 Subject: rsi: Remove internal header from Tx status skb Device specific descriptor for each TX packet is prepared on top of skb data address. This descriptor should be pulled out before indicating the TX status to mac80211. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 021e5ac5f107..36303ae820af 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -229,12 +229,20 @@ void rsi_indicate_tx_status(struct rsi_hw *adapter, int status) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct skb_info *tx_params; - memset(info->driver_data, 0, IEEE80211_TX_INFO_DRIVER_DATA_SIZE); + if (!adapter->hw) { + rsi_dbg(ERR_ZONE, "##### No MAC #####\n"); + return; + } if (!status) info->flags |= IEEE80211_TX_STAT_ACK; + tx_params = (struct skb_info *)info->driver_data; + skb_pull(skb, tx_params->internal_hdr_size); + memset(info->driver_data, 0, IEEE80211_TX_INFO_DRIVER_DATA_SIZE); + ieee80211_tx_status_irqsafe(adapter->hw, skb); } -- cgit v1.2.3 From 86583258499ce5c3ee0e49f897c2d1c013ebdbd9 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:47 +0530 Subject: rsi: Send rx filter frame to device when interface is down When interface is down it is better to stop all RX packets to host. 0xffff will block all packets to host. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 36303ae820af..d094b0ab05ba 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -301,6 +301,10 @@ static void rsi_mac80211_stop(struct ieee80211_hw *hw) mutex_lock(&common->mutex); common->iface_down = true; + + /* Block all rx frames */ + rsi_send_rx_filter_frame(common, 0xffff); + mutex_unlock(&common->mutex); } -- cgit v1.2.3 From d7203a83f065dc78d907c2a2f5d317972ec28d52 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Mon, 10 Jul 2017 18:10:48 +0530 Subject: rsi: regulatory enhancements Below regulatory changes are included this patch * Country code is saved as it will be used in bgscan. * Region codes are mapped according to RSI region codes. * Radar flag settings are moved under the check if 5GHZ band is enabled. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 49 ++++++++++++++++++++++------- drivers/net/wireless/rsi/rsi_main.h | 8 +++++ 2 files changed, 45 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index d094b0ab05ba..c91d6efa7c84 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -1163,6 +1163,21 @@ static int rsi_mac80211_get_antenna(struct ieee80211_hw *hw, return 0; } +static int rsi_map_region_code(enum nl80211_dfs_regions region_code) +{ + switch (region_code) { + case NL80211_DFS_FCC: + return RSI_REGION_FCC; + case NL80211_DFS_ETSI: + return RSI_REGION_ETSI; + case NL80211_DFS_JP: + return RSI_REGION_TELEC; + case NL80211_DFS_UNSET: + return RSI_REGION_WORLD; + } + return RSI_REGION_WORLD; +} + static void rsi_reg_notify(struct wiphy *wiphy, struct regulatory_request *request) { @@ -1170,23 +1185,33 @@ static void rsi_reg_notify(struct wiphy *wiphy, struct ieee80211_channel *ch; struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct rsi_hw * adapter = hw->priv; + struct rsi_common *common = adapter->priv; int i; - - sband = wiphy->bands[NL80211_BAND_5GHZ]; - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; + mutex_lock(&common->mutex); + + rsi_dbg(INFO_ZONE, "country = %s dfs_region = %d\n", + request->alpha2, request->dfs_region); + + if (common->num_supp_bands > 1) { + sband = wiphy->bands[NL80211_BAND_5GHZ]; - if (ch->flags & IEEE80211_CHAN_RADAR) - ch->flags |= IEEE80211_CHAN_NO_IR; + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (ch->flags & IEEE80211_CHAN_RADAR) + ch->flags |= IEEE80211_CHAN_NO_IR; + } } + adapter->dfs_region = rsi_map_region_code(request->dfs_region); + rsi_dbg(INFO_ZONE, "RSI region code = %d\n", adapter->dfs_region); - rsi_dbg(INFO_ZONE, - "country = %s dfs_region = %d\n", - request->alpha2, request->dfs_region); - adapter->dfs_region = request->dfs_region; + adapter->country[0] = request->alpha2[0]; + adapter->country[1] = request->alpha2[1]; + + mutex_unlock(&common->mutex); } static struct ieee80211_ops mac80211_ops = { diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 485b97ab5779..6a8e8e7ed1fb 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -170,6 +170,13 @@ struct xtended_desc { u16 reserved; }; +enum rsi_dfs_regions { + RSI_REGION_FCC = 0, + RSI_REGION_ETSI, + RSI_REGION_TELEC, + RSI_REGION_WORLD +}; + struct rsi_hw; struct rsi_common { @@ -287,6 +294,7 @@ struct rsi_hw { struct eepromrw_info eeprom; u32 interrupt_status; u8 dfs_region; + char country[2]; void *rsi_dev; struct rsi_host_intf_ops *host_intf_ops; int (*check_hw_queue_status)(struct rsi_hw *adapter, u8 q_num); -- cgit v1.2.3 From 523b724a769b69963ef46efa14b8b730b40419d5 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 11 Jul 2017 19:57:51 +0530 Subject: rsi: use macro for allocating USB buffer 4 bytes is fixed size for reading or writing USB register. We will use a macro instead of hardcoding this. Fixes: b97e9b94ad75c ("rsi: Add new host interface operations") Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 4 ++-- drivers/net/wireless/rsi/rsi_usb.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 3febf24d619b..99a520afc7c5 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -162,7 +162,7 @@ static int rsi_usb_reg_read(struct usb_device *usbdev, u8 *buf; int status = -ENOMEM; - buf = kmalloc(0x04, GFP_KERNEL); + buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL); if (!buf) return status; @@ -204,7 +204,7 @@ static int rsi_usb_reg_write(struct usb_device *usbdev, u8 *usb_reg_buf; int status = -ENOMEM; - usb_reg_buf = kmalloc(0x04, GFP_KERNEL); + usb_reg_buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL); if (!usb_reg_buf) return status; diff --git a/drivers/net/wireless/rsi/rsi_usb.h b/drivers/net/wireless/rsi/rsi_usb.h index 3babf81f5a39..891daea2d932 100644 --- a/drivers/net/wireless/rsi/rsi_usb.h +++ b/drivers/net/wireless/rsi/rsi_usb.h @@ -37,6 +37,7 @@ #define BT_EP 2 #define RSI_USB_BUF_SIZE 4096 +#define RSI_USB_CTRL_BUF_SIZE 0x04 struct rsi_91x_usbdev { struct rsi_thread rx_thread; -- cgit v1.2.3 From 59f73e2ae185a6ab5c40f63657a83192bf054fb4 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 11 Jul 2017 19:57:52 +0530 Subject: rsi: check length before USB read/write register These checks are required. Otherwise we may end up getting memory corruption if invalid length is passed. Fixes: b97e9b94ad75c ("rsi: Add new host interface operations") Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 99a520afc7c5..3d33ce9ca2ba 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -166,6 +166,9 @@ static int rsi_usb_reg_read(struct usb_device *usbdev, if (!buf) return status; + if (len > RSI_USB_CTRL_BUF_SIZE) + return -EINVAL; + status = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), USB_VENDOR_REGISTER_READ, @@ -208,6 +211,9 @@ static int rsi_usb_reg_write(struct usb_device *usbdev, if (!usb_reg_buf) return status; + if (len > RSI_USB_CTRL_BUF_SIZE) + return -EINVAL; + usb_reg_buf[0] = (value & 0x00ff); usb_reg_buf[1] = (value & 0xff00) >> 8; usb_reg_buf[2] = 0x0; -- cgit v1.2.3 From e6249e15fea2f915ddfcbee8efd9120cb64ed93d Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 11 Jul 2017 19:57:53 +0530 Subject: rsi: fix static checker warning u32 pointer is changed to u16 and filled the value. Problem is solved by using local temporary variable. Below static checker warning was reported. drivers/net/wireless/rsi/rsi_91x_usb.c:400 rsi_usb_master_reg_read() warn: passing casted pointer 'value' to 'rsi_usb_reg_read()' 32 vs 16. Fixes: b97e9b94ad75c ("rsi: Add new host interface operations") Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 3d33ce9ca2ba..9097f7e6229e 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -404,8 +404,15 @@ static int rsi_usb_master_reg_read(struct rsi_hw *adapter, u32 reg, { struct usb_device *usbdev = ((struct rsi_91x_usbdev *)adapter->rsi_dev)->usbdev; + u16 temp; + int ret; - return rsi_usb_reg_read(usbdev, reg, (u16 *)value, len); + ret = rsi_usb_reg_read(usbdev, reg, &temp, len); + if (ret < 0) + return ret; + *value = temp; + + return 0; } static int rsi_usb_master_reg_write(struct rsi_hw *adapter, -- cgit v1.2.3 From b6658b66d8a6eaaf216382c19d016116931a0f63 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:17 -0700 Subject: mwifiex: reunite copy-and-pasted remove/reset code When PCIe FLR code was added, it explicitly copy-and-pasted much of mwifiex_remove_card() into mwifiex_shutdown_sw(). This is unnecessary, as almost all of the code should be reused. Let's reunite what we can for now. The only functional changes for now: * call netif_device_detach() in the remove() code path -- this wasn't done before, but it really should be a no-op, when the device is getting totally unregistered soon anyway * call the ->down_dev() driver callback only after we've finished all SW teardown -- this should have no significant effect, since the only user (pcie.c) does very minimal work there, and it doesn't matter that we reorder this Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 104 ++++++++-------------------- 1 file changed, 28 insertions(+), 76 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index f2600b827e81..8615099468da 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1352,26 +1352,12 @@ static void mwifiex_main_work_queue(struct work_struct *work) mwifiex_main_process(adapter); } -/* - * This function gets called during PCIe function level reset. Required - * code is extracted from mwifiex_remove_card() - */ -int -mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) +/* Common teardown code used for both device removal and reset */ +static void mwifiex_uninit_sw(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; int i; - if (!adapter) - goto exit_return; - - wait_for_completion(adapter->fw_done); - /* Caller should ensure we aren't suspending while this happens */ - reinit_completion(adapter->fw_done); - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - mwifiex_deauthenticate(priv, NULL); - /* We can no longer handle interrupts once we start doing the teardown * below. */ @@ -1393,12 +1379,9 @@ mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) } mwifiex_dbg(adapter, CMD, "cmd: calling mwifiex_shutdown_drv...\n"); - mwifiex_shutdown_drv(adapter); - if (adapter->if_ops.down_dev) - adapter->if_ops.down_dev(adapter); - mwifiex_dbg(adapter, CMD, "cmd: mwifiex_shutdown_drv done\n"); + if (atomic_read(&adapter->rx_pending) || atomic_read(&adapter->tx_pending) || atomic_read(&adapter->cmd_pending)) { @@ -1421,9 +1404,30 @@ mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) rtnl_unlock(); } vfree(adapter->chan_stats); +} + +/* + * This function gets called during PCIe function level reset. + */ +int mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + + if (!adapter) + return 0; + + wait_for_completion(adapter->fw_done); + /* Caller should ensure we aren't suspending while this happens */ + reinit_completion(adapter->fw_done); + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + mwifiex_deauthenticate(priv, NULL); + + mwifiex_uninit_sw(adapter); + + if (adapter->if_ops.down_dev) + adapter->if_ops.down_dev(adapter); - mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); -exit_return: return 0; } EXPORT_SYMBOL_GPL(mwifiex_shutdown_sw); @@ -1676,61 +1680,10 @@ EXPORT_SYMBOL_GPL(mwifiex_add_card); */ int mwifiex_remove_card(struct mwifiex_adapter *adapter) { - struct mwifiex_private *priv = NULL; - int i; - if (!adapter) - goto exit_remove; - - /* We can no longer handle interrupts once we start doing the teardown - * below. */ - if (adapter->if_ops.disable_int) - adapter->if_ops.disable_int(adapter); - - adapter->surprise_removed = true; - - mwifiex_terminate_workqueue(adapter); - - /* Stop data */ - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv && priv->netdev) { - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - } - } - - mwifiex_dbg(adapter, CMD, - "cmd: calling mwifiex_shutdown_drv...\n"); - - mwifiex_shutdown_drv(adapter); - mwifiex_dbg(adapter, CMD, - "cmd: mwifiex_shutdown_drv done\n"); - if (atomic_read(&adapter->rx_pending) || - atomic_read(&adapter->tx_pending) || - atomic_read(&adapter->cmd_pending)) { - mwifiex_dbg(adapter, ERROR, - "rx_pending=%d, tx_pending=%d,\t" - "cmd_pending=%d\n", - atomic_read(&adapter->rx_pending), - atomic_read(&adapter->tx_pending), - atomic_read(&adapter->cmd_pending)); - } - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - - if (!priv) - continue; + return 0; - rtnl_lock(); - if (priv->netdev && - priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) - mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); - rtnl_unlock(); - } - vfree(adapter->chan_stats); + mwifiex_uninit_sw(adapter); wiphy_unregister(adapter->wiphy); wiphy_free(adapter->wiphy); @@ -1748,7 +1701,6 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter) "info: free adapter\n"); mwifiex_free_adapter(adapter); -exit_remove: return 0; } EXPORT_SYMBOL_GPL(mwifiex_remove_card); -- cgit v1.2.3 From 4b1f5a0d2eeb988d3e77dce7210e3ad3136b0912 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:18 -0700 Subject: mwifiex: reset interrupt status across device reset When resetting the device, we might have queued up interrupts that didn't get a chance to finish processing. We really don't need to handle them at this point; we just want to make sure they don't cause us to try to process old commands from before the device was reset. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 8615099468da..275cf8dc4f2a 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1366,6 +1366,7 @@ static void mwifiex_uninit_sw(struct mwifiex_adapter *adapter) adapter->surprise_removed = true; mwifiex_terminate_workqueue(adapter); + adapter->int_status = 0; /* Stop data */ for (i = 0; i < adapter->priv_num; i++) { -- cgit v1.2.3 From 7dc4a6b5ca942a7196e17ecb3827b406cf79bc60 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:19 -0700 Subject: mwifiex: pcie: don't allow cmd buffer reuse after reset In rogue cases (due to other bugs) it's possible we try to process an old command response *after* resetting the device. This could trigger a double-free (or the SKB can get reallocated elsewhere...causing other memory corruptions) in mwifiex_pcie_process_cmd_complete(). For safety (and symmetry) let's always NULL out the command buffer as we free it up. We're already doing this for the command response buffer. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 21f2201405d1..f54053e6fb60 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -1043,12 +1043,14 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf, PCI_DMA_FROMDEVICE); dev_kfree_skb_any(card->cmdrsp_buf); + card->cmdrsp_buf = NULL; } if (card && card->cmd_buf) { mwifiex_unmap_pci_memory(adapter, card->cmd_buf, PCI_DMA_TODEVICE); dev_kfree_skb_any(card->cmd_buf); + card->cmd_buf = NULL; } return 0; } @@ -2934,7 +2936,6 @@ static void mwifiex_pcie_free_buffers(struct mwifiex_adapter *adapter) mwifiex_pcie_delete_evtbd_ring(adapter); mwifiex_pcie_delete_rxbd_ring(adapter); mwifiex_pcie_delete_txbd_ring(adapter); - card->cmdrsp_buf = NULL; } /* -- cgit v1.2.3 From 643acea6297f9aa7241cfb17d5b5606186f54137 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:20 -0700 Subject: mwifiex: re-register wiphy across reset In general, it's helpful to use the same code for device removal as for device reset, as this tends to have fewer bugs. Let's move the wiphy unregistration code into the common reset and removal code. In particular, it's very hard to properly handle the reset sequence when something fails. Currently, if mwifiex_reinit_sw() fails, we've failed to unregister the associated wiphy, and so running something as simple as "iw phy" can trigger an OOPS, as the wiphy still has hooks back into freed mwifiex data structures. For example, KASAN complained: [... see reset fail for other reasons ...] [ 1184.821158] mwifiex_pcie 0000:01:00.0: info: dnld wifi firmware from 174948 bytes [ 1186.870914] mwifiex_pcie 0000:01:00.0: info: FW download over, size 608396 bytes [ 1187.685990] mwifiex_pcie 0000:01:00.0: WLAN FW is active [ 1187.692673] mwifiex_pcie 0000:01:00.0: cmd_wait_q terminated: -512 [ 1187.699075] mwifiex_pcie 0000:01:00.0: info: _mwifiex_fw_dpc: unregister device [ 1187.713476] mwifiex: Failed to bring up adapter: -5 [ 1187.718644] mwifiex_pcie 0000:01:00.0: reinit failed: -5 [... run `iw phy` ...] [ 1212.902419] ================================================================== [ 1212.909806] BUG: KASAN: use-after-free in mwifiex_cfg80211_get_antenna+0x54/0xfc [mwifiex] at addr ffffffc0ad1a8028 [ 1212.920246] Read of size 1 by task iw/3127 [...] [ 1212.934946] page dumped because: kasan: bad access detected [...] [ 1212.950665] Call trace: [ 1212.953148] [] dump_backtrace+0x0/0x190 [ 1212.958572] [] show_stack+0x20/0x28 [ 1212.963648] [] dump_stack+0xa4/0xcc [ 1212.968723] [] kasan_report+0x378/0x500 [ 1212.974140] [] __asan_load1+0x44/0x4c [ 1212.979462] [] mwifiex_cfg80211_get_antenna+0x54/0xfc [mwifiex] [ 1212.987131] [] nl80211_send_wiphy+0x75c/0x2de0 [cfg80211] [ 1212.994246] [] nl80211_dump_wiphy+0x32c/0x438 [cfg80211] [ 1213.001149] [] genl_lock_dumpit+0x48/0x64 [ 1213.006746] [] netlink_dump+0x178/0x398 [ 1213.012171] [] __netlink_dump_start+0x1bc/0x260 [...] This all goes away if we just tear down the wiphy on the way down, and set it back up if/when we bring the device back up. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 275cf8dc4f2a..9c8f7bcfef8b 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1405,6 +1405,10 @@ static void mwifiex_uninit_sw(struct mwifiex_adapter *adapter) rtnl_unlock(); } vfree(adapter->chan_stats); + + wiphy_unregister(adapter->wiphy); + wiphy_free(adapter->wiphy); + adapter->wiphy = NULL; } /* @@ -1686,9 +1690,6 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter) mwifiex_uninit_sw(adapter); - wiphy_unregister(adapter->wiphy); - wiphy_free(adapter->wiphy); - if (adapter->irq_wakeup >= 0) device_init_wakeup(adapter->dev, false); -- cgit v1.2.3 From ce32d1d83702064e7746453622fc2d6bdcd8baa4 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:21 -0700 Subject: mwifiex: unregister wiphy before freeing resources It's possible for some control interfaces (e.g., scans, set freq) to be active after we've stopped our main work queue and the netif TX queues. These don't get completely shut out until we've unregistered the wdevs and wiphy. So let's only free command buffers and poison our lists after wiphy_unregister(). This resolves various use-after-free issues seen when resetting the device. Cc: Johannes Berg Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/init.c | 3 +++ drivers/net/wireless/marvell/mwifiex/main.c | 7 ++++++- drivers/net/wireless/marvell/mwifiex/main.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index 3ecb59f7405b..de96675e43d5 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -418,7 +418,10 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) mwifiex_cancel_all_pending_cmd(adapter); wake_up_interruptible(&adapter->cmd_wait_q.wait); wake_up_interruptible(&adapter->hs_activate_wait_q); +} +void mwifiex_free_cmd_buffers(struct mwifiex_adapter *adapter) +{ /* Free lock variables */ mwifiex_free_lock_list(adapter); diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 9c8f7bcfef8b..77e491720664 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -653,6 +653,7 @@ err_dnld_fw: if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { pr_debug("info: %s: shutdown mwifiex\n", __func__); mwifiex_shutdown_drv(adapter); + mwifiex_free_cmd_buffers(adapter); } init_failed = true; @@ -1404,11 +1405,13 @@ static void mwifiex_uninit_sw(struct mwifiex_adapter *adapter) mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); rtnl_unlock(); } - vfree(adapter->chan_stats); wiphy_unregister(adapter->wiphy); wiphy_free(adapter->wiphy); adapter->wiphy = NULL; + + vfree(adapter->chan_stats); + mwifiex_free_cmd_buffers(adapter); } /* @@ -1515,6 +1518,7 @@ err_kmalloc: mwifiex_dbg(adapter, ERROR, "info: %s: shutdown mwifiex\n", __func__); mwifiex_shutdown_drv(adapter); + mwifiex_free_cmd_buffers(adapter); } complete_all(adapter->fw_done); @@ -1662,6 +1666,7 @@ err_registerdev: if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { pr_debug("info: %s: shutdown mwifiex\n", __func__); mwifiex_shutdown_drv(adapter); + mwifiex_free_cmd_buffers(adapter); } err_kmalloc: mwifiex_free_adapter(adapter); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index f8cf3079ac7d..62ce4e81f695 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1078,6 +1078,7 @@ int mwifiex_get_debug_info(struct mwifiex_private *, int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); +void mwifiex_free_cmd_buffers(struct mwifiex_adapter *adapter); void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); void mwifiex_cancel_pending_scan_cmd(struct mwifiex_adapter *adapter); -- cgit v1.2.3 From 6417dba33538a856cf1b7d605942c707422213c9 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:22 -0700 Subject: mwifiex: don't short-circuit netdev notifiers on interface deletion When we leave the delete interface function, there are still netdev hooks that might try to process the device. We're short-circuiting some of that by changing the interface type and clearing ieee80211_ptr. This means we skip NETDEV_UNREGISTER_FINAL in cfg80211. Fortunately, that is currently a no-op. We don't need most of the cleanup here anyway: * the connection state will get (un)set as part of the disconnect process (which cfg80211 already initiates for us) * the interface type doesn't actually need to be cleared at all (it'll trigger a WARN_ON() in cfg80211 if we do) * the iee80211_ptr isn't really "ours" to clear anyway So stop resetting those 3 things. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index fdfdf2371986..2be78170ec67 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -3123,11 +3123,7 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) priv->dfs_chan_sw_workqueue = NULL; } /* Clear the priv in adapter */ - priv->netdev->ieee80211_ptr = NULL; priv->netdev = NULL; - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - - priv->media_connected = false; switch (priv->bss_mode) { case NL80211_IFTYPE_UNSPECIFIED: -- cgit v1.2.3 From c253a62da9b456e4cd1db49f65c8f605c4f399ea Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:23 -0700 Subject: mwifiex: fixup init_channel_scan_gap error case In reading through _mwifiex_fw_dpc(), I noticed that after we've registered our wiphy, we still have error paths that don't free it back up. Let's do that. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 77e491720664..0448dcc07139 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -588,7 +588,7 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context) if (mwifiex_init_channel_scan_gap(adapter)) { mwifiex_dbg(adapter, ERROR, "could not init channel stats table\n"); - goto err_init_fw; + goto err_init_chan_scan; } if (driver_mode) { @@ -636,6 +636,7 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context) err_add_intf: vfree(adapter->chan_stats); +err_init_chan_scan: wiphy_unregister(adapter->wiphy); wiphy_free(adapter->wiphy); err_init_fw: -- cgit v1.2.3 From 9557d9f2e62b0c929510e4dda32013aa62453558 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:24 -0700 Subject: mwifiex: ensure "disable auto DS" struct is initialized The .idle_time field *should* be unused, but technically, we're allowing unitialized stack garbage to pass all the way through to the firmware host command. Let's zero it out instead. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 42997e05d90f..43ecd621d1ef 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -654,9 +654,9 @@ int mwifiex_get_bss_info(struct mwifiex_private *priv, */ int mwifiex_disable_auto_ds(struct mwifiex_private *priv) { - struct mwifiex_ds_auto_ds auto_ds; - - auto_ds.auto_ds = DEEP_SLEEP_OFF; + struct mwifiex_ds_auto_ds auto_ds = { + .auto_ds = DEEP_SLEEP_OFF, + }; return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true); -- cgit v1.2.3 From 5e6588b9d4ab0f97ff7ea58195f4c6fd6380bd91 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:25 -0700 Subject: mwifiex: fix misnomers in mwifiex_free_lock_list() Despite the name (and meticulous comments), this function frees no memory and does not touch any locks. All it does is "delete" the list heads -- which just means they'll be dangling, and we'll need to re-init them if we use them again. It seems like this code would work OK as a sort of canary for using the list after we've torn everything down, so it's fine to keep the code; let's just get the name and comments to match what's actually happening. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/init.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index de96675e43d5..de974e8bb9c6 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -373,15 +373,13 @@ void mwifiex_stop_net_dev_queue(struct net_device *netdev, } /* - * This function releases the lock variables and frees the locks and - * associated locks. + * This function invalidates the list heads. */ -static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) +static void mwifiex_invalidate_lists(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; s32 i, j; - /* Free lists */ list_del(&adapter->cmd_free_q); list_del(&adapter->cmd_pending_q); list_del(&adapter->scan_pending_q); @@ -422,8 +420,7 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) void mwifiex_free_cmd_buffers(struct mwifiex_adapter *adapter) { - /* Free lock variables */ - mwifiex_free_lock_list(adapter); + mwifiex_invalidate_lists(adapter); /* Free command buffer */ mwifiex_dbg(adapter, INFO, "info: free cmd buffer\n"); -- cgit v1.2.3 From f7d7e4b689ca7889cf7733549844eb913c9665c1 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:26 -0700 Subject: mwifiex: make mwifiex_free_cmd_buffer() return void It doesn't fail. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cmdevt.c | 6 ++---- drivers/net/wireless/marvell/mwifiex/main.h | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index 8dad52886034..6ff8e84b01e0 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -427,7 +427,7 @@ int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter) * The function calls the completion callback for all the command * buffers that still have response buffers associated with them. */ -int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) +void mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_array; u32 i; @@ -436,7 +436,7 @@ int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) if (!adapter->cmd_pool) { mwifiex_dbg(adapter, FATAL, "info: FREE_CMD_BUF: cmd_pool is null\n"); - return 0; + return; } cmd_array = adapter->cmd_pool; @@ -464,8 +464,6 @@ int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) kfree(adapter->cmd_pool); adapter->cmd_pool = NULL; } - - return 0; } /* diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 62ce4e81f695..2bee5cdf1fc8 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1077,7 +1077,7 @@ int mwifiex_get_debug_info(struct mwifiex_private *, struct mwifiex_debug_info *); int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); -int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); +void mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); void mwifiex_free_cmd_buffers(struct mwifiex_adapter *adapter); void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); -- cgit v1.2.3 From fe8d730adaee7988c46e6e4204f4f49f9dba5cc2 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:27 -0700 Subject: mwifiex: utilize netif_tx_{wake,stop}_all_queues() We're open-coding these. Just use the helpers. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/init.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index de974e8bb9c6..e11919db7818 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -337,17 +337,9 @@ void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter) { unsigned long dev_queue_flags; - unsigned int i; spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); - - for (i = 0; i < netdev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); - - if (netif_tx_queue_stopped(txq)) - netif_tx_wake_queue(txq); - } - + netif_tx_wake_all_queues(netdev); spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); } @@ -358,17 +350,9 @@ void mwifiex_stop_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter) { unsigned long dev_queue_flags; - unsigned int i; spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); - - for (i = 0; i < netdev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); - - if (!netif_tx_queue_stopped(txq)) - netif_tx_stop_queue(txq); - } - + netif_tx_stop_all_queues(netdev); spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); } -- cgit v1.2.3 From 8395fd9b194cecbd9748d1b9b6ea87e3f8a6067c Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:28 -0700 Subject: mwifiex: don't open-code ARRAY_SIZE() Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfp.c | 4 +--- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 8 ++------ drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c | 5 ++--- 3 files changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c index 6e2994308526..bfe84e55df77 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfp.c +++ b/drivers/net/wireless/marvell/mwifiex/cfp.c @@ -180,11 +180,9 @@ static struct region_code_mapping region_code_mapping_t[] = { u8 *mwifiex_11d_code_2_region(u8 code) { u8 i; - u8 size = sizeof(region_code_mapping_t)/ - sizeof(struct region_code_mapping); /* Look for code in mapping table */ - for (i = 0; i < size; i++) + for (i = 0; i < ARRAY_SIZE(region_code_mapping_t); i++) if (region_code_mapping_t[i].code == code) return region_code_mapping_t[i].region; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 534d94a206a5..b71ad4de5e54 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -189,9 +189,7 @@ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, if (pbitmap_rates != NULL) { rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); - for (i = 0; - i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); - i++) + for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(pbitmap_rates[2 + i]); if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { @@ -206,9 +204,7 @@ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, cpu_to_le16(priv->bitmap_rates[0]); rate_scope->ofdm_rate_bitmap = cpu_to_le16(priv->bitmap_rates[1]); - for (i = 0; - i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); - i++) + for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(priv->bitmap_rates[2 + i]); if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c index 2945775e83c5..0fba5b10ef2d 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c @@ -298,9 +298,8 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, priv->bitmap_rates[1] = le16_to_cpu(rate_scope->ofdm_rate_bitmap); for (i = 0; - i < - sizeof(rate_scope->ht_mcs_rate_bitmap) / - sizeof(u16); i++) + i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); + i++) priv->bitmap_rates[2 + i] = le16_to_cpu(rate_scope-> ht_mcs_rate_bitmap[i]); -- cgit v1.2.3 From 463df4719084c96e13af5ddefca11b4bcd563074 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:29 -0700 Subject: mwifiex: drop 'add_tail' param from mwifiex_insert_cmd_to_pending_q() It's always called with 'true' -- we only determine it 'false' locally within this function. So drop the parameter. Also, this should be 'bool' (since we use true/false), not 'u32'. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cmdevt.c | 5 +++-- drivers/net/wireless/marvell/mwifiex/main.h | 3 +-- drivers/net/wireless/marvell/mwifiex/scan.c | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index 6ff8e84b01e0..3f5e822673bf 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -664,7 +664,7 @@ int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, cmd_no == HostCmd_CMD_802_11_SCAN_EXT) { mwifiex_queue_scan_cmd(priv, cmd_node); } else { - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node); queue_work(adapter->workqueue, &adapter->main_work); if (cmd_node->wait_q_enabled) ret = mwifiex_wait_queue_complete(adapter, cmd_node); @@ -682,11 +682,12 @@ int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, */ void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node, u32 add_tail) + struct cmd_ctrl_node *cmd_node) { struct host_cmd_ds_command *host_cmd = NULL; u16 command; unsigned long flags; + bool add_tail = true; host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); if (!host_cmd) { diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 2bee5cdf1fc8..909bd1ad3838 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1088,8 +1088,7 @@ void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node); void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node, - u32 addtail); + struct cmd_ctrl_node *cmd_node); int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter); int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter); diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index 9900855746ac..d8e8b857ddfb 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -1534,8 +1534,7 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, - true); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node); queue_work(adapter->workqueue, &adapter->main_work); /* Perform internal scan synchronously */ @@ -2033,7 +2032,7 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) struct cmd_ctrl_node, list); list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node); } return; -- cgit v1.2.3 From 605db27f7405eb410a2c1d332c9f35d2631ffa14 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:30 -0700 Subject: mwifiex: pcie: remove unnecessary masks After removing the interrupt loop in commit 5d5ddb5e0d9b ("mwifiex: pcie: don't loop/retry interrupt status checks"), we don't need to keep track of the cleared interrupts (actually, we didn't need to do that before, but we *really* don't need to now). Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index f54053e6fb60..13722961def2 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2473,28 +2473,24 @@ static int mwifiex_process_pcie_int(struct mwifiex_adapter *adapter) } if (pcie_ireg & HOST_INTR_DNLD_DONE) { - pcie_ireg &= ~HOST_INTR_DNLD_DONE; mwifiex_dbg(adapter, INTR, "info: TX DNLD Done\n"); ret = mwifiex_pcie_send_data_complete(adapter); if (ret) return ret; } if (pcie_ireg & HOST_INTR_UPLD_RDY) { - pcie_ireg &= ~HOST_INTR_UPLD_RDY; mwifiex_dbg(adapter, INTR, "info: Rx DATA\n"); ret = mwifiex_pcie_process_recv_data(adapter); if (ret) return ret; } if (pcie_ireg & HOST_INTR_EVENT_RDY) { - pcie_ireg &= ~HOST_INTR_EVENT_RDY; mwifiex_dbg(adapter, INTR, "info: Rx EVENT\n"); ret = mwifiex_pcie_process_event_ready(adapter); if (ret) return ret; } if (pcie_ireg & HOST_INTR_CMD_DONE) { - pcie_ireg &= ~HOST_INTR_CMD_DONE; if (adapter->cmd_sent) { mwifiex_dbg(adapter, INTR, "info: CMD sent Interrupt\n"); -- cgit v1.2.3 From 87a602126aaf1ecd52c2b70754a5a4b948b27903 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:31 -0700 Subject: mwifiex: pcie: unify MSI-X / non-MSI-X interrupt process After removing the interrupt loop in commit 5d5ddb5e0d9b ("mwifiex: pcie: don't loop/retry interrupt status checks"), there is practically zero difference between mwifiex_process_pcie_int() (which handled legacy PCI interrupts and MSI interrupts) and mwifiex_process_msix_int() (which handled MSI-X interrupts). Let's add the one relevant line to mwifiex_process_pcie_int() and kill the copy-and-paste. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 68 ++--------------------------- 1 file changed, 3 insertions(+), 65 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 13722961def2..bffe46a29287 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2430,7 +2430,7 @@ exit: * In case of Rx packets received, the packets are uploaded from card to * host and processed accordingly. */ -static int mwifiex_process_pcie_int(struct mwifiex_adapter *adapter) +static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) { int ret; u32 pcie_ireg = 0; @@ -2505,75 +2505,13 @@ static int mwifiex_process_pcie_int(struct mwifiex_adapter *adapter) mwifiex_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n", adapter->cmd_sent, adapter->data_sent); - if (!card->msi_enable && adapter->ps_state != PS_STATE_SLEEP) + if (!card->msi_enable && !card->msix_enable && + adapter->ps_state != PS_STATE_SLEEP) mwifiex_pcie_enable_host_int(adapter); return 0; } -static int mwifiex_process_msix_int(struct mwifiex_adapter *adapter) -{ - int ret; - u32 pcie_ireg; - unsigned long flags; - - spin_lock_irqsave(&adapter->int_lock, flags); - /* Clear out unused interrupts */ - pcie_ireg = adapter->int_status; - adapter->int_status = 0; - spin_unlock_irqrestore(&adapter->int_lock, flags); - - if (pcie_ireg & HOST_INTR_DNLD_DONE) { - mwifiex_dbg(adapter, INTR, - "info: TX DNLD Done\n"); - ret = mwifiex_pcie_send_data_complete(adapter); - if (ret) - return ret; - } - if (pcie_ireg & HOST_INTR_UPLD_RDY) { - mwifiex_dbg(adapter, INTR, - "info: Rx DATA\n"); - ret = mwifiex_pcie_process_recv_data(adapter); - if (ret) - return ret; - } - if (pcie_ireg & HOST_INTR_EVENT_RDY) { - mwifiex_dbg(adapter, INTR, - "info: Rx EVENT\n"); - ret = mwifiex_pcie_process_event_ready(adapter); - if (ret) - return ret; - } - - if (pcie_ireg & HOST_INTR_CMD_DONE) { - if (adapter->cmd_sent) { - mwifiex_dbg(adapter, INTR, - "info: CMD sent Interrupt\n"); - adapter->cmd_sent = false; - } - /* Handle command response */ - ret = mwifiex_pcie_process_cmd_complete(adapter); - if (ret) - return ret; - } - - mwifiex_dbg(adapter, INTR, - "info: cmd_sent=%d data_sent=%d\n", - adapter->cmd_sent, adapter->data_sent); - - return 0; -} - -static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - - if (card->msix_enable) - return mwifiex_process_msix_int(adapter); - else - return mwifiex_process_pcie_int(adapter); -} - /* * This function downloads data from driver to card. * -- cgit v1.2.3 From 37680819c6e1f5f22c171cd76d1ac093528fae56 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:32 -0700 Subject: mwifiex: debugfs: allow card_reset() to cancel things The card_reset() implementation should be setting our state flags and cancelling commands for us (i.e., in mwifiex_shutdown_drv()), so let's not do it here. Also, this debugfs file is useful for testing and debugging the reset feature, so we shouldn't do extra preparatory steps here, as that might cause different reset behavior, which could either cause new bugs or paper over existing ones that this debug feature should otherwise help us catch. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/debugfs.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c index f6f105a7d3ff..6f4239be609d 100644 --- a/drivers/net/wireless/marvell/mwifiex/debugfs.c +++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c @@ -940,8 +940,6 @@ mwifiex_reset_write(struct file *file, if (adapter->if_ops.card_reset) { dev_info(adapter->dev, "Resetting per request\n"); - adapter->hw_status = MWIFIEX_HW_STATUS_RESET; - mwifiex_cancel_all_pending_cmd(adapter); adapter->if_ops.card_reset(adapter); } -- cgit v1.2.3 From 2f47150ab3efd338ccdf75bae9f91ec147d6d98b Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:33 -0700 Subject: mwifiex: pcie: disable device DMA before unmapping/freeing buffers In testing the mwifiex reset code path, I've noticed KASAN complaining about some "overwritten poison values" in our RX buffer descriptors. Because KASAN didn't notice this at the time of a CPU write, this seems to suggest that the device is writing to this memory. This makes a little sense, because when resetting, we don't necessarily expect the device to be responsive, so we don't have a chance to disable everything cleanly. We can at least take the precaution of disabling DMA for the device though, and in my testing that seems to clear up this particular issue. This patch reorders the removal path so that we disable the device *before* releasing our last PCIe buffers, and it clears/sets the bus master feature from the PCI device when resetting. Along the way, remove the insufficient (and confusing) error path in mwifiex_pcie_up_dev() (it doesn't unwind things well enough, and it doesn't propagate its errors upward anyway). Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index bffe46a29287..fc42697d7ec8 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2971,15 +2971,17 @@ static void mwifiex_cleanup_pcie(struct mwifiex_adapter *adapter) "Failed to write driver not-ready signature\n"); } - mwifiex_pcie_free_buffers(adapter); - if (pdev) { + pci_disable_device(pdev); + pci_iounmap(pdev, card->pci_mmap); pci_iounmap(pdev, card->pci_mmap1); pci_disable_device(pdev); pci_release_region(pdev, 2); pci_release_region(pdev, 0); } + + mwifiex_pcie_free_buffers(adapter); } static int mwifiex_pcie_request_irq(struct mwifiex_adapter *adapter) @@ -3155,7 +3157,6 @@ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) static void mwifiex_pcie_up_dev(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; - int ret; struct pci_dev *pdev = card->dev; /* tx_buf_size might be changed to 3584 by firmware during @@ -3163,11 +3164,9 @@ static void mwifiex_pcie_up_dev(struct mwifiex_adapter *adapter) */ adapter->tx_buf_size = card->pcie.tx_buf_size; - ret = mwifiex_pcie_alloc_buffers(adapter); - if (!ret) - return; + mwifiex_pcie_alloc_buffers(adapter); - pci_iounmap(pdev, card->pci_mmap1); + pci_set_master(pdev); } /* This function cleans up the PCI-E host memory space. */ @@ -3175,10 +3174,13 @@ static void mwifiex_pcie_down_dev(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct pci_dev *pdev = card->dev; if (mwifiex_write_reg(adapter, reg->drv_rdy, 0x00000000)) mwifiex_dbg(adapter, ERROR, "Failed to write driver not-ready signature\n"); + pci_clear_master(pdev); + adapter->seq_num = 0; mwifiex_pcie_free_buffers(adapter); -- cgit v1.2.3 From 43a0c9aea64d50d126bc2d7babecc8a2a2b21e59 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:34 -0700 Subject: mwifiex: pcie: remove unnecessary 'pdev' check 'card->dev' is initialized once and is never cleared. Drop the unnecessary "safety" check, as it simply obscures things, and we don't do this check everywhere (and therefore it's not really "safe"). Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index fc42697d7ec8..3da1eeb730eb 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2971,15 +2971,12 @@ static void mwifiex_cleanup_pcie(struct mwifiex_adapter *adapter) "Failed to write driver not-ready signature\n"); } - if (pdev) { - pci_disable_device(pdev); + pci_disable_device(pdev); - pci_iounmap(pdev, card->pci_mmap); - pci_iounmap(pdev, card->pci_mmap1); - pci_disable_device(pdev); - pci_release_region(pdev, 2); - pci_release_region(pdev, 0); - } + pci_iounmap(pdev, card->pci_mmap); + pci_iounmap(pdev, card->pci_mmap1); + pci_release_region(pdev, 2); + pci_release_region(pdev, 0); mwifiex_pcie_free_buffers(adapter); } -- cgit v1.2.3 From 2d98cfd17e928ec3c89ed7cffc9d340fb703389b Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:35 -0700 Subject: mwifiex: keep mwifiex_cancel_pending_ioctl() static It has some scary comments about "only being called" from the timeout handler, so let's help keep it that way. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cmdevt.c | 4 +++- drivers/net/wireless/marvell/mwifiex/main.h | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index 3f5e822673bf..0edc5d621304 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -26,6 +26,8 @@ #include "11n.h" #include "11ac.h" +static void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); + /* * This function initializes a command node. * @@ -1074,7 +1076,7 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) * In case of scan commands, all pending commands in scan pending queue * are cancelled. */ -void +static void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_node = NULL; diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 909bd1ad3838..537a0ad795ff 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1080,7 +1080,6 @@ int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); void mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); void mwifiex_free_cmd_buffers(struct mwifiex_adapter *adapter); void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); -void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); void mwifiex_cancel_pending_scan_cmd(struct mwifiex_adapter *adapter); void mwifiex_cancel_scan(struct mwifiex_adapter *adapter); -- cgit v1.2.3 From 0bc03cfd824789b0efacf3cc204034db1f3e6f0a Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 24 Jul 2017 18:13:36 -0700 Subject: mwifiex: drop num CPU notice This print isn't very useful. It's also different between mwifiex_add_card() and mwifiex_reinit_sw(), and I'd like to consolidate them eventually. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 0448dcc07139..13fc7b6ed11d 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1619,10 +1619,8 @@ mwifiex_add_card(void *card, struct completion *fw_done, adapter->cmd_wait_q.status = 0; adapter->scan_wait_q_woken = false; - if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) { + if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) adapter->rx_work_enabled = true; - pr_notice("rx work enabled, cpus %d\n", num_possible_cpus()); - } adapter->workqueue = alloc_workqueue("MWIFIEX_WORK_QUEUE", -- cgit v1.2.3 From f46a5b0156b166fcfdf05bca02d13bf482d95bf1 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 6 Jul 2017 15:50:33 +0800 Subject: mwifiex: fix compile warning of unused variable We got a compile warning shows below: drivers/net/wireless/marvell/mwifiex/sdio.c: In function 'mwifiex_sdio_remove': drivers/net/wireless/marvell/mwifiex/sdio.c:377:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] Per the code, it didn't check if mwifiex_sdio_read_fw_status finish successfully. We should at least check the return of mwifiex_sdio_read_fw_status, otherwise the following check of firmware_stat and adapter->mfg_mode is pointless as the device is probably dead. Signed-off-by: Shawn Lin Reviewed-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sdio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index f81a006668f3..fd5183c10c4e 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -390,7 +390,8 @@ mwifiex_sdio_remove(struct sdio_func *func) mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); - if (firmware_stat == FIRMWARE_READY_SDIO && !adapter->mfg_mode) { + if (!ret && firmware_stat == FIRMWARE_READY_SDIO && + !adapter->mfg_mode) { mwifiex_deauthenticate_all(adapter); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); -- cgit v1.2.3 From f101d9649c42daecaa9650b362be8068f8f8c933 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Thu, 6 Jul 2017 15:55:28 +0800 Subject: mwifiex: uninit wakeup info in the error handling We inited wakeup info at the beginning of mwifiex_add_card, so we need to uninit it in the error handling. It's much the same as what we did in: 36908c4 mwifiex: uninit wakeup info when removing device Signed-off-by: Jeffy Chen Reviewed-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 13fc7b6ed11d..386993aa2ac1 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -667,8 +667,11 @@ done: release_firmware(adapter->firmware); adapter->firmware = NULL; } - if (init_failed) + if (init_failed) { + if (adapter->irq_wakeup >= 0) + device_init_wakeup(adapter->dev, false); mwifiex_free_adapter(adapter); + } /* Tell all current and future waiters we're finished */ complete_all(fw_done); @@ -1668,6 +1671,8 @@ err_registerdev: mwifiex_free_cmd_buffers(adapter); } err_kmalloc: + if (adapter->irq_wakeup >= 0) + device_init_wakeup(adapter->dev, false); mwifiex_free_adapter(adapter); err_init_sw: -- cgit v1.2.3 From fe0c94be77a0b69456d6a35c838ee23d68204ba2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 10 Jul 2017 10:21:15 +0300 Subject: mwifiex: usb: unlock on error in mwifiex_usb_tx_aggr_tmo() We need to unlock if mwifiex_usb_prepare_tx_aggr_skb() fails. Fixes: a2ca85ad721d ("mwifiex: usb: add timer to flush aggregation packets") Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index cb1753e43ef4..880ef1cb4088 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -1112,7 +1112,7 @@ static void mwifiex_usb_tx_aggr_tmo(unsigned long context) if (err) { mwifiex_dbg(adapter, ERROR, "prepare tx aggr skb failed, err=%d\n", err); - return; + goto unlock; } if (atomic_read(&port->tx_data_urb_pending) >= @@ -1133,6 +1133,7 @@ static void mwifiex_usb_tx_aggr_tmo(unsigned long context) done: if (err == -1) mwifiex_write_data_complete(adapter, skb_send, 0, -1); +unlock: spin_unlock_irqrestore(&port->tx_aggr_lock, flags); } -- cgit v1.2.3 From 9e467c52ae4c4dde3f3aa1427264578f7141eda5 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 21 Jul 2017 09:57:37 +0000 Subject: mwifiex: disable uapsd in tdls config Tdls uapsd support capability is default disabled during tdls setup, correspondingly it should also been disabled in tdls config. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Zhiyuan Yang Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index b71ad4de5e54..fb090144a6d8 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1751,7 +1751,7 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, struct mwifiex_ie_types_vhtcap *vht_capab; struct mwifiex_ie_types_aid *aid; struct mwifiex_ie_types_tdls_idle_timeout *timeout; - u8 *pos, qos_info; + u8 *pos; u16 config_len = 0; struct station_parameters *params = priv->sta_params; @@ -1785,12 +1785,11 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, put_unaligned_le16(params->capability, pos); config_len += sizeof(params->capability); - qos_info = params->uapsd_queues | (params->max_sp << 5); - wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos + - config_len); + wmm_qos_info = (void *)(pos + config_len); wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA); - wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info)); - wmm_qos_info->qos_info = qos_info; + wmm_qos_info->header.len = + cpu_to_le16(sizeof(wmm_qos_info->qos_info)); + wmm_qos_info->qos_info = 0; config_len += sizeof(struct mwifiex_ie_types_qos_info); if (params->ht_capa) { -- cgit v1.2.3 From c55971726c40b5d861f3bb457478545173a4e136 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 24 Jul 2017 23:26:23 +0100 Subject: mwifiex: usb: fix spelling mistake: "aggreataon"-> "aggregation" Trivial fix to spelling mistake in aggr_ctrl module parameter message text. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 386993aa2ac1..d67d70002ea9 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -46,7 +46,7 @@ MODULE_PARM_DESC(mfg_mode, "manufacturing mode enable:1, disable:0"); bool aggr_ctrl; module_param(aggr_ctrl, bool, 0000); -MODULE_PARM_DESC(aggr_ctrl, "usb tx aggreataon enable:1, disable:0"); +MODULE_PARM_DESC(aggr_ctrl, "usb tx aggregation enable:1, disable:0"); /* * This function registers the device and performs all the necessary -- cgit v1.2.3 From 17830147c40a26b7c323fbab525e6dd63bd45d49 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 27 Jul 2017 23:06:22 +0100 Subject: mwifiex: fix spelling mistake: "Insuffient" -> "Insufficient" Trivial fix to spelling mistake in mwifiex_dbg debug message Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/tdls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index 39cd677d4159..e76af2866a19 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -130,7 +130,7 @@ mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, if (skb_tailroom(skb) < rates_size + 4) { mwifiex_dbg(priv->adapter, ERROR, - "Insuffient space while adding rates\n"); + "Insufficient space while adding rates\n"); return -ENOMEM; } -- cgit v1.2.3 From 185ffc194800a914f793643adf0ce09592124ef6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 12 Jul 2017 11:47:02 -0500 Subject: ipw2100: don't return positive values to PCI probe on error Causes the PCI stack to complain, and then eventually call the PCI remove function, which ipw2100 is not expecting. It then tries to unregister an already-released netdev and other nasty things, leading to a panic. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1185518 Signed-off-by: Dan Williams Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index ccbe74589eec..77f3f92b3c85 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -1724,7 +1724,7 @@ static const struct libipw_geo ipw_geos[] = { static int ipw2100_up(struct ipw2100_priv *priv, int deferred) { unsigned long flags; - int rc = 0; + int err = 0; u32 lock; u32 ord_len = sizeof(lock); @@ -1757,33 +1757,33 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) if (priv->status & STATUS_POWERED || (priv->status & STATUS_RESET_PENDING)) { /* Power cycle the card ... */ - if (ipw2100_power_cycle_adapter(priv)) { + err = ipw2100_power_cycle_adapter(priv); + if (err) { printk(KERN_WARNING DRV_NAME ": %s: Could not cycle adapter.\n", priv->net_dev->name); - rc = 1; goto exit; } } else priv->status |= STATUS_POWERED; /* Load the firmware, start the clocks, etc. */ - if (ipw2100_start_adapter(priv)) { + err = ipw2100_start_adapter(priv); + if (err) { printk(KERN_ERR DRV_NAME ": %s: Failed to start the firmware.\n", priv->net_dev->name); - rc = 1; goto exit; } ipw2100_initialize_ordinals(priv); /* Determine capabilities of this particular HW configuration */ - if (ipw2100_get_hw_features(priv)) { + err = ipw2100_get_hw_features(priv); + if (err) { printk(KERN_ERR DRV_NAME ": %s: Failed to determine HW features.\n", priv->net_dev->name); - rc = 1; goto exit; } @@ -1792,11 +1792,11 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) priv->ieee->freq_band = LIBIPW_24GHZ_BAND; lock = LOCK_NONE; - if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) { + err = ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len); + if (err) { printk(KERN_ERR DRV_NAME ": %s: Failed to clear ordinal lock.\n", priv->net_dev->name); - rc = 1; goto exit; } @@ -1820,21 +1820,21 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) /* Send all of the commands that must be sent prior to * HOST_COMPLETE */ - if (ipw2100_adapter_setup(priv)) { + err = ipw2100_adapter_setup(priv); + if (err) { printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n", priv->net_dev->name); - rc = 1; goto exit; } if (!deferred) { /* Enable the adapter - sends HOST_COMPLETE */ - if (ipw2100_enable_adapter(priv)) { + err = ipw2100_enable_adapter(priv); + if (err) { printk(KERN_ERR DRV_NAME ": " "%s: failed in call to enable adapter.\n", priv->net_dev->name); ipw2100_hw_stop_adapter(priv); - rc = 1; goto exit; } @@ -1844,7 +1844,7 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) } exit: - return rc; + return err; } static void ipw2100_down(struct ipw2100_priv *priv) -- cgit v1.2.3 From af643fe9bbe0d4835ca271063db8bbb7232f2200 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 13 Jul 2017 23:48:29 +0100 Subject: zd1211rw: fix spelling mistake 'hybernate' -> 'hibernate' Trivial fix to spelling mistake in PDEBUG debug message. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c index a93f657a41c7..d4e512f50945 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c @@ -61,7 +61,7 @@ static void dump_regwrite(u32 rw) switch (reg) { case 0: - PDEBUG("reg0 CFG1 ref_sel %d hybernate %d rf_vco_reg_en %d" + PDEBUG("reg0 CFG1 ref_sel %d hibernate %d rf_vco_reg_en %d" " if_vco_reg_en %d if_vga_en %d", bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1), bit(rw, 0)); -- cgit v1.2.3 From e66d70b789d1db8e152d79dd070459ae9e566acc Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:46:42 +0530 Subject: brcmfmac: constify pci_device_id pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index f878706613e6..e6e9b00b79d7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1951,7 +1951,7 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ subvend, subdev, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } -static struct pci_device_id brcmf_pcie_devid_table[] = { +static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), -- cgit v1.2.3 From fcc870d76a2cb4451fb5685dfebe1f37a6a17b93 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 27 Jul 2017 23:09:54 +0100 Subject: wl3501_cs: fix spelling mistake: "Insupported" -> "Unsupported" Trivial fix to spelling mistake in printk message Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/wl3501_cs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index acec0d9ec422..da62220b9c01 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -965,7 +965,7 @@ static inline void wl3501_md_ind_interrupt(struct net_device *dev, &addr4, sizeof(addr4)); if (!(addr4[0] == 0xAA && addr4[1] == 0xAA && addr4[2] == 0x03 && addr4[4] == 0x00)) { - printk(KERN_INFO "Insupported packet type!\n"); + printk(KERN_INFO "Unsupported packet type!\n"); return; } pkt_len = sig.size + 12 - 24 - 4 - 6; -- cgit v1.2.3 From 11f35c9504669c9c3ff8108c3993a46197edddd3 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 2 Jul 2017 13:12:30 -0500 Subject: rtlwifi: Fill in_4way field by driver Because it isn't always correct to use EAPOL to check 4-way, we add a timer to handle exception. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Cc: Yan-Hsuan Chuang Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/base.c | 11 +++++++++++ drivers/net/wireless/realtek/rtlwifi/core.c | 2 ++ drivers/net/wireless/realtek/rtlwifi/wifi.h | 3 +++ 3 files changed, 16 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index 208f56297a75..1231ca5879d6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1408,6 +1408,11 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx, return true; } else if (ETH_P_PAE == ether_type) { + /* EAPOL is seens as in-4way */ + rtlpriv->btcoexist.btc_info.in_4way = true; + rtlpriv->btcoexist.btc_info.in_4way_ts = jiffies; + rtlpriv->btcoexist.btc_info.in_4way_ts = jiffies; + RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, "802.1X %s EAPOL pkt!!\n", (is_tx) ? "Tx" : "Rx"); @@ -1959,6 +1964,12 @@ label_lps_done: if (rtlpriv->cfg->ops->get_btc_status()) rtlpriv->btcoexist.btc_ops->btc_periodical(rtlpriv); + if (rtlpriv->btcoexist.btc_info.in_4way) { + if (time_after(jiffies, rtlpriv->btcoexist.btc_info.in_4way_ts + + msecs_to_jiffies(IN_4WAY_TIMEOUT_TIME))) + rtlpriv->btcoexist.btc_info.in_4way = false; + } + rtlpriv->link_info.bcn_rx_inperiod = 0; /* <6> scan list */ diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index b0ad061048c5..c53cbf3d52bd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -1505,6 +1505,8 @@ static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, u8 mac_addr[ETH_ALEN]; u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + rtlpriv->btcoexist.btc_info.in_4way = false; + if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "not open hw encryption\n"); diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index fb1ebb01133f..7ec0d502a0d9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -2477,6 +2477,8 @@ struct rtl_global_var { spinlock_t glb_list_lock; }; +#define IN_4WAY_TIMEOUT_TIME (30 * MSEC_PER_SEC) /* 30 seconds */ + struct rtl_btc_info { u8 bt_type; u8 btcoexist; @@ -2485,6 +2487,7 @@ struct rtl_btc_info { u8 ap_num; bool in_4way; + unsigned long in_4way_ts; }; struct bt_coexist_info { -- cgit v1.2.3 From 6aad6075ccd540910438fb0eaa0264c886f36304 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 2 Jul 2017 13:12:31 -0500 Subject: rtlwifi: Add BT_MP_INFO to c2h handler. We use H2C to ask BT's status, and C2H will return the status. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Cc: Yan-Hsuan Chuang Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtcoutsrc.c | 17 ++++++++++++- .../wireless/realtek/rtlwifi/btcoexist/rtl_btc.c | 28 ++++++++++++++++++++++ .../wireless/realtek/rtlwifi/btcoexist/rtl_btc.h | 1 + .../net/wireless/realtek/rtlwifi/rtl8192ee/fw.c | 9 +++++-- .../net/wireless/realtek/rtlwifi/rtl8723be/fw.c | 9 +++++-- .../net/wireless/realtek/rtlwifi/rtl8821ae/fw.c | 13 +++++++--- drivers/net/wireless/realtek/rtlwifi/wifi.h | 2 ++ 7 files changed, 71 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c index e6024b013ca5..c1eacd8352a2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c @@ -327,7 +327,22 @@ static void halbtc_aggregation_check(struct btc_coexist *btcoexist) static u32 halbtc_get_bt_patch_version(struct btc_coexist *btcoexist) { - return 0; + struct rtl_priv *rtlpriv = btcoexist->adapter; + u8 cmd_buffer[4] = {0}; + u8 oper_ver = 0; + u8 req_num = 0x0E; + + if (btcoexist->bt_info.bt_real_fw_ver) + goto label_done; + + cmd_buffer[0] |= (oper_ver & 0x0f); /* Set OperVer */ + cmd_buffer[0] |= ((req_num << 4) & 0xf0); /* Set ReqNum */ + cmd_buffer[1] = 0; /* BT_OP_GET_BT_VERSION = 0 */ + rtlpriv->cfg->ops->fill_h2c_cmd(rtlpriv->mac80211.hw, 0x67, 4, + &cmd_buffer[0]); + +label_done: + return btcoexist->bt_info.bt_real_fw_ver; } u32 halbtc_get_wifi_link_status(struct btc_coexist *btcoexist) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c index 4366c9817e1e..7d296a401b6f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c @@ -41,6 +41,7 @@ static struct rtl_btc_ops rtl_btc_operation = { .btc_periodical = rtl_btc_periodical, .btc_halt_notify = rtl_btc_halt_notify, .btc_btinfo_notify = rtl_btc_btinfo_notify, + .btc_btmpinfo_notify = rtl_btc_btmpinfo_notify, .btc_is_limited_dig = rtl_btc_is_limited_dig, .btc_is_disable_edca_turbo = rtl_btc_is_disable_edca_turbo, .btc_is_bt_disabled = rtl_btc_is_bt_disabled, @@ -165,6 +166,33 @@ void rtl_btc_btinfo_notify(struct rtl_priv *rtlpriv, u8 *tmp_buf, u8 length) exhalbtc_bt_info_notify(&gl_bt_coexist, tmp_buf, length); } +void rtl_btc_btmpinfo_notify(struct rtl_priv *rtlpriv, u8 *tmp_buf, u8 length) +{ + u8 extid, seq, len; + u16 bt_real_fw_ver; + u8 bt_fw_ver; + + if ((length < 4) || (!tmp_buf)) + return; + + extid = tmp_buf[0]; + /* not response from BT FW then exit*/ + if (extid != 1) /* C2H_TRIG_BY_BT_FW = 1 */ + return; + + len = tmp_buf[1] >> 4; + seq = tmp_buf[2] >> 4; + + /* BT Firmware version response */ + if (seq == 0x0E) { + bt_real_fw_ver = tmp_buf[3] | (tmp_buf[4] << 8); + bt_fw_ver = tmp_buf[5]; + + gl_bt_coexist.bt_info.bt_real_fw_ver = bt_real_fw_ver; + gl_bt_coexist.bt_info.bt_fw_ver = bt_fw_ver; + } +} + bool rtl_btc_is_limited_dig(struct rtl_priv *rtlpriv) { return gl_bt_coexist.bt_info.limited_dig; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.h index 6fe521cbe7f0..ac1253c46f44 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.h @@ -39,6 +39,7 @@ void rtl_btc_mediastatus_notify(struct rtl_priv *rtlpriv, void rtl_btc_periodical(struct rtl_priv *rtlpriv); void rtl_btc_halt_notify(void); void rtl_btc_btinfo_notify(struct rtl_priv *rtlpriv, u8 *tmpbuf, u8 length); +void rtl_btc_btmpinfo_notify(struct rtl_priv *rtlpriv, u8 *tmp_buf, u8 length); bool rtl_btc_is_limited_dig(struct rtl_priv *rtlpriv); bool rtl_btc_is_disable_edca_turbo(struct rtl_priv *rtlpriv); bool rtl_btc_is_bt_disabled(struct rtl_priv *rtlpriv); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.c index f5d4df985c37..7eae27f8e173 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.c @@ -887,6 +887,7 @@ void rtl92ee_c2h_content_parsing(struct ieee80211_hw *hw, u8 c2h_cmd_id, u8 c2h_cmd_len, u8 *tmp_buf) { struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_btc_ops *btc_ops = rtlpriv->btcoexist.btc_ops; switch (c2h_cmd_id) { case C2H_8192E_DBG: @@ -905,12 +906,16 @@ void rtl92ee_c2h_content_parsing(struct ieee80211_hw *hw, u8 c2h_cmd_id, case C2H_8192E_BT_INFO: RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "[C2H], C2H_8723BE_BT_INFO!!\n"); - rtlpriv->btcoexist.btc_ops->btc_btinfo_notify(rtlpriv, tmp_buf, - c2h_cmd_len); + if (rtlpriv->cfg->ops->get_btc_status()) + btc_ops->btc_btinfo_notify(rtlpriv, tmp_buf, + c2h_cmd_len); break; case C2H_8192E_BT_MP: RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "[C2H], C2H_8723BE_BT_MP!!\n"); + if (rtlpriv->cfg->ops->get_btc_status()) + btc_ops->btc_btmpinfo_notify(rtlpriv, tmp_buf, + c2h_cmd_len); break; case C2H_8192E_RA_RPT: _rtl92ee_c2h_ra_report_handler(hw, tmp_buf, c2h_cmd_len); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.c index dd6f95cfaec9..4b963fd27d64 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.c @@ -709,6 +709,7 @@ void rtl8723be_c2h_content_parsing(struct ieee80211_hw *hw, u8 c2h_cmd_len, u8 *tmp_buf) { struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_btc_ops *btc_ops = rtlpriv->btcoexist.btc_ops; switch (c2h_cmd_id) { case C2H_8723B_DBG: @@ -723,12 +724,16 @@ void rtl8723be_c2h_content_parsing(struct ieee80211_hw *hw, case C2H_8723B_BT_INFO: RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "[C2H], C2H_8723BE_BT_INFO!!\n"); - rtlpriv->btcoexist.btc_ops->btc_btinfo_notify(rtlpriv, tmp_buf, - c2h_cmd_len); + if (rtlpriv->cfg->ops->get_btc_status()) + btc_ops->btc_btinfo_notify(rtlpriv, tmp_buf, + c2h_cmd_len); break; case C2H_8723B_BT_MP: RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "[C2H], C2H_8723BE_BT_MP!!\n"); + if (rtlpriv->cfg->ops->get_btc_status()) + btc_ops->btc_btmpinfo_notify(rtlpriv, tmp_buf, + c2h_cmd_len); break; default: RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c index 03259aa150fd..b84b4fa7b71c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c @@ -1923,6 +1923,7 @@ void rtl8821ae_c2h_content_parsing(struct ieee80211_hw *hw, u8 *tmp_buf) { struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_btc_ops *btc_ops = rtlpriv->btcoexist.btc_ops; switch (c2h_cmd_id) { case C2H_8812_DBG: @@ -1938,9 +1939,15 @@ void rtl8821ae_c2h_content_parsing(struct ieee80211_hw *hw, RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "[C2H], C2H_8812_BT_INFO!!\n"); if (rtlpriv->cfg->ops->get_btc_status()) - rtlpriv->btcoexist.btc_ops->btc_btinfo_notify(rtlpriv, - tmp_buf, - c2h_cmd_len); + btc_ops->btc_btinfo_notify(rtlpriv, tmp_buf, + c2h_cmd_len); + break; + case C2H_8812_BT_MP: + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "[C2H], C2H_8812_BT_MP!!\n"); + if (rtlpriv->cfg->ops->get_btc_status()) + btc_ops->btc_btmpinfo_notify(rtlpriv, tmp_buf, + c2h_cmd_len); break; default: break; diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index 7ec0d502a0d9..77c3b186900e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -2562,6 +2562,8 @@ struct rtl_btc_ops { void (*btc_halt_notify) (void); void (*btc_btinfo_notify) (struct rtl_priv *rtlpriv, u8 *tmp_buf, u8 length); + void (*btc_btmpinfo_notify)(struct rtl_priv *rtlpriv, + u8 *tmp_buf, u8 length); bool (*btc_is_limited_dig) (struct rtl_priv *rtlpriv); bool (*btc_is_disable_edca_turbo) (struct rtl_priv *rtlpriv); bool (*btc_is_bt_disabled) (struct rtl_priv *rtlpriv); -- cgit v1.2.3 From 881d53ab1f433d50f9e8bbdbf199a005815f84b9 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 2 Jul 2017 13:12:32 -0500 Subject: rtlwifi: Add board type for 8723be and 8192ee With correct board_type, the phy praser can choose correct parameters. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Cc: Yan-Hsuan Chuang Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c | 5 +++++ drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c | 7 +++++++ 2 files changed, 12 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c index d84ac7adfd82..ef9394be7016 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c @@ -2133,7 +2133,12 @@ static void _rtl92ee_read_adapter_info(struct ieee80211_hw *hw) if ((*(u8 *)&hwinfo[EEPROM_RF_BOARD_OPTION_92E]) == 0xFF) rtlefuse->board_type = 0; + if (rtlpriv->btcoexist.btc_info.btcoexist == 1) + rtlefuse->board_type |= BIT(2); /* ODM_BOARD_BT */ + rtlhal->board_type = rtlefuse->board_type; + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "board_type = 0x%x\n", rtlefuse->board_type); /*parse xtal*/ rtlefuse->crystalcap = hwinfo[EEPROM_XTAL_92E]; if (hwinfo[EEPROM_XTAL_92E] == 0xFF) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c index 2a7ad5ffe997..0b9366e7acbd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c @@ -2114,6 +2114,13 @@ static void _rtl8723be_read_adapter_info(struct ieee80211_hw *hw, rtlefuse->autoload_failflag, hwinfo); + if (rtlpriv->btcoexist.btc_info.btcoexist == 1) + rtlefuse->board_type |= BIT(2); /* ODM_BOARD_BT */ + + rtlhal->board_type = rtlefuse->board_type; + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "board_type = 0x%x\n", rtlefuse->board_type); + rtlhal->package_type = _rtl8723be_read_package_type(hw); /* set channel plan from efuse */ -- cgit v1.2.3 From ca0e657bda7e6f6768050e9f0e2221f2a28a0a30 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 2 Jul 2017 13:12:33 -0500 Subject: rtlwifi: add amplifier type for 8812ae With correct amplifier_type, the phy praser can choose correct parameters. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Cc: Yan-Hsuan Chuang Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8821ae/hw.c | 41 +++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index 2bc6bace069c..8f4abb3d7669 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -2966,6 +2966,44 @@ static void _rtl8812ae_read_pa_type(struct ieee80211_hw *hw, u8 *hwinfo, } } +static void _rtl8812ae_read_amplifier_type(struct ieee80211_hw *hw, u8 *hwinfo, + bool autoload_fail) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + + u8 ext_type_pa_2g_a = (hwinfo[0xBD] & BIT(2)) >> 2; /* 0xBD[2] */ + u8 ext_type_pa_2g_b = (hwinfo[0xBD] & BIT(6)) >> 6; /* 0xBD[6] */ + u8 ext_type_pa_5g_a = (hwinfo[0xBF] & BIT(2)) >> 2; /* 0xBF[2] */ + u8 ext_type_pa_5g_b = (hwinfo[0xBF] & BIT(6)) >> 6; /* 0xBF[6] */ + /* 0xBD[1:0] */ + u8 ext_type_lna_2g_a = (hwinfo[0xBD] & (BIT(1) | BIT(0))) >> 0; + /* 0xBD[5:4] */ + u8 ext_type_lna_2g_b = (hwinfo[0xBD] & (BIT(5) | BIT(4))) >> 4; + /* 0xBF[1:0] */ + u8 ext_type_lna_5g_a = (hwinfo[0xBF] & (BIT(1) | BIT(0))) >> 0; + /* 0xBF[5:4] */ + u8 ext_type_lna_5g_b = (hwinfo[0xBF] & (BIT(5) | BIT(4))) >> 4; + + _rtl8812ae_read_pa_type(hw, hwinfo, autoload_fail); + + /* [2.4G] Path A and B are both extPA */ + if ((rtlhal->pa_type_2g & (BIT(5) | BIT(4))) == (BIT(5) | BIT(4))) + rtlhal->type_gpa = ext_type_pa_2g_b << 2 | ext_type_pa_2g_a; + + /* [5G] Path A and B are both extPA */ + if ((rtlhal->pa_type_5g & (BIT(1) | BIT(0))) == (BIT(1) | BIT(0))) + rtlhal->type_apa = ext_type_pa_5g_b << 2 | ext_type_pa_5g_a; + + /* [2.4G] Path A and B are both extLNA */ + if ((rtlhal->lna_type_2g & (BIT(7) | BIT(3))) == (BIT(7) | BIT(3))) + rtlhal->type_glna = ext_type_lna_2g_b << 2 | ext_type_lna_2g_a; + + /* [5G] Path A and B are both extLNA */ + if ((rtlhal->lna_type_5g & (BIT(7) | BIT(3))) == (BIT(7) | BIT(3))) + rtlhal->type_alna = ext_type_lna_5g_b << 2 | ext_type_lna_5g_a; +} + static void _rtl8821ae_read_pa_type(struct ieee80211_hw *hw, u8 *hwinfo, bool autoload_fail) { @@ -3114,7 +3152,8 @@ static void _rtl8821ae_read_adapter_info(struct ieee80211_hw *hw, bool b_pseudo_ hwinfo); if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - _rtl8812ae_read_pa_type(hw, hwinfo, rtlefuse->autoload_failflag); + _rtl8812ae_read_amplifier_type(hw, hwinfo, + rtlefuse->autoload_failflag); _rtl8812ae_read_bt_coexist_info_from_hwpg(hw, rtlefuse->autoload_failflag, hwinfo); } else { -- cgit v1.2.3 From 66970e38e544138e3656f4feda56a5d88618538f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 2 Jul 2017 13:12:34 -0500 Subject: rtlwifi: Update 8723be new phy parameters and its parser. There are new PHY table values for the RTL8723BE. The changes require new parsing code. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Cc: Yan-Hsuan Chuang Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8723be/phy.c | 365 +++++++++------------ .../net/wireless/realtek/rtlwifi/rtl8723be/table.c | 192 ++++++----- .../net/wireless/realtek/rtlwifi/rtl8723be/table.h | 10 +- 3 files changed, 281 insertions(+), 286 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/phy.c index 9752175cc466..9606641519e7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/phy.c @@ -152,33 +152,86 @@ bool rtl8723be_phy_rf_config(struct ieee80211_hw *hw) return rtl8723be_phy_rf6052_config(hw); } -static bool _rtl8723be_check_condition(struct ieee80211_hw *hw, - const u32 condition) +static bool _rtl8723be_check_positive(struct ieee80211_hw *hw, + const u32 condition1, + const u32 condition2) { - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); - u32 _board = rtlefuse->board_type; /*need efuse define*/ - u32 _interface = rtlhal->interface; - u32 _platform = 0x08;/*SupportPlatform */ - u32 cond = condition; + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u32 cut_ver = ((rtlhal->version & CHIP_VER_RTL_MASK) + >> CHIP_VER_RTL_SHIFT); + u32 intf = (rtlhal->interface == INTF_USB ? BIT(1) : BIT(0)); + + u8 board_type = ((rtlhal->board_type & BIT(4)) >> 4) << 0 | /* _GLNA */ + ((rtlhal->board_type & BIT(3)) >> 3) << 1 | /* _GPA */ + ((rtlhal->board_type & BIT(7)) >> 7) << 2 | /* _ALNA */ + ((rtlhal->board_type & BIT(6)) >> 6) << 3 | /* _APA */ + ((rtlhal->board_type & BIT(2)) >> 2) << 4; /* _BT */ + + u32 cond1 = condition1, cond2 = condition2; + u32 driver1 = cut_ver << 24 | /* CUT ver */ + 0 << 20 | /* interface 2/2 */ + 0x04 << 16 | /* platform */ + rtlhal->package_type << 12 | + intf << 8 | /* interface 1/2 */ + board_type; + + u32 driver2 = rtlhal->type_glna << 0 | + rtlhal->type_gpa << 8 | + rtlhal->type_alna << 16 | + rtlhal->type_apa << 24; - if (condition == 0xCDCDCDCD) - return true; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "===> [8812A] CheckPositive (cond1, cond2) = (0x%X 0x%X)\n", + cond1, cond2); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "===> [8812A] CheckPositive (driver1, driver2) = (0x%X 0x%X)\n", + driver1, driver2); - cond = condition & 0xFF; - if ((_board & cond) == 0 && cond != 0x1F) - return false; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + " (Platform, Interface) = (0x%X, 0x%X)\n", 0x04, intf); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + " (Board, Package) = (0x%X, 0x%X)\n", + rtlhal->board_type, rtlhal->package_type); - cond = condition & 0xFF00; - cond = cond >> 8; - if ((_interface & cond) == 0 && cond != 0x07) - return false; + /*============== Value Defined Check ===============*/ + /*QFN Type [15:12] and Cut Version [27:24] need to do value check*/ - cond = condition & 0xFF0000; - cond = cond >> 16; - if ((_platform & cond) == 0 && cond != 0x0F) + if (((cond1 & 0x0000F000) != 0) && ((cond1 & 0x0000F000) != + (driver1 & 0x0000F000))) return false; - return true; + if (((cond1 & 0x0F000000) != 0) && ((cond1 & 0x0F000000) != + (driver1 & 0x0F000000))) + return false; + + /*=============== Bit Defined Check ================*/ + /* We don't care [31:28] */ + + cond1 &= 0x00FF0FFF; + driver1 &= 0x00FF0FFF; + + if ((cond1 & driver1) == cond1) { + u32 mask = 0; + + if ((cond1 & 0x0F) == 0) /* BoardType is DONTCARE*/ + return true; + + if ((cond1 & BIT(0)) != 0) /*GLNA*/ + mask |= 0x000000FF; + if ((cond1 & BIT(1)) != 0) /*GPA*/ + mask |= 0x0000FF00; + if ((cond1 & BIT(2)) != 0) /*ALNA*/ + mask |= 0x00FF0000; + if ((cond1 & BIT(3)) != 0) /*APA*/ + mask |= 0xFF000000; + + /* BoardType of each RF path is matched*/ + if ((cond2 & mask) == (driver2 & mask)) + return true; + else + return false; + } + return false; } static void _rtl8723be_config_rf_reg(struct ieee80211_hw *hw, u32 addr, @@ -464,6 +517,16 @@ static bool _rtl8723be_phy_bb8723b_config_parafile(struct ieee80211_hw *hw) struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); bool rtstatus; + /* switch ant to BT */ + if (rtlpriv->rtlhal.interface == INTF_USB) { + rtl_write_dword(rtlpriv, 0x948, 0x0); + } else { + if (rtlpriv->btcoexist.btc_info.single_ant_path == 0) + rtl_write_dword(rtlpriv, 0x948, 0x280); + else + rtl_write_dword(rtlpriv, 0x948, 0x0); + } + rtstatus = _rtl8723be_phy_config_bb_with_headerfile(hw, BASEBAND_CONFIG_PHY_REG); if (!rtstatus) { @@ -493,142 +556,84 @@ static bool _rtl8723be_phy_bb8723b_config_parafile(struct ieee80211_hw *hw) return true; } +static bool rtl8723be_phy_config_with_headerfile(struct ieee80211_hw *hw, + u32 *array_table, + u16 arraylen, + void (*set_reg)(struct ieee80211_hw *hw, u32 regaddr, u32 data)) +{ + #define COND_ELSE 2 + #define COND_ENDIF 3 + + int i = 0; + u8 cond; + bool matched = true, skipped = false; + + while ((i + 1) < arraylen) { + u32 v1 = array_table[i]; + u32 v2 = array_table[i + 1]; + + if (v1 & (BIT(31) | BIT(30))) {/*positive & negative condition*/ + if (v1 & BIT(31)) {/* positive condition*/ + cond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); + if (cond == COND_ENDIF) { /*end*/ + matched = true; + skipped = false; + } else if (cond == COND_ELSE) { /*else*/ + matched = skipped ? false : true; + } else {/*if , else if*/ + if (skipped) { + matched = false; + } else { + if (_rtl8723be_check_positive( + hw, v1, v2)) { + matched = true; + skipped = true; + } else { + matched = false; + skipped = false; + } + } + } + } else if (v1 & BIT(30)) { /*negative condition*/ + /*do nothing*/ + } + } else { + if (matched) + set_reg(hw, v1, v2); + } + i = i + 2; + } + + return true; +} + static bool _rtl8723be_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 i; - u32 arraylength; - u32 *ptrarray; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Read rtl8723beMACPHY_Array\n"); - arraylength = RTL8723BEMAC_1T_ARRAYLEN; - ptrarray = RTL8723BEMAC_1T_ARRAY; - RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, - "Img:RTL8723bEMAC_1T_ARRAY LEN %d\n", arraylength); - for (i = 0; i < arraylength; i = i + 2) - rtl_write_byte(rtlpriv, ptrarray[i], (u8)ptrarray[i + 1]); - return true; + + return rtl8723be_phy_config_with_headerfile(hw, + RTL8723BEMAC_1T_ARRAY, RTL8723BEMAC_1T_ARRAYLEN, + rtl_write_byte_with_val32); } static bool _rtl8723be_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype) { - #define READ_NEXT_PAIR(v1, v2, i) \ - do { \ - i += 2; \ - v1 = array_table[i];\ - v2 = array_table[i+1]; \ - } while (0) - - int i; - u32 *array_table; - u16 arraylen; - struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 v1 = 0, v2 = 0; - - if (configtype == BASEBAND_CONFIG_PHY_REG) { - arraylen = RTL8723BEPHY_REG_1TARRAYLEN; - array_table = RTL8723BEPHY_REG_1TARRAY; - - for (i = 0; i < arraylen; i = i + 2) { - v1 = array_table[i]; - v2 = array_table[i+1]; - if (v1 < 0xcdcdcdcd) { - _rtl8723be_config_bb_reg(hw, v1, v2); - } else {/*This line is the start line of branch.*/ - /* to protect READ_NEXT_PAIR not overrun */ - if (i >= arraylen - 2) - break; - - if (!_rtl8723be_check_condition(hw, - array_table[i])) { - /*Discard the following - *(offset, data) pairs - */ - READ_NEXT_PAIR(v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < arraylen - 2) { - READ_NEXT_PAIR(v1, v2, i); - } - i -= 2; /* prevent from for-loop += 2*/ - /*Configure matched pairs and - *skip to end of if-else. - */ - } else { - READ_NEXT_PAIR(v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < arraylen - 2) { - _rtl8723be_config_bb_reg(hw, - v1, v2); - READ_NEXT_PAIR(v1, v2, i); - } - while (v2 != 0xDEAD && i < arraylen - 2) - READ_NEXT_PAIR(v1, v2, i); - } - } - } - } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { - arraylen = RTL8723BEAGCTAB_1TARRAYLEN; - array_table = RTL8723BEAGCTAB_1TARRAY; - - for (i = 0; i < arraylen; i = i + 2) { - v1 = array_table[i]; - v2 = array_table[i+1]; - if (v1 < 0xCDCDCDCD) { - rtl_set_bbreg(hw, array_table[i], - MASKDWORD, - array_table[i + 1]); - udelay(1); - continue; - } else {/*This line is the start line of branch.*/ - /* to protect READ_NEXT_PAIR not overrun */ - if (i >= arraylen - 2) - break; - - if (!_rtl8723be_check_condition(hw, - array_table[i])) { - /*Discard the following - *(offset, data) pairs - */ - READ_NEXT_PAIR(v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < arraylen - 2) { - READ_NEXT_PAIR(v1, v2, i); - } - i -= 2; /* prevent from for-loop += 2*/ - /*Configure matched pairs and - *skip to end of if-else. - */ - } else { - READ_NEXT_PAIR(v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < arraylen - 2) { - rtl_set_bbreg(hw, array_table[i], - MASKDWORD, - array_table[i + 1]); - udelay(1); - READ_NEXT_PAIR(v1, v2, i); - } + if (configtype == BASEBAND_CONFIG_PHY_REG) + return rtl8723be_phy_config_with_headerfile(hw, + RTL8723BEPHY_REG_1TARRAY, + RTL8723BEPHY_REG_1TARRAYLEN, + _rtl8723be_config_bb_reg); + else if (configtype == BASEBAND_CONFIG_AGC_TAB) + return rtl8723be_phy_config_with_headerfile(hw, + RTL8723BEAGCTAB_1TARRAY, + RTL8723BEAGCTAB_1TARRAYLEN, + rtl_set_bbreg_with_dwmask); - while (v2 != 0xDEAD && i < arraylen - 2) - READ_NEXT_PAIR(v1, v2, i); - } - } - RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, - "The agctab_array_table[0] is %x Rtl818EEPHY_REGArray[1] is %x\n", - array_table[i], array_table[i + 1]); - } - } - return true; + return false; } static u8 _rtl8723be_get_rate_section_index(u32 regaddr) @@ -761,73 +766,17 @@ static bool _rtl8723be_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, bool rtl8723be_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath) { - #define READ_NEXT_RF_PAIR(v1, v2, i) \ - do { \ - i += 2; \ - v1 = radioa_array_table[i]; \ - v2 = radioa_array_table[i+1]; \ - } while (0) - - int i; - bool rtstatus = true; - u32 *radioa_array_table; - u16 radioa_arraylen; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - u32 v1 = 0, v2 = 0; + bool ret = true; - radioa_arraylen = RTL8723BE_RADIOA_1TARRAYLEN; - radioa_array_table = RTL8723BE_RADIOA_1TARRAY; - RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, - "Radio_A:RTL8723BE_RADIOA_1TARRAY %d\n", radioa_arraylen); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Radio No %x\n", rfpath); - rtstatus = true; switch (rfpath) { case RF90_PATH_A: - for (i = 0; i < radioa_arraylen; i = i + 2) { - v1 = radioa_array_table[i]; - v2 = radioa_array_table[i+1]; - if (v1 < 0xcdcdcdcd) { - _rtl8723be_config_rf_radio_a(hw, v1, v2); - } else {/*This line is the start line of branch.*/ - /* to protect READ_NEXT_PAIR not overrun */ - if (i >= radioa_arraylen - 2) - break; - - if (!_rtl8723be_check_condition(hw, - radioa_array_table[i])) { - /*Discard the following - *(offset, data) pairs - */ - READ_NEXT_RF_PAIR(v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < radioa_arraylen - 2) { - READ_NEXT_RF_PAIR(v1, v2, i); - } - i -= 2; /* prevent from for-loop += 2*/ - } else { - /*Configure matched pairs - *and skip to end of if-else. - */ - READ_NEXT_RF_PAIR(v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < radioa_arraylen - 2) { - _rtl8723be_config_rf_radio_a(hw, - v1, v2); - READ_NEXT_RF_PAIR(v1, v2, i); - } - - while (v2 != 0xDEAD && - i < radioa_arraylen - 2) { - READ_NEXT_RF_PAIR(v1, v2, i); - } - } - } - } + ret = rtl8723be_phy_config_with_headerfile(hw, + RTL8723BE_RADIOA_1TARRAY, + RTL8723BE_RADIOA_1TARRAYLEN, + _rtl8723be_config_rf_radio_a); if (rtlhal->oem_id == RT_CID_819X_HP) _rtl8723be_config_rf_radio_a(hw, 0x52, 0x7E4BD); @@ -840,7 +789,7 @@ bool rtl8723be_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, "switch case %#x not processed\n", rfpath); break; } - return true; + return ret; } void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) @@ -1350,7 +1299,7 @@ void rtl8723be_phy_sw_chnl_callback(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &rtlpriv->phy; - u32 delay; + u32 delay = 0; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "switch to channel%d\n", rtlphy->current_channel); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.c index a180761e8810..381c16b9b3a9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.c @@ -26,6 +26,7 @@ *****************************************************************************/ #include "table.h" + u32 RTL8723BEPHY_REG_1TARRAY[] = { 0x800, 0x80040000, 0x804, 0x00000003, @@ -36,7 +37,7 @@ u32 RTL8723BEPHY_REG_1TARRAY[] = { 0x818, 0x02200385, 0x81C, 0x00000000, 0x820, 0x01000100, - 0x824, 0x00390204, + 0x824, 0x00190204, 0x828, 0x00000000, 0x82C, 0x00000000, 0x830, 0x00000000, @@ -73,9 +74,8 @@ u32 RTL8723BEPHY_REG_1TARRAY[] = { 0x90C, 0x81121111, 0x910, 0x00000002, 0x914, 0x00000201, - 0x948, 0x00000280, 0xA00, 0x00D047C8, - 0xA04, 0x80FF000C, + 0xA04, 0x80FF800C, 0xA08, 0x8C838300, 0xA0C, 0x2E7F120F, 0xA10, 0x9500BB78, @@ -114,7 +114,7 @@ u32 RTL8723BEPHY_REG_1TARRAY[] = { 0xC4C, 0x007F037F, 0xC50, 0x69553420, 0xC54, 0x43BC0094, - 0xC58, 0x00023169, + 0xC58, 0x00013147, 0xC5C, 0x00250492, 0xC60, 0x00000000, 0xC64, 0x7112848B, @@ -125,7 +125,7 @@ u32 RTL8723BEPHY_REG_1TARRAY[] = { 0xC78, 0x0000001F, 0xC7C, 0x00B91612, 0xC80, 0x390000E4, - 0xC84, 0x20F60000, + 0xC84, 0x21F60000, 0xC88, 0x40000100, 0xC8C, 0x20200000, 0xC90, 0x00020E1A, @@ -224,15 +224,21 @@ u32 RTL8723BEPHY_REG_1TARRAY[] = { }; +u32 RTL8723BEPHY_REG_1TARRAYLEN = + sizeof(RTL8723BEPHY_REG_1TARRAY) / sizeof(u32); + u32 RTL8723BEPHY_REG_ARRAY_PG[] = { - 0, 0, 0, 0x00000e08, 0x0000ff00, 0x00004000, - 0, 0, 0, 0x0000086c, 0xffffff00, 0x34363800, - 0, 0, 0, 0x00000e00, 0xffffffff, 0x42444646, - 0, 0, 0, 0x00000e04, 0xffffffff, 0x30343840, + 0, 0, 0, 0x00000e08, 0x0000ff00, 0x00003800, + 0, 0, 0, 0x0000086c, 0xffffff00, 0x32343600, + 0, 0, 0, 0x00000e00, 0xffffffff, 0x40424444, + 0, 0, 0, 0x00000e04, 0xffffffff, 0x28323638, 0, 0, 0, 0x00000e10, 0xffffffff, 0x38404244, 0, 0, 0, 0x00000e14, 0xffffffff, 0x26303436 }; +u32 RTL8723BEPHY_REG_ARRAY_PGLEN = + sizeof(RTL8723BEPHY_REG_ARRAY_PG) / sizeof(u32); + u32 RTL8723BE_RADIOA_1TARRAY[] = { 0x000, 0x00010000, 0x0B0, 0x000DFFE0, @@ -257,15 +263,37 @@ u32 RTL8723BE_RADIOA_1TARRAY[] = { 0x01E, 0x00000000, 0x0DF, 0x00000780, 0x050, 0x00067435, + 0x80002000, 0x00000000, 0x40000000, 0x00000000, + 0x051, 0x0006F10E, + 0x052, 0x000007D3, + 0x90003000, 0x00000000, 0x40000000, 0x00000000, + 0x051, 0x0006F10E, + 0x052, 0x000007D3, + 0x90004000, 0x00000000, 0x40000000, 0x00000000, + 0x051, 0x0006F10E, + 0x052, 0x000007D3, + 0xA0000000, 0x00000000, 0x051, 0x0006B04E, 0x052, 0x000007D2, + 0xB0000000, 0x00000000, 0x053, 0x00000000, 0x054, 0x00050400, 0x055, 0x0004026E, 0x0DD, 0x0000004C, 0x070, 0x00067435, + 0x80002000, 0x00000000, 0x40000000, 0x00000000, + 0x071, 0x0006F10E, + 0x072, 0x000007D3, + 0x90003000, 0x00000000, 0x40000000, 0x00000000, + 0x071, 0x0006F10E, + 0x072, 0x000007D3, + 0x90004000, 0x00000000, 0x40000000, 0x00000000, + 0x071, 0x0006F10E, + 0x072, 0x000007D3, + 0xA0000000, 0x00000000, 0x071, 0x0006B04E, 0x072, 0x000007D2, + 0xB0000000, 0x00000000, 0x073, 0x00000000, 0x074, 0x00050400, 0x075, 0x0004026E, @@ -308,6 +336,7 @@ u32 RTL8723BE_RADIOA_1TARRAY[] = { 0x044, 0x00000051, 0x0EF, 0x00000000, 0x0ED, 0x00000000, + 0x07F, 0x00020080, 0x0EF, 0x00002000, 0x03B, 0x000380EF, 0x03B, 0x000302FE, @@ -336,14 +365,24 @@ u32 RTL8723BE_RADIOA_1TARRAY[] = { 0x0A3, 0x00008000, 0x0A4, 0x00048D80, 0x0A5, 0x00068000, - 0x000, 0x00033D80, + 0x0ED, 0x00000002, + 0x0EF, 0x00000002, + 0x056, 0x00000032, + 0x076, 0x00000032, + 0x001, 0x00000780, }; +u32 RTL8723BE_RADIOA_1TARRAYLEN = + sizeof(RTL8723BE_RADIOA_1TARRAY) / sizeof(u32); + u32 RTL8723BEMAC_1T_ARRAY[] = { 0x02F, 0x00000030, 0x035, 0x00000000, + 0x039, 0x00000008, + 0x064, 0x00000000, 0x067, 0x00000020, + 0x421, 0x0000000F, 0x428, 0x0000000A, 0x429, 0x00000010, 0x430, 0x00000000, @@ -439,9 +478,13 @@ u32 RTL8723BEMAC_1T_ARRAY[] = { 0x709, 0x00000043, 0x70A, 0x00000065, 0x70B, 0x00000087, + 0x765, 0x00000018, + 0x76E, 0x00000004, }; +u32 RTL8723BEMAC_1T_ARRAYLEN = sizeof(RTL8723BEMAC_1T_ARRAY) / sizeof(u32); + u32 RTL8723BEAGCTAB_1TARRAY[] = { 0xC78, 0xFD000001, 0xC78, 0xFC010001, @@ -466,21 +509,21 @@ u32 RTL8723BEAGCTAB_1TARRAY[] = { 0xC78, 0xE9140001, 0xC78, 0xE8150001, 0xC78, 0xE7160001, - 0xC78, 0xAA170001, - 0xC78, 0xA9180001, - 0xC78, 0xA8190001, - 0xC78, 0xA71A0001, - 0xC78, 0xA61B0001, - 0xC78, 0xA51C0001, - 0xC78, 0xA41D0001, - 0xC78, 0xA31E0001, - 0xC78, 0x671F0001, - 0xC78, 0x66200001, - 0xC78, 0x65210001, - 0xC78, 0x64220001, - 0xC78, 0x63230001, - 0xC78, 0x62240001, - 0xC78, 0x61250001, + 0xC78, 0xE6170001, + 0xC78, 0xE5180001, + 0xC78, 0xE4190001, + 0xC78, 0xE31A0001, + 0xC78, 0xA51B0001, + 0xC78, 0xA41C0001, + 0xC78, 0xA31D0001, + 0xC78, 0x671E0001, + 0xC78, 0x661F0001, + 0xC78, 0x65200001, + 0xC78, 0x64210001, + 0xC78, 0x63220001, + 0xC78, 0x4A230001, + 0xC78, 0x49240001, + 0xC78, 0x48250001, 0xC78, 0x47260001, 0xC78, 0x46270001, 0xC78, 0x45280001, @@ -491,22 +534,22 @@ u32 RTL8723BEAGCTAB_1TARRAY[] = { 0xC78, 0x282D0001, 0xC78, 0x272E0001, 0xC78, 0x262F0001, - 0xC78, 0x25300001, - 0xC78, 0x24310001, - 0xC78, 0x09320001, - 0xC78, 0x08330001, - 0xC78, 0x07340001, - 0xC78, 0x06350001, - 0xC78, 0x05360001, - 0xC78, 0x04370001, - 0xC78, 0x03380001, - 0xC78, 0x02390001, + 0xC78, 0x0A300001, + 0xC78, 0x09310001, + 0xC78, 0x08320001, + 0xC78, 0x07330001, + 0xC78, 0x06340001, + 0xC78, 0x05350001, + 0xC78, 0x04360001, + 0xC78, 0x03370001, + 0xC78, 0x02380001, + 0xC78, 0x01390001, 0xC78, 0x013A0001, - 0xC78, 0x003B0001, - 0xC78, 0x003C0001, - 0xC78, 0x003D0001, - 0xC78, 0x003E0001, - 0xC78, 0x003F0001, + 0xC78, 0x013B0001, + 0xC78, 0x013C0001, + 0xC78, 0x013D0001, + 0xC78, 0x013E0001, + 0xC78, 0x013F0001, 0xC78, 0xFC400001, 0xC78, 0xFB410001, 0xC78, 0xFA420001, @@ -531,47 +574,50 @@ u32 RTL8723BEAGCTAB_1TARRAY[] = { 0xC78, 0xE7550001, 0xC78, 0xE6560001, 0xC78, 0xE5570001, - 0xC78, 0xAA580001, - 0xC78, 0xA9590001, - 0xC78, 0xA85A0001, - 0xC78, 0xA75B0001, - 0xC78, 0xA65C0001, - 0xC78, 0xA55D0001, - 0xC78, 0xA45E0001, - 0xC78, 0x675F0001, - 0xC78, 0x66600001, - 0xC78, 0x65610001, - 0xC78, 0x64620001, - 0xC78, 0x63630001, - 0xC78, 0x62640001, - 0xC78, 0x61650001, + 0xC78, 0xE4580001, + 0xC78, 0xE3590001, + 0xC78, 0xA65A0001, + 0xC78, 0xA55B0001, + 0xC78, 0xA45C0001, + 0xC78, 0xA35D0001, + 0xC78, 0x675E0001, + 0xC78, 0x665F0001, + 0xC78, 0x65600001, + 0xC78, 0x64610001, + 0xC78, 0x63620001, + 0xC78, 0x62630001, + 0xC78, 0x61640001, + 0xC78, 0x48650001, 0xC78, 0x47660001, 0xC78, 0x46670001, 0xC78, 0x45680001, 0xC78, 0x44690001, 0xC78, 0x436A0001, 0xC78, 0x426B0001, - 0xC78, 0x296C0001, - 0xC78, 0x286D0001, - 0xC78, 0x276E0001, - 0xC78, 0x266F0001, - 0xC78, 0x25700001, - 0xC78, 0x24710001, - 0xC78, 0x09720001, - 0xC78, 0x08730001, - 0xC78, 0x07740001, - 0xC78, 0x06750001, - 0xC78, 0x05760001, - 0xC78, 0x04770001, - 0xC78, 0x03780001, - 0xC78, 0x02790001, + 0xC78, 0x286C0001, + 0xC78, 0x276D0001, + 0xC78, 0x266E0001, + 0xC78, 0x256F0001, + 0xC78, 0x24700001, + 0xC78, 0x09710001, + 0xC78, 0x08720001, + 0xC78, 0x07730001, + 0xC78, 0x06740001, + 0xC78, 0x05750001, + 0xC78, 0x04760001, + 0xC78, 0x03770001, + 0xC78, 0x02780001, + 0xC78, 0x01790001, 0xC78, 0x017A0001, - 0xC78, 0x007B0001, - 0xC78, 0x007C0001, - 0xC78, 0x007D0001, - 0xC78, 0x007E0001, - 0xC78, 0x007F0001, + 0xC78, 0x017B0001, + 0xC78, 0x017C0001, + 0xC78, 0x017D0001, + 0xC78, 0x017E0001, + 0xC78, 0x017F0001, 0xC50, 0x69553422, 0xC50, 0x69553420, + 0x824, 0x00390204, }; + +u32 RTL8723BEAGCTAB_1TARRAYLEN = sizeof(RTL8723BEAGCTAB_1TARRAY) / sizeof(u32); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.h index dc17001632f7..1deaffe22251 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.h @@ -29,15 +29,15 @@ #define __RTL8723BE_TABLE__H_ #include -#define RTL8723BEPHY_REG_1TARRAYLEN 388 +extern u32 RTL8723BEPHY_REG_1TARRAYLEN; extern u32 RTL8723BEPHY_REG_1TARRAY[]; -#define RTL8723BEPHY_REG_ARRAY_PGLEN 36 +extern u32 RTL8723BEPHY_REG_ARRAY_PGLEN; extern u32 RTL8723BEPHY_REG_ARRAY_PG[]; -#define RTL8723BE_RADIOA_1TARRAYLEN 206 +extern u32 RTL8723BE_RADIOA_1TARRAYLEN; extern u32 RTL8723BE_RADIOA_1TARRAY[]; -#define RTL8723BEMAC_1T_ARRAYLEN 196 +extern u32 RTL8723BEMAC_1T_ARRAYLEN; extern u32 RTL8723BEMAC_1T_ARRAY[]; -#define RTL8723BEAGCTAB_1TARRAYLEN 260 +extern u32 RTL8723BEAGCTAB_1TARRAYLEN; extern u32 RTL8723BEAGCTAB_1TARRAY[]; #endif -- cgit v1.2.3 From 6ec9dfbc057cefe50e3ac55bdf6bfc24f24e4a1b Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 2 Jul 2017 13:12:35 -0500 Subject: rtlwifi: Rename rtl_desc92_rate to rtl_desc_rate This is a common enumeration, so we use a common name. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Cc: Yan-Hsuan Chuang Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/wifi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index 77c3b186900e..4c6d01eb065d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -592,7 +592,7 @@ enum rtl_hal_state { _HAL_STATE_START = 1, }; -enum rtl_desc92_rate { +enum rtl_desc_rate { DESC_RATE1M = 0x00, DESC_RATE2M = 0x01, DESC_RATE5_5M = 0x02, -- cgit v1.2.3 From e7c528615e34a924c94d135e2f8e17bb9bf3b611 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 2 Jul 2017 13:12:36 -0500 Subject: rtlwifi: Uses addr1 instead DA to determine broadcast and multicast addr. We should check addr1 to indicate a packet as broadcast or multicast in tx desc. An obvious example, a STA transmit an *unicast* ARP packet where addr1 and DA are the addresses of AP and broadcast respectively. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Cc: Yan-Hsuan Chuang Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index 1231ca5879d6..8b833e21b5cb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1166,9 +1166,9 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw, } } - if (is_multicast_ether_addr(ieee80211_get_DA(hdr))) + if (is_multicast_ether_addr(hdr->addr1)) tcb_desc->multicast = 1; - else if (is_broadcast_ether_addr(ieee80211_get_DA(hdr))) + else if (is_broadcast_ether_addr(hdr->addr1)) tcb_desc->broadcast = 1; _rtl_txrate_selectmode(hw, sta, tcb_desc); -- cgit v1.2.3 From 58438d9ae1724f6920adbb749f931a024bd00d5d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 2 Jul 2017 13:12:37 -0500 Subject: rtlwifi: move IS_HARDWARE_TYPE_xxx checker to wifi.h Use rtlpriv instead of rtlhal as argument, so driver and btcoex use the same definitions. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Cc: Yan-Hsuan Chuang Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbt_precomp.h | 16 --------- .../net/wireless/realtek/rtlwifi/rtl8192cu/mac.c | 3 +- drivers/net/wireless/realtek/rtlwifi/wifi.h | 42 ++++++++++------------ 3 files changed, 19 insertions(+), 42 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbt_precomp.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbt_precomp.h index 2ac989a4b2bb..02dff4c3f664 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbt_precomp.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbt_precomp.h @@ -43,22 +43,6 @@ #define RT_SDIO_INTERFACE 3 #define DEV_BUS_TYPE RT_PCI_INTERFACE -/* IC type */ -#define RTL_HW_TYPE(adapter) (rtl_hal((struct rtl_priv *)adapter)->hw_type) - -#define IS_NEW_GENERATION_IC(adapter) \ - (RTL_HW_TYPE(adapter) >= HARDWARE_TYPE_RTL8192EE) -#define IS_HARDWARE_TYPE_8812(adapter) \ - (RTL_HW_TYPE(adapter) == HARDWARE_TYPE_RTL8812AE) -#define IS_HARDWARE_TYPE_8821(adapter) \ - (RTL_HW_TYPE(adapter) == HARDWARE_TYPE_RTL8821AE) -#define IS_HARDWARE_TYPE_8723A(adapter) \ - (RTL_HW_TYPE(adapter) == HARDWARE_TYPE_RTL8723AE) -#define IS_HARDWARE_TYPE_8723B(adapter) \ - (RTL_HW_TYPE(adapter) == HARDWARE_TYPE_RTL8723BE) -#define IS_HARDWARE_TYPE_8192E(adapter) \ - (RTL_HW_TYPE(adapter) == HARDWARE_TYPE_RTL8192EE) - #include "halbtc8192e2ant.h" #include "halbtc8723b1ant.h" #include "halbtc8723b2ant.h" diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c index 1b124eade846..5657b1e34ad0 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c @@ -352,11 +352,10 @@ u32 rtl92c_get_txdma_status(struct ieee80211_hw *hw) void rtl92c_enable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); - if (IS_HARDWARE_TYPE_8192CE(rtlhal)) { + if (IS_HARDWARE_TYPE_8192CE(rtlpriv)) { rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF); rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index 4c6d01eb065d..f77acd329e0e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -314,35 +314,29 @@ enum hardware_type { HARDWARE_TYPE_RTL8192EE, HARDWARE_TYPE_RTL8821AE, HARDWARE_TYPE_RTL8812AE, + HARDWARE_TYPE_RTL8822BE, /* keep it last */ HARDWARE_TYPE_NUM }; -#define IS_HARDWARE_TYPE_8192SU(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SU) -#define IS_HARDWARE_TYPE_8192SE(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SE) -#define IS_HARDWARE_TYPE_8192CE(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CE) -#define IS_HARDWARE_TYPE_8192CU(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CU) -#define IS_HARDWARE_TYPE_8192DE(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8192DE) -#define IS_HARDWARE_TYPE_8192DU(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8192DU) -#define IS_HARDWARE_TYPE_8723E(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8723E) -#define IS_HARDWARE_TYPE_8723U(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8723U) -#define IS_HARDWARE_TYPE_8192S(rtlhal) \ -(IS_HARDWARE_TYPE_8192SE(rtlhal) || IS_HARDWARE_TYPE_8192SU(rtlhal)) -#define IS_HARDWARE_TYPE_8192C(rtlhal) \ -(IS_HARDWARE_TYPE_8192CE(rtlhal) || IS_HARDWARE_TYPE_8192CU(rtlhal)) -#define IS_HARDWARE_TYPE_8192D(rtlhal) \ -(IS_HARDWARE_TYPE_8192DE(rtlhal) || IS_HARDWARE_TYPE_8192DU(rtlhal)) -#define IS_HARDWARE_TYPE_8723(rtlhal) \ -(IS_HARDWARE_TYPE_8723E(rtlhal) || IS_HARDWARE_TYPE_8723U(rtlhal)) +#define RTL_HW_TYPE(rtlpriv) (rtl_hal((struct rtl_priv *)rtlpriv)->hw_type) +#define IS_NEW_GENERATION_IC(rtlpriv) \ + (RTL_HW_TYPE(rtlpriv) >= HARDWARE_TYPE_RTL8192EE) +#define IS_HARDWARE_TYPE_8192CE(rtlpriv) \ + (RTL_HW_TYPE(rtlpriv) == HARDWARE_TYPE_RTL8192CE) +#define IS_HARDWARE_TYPE_8812(rtlpriv) \ + (RTL_HW_TYPE(rtlpriv) == HARDWARE_TYPE_RTL8812AE) +#define IS_HARDWARE_TYPE_8821(rtlpriv) \ + (RTL_HW_TYPE(rtlpriv) == HARDWARE_TYPE_RTL8821AE) +#define IS_HARDWARE_TYPE_8723A(rtlpriv) \ + (RTL_HW_TYPE(rtlpriv) == HARDWARE_TYPE_RTL8723AE) +#define IS_HARDWARE_TYPE_8723B(rtlpriv) \ + (RTL_HW_TYPE(rtlpriv) == HARDWARE_TYPE_RTL8723BE) +#define IS_HARDWARE_TYPE_8192E(rtlpriv) \ + (RTL_HW_TYPE(rtlpriv) == HARDWARE_TYPE_RTL8192EE) +#define IS_HARDWARE_TYPE_8822B(rtlpriv) \ + (RTL_HW_TYPE(rtlpriv) == HARDWARE_TYPE_RTL8822BE) #define RX_HAL_IS_CCK_RATE(rxmcs) \ ((rxmcs) == DESC_RATE1M || \ -- cgit v1.2.3 From f81372ed8b1ea04d3cee5e8f7d3e0323a1e7b92e Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Wed, 28 Jun 2017 13:27:11 +0530 Subject: rtlwifi: remove dummy function call _rtl92cu_init_usb_aggregation() can be removed as it is dummy one Signed-off-by: Souptick Joarder Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c index f95a64507f17..530e80f0ef0b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c @@ -777,10 +777,6 @@ static void _rtl92cu_init_queue_priority(struct ieee80211_hw *hw, queue_sel); } -static void _rtl92cu_init_usb_aggregation(struct ieee80211_hw *hw) -{ -} - static void _rtl92cu_init_wmac_setting(struct ieee80211_hw *hw) { u16 value16; @@ -870,7 +866,6 @@ static int _rtl92cu_init_mac(struct ieee80211_hw *hw) rtl92c_init_edca(hw); rtl92c_init_rate_fallback(hw); rtl92c_init_retry_function(hw); - _rtl92cu_init_usb_aggregation(hw); rtlpriv->cfg->ops->set_bw_mode(hw, NL80211_CHAN_HT20); rtl92c_set_min_space(hw, IS_92C_SERIAL(rtlhal->version)); _rtl92cu_init_beacon_parameters(hw); -- cgit v1.2.3 From 4de95fc5fa1192b6c4d89545b6a788cc6de97429 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Wed, 28 Jun 2017 18:02:33 +0530 Subject: rtlwifi: Remove unused dummy function Removing unused dummy function Signed-off-by: Souptick Joarder Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c | 2 +- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c | 12 ------------ drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h | 3 --- 3 files changed, 1 insertion(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c index 96c923b3feb4..62d9214b6fdc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c @@ -173,7 +173,7 @@ static struct rtl_hal_usbint_cfg rtl92cu_interface_cfg = { .rx_urb_num = RTL92C_NUM_RX_URBS, .rx_max_size = RTL92C_SIZE_MAX_RX_BUFFER, .usb_rx_hdl = rtl8192cu_rx_hdl, - .usb_rx_segregate_hdl = NULL, /* rtl8192c_rx_segregate_hdl; */ + .usb_rx_segregate_hdl = NULL, /* tx */ .usb_tx_cleanup = rtl8192c_tx_cleanup, .usb_tx_post_hdl = rtl8192c_tx_post_hdl, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c index de6c3428f7c6..ac4a82de40c7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c @@ -436,13 +436,6 @@ void rtl8192cu_rx_hdl(struct ieee80211_hw *hw, struct sk_buff * skb) _rtl_rx_process(hw, skb); } -void rtl8192c_rx_segregate_hdl( - struct ieee80211_hw *hw, - struct sk_buff *skb, - struct sk_buff_head *skb_list) -{ -} - /*---------------------------------------------------------------------- * * Tx handler @@ -675,8 +668,3 @@ void rtl92cu_tx_fill_cmddesc(struct ieee80211_hw *hw, RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, "H2C Tx Cmd Content", pdesc, RTL_TX_DESC_SIZE); } - -bool rtl92cu_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb) -{ - return true; -} diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h index 487eec89bc29..15a66c547287 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h @@ -385,8 +385,6 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, struct ieee80211_rx_status *rx_status, u8 *p_desc, struct sk_buff *skb); void rtl8192cu_rx_hdl(struct ieee80211_hw *hw, struct sk_buff * skb); -void rtl8192c_rx_segregate_hdl(struct ieee80211_hw *, struct sk_buff *, - struct sk_buff_head *); void rtl8192c_tx_cleanup(struct ieee80211_hw *hw, struct sk_buff *skb); int rtl8192c_tx_post_hdl(struct ieee80211_hw *hw, struct urb *urb, struct sk_buff *skb); @@ -404,6 +402,5 @@ void rtl92cu_fill_fake_txdesc(struct ieee80211_hw *hw, u8 * pDesc, void rtl92cu_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool b_firstseg, bool b_lastseg, struct sk_buff *skb); -bool rtl92cu_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb); #endif -- cgit v1.2.3 From f2764f61fa10593204b0c5e4e9a68dba02112e50 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Wed, 5 Jul 2017 19:55:06 +0530 Subject: rtlwifi: Fix memory leak when firmware request fails This patch will fix memory leak when firmware request fails Signed-off-by: Souptick Joarder Acked-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c | 2 ++ drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c | 2 ++ drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c | 4 ++++ drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c | 2 ++ drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c | 2 ++ drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c | 2 ++ drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c | 2 ++ drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c | 2 ++ drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c | 6 ++++++ 9 files changed, 24 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c index 774e72058d24..bddd5a5ebe52 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c @@ -175,6 +175,8 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw) rtl_fw_cb); if (err) { pr_info("Failed to request firmware!\n"); + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; return 1; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c index bcbb0c60f1f1..38f85bfdf0c7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c @@ -176,6 +176,8 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw) rtl_fw_cb); if (err) { pr_err("Failed to request firmware!\n"); + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; return 1; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c index 62d9214b6fdc..dfbbd35bb966 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c @@ -85,6 +85,10 @@ static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw) err = request_firmware_nowait(THIS_MODULE, 1, fw_name, rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); + if (err) { + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; + } return err; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c index 16132c66e5e1..e38d6f7370aa 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c @@ -183,6 +183,8 @@ static int rtl92d_init_sw_vars(struct ieee80211_hw *hw) rtl_fw_cb); if (err) { pr_err("Failed to request firmware!\n"); + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; return 1; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c index eaa503b7c4b4..745e9c32655c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c @@ -177,6 +177,8 @@ int rtl92ee_init_sw_vars(struct ieee80211_hw *hw) rtl_fw_cb); if (err) { pr_err("Failed to request firmware!\n"); + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; return 1; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c index 2006b09ea74f..1ec20efb9ce1 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c @@ -216,6 +216,8 @@ static int rtl92s_init_sw_vars(struct ieee80211_hw *hw) rtl92se_fw_cb); if (err) { pr_err("Failed to request firmware!\n"); + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; return 1; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c index 7bf9f2557920..aab86667a7f3 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c @@ -184,6 +184,8 @@ int rtl8723e_init_sw_vars(struct ieee80211_hw *hw) rtl_fw_cb); if (err) { pr_err("Failed to request firmware!\n"); + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; return 1; } return 0; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c index f9d10f1e7cf8..56c05c4e1499 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c @@ -195,6 +195,8 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw) rtl_fw_cb); if (err) { pr_err("Failed to request firmware!\n"); + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; return 1; } } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c index d71d2776ca03..ec2d577ba85b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c @@ -196,6 +196,8 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->rtlhal.wowlan_firmware = vzalloc(0x8000); if (!rtlpriv->rtlhal.wowlan_firmware) { pr_err("Can't alloc buffer for wowlan fw.\n"); + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; return 1; } @@ -222,6 +224,8 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) rtl_fw_cb); if (err) { pr_err("Failed to request normal firmware!\n"); + vfree(rtlpriv->rtlhal.wowlan_firmware); + vfree(rtlpriv->rtlhal.pfirmware); return 1; } } @@ -233,6 +237,8 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) rtl_wowlan_fw_cb); if (err) { pr_err("Failed to request wowlan firmware!\n"); + vfree(rtlpriv->rtlhal.wowlan_firmware); + vfree(rtlpriv->rtlhal.pfirmware); return 1; } return 0; -- cgit v1.2.3 From d28ac7be15c70343cb270648e252f54d770eca6a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 13 Jul 2017 10:43:23 +0300 Subject: rtlwifi: rtl8821ae: Fix HW_VAR_NAV_UPPER operation The cast here is wrong. We want to cast the pointer but we accidentally do a no-op cast of the value. We normally want to set us_nav_upper to WIFI_NAV_UPPER_US (30000) but because of this bug we instead set it to 184 on little endian systems and 0 on big endian ones. Fixes: 3c05bedb5fef ("Staging: rtl8812ae: Add Realtek 8821 PCI WIFI driver") Signed-off-by: Dan Carpenter Acked-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index 8f4abb3d7669..4f73012978e9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -779,7 +779,7 @@ void rtl8821ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) _rtl8821ae_resume_tx_beacon(hw); break; } case HW_VAR_NAV_UPPER: { - u32 us_nav_upper = ((u32)*val); + u32 us_nav_upper = *(u32 *)val; if (us_nav_upper > HAL_92C_NAV_UPPER_UNIT * 0xFF) { RT_TRACE(rtlpriv, COMP_INIT , DBG_WARNING, -- cgit v1.2.3 From 68f70d837d7e07ba118eb0ea4a44ea276ce53f60 Mon Sep 17 00:00:00 2001 From: SZ Lin Date: Sat, 29 Jul 2017 18:42:34 +0800 Subject: net: moxa: Remove braces from single-line body Remove unnecessary braces from single-line if statement This warning is found using checkpatch.pl Signed-off-by: SZ Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index c0d7d5eec7e7..105215862949 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -269,9 +269,8 @@ rx_next: priv->rx_head = rx_head; } - if (rx < budget) { + if (rx < budget) napi_complete_done(napi, rx); - } priv->reg_imr |= RPKT_FINISH_M; writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK); -- cgit v1.2.3 From d458f4c5fd55ae921ac6f3bf2c169da8d2379b07 Mon Sep 17 00:00:00 2001 From: SZ Lin Date: Sat, 29 Jul 2017 18:42:35 +0800 Subject: net: moxa: Prefer 'unsigned int' to bare use of 'unsigned' Use 'unsigned int' instead of 'unsigned' This warning is found using checkpatch.pl Signed-off-by: SZ Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 105215862949..9997e72103d5 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -288,8 +288,8 @@ static int moxart_tx_queue_space(struct net_device *ndev) static void moxart_tx_finished(struct net_device *ndev) { struct moxart_mac_priv_t *priv = netdev_priv(ndev); - unsigned tx_head = priv->tx_head; - unsigned tx_tail = priv->tx_tail; + unsigned int tx_head = priv->tx_head; + unsigned int tx_tail = priv->tx_tail; while (tx_tail != tx_head) { dma_unmap_single(&ndev->dev, priv->tx_mapping[tx_tail], -- cgit v1.2.3 From e8048b84bbfd3e7010beb41c251ff5dc27dab955 Mon Sep 17 00:00:00 2001 From: SZ Lin Date: Sat, 29 Jul 2017 18:42:36 +0800 Subject: net: moxa: Fix comparison to NULL could be written with ! Fixed coding style for null comparisons in moxart_ether driver to be more consistent with the rest of the kernel coding style Signed-off-by: SZ Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 9997e72103d5..1d6384873393 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -494,7 +494,7 @@ static int moxart_mac_probe(struct platform_device *pdev) priv->tx_desc_base = dma_alloc_coherent(NULL, TX_REG_DESC_SIZE * TX_DESC_NUM, &priv->tx_base, GFP_DMA | GFP_KERNEL); - if (priv->tx_desc_base == NULL) { + if (!priv->tx_desc_base) { ret = -ENOMEM; goto init_fail; } @@ -502,7 +502,7 @@ static int moxart_mac_probe(struct platform_device *pdev) priv->rx_desc_base = dma_alloc_coherent(NULL, RX_REG_DESC_SIZE * RX_DESC_NUM, &priv->rx_base, GFP_DMA | GFP_KERNEL); - if (priv->rx_desc_base == NULL) { + if (!priv->rx_desc_base) { ret = -ENOMEM; goto init_fail; } -- cgit v1.2.3 From c45c5d03937dfa5da38b3d2e2172199641787190 Mon Sep 17 00:00:00 2001 From: SZ Lin Date: Sat, 29 Jul 2017 18:42:37 +0800 Subject: net: moxa: Remove extra space after a cast No space is necessary after a cast This warning is found using checkpatch.pl Signed-off-by: SZ Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 1d6384873393..31e179a651ae 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -311,7 +311,7 @@ static void moxart_tx_finished(struct net_device *ndev) static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id) { - struct net_device *ndev = (struct net_device *) dev_id; + struct net_device *ndev = (struct net_device *)dev_id; struct moxart_mac_priv_t *priv = netdev_priv(ndev); unsigned int ists = readl(priv->base + REG_INTERRUPT_STATUS); -- cgit v1.2.3 From 2fcc440267d52a4d2745d486b4d5001669fe6ac8 Mon Sep 17 00:00:00 2001 From: SZ Lin Date: Sat, 29 Jul 2017 18:42:38 +0800 Subject: net: moxa: Fix for typo in comment to function moxart_mac_setup_desc_ring() Signed-off-by: SZ Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 31e179a651ae..2e4effa9fe45 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -161,7 +161,7 @@ static void moxart_mac_setup_desc_ring(struct net_device *ndev) priv->rx_head = 0; - /* reset the MAC controller TX/RX desciptor base address */ + /* reset the MAC controller TX/RX descriptor base address */ writel(priv->tx_base, priv->base + REG_TXR_BASE_ADDRESS); writel(priv->rx_base, priv->base + REG_RXR_BASE_ADDRESS); } -- cgit v1.2.3 From dd4de07f20c05727818c330d4aa9bfdec25e3815 Mon Sep 17 00:00:00 2001 From: SZ Lin Date: Sat, 29 Jul 2017 18:42:39 +0800 Subject: net: moxa: Add spaces preferred around that '{+,-}' This patch fixes all checkpatch occurences of "CHECK: spaces preferred around that '{+,-}' (ctx:VxV)" in moxart_ether code. Signed-off-by: SZ Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h index 686b8957d5cf..bee608b547d1 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.h +++ b/drivers/net/ethernet/moxa/moxart_ether.h @@ -55,17 +55,17 @@ #define RX_DESC2_ADDRESS_VIRT 4 #define TX_DESC_NUM 64 -#define TX_DESC_NUM_MASK (TX_DESC_NUM-1) +#define TX_DESC_NUM_MASK (TX_DESC_NUM - 1) #define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM_MASK)) #define TX_BUF_SIZE 1600 -#define TX_BUF_SIZE_MAX (TX_DESC1_BUF_SIZE_MASK+1) +#define TX_BUF_SIZE_MAX (TX_DESC1_BUF_SIZE_MASK + 1) #define TX_WAKE_THRESHOLD 16 #define RX_DESC_NUM 64 -#define RX_DESC_NUM_MASK (RX_DESC_NUM-1) +#define RX_DESC_NUM_MASK (RX_DESC_NUM - 1) #define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM_MASK)) #define RX_BUF_SIZE 1600 -#define RX_BUF_SIZE_MAX (RX_DESC1_BUF_SIZE_MASK+1) +#define RX_BUF_SIZE_MAX (RX_DESC1_BUF_SIZE_MASK + 1) #define REG_INTERRUPT_STATUS 0 #define REG_INTERRUPT_MASK 4 -- cgit v1.2.3 From a847135a56ff5d5994fcaabd36b2a8b581913433 Mon Sep 17 00:00:00 2001 From: Felix Manlunas Date: Thu, 27 Jul 2017 12:32:28 -0700 Subject: liquidio: bump up driver version to match newer NIC firmware Bump up driver version to match newer NIC firmware. Also update nic_rx_stats (a struct common to host driver and firmware) by adding a new field: fw_total_fwd_bytes. Signed-off-by: Felix Manlunas Signed-off-by: Raghu Vatsavayi Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/liquidio_common.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 53aaf417e722..3b9e3646b971 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -27,8 +27,8 @@ #define LIQUIDIO_PACKAGE "" #define LIQUIDIO_BASE_MAJOR_VERSION 1 -#define LIQUIDIO_BASE_MINOR_VERSION 5 -#define LIQUIDIO_BASE_MICRO_VERSION 1 +#define LIQUIDIO_BASE_MINOR_VERSION 6 +#define LIQUIDIO_BASE_MICRO_VERSION 0 #define LIQUIDIO_BASE_VERSION __stringify(LIQUIDIO_BASE_MAJOR_VERSION) "." \ __stringify(LIQUIDIO_BASE_MINOR_VERSION) #define LIQUIDIO_MICRO_VERSION "." __stringify(LIQUIDIO_BASE_MICRO_VERSION) @@ -768,6 +768,7 @@ struct nic_rx_stats { /* firmware stats */ u64 fw_total_rcvd; u64 fw_total_fwd; + u64 fw_total_fwd_bytes; u64 fw_err_pko; u64 fw_err_link; u64 fw_err_drop; -- cgit v1.2.3 From d6aac1f218873f2266de23280bffb909b4a98fbf Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Jul 2017 08:59:41 -0700 Subject: netvsc: fix return value for set_channels The error and normal case got swapped. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 262486ce8e2a..f1eaf675d2e9 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -758,8 +758,8 @@ static int netvsc_set_channels(struct net_device *net, if (!IS_ERR(nvdev)) { netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); - ret = PTR_ERR(nvdev); } else { + ret = PTR_ERR(nvdev); device_info.num_chn = orig; rndis_filter_device_add(dev, &device_info); } -- cgit v1.2.3 From 867047c4512aa65fb4cf66b253b51b830c7fa172 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Jul 2017 08:59:42 -0700 Subject: netvsc: fix warnings reported by lockdep This includes a bunch of fixups for issues reported by lockdep. * ethtool routines can assume RTNL * send is done with RCU lock (and BH disable) * avoid refetching internal device struct (netvsc) instead pass it as a parameter. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 3 +- drivers/net/hyperv/netvsc.c | 2 +- drivers/net/hyperv/netvsc_drv.c | 15 ++++--- drivers/net/hyperv/rndis_filter.c | 84 +++++++++++++++++++-------------------- 4 files changed, 54 insertions(+), 50 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 4e7ff348327e..fb62ea632914 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -217,7 +217,8 @@ int rndis_filter_receive(struct net_device *ndev, struct vmbus_channel *channel, void *data, u32 buflen); -int rndis_filter_set_device_mac(struct net_device *ndev, char *mac); +int rndis_filter_set_device_mac(struct netvsc_device *ndev, + const char *mac); void netvsc_switch_datapath(struct net_device *nv_dev, bool vf); diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 06f39a99da7c..94c00acac58a 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -832,7 +832,7 @@ int netvsc_send(struct net_device_context *ndev_ctx, struct sk_buff *skb) { struct netvsc_device *net_device - = rcu_dereference_rtnl(ndev_ctx->nvdev); + = rcu_dereference_bh(ndev_ctx->nvdev); struct hv_device *device = ndev_ctx->device_ctx; int ret = 0; struct netvsc_channel *nvchan; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f1eaf675d2e9..a04f2efbbc25 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -923,6 +923,8 @@ static void netvsc_get_stats64(struct net_device *net, static int netvsc_set_mac_addr(struct net_device *ndev, void *p) { + struct net_device_context *ndc = netdev_priv(ndev); + struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev); struct sockaddr *addr = p; char save_adr[ETH_ALEN]; unsigned char save_aatype; @@ -935,7 +937,10 @@ static int netvsc_set_mac_addr(struct net_device *ndev, void *p) if (err != 0) return err; - err = rndis_filter_set_device_mac(ndev, addr->sa_data); + if (!nvdev) + return -ENODEV; + + err = rndis_filter_set_device_mac(nvdev, addr->sa_data); if (err != 0) { /* roll back to saved MAC */ memcpy(ndev->dev_addr, save_adr, ETH_ALEN); @@ -981,7 +986,7 @@ static void netvsc_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *nvdev = rcu_dereference(ndc->nvdev); + struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev); const void *nds = &ndc->eth_stats; const struct netvsc_stats *qstats; unsigned int start; @@ -1019,7 +1024,7 @@ static void netvsc_get_ethtool_stats(struct net_device *dev, static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *nvdev = rcu_dereference(ndc->nvdev); + struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev); u8 *p = data; int i; @@ -1077,7 +1082,7 @@ netvsc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rules) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *nvdev = rcu_dereference(ndc->nvdev); + struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev); if (!nvdev) return -ENODEV; @@ -1127,7 +1132,7 @@ static int netvsc_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, u8 *hfunc) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *ndev = rcu_dereference(ndc->nvdev); + struct netvsc_device *ndev = rtnl_dereference(ndc->nvdev); struct rndis_device *rndis_dev; int i; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index eaa3f0d5682a..bf21ea92c743 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -85,14 +85,6 @@ static struct rndis_device *get_rndis_device(void) return device; } -static struct netvsc_device * -net_device_to_netvsc_device(struct net_device *ndev) -{ - struct net_device_context *net_device_ctx = netdev_priv(ndev); - - return rtnl_dereference(net_device_ctx->nvdev); -} - static struct rndis_request *get_rndis_request(struct rndis_device *dev, u32 msg_type, u32 msg_len) @@ -252,7 +244,10 @@ static int rndis_filter_send_request(struct rndis_device *dev, pb[0].len; } + rcu_read_lock_bh(); ret = netvsc_send(net_device_ctx, packet, NULL, &pb, NULL); + rcu_read_unlock_bh(); + return ret; } @@ -452,8 +447,9 @@ int rndis_filter_receive(struct net_device *ndev, return 0; } -static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, - void *result, u32 *result_size) +static int rndis_filter_query_device(struct rndis_device *dev, + struct netvsc_device *nvdev, + u32 oid, void *result, u32 *result_size) { struct rndis_request *request; u32 inresult_size = *result_size; @@ -480,8 +476,6 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, query->dev_vc_handle = 0; if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) { - struct net_device_context *ndevctx = netdev_priv(dev->ndev); - struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev); struct ndis_offload *hwcaps; u32 nvsp_version = nvdev->nvsp_version; u8 ndis_rev; @@ -550,14 +544,15 @@ cleanup: /* Get the hardware offload capabilities */ static int -rndis_query_hwcaps(struct rndis_device *dev, struct ndis_offload *caps) +rndis_query_hwcaps(struct rndis_device *dev, struct netvsc_device *net_device, + struct ndis_offload *caps) { u32 caps_len = sizeof(*caps); int ret; memset(caps, 0, sizeof(*caps)); - ret = rndis_filter_query_device(dev, + ret = rndis_filter_query_device(dev, net_device, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES, caps, &caps_len); if (ret) @@ -586,11 +581,12 @@ rndis_query_hwcaps(struct rndis_device *dev, struct ndis_offload *caps) return 0; } -static int rndis_filter_query_device_mac(struct rndis_device *dev) +static int rndis_filter_query_device_mac(struct rndis_device *dev, + struct netvsc_device *net_device) { u32 size = ETH_ALEN; - return rndis_filter_query_device(dev, + return rndis_filter_query_device(dev, net_device, RNDIS_OID_802_3_PERMANENT_ADDRESS, dev->hw_mac_adr, &size); } @@ -598,9 +594,9 @@ static int rndis_filter_query_device_mac(struct rndis_device *dev) #define NWADR_STR "NetworkAddress" #define NWADR_STRLEN 14 -int rndis_filter_set_device_mac(struct net_device *ndev, char *mac) +int rndis_filter_set_device_mac(struct netvsc_device *nvdev, + const char *mac) { - struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev); struct rndis_device *rdev = nvdev->extension; struct rndis_request *request; struct rndis_set_request *set; @@ -654,11 +650,8 @@ int rndis_filter_set_device_mac(struct net_device *ndev, char *mac) wait_for_completion(&request->wait_event); set_complete = &request->response_msg.msg.set_complete; - if (set_complete->status != RNDIS_STATUS_SUCCESS) { - netdev_err(ndev, "Fail to set MAC on host side:0x%x\n", - set_complete->status); - ret = -EINVAL; - } + if (set_complete->status != RNDIS_STATUS_SUCCESS) + ret = -EIO; cleanup: put_rndis_request(rdev, request); @@ -791,27 +784,27 @@ cleanup: return ret; } -static int rndis_filter_query_device_link_status(struct rndis_device *dev) +static int rndis_filter_query_device_link_status(struct rndis_device *dev, + struct netvsc_device *net_device) { u32 size = sizeof(u32); u32 link_status; - int ret; - ret = rndis_filter_query_device(dev, - RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, - &link_status, &size); - - return ret; + return rndis_filter_query_device(dev, net_device, + RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, + &link_status, &size); } -static int rndis_filter_query_link_speed(struct rndis_device *dev) +static int rndis_filter_query_link_speed(struct rndis_device *dev, + struct netvsc_device *net_device) { u32 size = sizeof(u32); u32 link_speed; struct net_device_context *ndc; int ret; - ret = rndis_filter_query_device(dev, RNDIS_OID_GEN_LINK_SPEED, + ret = rndis_filter_query_device(dev, net_device, + RNDIS_OID_GEN_LINK_SPEED, &link_speed, &size); if (!ret) { @@ -880,14 +873,14 @@ void rndis_filter_update(struct netvsc_device *nvdev) schedule_work(&rdev->mcast_work); } -static int rndis_filter_init_device(struct rndis_device *dev) +static int rndis_filter_init_device(struct rndis_device *dev, + struct netvsc_device *nvdev) { struct rndis_request *request; struct rndis_initialize_request *init; struct rndis_initialize_complete *init_complete; u32 status; int ret; - struct netvsc_device *nvdev = net_device_to_netvsc_device(dev->ndev); request = get_rndis_request(dev, RNDIS_MSG_INIT, RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); @@ -1024,12 +1017,17 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) { struct net_device *ndev = hv_get_drvdata(new_sc->primary_channel->device_obj); - struct netvsc_device *nvscdev = net_device_to_netvsc_device(ndev); + struct net_device_context *ndev_ctx = netdev_priv(ndev); + struct netvsc_device *nvscdev; u16 chn_index = new_sc->offermsg.offer.sub_channel_index; struct netvsc_channel *nvchan; int ret; - if (chn_index >= nvscdev->num_chn) + /* This is safe because this callback only happens when + * new device is being setup and waiting on the channel_init_wait. + */ + nvscdev = rcu_dereference_raw(ndev_ctx->nvdev); + if (!nvscdev || chn_index >= nvscdev->num_chn) return; nvchan = nvscdev->chan_table + chn_index; @@ -1104,27 +1102,27 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, rndis_device->ndev = net; /* Send the rndis initialization message */ - ret = rndis_filter_init_device(rndis_device); + ret = rndis_filter_init_device(rndis_device, net_device); if (ret != 0) goto err_dev_remv; /* Get the MTU from the host */ size = sizeof(u32); - ret = rndis_filter_query_device(rndis_device, + ret = rndis_filter_query_device(rndis_device, net_device, RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, &mtu, &size); if (ret == 0 && size == sizeof(u32) && mtu < net->mtu) net->mtu = mtu; /* Get the mac address */ - ret = rndis_filter_query_device_mac(rndis_device); + ret = rndis_filter_query_device_mac(rndis_device, net_device); if (ret != 0) goto err_dev_remv; memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); /* Find HW offload capabilities */ - ret = rndis_query_hwcaps(rndis_device, &hwcaps); + ret = rndis_query_hwcaps(rndis_device, net_device, &hwcaps); if (ret != 0) goto err_dev_remv; @@ -1185,7 +1183,7 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, if (ret) goto err_dev_remv; - rndis_filter_query_device_link_status(rndis_device); + rndis_filter_query_device_link_status(rndis_device, net_device); netdev_dbg(net, "Device MAC %pM link state %s\n", rndis_device->hw_mac_adr, @@ -1194,11 +1192,11 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5) return net_device; - rndis_filter_query_link_speed(rndis_device); + rndis_filter_query_link_speed(rndis_device, net_device); /* vRSS setup */ memset(&rsscap, 0, rsscap_size); - ret = rndis_filter_query_device(rndis_device, + ret = rndis_filter_query_device(rndis_device, net_device, OID_GEN_RECEIVE_SCALE_CAPABILITIES, &rsscap, &rsscap_size); if (ret || rsscap.num_recv_que < 2) -- cgit v1.2.3 From 4a2176c63b0f95fdb66d363c7e3dacc145a4f038 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Jul 2017 08:59:43 -0700 Subject: netvsc: don't print pointer value in error message Using %p to print pointer to packet meta-data doesn't give any good info, and exposes kernel memory offsets. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 94c00acac58a..f0c15e782ce0 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -805,8 +805,10 @@ static inline int netvsc_send_pkt( ret = -ENOSPC; } } else { - netdev_err(ndev, "Unable to send packet %p ret %d\n", - packet, ret); + netdev_err(ndev, + "Unable to send packet pages %u len %u, ret %d\n", + packet->page_buf_cnt, packet->total_data_buflen, + ret); } return ret; -- cgit v1.2.3 From 02b6de01af1d116c107d61fa3e8583be3b97ac3c Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Jul 2017 08:59:44 -0700 Subject: netvsc: remove unnecessary indirection of page_buffer The internal API was passing struct hv_page_buffer ** when only simple struct hv_page_buffer * was necessary for passing an array. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 2 +- drivers/net/hyperv/netvsc.c | 21 ++++++++++----------- drivers/net/hyperv/netvsc_drv.c | 10 ++++------ drivers/net/hyperv/rndis_filter.c | 4 ++-- 4 files changed, 17 insertions(+), 20 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index fb62ea632914..9ca3ed692d73 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -190,7 +190,7 @@ void netvsc_device_remove(struct hv_device *device); int netvsc_send(struct net_device_context *ndc, struct hv_netvsc_packet *packet, struct rndis_message *rndis_msg, - struct hv_page_buffer **page_buffer, + struct hv_page_buffer *page_buffer, struct sk_buff *skb); void netvsc_linkstatus_callback(struct hv_device *device_obj, struct rndis_message *resp); diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index f0c15e782ce0..d3c0b19f6d34 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -697,7 +697,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, u32 pend_size, struct hv_netvsc_packet *packet, struct rndis_message *rndis_msg, - struct hv_page_buffer **pb, + struct hv_page_buffer *pb, struct sk_buff *skb) { char *start = net_device->send_buf; @@ -718,9 +718,9 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, } for (i = 0; i < page_count; i++) { - char *src = phys_to_virt((*pb)[i].pfn << PAGE_SHIFT); - u32 offset = (*pb)[i].offset; - u32 len = (*pb)[i].len; + char *src = phys_to_virt(pb[i].pfn << PAGE_SHIFT); + u32 offset = pb[i].offset; + u32 len = pb[i].len; memcpy(dest, (src + offset), len); msg_size += len; @@ -739,7 +739,7 @@ static inline int netvsc_send_pkt( struct hv_device *device, struct hv_netvsc_packet *packet, struct netvsc_device *net_device, - struct hv_page_buffer **pb, + struct hv_page_buffer *pb, struct sk_buff *skb) { struct nvsp_message nvmsg; @@ -750,7 +750,6 @@ static inline int netvsc_send_pkt( struct netdev_queue *txq = netdev_get_tx_queue(ndev, packet->q_idx); u64 req_id; int ret; - struct hv_page_buffer *pgbuf; u32 ring_avail = hv_ringbuf_avail_percent(&out_channel->outbound); nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; @@ -776,11 +775,11 @@ static inline int netvsc_send_pkt( return -ENODEV; if (packet->page_buf_cnt) { - pgbuf = packet->cp_partial ? (*pb) + - packet->rmsg_pgcnt : (*pb); + if (packet->cp_partial) + pb += packet->rmsg_pgcnt; + ret = vmbus_sendpacket_pagebuffer_ctl(out_channel, - pgbuf, - packet->page_buf_cnt, + pb, packet->page_buf_cnt, &nvmsg, sizeof(struct nvsp_message), req_id, @@ -830,7 +829,7 @@ static inline void move_pkt_msd(struct hv_netvsc_packet **msd_send, int netvsc_send(struct net_device_context *ndev_ctx, struct hv_netvsc_packet *packet, struct rndis_message *rndis_msg, - struct hv_page_buffer **pb, + struct hv_page_buffer *pb, struct sk_buff *skb) { struct netvsc_device *net_device diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index a04f2efbbc25..8ff4cbf582cc 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -282,9 +282,8 @@ static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, struct hv_netvsc_packet *packet, - struct hv_page_buffer **page_buf) + struct hv_page_buffer *pb) { - struct hv_page_buffer *pb = *page_buf; u32 slots_used = 0; char *data = skb->data; int frags = skb_shinfo(skb)->nr_frags; @@ -359,8 +358,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) u32 rndis_msg_size; struct rndis_per_packet_info *ppi; u32 hash; - struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT]; - struct hv_page_buffer *pb = page_buf; + struct hv_page_buffer pb[MAX_PAGE_BUFFER_COUNT]; /* We can only transmit MAX_PAGE_BUFFER_COUNT number * of pages in a single packet. If skb is scattered around @@ -503,12 +501,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) rndis_msg->msg_len += rndis_msg_size; packet->total_data_buflen = rndis_msg->msg_len; packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, - skb, packet, &pb); + skb, packet, pb); /* timestamp packet in software */ skb_tx_timestamp(skb); - ret = netvsc_send(net_device_ctx, packet, rndis_msg, &pb, skb); + ret = netvsc_send(net_device_ctx, packet, rndis_msg, pb, skb); if (likely(ret == 0)) return NETDEV_TX_OK; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index bf21ea92c743..d80e9e3f433e 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -214,11 +214,11 @@ static void dump_rndis_message(struct hv_device *hv_dev, static int rndis_filter_send_request(struct rndis_device *dev, struct rndis_request *req) { - int ret; struct hv_netvsc_packet *packet; struct hv_page_buffer page_buf[2]; struct hv_page_buffer *pb = page_buf; struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); + int ret; /* Setup the packet to send it */ packet = &req->pkt; @@ -245,7 +245,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, } rcu_read_lock_bh(); - ret = netvsc_send(net_device_ctx, packet, NULL, &pb, NULL); + ret = netvsc_send(net_device_ctx, packet, NULL, pb, NULL); rcu_read_unlock_bh(); return ret; -- cgit v1.2.3 From 7426b1a51803ba2d368177363a134b98b0a8d1c0 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Jul 2017 08:59:45 -0700 Subject: netvsc: optimize receive completions Optimize how receive completion ring are managed. * Allocate only as many slots as needed for all buffers from host * Allocate before setting up sub channel for better error detection * Don't need to keep copy of initial receive section message * Precompute the watermark for when receive flushing is needed * Replace division with conditional test * Replace atomic per-device variable with per-channel check. * Handle corner case where receive completion send fails if ring buffer to host is full. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 14 +- drivers/net/hyperv/netvsc.c | 267 ++++++++++++++++---------------------- drivers/net/hyperv/rndis_filter.c | 20 +-- 3 files changed, 126 insertions(+), 175 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 9ca3ed692d73..f2cef5aaed1f 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -186,6 +186,7 @@ struct net_device_context; struct netvsc_device *netvsc_device_add(struct hv_device *device, const struct netvsc_device_info *info); +int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx); void netvsc_device_remove(struct hv_device *device); int netvsc_send(struct net_device_context *ndc, struct hv_netvsc_packet *packet, @@ -657,13 +658,10 @@ struct recv_comp_data { u32 status; }; -/* Netvsc Receive Slots Max */ -#define NETVSC_RECVSLOT_MAX (NETVSC_RECEIVE_BUFFER_SIZE / ETH_DATA_LEN + 1) - struct multi_recv_comp { - void *buf; /* queued receive completions */ - u32 first; /* first data entry */ - u32 next; /* next entry for writing */ + struct recv_comp_data *slots; + u32 first; /* first data entry */ + u32 next; /* next entry for writing */ }; struct netvsc_stats { @@ -750,7 +748,7 @@ struct netvsc_device { u32 recv_buf_size; u32 recv_buf_gpadl_handle; u32 recv_section_cnt; - struct nvsp_1_receive_buffer_section *recv_section; + u32 recv_completion_cnt; /* Send buffer allocated by us */ void *send_buf; @@ -778,8 +776,6 @@ struct netvsc_device { u32 max_pkt; /* max number of pkt in one send, e.g. 8 */ u32 pkt_align; /* alignment bytes, e.g. 8 */ - atomic_t num_outstanding_recvs; - atomic_t open_cnt; struct netvsc_channel chan_table[VRSS_CHANNEL_MAX]; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index d3c0b19f6d34..4c709b454d34 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -72,9 +72,6 @@ static struct netvsc_device *alloc_net_device(void) if (!net_device) return NULL; - net_device->chan_table[0].mrc.buf - = vzalloc(NETVSC_RECVSLOT_MAX * sizeof(struct recv_comp_data)); - init_waitqueue_head(&net_device->wait_drain); net_device->destroy = false; atomic_set(&net_device->open_cnt, 0); @@ -92,7 +89,7 @@ static void free_netvsc_device(struct rcu_head *head) int i; for (i = 0; i < VRSS_CHANNEL_MAX; i++) - vfree(nvdev->chan_table[i].mrc.buf); + vfree(nvdev->chan_table[i].mrc.slots); kfree(nvdev); } @@ -171,12 +168,6 @@ static void netvsc_destroy_buf(struct hv_device *device) net_device->recv_buf = NULL; } - if (net_device->recv_section) { - net_device->recv_section_cnt = 0; - kfree(net_device->recv_section); - net_device->recv_section = NULL; - } - /* Deal with the send buffer we may have setup. * If we got a send section size, it means we received a * NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE msg (ie sent @@ -239,11 +230,26 @@ static void netvsc_destroy_buf(struct hv_device *device) kfree(net_device->send_section_map); } +int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx) +{ + struct netvsc_channel *nvchan = &net_device->chan_table[q_idx]; + int node = cpu_to_node(nvchan->channel->target_cpu); + size_t size; + + size = net_device->recv_completion_cnt * sizeof(struct recv_comp_data); + nvchan->mrc.slots = vzalloc_node(size, node); + if (!nvchan->mrc.slots) + nvchan->mrc.slots = vzalloc(size); + + return nvchan->mrc.slots ? 0 : -ENOMEM; +} + static int netvsc_init_buf(struct hv_device *device, struct netvsc_device *net_device) { int ret = 0; struct nvsp_message *init_packet; + struct nvsp_1_message_send_receive_buffer_complete *resp; struct net_device *ndev; size_t map_words; int node; @@ -300,43 +306,41 @@ static int netvsc_init_buf(struct hv_device *device, wait_for_completion(&net_device->channel_init_wait); /* Check the response */ - if (init_packet->msg.v1_msg. - send_recv_buf_complete.status != NVSP_STAT_SUCCESS) { - netdev_err(ndev, "Unable to complete receive buffer " - "initialization with NetVsp - status %d\n", - init_packet->msg.v1_msg. - send_recv_buf_complete.status); + resp = &init_packet->msg.v1_msg.send_recv_buf_complete; + if (resp->status != NVSP_STAT_SUCCESS) { + netdev_err(ndev, + "Unable to complete receive buffer initialization with NetVsp - status %d\n", + resp->status); ret = -EINVAL; goto cleanup; } /* Parse the response */ + netdev_dbg(ndev, "Receive sections: %u sub_allocs: size %u count: %u\n", + resp->num_sections, resp->sections[0].sub_alloc_size, + resp->sections[0].num_sub_allocs); - net_device->recv_section_cnt = init_packet->msg. - v1_msg.send_recv_buf_complete.num_sections; - - net_device->recv_section = kmemdup( - init_packet->msg.v1_msg.send_recv_buf_complete.sections, - net_device->recv_section_cnt * - sizeof(struct nvsp_1_receive_buffer_section), - GFP_KERNEL); - if (net_device->recv_section == NULL) { - ret = -EINVAL; - goto cleanup; - } + net_device->recv_section_cnt = resp->num_sections; /* * For 1st release, there should only be 1 section that represents the * entire receive buffer */ if (net_device->recv_section_cnt != 1 || - net_device->recv_section->offset != 0) { + resp->sections[0].offset != 0) { ret = -EINVAL; goto cleanup; } - /* Now setup the send buffer. - */ + /* Setup receive completion ring */ + net_device->recv_completion_cnt + = round_up(resp->sections[0].num_sub_allocs + 1, + PAGE_SIZE / sizeof(u64)); + ret = netvsc_alloc_recv_comp_ring(net_device, 0); + if (ret) + goto cleanup; + + /* Now setup the send buffer. */ net_device->send_buf = vzalloc_node(net_device->send_buf_size, node); if (!net_device->send_buf) net_device->send_buf = vzalloc(net_device->send_buf_size); @@ -951,130 +955,94 @@ send_now: return ret; } -static int netvsc_send_recv_completion(struct vmbus_channel *channel, - u64 transaction_id, u32 status) +/* Send pending recv completions */ +static int send_recv_completions(struct netvsc_channel *nvchan) { - struct nvsp_message recvcompMessage; + struct netvsc_device *nvdev = nvchan->net_device; + struct multi_recv_comp *mrc = &nvchan->mrc; + struct recv_comp_msg { + struct nvsp_message_header hdr; + u32 status; + } __packed; + struct recv_comp_msg msg = { + .hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, + }; int ret; - recvcompMessage.hdr.msg_type = - NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE; - - recvcompMessage.msg.v1_msg.send_rndis_pkt_complete.status = status; - - /* Send the completion */ - ret = vmbus_sendpacket(channel, &recvcompMessage, - sizeof(struct nvsp_message_header) + sizeof(u32), - transaction_id, VM_PKT_COMP, 0); + while (mrc->first != mrc->next) { + const struct recv_comp_data *rcd + = mrc->slots + mrc->first; - return ret; -} - -static inline void count_recv_comp_slot(struct netvsc_device *nvdev, u16 q_idx, - u32 *filled, u32 *avail) -{ - struct multi_recv_comp *mrc = &nvdev->chan_table[q_idx].mrc; - u32 first = mrc->first; - u32 next = mrc->next; + msg.status = rcd->status; + ret = vmbus_sendpacket(nvchan->channel, &msg, sizeof(msg), + rcd->tid, VM_PKT_COMP, 0); + if (unlikely(ret)) + return ret; - *filled = (first > next) ? NETVSC_RECVSLOT_MAX - first + next : - next - first; - - *avail = NETVSC_RECVSLOT_MAX - *filled - 1; -} - -/* Read the first filled slot, no change to index */ -static inline struct recv_comp_data *read_recv_comp_slot(struct netvsc_device - *nvdev, u16 q_idx) -{ - struct multi_recv_comp *mrc = &nvdev->chan_table[q_idx].mrc; - u32 filled, avail; - - if (unlikely(!mrc->buf)) - return NULL; + if (++mrc->first == nvdev->recv_completion_cnt) + mrc->first = 0; + } - count_recv_comp_slot(nvdev, q_idx, &filled, &avail); - if (!filled) - return NULL; + /* receive completion ring has been emptied */ + if (unlikely(nvdev->destroy)) + wake_up(&nvdev->wait_drain); - return mrc->buf + mrc->first * sizeof(struct recv_comp_data); + return 0; } -/* Put the first filled slot back to available pool */ -static inline void put_recv_comp_slot(struct netvsc_device *nvdev, u16 q_idx) +/* Count how many receive completions are outstanding */ +static void recv_comp_slot_avail(const struct netvsc_device *nvdev, + const struct multi_recv_comp *mrc, + u32 *filled, u32 *avail) { - struct multi_recv_comp *mrc = &nvdev->chan_table[q_idx].mrc; - int num_recv; + u32 count = nvdev->recv_completion_cnt; - mrc->first = (mrc->first + 1) % NETVSC_RECVSLOT_MAX; - - num_recv = atomic_dec_return(&nvdev->num_outstanding_recvs); + if (mrc->next >= mrc->first) + *filled = mrc->next - mrc->first; + else + *filled = (count - mrc->first) + mrc->next; - if (nvdev->destroy && num_recv == 0) - wake_up(&nvdev->wait_drain); + *avail = count - *filled - 1; } -/* Check and send pending recv completions */ -static void netvsc_chk_recv_comp(struct netvsc_device *nvdev, - struct vmbus_channel *channel, u16 q_idx) +/* Add receive complete to ring to send to host. */ +static void enq_receive_complete(struct net_device *ndev, + struct netvsc_device *nvdev, u16 q_idx, + u64 tid, u32 status) { + struct netvsc_channel *nvchan = &nvdev->chan_table[q_idx]; + struct multi_recv_comp *mrc = &nvchan->mrc; struct recv_comp_data *rcd; - int ret; - - while (true) { - rcd = read_recv_comp_slot(nvdev, q_idx); - if (!rcd) - break; + u32 filled, avail; - ret = netvsc_send_recv_completion(channel, rcd->tid, - rcd->status); - if (ret) - break; + recv_comp_slot_avail(nvdev, mrc, &filled, &avail); - put_recv_comp_slot(nvdev, q_idx); + if (unlikely(filled > NAPI_POLL_WEIGHT)) { + send_recv_completions(nvchan); + recv_comp_slot_avail(nvdev, mrc, &filled, &avail); } -} - -#define NETVSC_RCD_WATERMARK 80 -/* Get next available slot */ -static inline struct recv_comp_data *get_recv_comp_slot( - struct netvsc_device *nvdev, struct vmbus_channel *channel, u16 q_idx) -{ - struct multi_recv_comp *mrc = &nvdev->chan_table[q_idx].mrc; - u32 filled, avail, next; - struct recv_comp_data *rcd; - - if (unlikely(!nvdev->recv_section)) - return NULL; - - if (unlikely(!mrc->buf)) - return NULL; - - if (atomic_read(&nvdev->num_outstanding_recvs) > - nvdev->recv_section->num_sub_allocs * NETVSC_RCD_WATERMARK / 100) - netvsc_chk_recv_comp(nvdev, channel, q_idx); - - count_recv_comp_slot(nvdev, q_idx, &filled, &avail); - if (!avail) - return NULL; - - next = mrc->next; - rcd = mrc->buf + next * sizeof(struct recv_comp_data); - mrc->next = (next + 1) % NETVSC_RECVSLOT_MAX; + if (unlikely(!avail)) { + netdev_err(ndev, "Recv_comp full buf q:%hd, tid:%llx\n", + q_idx, tid); + return; + } - atomic_inc(&nvdev->num_outstanding_recvs); + rcd = mrc->slots + mrc->next; + rcd->tid = tid; + rcd->status = status; - return rcd; + if (++mrc->next == nvdev->recv_completion_cnt) + mrc->next = 0; } static int netvsc_receive(struct net_device *ndev, - struct netvsc_device *net_device, - struct net_device_context *net_device_ctx, - struct hv_device *device, - struct vmbus_channel *channel, - const struct vmpacket_descriptor *desc, - struct nvsp_message *nvsp) + struct netvsc_device *net_device, + struct net_device_context *net_device_ctx, + struct hv_device *device, + struct vmbus_channel *channel, + const struct vmpacket_descriptor *desc, + struct nvsp_message *nvsp) { const struct vmtransfer_page_packet_header *vmxferpage_packet = container_of(desc, const struct vmtransfer_page_packet_header, d); @@ -1083,7 +1051,6 @@ static int netvsc_receive(struct net_device *ndev, u32 status = NVSP_STAT_SUCCESS; int i; int count = 0; - int ret; /* Make sure this is a valid nvsp packet */ if (unlikely(nvsp->hdr.msg_type != NVSP_MSG1_TYPE_SEND_RNDIS_PKT)) { @@ -1114,25 +1081,9 @@ static int netvsc_receive(struct net_device *ndev, channel, data, buflen); } - if (net_device->chan_table[q_idx].mrc.buf) { - struct recv_comp_data *rcd; + enq_receive_complete(ndev, net_device, q_idx, + vmxferpage_packet->d.trans_id, status); - rcd = get_recv_comp_slot(net_device, channel, q_idx); - if (rcd) { - rcd->tid = vmxferpage_packet->d.trans_id; - rcd->status = status; - } else { - netdev_err(ndev, "Recv_comp full buf q:%hd, tid:%llx\n", - q_idx, vmxferpage_packet->d.trans_id); - } - } else { - ret = netvsc_send_recv_completion(channel, - vmxferpage_packet->d.trans_id, - status); - if (ret) - netdev_err(ndev, "Recv_comp q:%hd, tid:%llx, err:%d\n", - q_idx, vmxferpage_packet->d.trans_id, ret); - } return count; } @@ -1231,7 +1182,6 @@ int netvsc_poll(struct napi_struct *napi, int budget) struct netvsc_device *net_device = nvchan->net_device; struct vmbus_channel *channel = nvchan->channel; struct hv_device *device = netvsc_channel_to_device(channel); - u16 q_idx = channel->offermsg.offer.sub_channel_index; struct net_device *ndev = hv_get_drvdata(device); int work_done = 0; @@ -1245,17 +1195,18 @@ int netvsc_poll(struct napi_struct *napi, int budget) nvchan->desc = hv_pkt_iter_next(channel, nvchan->desc); } - /* If receive ring was exhausted - * and not doing busy poll - * then re-enable host interrupts - * and reschedule if ring is not empty. + /* If send of pending receive completions suceeded + * and did not exhaust NAPI budget + * and not doing busy poll + * then reschedule if more data has arrived from host */ - if (work_done < budget && + if (send_recv_completions(nvchan) == 0 && + work_done < budget && napi_complete_done(napi, work_done) && - hv_end_read(&channel->inbound) != 0) + hv_end_read(&channel->inbound)) { + hv_begin_read(&channel->inbound); napi_reschedule(napi); - - netvsc_chk_recv_comp(net_device, channel, q_idx); + } /* Driver may overshoot since multiple packets per descriptor */ return min(work_done, budget); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index d80e9e3f433e..44165fe328a4 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -928,12 +928,12 @@ static bool netvsc_device_idle(const struct netvsc_device *nvdev) { int i; - if (atomic_read(&nvdev->num_outstanding_recvs) > 0) - return false; - for (i = 0; i < nvdev->num_chn; i++) { const struct netvsc_channel *nvchan = &nvdev->chan_table[i]; + if (nvchan->mrc.first != nvchan->mrc.next) + return false; + if (atomic_read(&nvchan->queue_sends) > 0) return false; } @@ -1031,11 +1031,6 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) return; nvchan = nvscdev->chan_table + chn_index; - nvchan->mrc.buf - = vzalloc(NETVSC_RECVSLOT_MAX * sizeof(struct recv_comp_data)); - - if (!nvchan->mrc.buf) - return; /* Because the device uses NAPI, all the interrupt batching and * control is done via Net softirq, not the channel handling @@ -1225,6 +1220,15 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, if (num_rss_qs == 0) return net_device; + for (i = 1; i < net_device->num_chn; i++) { + ret = netvsc_alloc_recv_comp_ring(net_device, i); + if (ret) { + while (--i != 0) + vfree(net_device->chan_table[i].mrc.slots); + goto out; + } + } + refcount_set(&net_device->sc_offered, num_rss_qs); vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); -- cgit v1.2.3 From 493933472d33d3e82d6323842edeb281199ee430 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Jul 2017 08:59:46 -0700 Subject: netvsc: fix error unwind on device setup failure If setting receive buffer fails, the error unwind would cause kernel panic because it was not correctly doing RCU and NAPI unwind. RCU'd pointer needs to be reset to NULL, and NAPI needs to be disabled not deleted. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 4c709b454d34..db95487807fd 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1307,7 +1307,8 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device, return net_device; close: - netif_napi_del(&net_device->chan_table[0].napi); + RCU_INIT_POINTER(net_device_ctx->nvdev, NULL); + napi_disable(&net_device->chan_table[0].napi); /* Now, we can close the channel safely */ vmbus_close(device->channel); -- cgit v1.2.3 From f4e403633bcd290a4db2568364657d07b42ff890 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Jul 2017 08:59:47 -0700 Subject: netvsc: signal host if receive ring is emptied Latency improvement related to NAPI conversion. If all packets are processed from receive ring then need to signal host. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index db95487807fd..c64934c64dca 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1195,10 +1195,15 @@ int netvsc_poll(struct napi_struct *napi, int budget) nvchan->desc = hv_pkt_iter_next(channel, nvchan->desc); } - /* If send of pending receive completions suceeded - * and did not exhaust NAPI budget + /* if ring is empty, signal host */ + if (!nvchan->desc) + hv_pkt_iter_close(channel); + + /* If send of pending receive completions suceeded + * and did not exhaust NAPI budget this time * and not doing busy poll - * then reschedule if more data has arrived from host + * then re-enable host interrupts + * and reschedule if ring is not empty. */ if (send_recv_completions(nvchan) == 0 && work_done < budget && -- cgit v1.2.3 From 158a5c0a24d1d83b3d7e7ba159eafbae1b9f9d60 Mon Sep 17 00:00:00 2001 From: Casey Leedom Date: Thu, 27 Jul 2017 16:47:27 -0700 Subject: cxgb4: core hardware/firmware support for Forward Error Correction on a link Signed-off-by: Casey Leedom Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 152 ++++++++++++++++++++++------- 1 file changed, 117 insertions(+), 35 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index db41b3e99b81..24087c886974 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -3840,11 +3840,64 @@ void t4_ulprx_read_la(struct adapter *adap, u32 *la_buf) FW_PORT_CAP_SPEED_40G | FW_PORT_CAP_SPEED_100G | \ FW_PORT_CAP_ANEG) +/* Translate Firmware Port Capabilities Pause specification to Common Code */ +static inline unsigned int fwcap_to_cc_pause(unsigned int fw_pause) +{ + unsigned int cc_pause = 0; + + if (fw_pause & FW_PORT_CAP_FC_RX) + cc_pause |= PAUSE_RX; + if (fw_pause & FW_PORT_CAP_FC_TX) + cc_pause |= PAUSE_TX; + + return cc_pause; +} + +/* Translate Common Code Pause specification into Firmware Port Capabilities */ +static inline unsigned int cc_to_fwcap_pause(unsigned int cc_pause) +{ + unsigned int fw_pause = 0; + + if (cc_pause & PAUSE_RX) + fw_pause |= FW_PORT_CAP_FC_RX; + if (cc_pause & PAUSE_TX) + fw_pause |= FW_PORT_CAP_FC_TX; + + return fw_pause; +} + +/* Translate Firmware Forward Error Correction specification to Common Code */ +static inline unsigned int fwcap_to_cc_fec(unsigned int fw_fec) +{ + unsigned int cc_fec = 0; + + if (fw_fec & FW_PORT_CAP_FEC_RS) + cc_fec |= FEC_RS; + if (fw_fec & FW_PORT_CAP_FEC_BASER_RS) + cc_fec |= FEC_BASER_RS; + + return cc_fec; +} + +/* Translate Common Code Forward Error Correction specification to Firmware */ +static inline unsigned int cc_to_fwcap_fec(unsigned int cc_fec) +{ + unsigned int fw_fec = 0; + + if (cc_fec & FEC_RS) + fw_fec |= FW_PORT_CAP_FEC_RS; + if (cc_fec & FEC_BASER_RS) + fw_fec |= FW_PORT_CAP_FEC_BASER_RS; + + return fw_fec; +} + /** * t4_link_l1cfg - apply link configuration to MAC/PHY - * @phy: the PHY to setup - * @mac: the MAC to setup - * @lc: the requested link configuration + * @adapter: the adapter + * @mbox: the Firmware Mailbox to use + * @port: the Port ID + * @lc: the Port's Link Configuration * * Set up a port's MAC and PHY according to a desired link configuration. * - If the PHY can auto-negotiate first decide what to advertise, then @@ -3857,22 +3910,46 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port, struct link_config *lc) { struct fw_port_cmd c; - unsigned int mdi = FW_PORT_CAP_MDI_V(FW_PORT_CAP_MDI_AUTO); - unsigned int fc = 0, fec = 0, fw_fec = 0; + unsigned int fw_mdi = FW_PORT_CAP_MDI_V(FW_PORT_CAP_MDI_AUTO); + unsigned int fw_fc, cc_fec, fw_fec; + unsigned int rcap; lc->link_ok = 0; - if (lc->requested_fc & PAUSE_RX) - fc |= FW_PORT_CAP_FC_RX; - if (lc->requested_fc & PAUSE_TX) - fc |= FW_PORT_CAP_FC_TX; - fec = lc->requested_fec & FEC_AUTO ? lc->auto_fec : lc->requested_fec; + /* Convert driver coding of Pause Frame Flow Control settings into the + * Firmware's API. + */ + fw_fc = cc_to_fwcap_pause(lc->requested_fc); + + /* Convert Common Code Forward Error Control settings into the + * Firmware's API. If the current Requested FEC has "Automatic" + * (IEEE 802.3) specified, then we use whatever the Firmware + * sent us as part of it's IEEE 802.3-based interpratation of + * the Transceiver Module EPROM FEC parameters. Otherwise we + * use whatever is in the current Requested FEC settings. + */ + if (lc->requested_fec & FEC_AUTO) + cc_fec = lc->auto_fec; + else + cc_fec = lc->requested_fec; + fw_fec = cc_to_fwcap_fec(cc_fec); - if (fec & FEC_RS) - fw_fec |= FW_PORT_CAP_FEC_RS; - if (fec & FEC_BASER_RS) - fw_fec |= FW_PORT_CAP_FEC_BASER_RS; + /* Figure out what our Requested Port Capabilities are going to be. + */ + if (!(lc->supported & FW_PORT_CAP_ANEG)) { + rcap = (lc->supported & ADVERT_MASK) | fw_fc | fw_fec; + lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); + lc->fec = cc_fec; + } else if (lc->autoneg == AUTONEG_DISABLE) { + rcap = lc->requested_speed | fw_fc | fw_fec | fw_mdi; + lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); + lc->fec = cc_fec; + } else { + rcap = lc->advertising | fw_fc | fw_fec | fw_mdi; + } + /* And send that on to the Firmware ... + */ memset(&c, 0, sizeof(c)); c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | FW_CMD_REQUEST_F | FW_CMD_EXEC_F | @@ -3880,19 +3957,7 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port, c.action_to_len16 = cpu_to_be32(FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_L1_CFG) | FW_LEN16(c)); - - if (!(lc->supported & FW_PORT_CAP_ANEG)) { - c.u.l1cfg.rcap = cpu_to_be32((lc->supported & ADVERT_MASK) | - fc | fw_fec); - lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); - } else if (lc->autoneg == AUTONEG_DISABLE) { - c.u.l1cfg.rcap = cpu_to_be32(lc->requested_speed | fc | - fw_fec | mdi); - lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); - } else - c.u.l1cfg.rcap = cpu_to_be32(lc->advertising | fc | - fw_fec | mdi); - + c.u.l1cfg.rcap = cpu_to_be32(rcap); return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); } @@ -7630,19 +7695,28 @@ static const char *t4_link_down_rc_str(unsigned char link_down_rc) void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) { const struct fw_port_cmd *p = (const void *)rpl; + unsigned int acaps = be16_to_cpu(p->u.info.acap); struct adapter *adap = pi->adapter; /* link/module state change message */ - int speed = 0, fc = 0; + int speed = 0, fc, fec; struct link_config *lc; u32 stat = be32_to_cpu(p->u.info.lstatus_to_modtype); int link_ok = (stat & FW_PORT_CMD_LSTATUS_F) != 0; u32 mod = FW_PORT_CMD_MODTYPE_G(stat); + /* Unfortunately the format of the Link Status returned by the + * Firmware isn't the same as the Firmware Port Capabilities bitfield + * used everywhere else ... + */ + fc = 0; if (stat & FW_PORT_CMD_RXPAUSE_F) fc |= PAUSE_RX; if (stat & FW_PORT_CMD_TXPAUSE_F) fc |= PAUSE_TX; + + fec = fwcap_to_cc_fec(acaps); + if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) speed = 100; else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) @@ -7659,11 +7733,20 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) lc = &pi->link_cfg; if (mod != pi->mod_type) { + /* When a new Transceiver Module is inserted, the Firmware + * will examine any Forward Error Correction parameters + * present in the Transceiver Module i2c EPROM and determine + * the supported and recommended FEC settings from those + * based on IEEE 802.3 standards. We always record the + * IEEE 802.3 recommended "automatic" settings. + */ + lc->auto_fec = fec; + pi->mod_type = mod; t4_os_portmod_changed(adap, pi->port_id); } if (link_ok != lc->link_ok || speed != lc->speed || - fc != lc->fc) { /* something changed */ + fc != lc->fc || fec != lc->fec) { /* something changed */ if (!link_ok && lc->link_ok) { unsigned char rc = FW_PORT_CMD_LINKDNRC_G(stat); @@ -7675,6 +7758,8 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) lc->link_ok = link_ok; lc->speed = speed; lc->fc = fc; + lc->fec = fec; + lc->supported = be16_to_cpu(p->u.info.pcap); lc->lp_advertising = be16_to_cpu(p->u.info.lpacap); @@ -7764,7 +7849,8 @@ static void get_pci_mode(struct adapter *adapter, struct pci_params *p) /** * init_link_config - initialize a link's SW state * @lc: structure holding the link state - * @caps: link capabilities + * @pcaps: link Port Capabilities + * @acaps: link current Advertised Port Capabilities * * Initializes the SW state maintained for each link, including the link's * capabilities and default speed/flow-control/autonegotiation settings. @@ -7777,15 +7863,11 @@ static void init_link_config(struct link_config *lc, unsigned int pcaps, lc->requested_speed = 0; lc->speed = 0; lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; - lc->auto_fec = 0; /* For Forward Error Control, we default to whatever the Firmware * tells us the Link is currently advertising. */ - if (acaps & FW_PORT_CAP_FEC_RS) - lc->auto_fec |= FEC_RS; - if (acaps & FW_PORT_CAP_FEC_BASER_RS) - lc->auto_fec |= FEC_BASER_RS; + lc->auto_fec = fwcap_to_cc_fec(acaps); lc->requested_fec = FEC_AUTO; lc->fec = lc->auto_fec; -- cgit v1.2.3 From 7fece840e35e52c7114dcf874a72b2c962923800 Mon Sep 17 00:00:00 2001 From: Casey Leedom Date: Thu, 27 Jul 2017 16:47:28 -0700 Subject: cxgb4: ethtool forward error correction management support Signed-off-by: Casey Leedom Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c | 100 +++++++++++++++++++++ 1 file changed, 100 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 26eb00a45db1..03f593e84c24 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -801,6 +801,104 @@ static int set_link_ksettings(struct net_device *dev, return ret; } +/* Translate the Firmware FEC value into the ethtool value. */ +static inline unsigned int fwcap_to_eth_fec(unsigned int fw_fec) +{ + unsigned int eth_fec = 0; + + if (fw_fec & FW_PORT_CAP_FEC_RS) + eth_fec |= ETHTOOL_FEC_RS; + if (fw_fec & FW_PORT_CAP_FEC_BASER_RS) + eth_fec |= ETHTOOL_FEC_BASER; + + /* if nothing is set, then FEC is off */ + if (!eth_fec) + eth_fec = ETHTOOL_FEC_OFF; + + return eth_fec; +} + +/* Translate Common Code FEC value into ethtool value. */ +static inline unsigned int cc_to_eth_fec(unsigned int cc_fec) +{ + unsigned int eth_fec = 0; + + if (cc_fec & FEC_AUTO) + eth_fec |= ETHTOOL_FEC_AUTO; + if (cc_fec & FEC_RS) + eth_fec |= ETHTOOL_FEC_RS; + if (cc_fec & FEC_BASER_RS) + eth_fec |= ETHTOOL_FEC_BASER; + + /* if nothing is set, then FEC is off */ + if (!eth_fec) + eth_fec = ETHTOOL_FEC_OFF; + + return eth_fec; +} + +/* Translate ethtool FEC value into Common Code value. */ +static inline unsigned int eth_to_cc_fec(unsigned int eth_fec) +{ + unsigned int cc_fec = 0; + + if (eth_fec & ETHTOOL_FEC_OFF) + return cc_fec; + + if (eth_fec & ETHTOOL_FEC_AUTO) + cc_fec |= FEC_AUTO; + if (eth_fec & ETHTOOL_FEC_RS) + cc_fec |= FEC_RS; + if (eth_fec & ETHTOOL_FEC_BASER) + cc_fec |= FEC_BASER_RS; + + return cc_fec; +} + +static int get_fecparam(struct net_device *dev, struct ethtool_fecparam *fec) +{ + const struct port_info *pi = netdev_priv(dev); + const struct link_config *lc = &pi->link_cfg; + + /* Translate the Firmware FEC Support into the ethtool value. We + * always support IEEE 802.3 "automatic" selection of Link FEC type if + * any FEC is supported. + */ + fec->fec = fwcap_to_eth_fec(lc->supported); + if (fec->fec != ETHTOOL_FEC_OFF) + fec->fec |= ETHTOOL_FEC_AUTO; + + /* Translate the current internal FEC parameters into the + * ethtool values. + */ + fec->active_fec = cc_to_eth_fec(lc->fec); + + return 0; +} + +static int set_fecparam(struct net_device *dev, struct ethtool_fecparam *fec) +{ + struct port_info *pi = netdev_priv(dev); + struct link_config *lc = &pi->link_cfg; + struct link_config old_lc; + int ret; + + /* Save old Link Configuration in case the L1 Configure below + * fails. + */ + old_lc = *lc; + + /* Try to perform the L1 Configure and return the result of that + * effort. If it fails, revert the attempted change. + */ + lc->requested_fec = eth_to_cc_fec(fec->fec); + ret = t4_link_l1cfg(pi->adapter, pi->adapter->mbox, + pi->tx_chan, lc); + if (ret) + *lc = old_lc; + return ret; +} + static void get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause) { @@ -1255,6 +1353,8 @@ static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, static const struct ethtool_ops cxgb_ethtool_ops = { .get_link_ksettings = get_link_ksettings, .set_link_ksettings = set_link_ksettings, + .get_fecparam = get_fecparam, + .set_fecparam = set_fecparam, .get_drvinfo = get_drvinfo, .get_msglevel = get_msglevel, .set_msglevel = set_msglevel, -- cgit v1.2.3 From d329ac88eb217fef1516a1dcfda27b7f5a8eb07b Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Sun, 30 Jul 2017 19:58:53 +0200 Subject: net: dsa: lan9303: Fix lan9303_detect_phy_setup() for MDIO Handle that MDIO read with no response return 0xffff. Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index cd76e61f1fca..9d0ab77edb4a 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -427,6 +427,7 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip) * Special reg 18 of phy 3 reads as 0x0000, if 'phy_addr_sel_strap' is 0 * and the IDs are 0-1-2, else it contains something different from * 0x0000, which means 'phy_addr_sel_strap' is 1 and the IDs are 1-2-3. + * 0xffff is returned on MDIO read with no response. */ reg = lan9303_port_phy_reg_read(chip, 3, MII_LAN911X_SPECIAL_MODES); if (reg < 0) { @@ -434,7 +435,7 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip) return reg; } - if (reg != 0) + if ((reg != 0) && (reg != 0xffff)) chip->phy_addr_sel_strap = 1; else chip->phy_addr_sel_strap = 0; -- cgit v1.2.3 From ab78acb152e3f0ded13a13fd27df46448d34a4b7 Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Sun, 30 Jul 2017 19:58:54 +0200 Subject: net: dsa: lan9303: Multiply by 4 to get MDIO register lan9303_mdio_write()/_read() must multiply register number by 4 to get offset. Added some commments to the register definitions. Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 6 ++++++ drivers/net/dsa/lan9303_mdio.c | 2 ++ 2 files changed, 8 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 9d0ab77edb4a..96ebeb9bd59a 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -20,6 +20,9 @@ #include "lan9303.h" +/* 13.2 System Control and Status Registers + * Multiply register number by 4 to get address offset. + */ #define LAN9303_CHIP_REV 0x14 # define LAN9303_CHIP_ID 0x9303 #define LAN9303_IRQ_CFG 0x15 @@ -53,6 +56,9 @@ #define LAN9303_VIRT_PHY_BASE 0x70 #define LAN9303_VIRT_SPECIAL_CTRL 0x77 +/*13.4 Switch Fabric Control and Status Registers + * Accessed indirectly via SWITCH_CSR_CMD, SWITCH_CSR_DATA. + */ #define LAN9303_SW_DEV_ID 0x0000 #define LAN9303_SW_RESET 0x0001 #define LAN9303_SW_RESET_RESET BIT(0) diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c index 93c36c0541cf..2db7970fc88c 100644 --- a/drivers/net/dsa/lan9303_mdio.c +++ b/drivers/net/dsa/lan9303_mdio.c @@ -40,6 +40,7 @@ static int lan9303_mdio_write(void *ctx, uint32_t reg, uint32_t val) { struct lan9303_mdio *sw_dev = (struct lan9303_mdio *)ctx; + reg <<= 2; /* reg num to offset */ mutex_lock(&sw_dev->device->bus->mdio_lock); lan9303_mdio_real_write(sw_dev->device, reg, val & 0xffff); lan9303_mdio_real_write(sw_dev->device, reg + 2, (val >> 16) & 0xffff); @@ -57,6 +58,7 @@ static int lan9303_mdio_read(void *ctx, uint32_t reg, uint32_t *val) { struct lan9303_mdio *sw_dev = (struct lan9303_mdio *)ctx; + reg <<= 2; /* reg num to offset */ mutex_lock(&sw_dev->device->bus->mdio_lock); *val = lan9303_mdio_real_read(sw_dev->device, reg); *val |= (lan9303_mdio_real_read(sw_dev->device, reg + 2) << 16); -- cgit v1.2.3 From 9e866e5dab383a295001b13f08e42b906b9dd34d Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Sun, 30 Jul 2017 19:58:55 +0200 Subject: net: dsa: lan9303: Renamed indirect phy access functions Preparing for the following fix of MDIO phy access: Renamed functions that access PHY 1 and 2 indirectly through PMI registers. lan9303_port_phy_reg_wait_for_completion() to lan9303_indirect_phy_wait_for_completion() lan9303_port_phy_reg_read() to lan9303_indirect_phy_read() lan9303_port_phy_reg_write() to lan9303_indirect_phy_write() Also changed "val" parameter of lan9303_indirect_phy_write() to u16, for clarity. Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 96ebeb9bd59a..9427c3b0ced2 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -248,7 +248,7 @@ static int lan9303_virt_phy_reg_write(struct lan9303 *chip, int regnum, u16 val) return regmap_write(chip->regmap, LAN9303_VIRT_PHY_BASE + regnum, val); } -static int lan9303_port_phy_reg_wait_for_completion(struct lan9303 *chip) +static int lan9303_indirect_phy_wait_for_completion(struct lan9303 *chip) { int ret, i; u32 reg; @@ -268,7 +268,7 @@ static int lan9303_port_phy_reg_wait_for_completion(struct lan9303 *chip) return -EIO; } -static int lan9303_port_phy_reg_read(struct lan9303 *chip, int addr, int regnum) +static int lan9303_indirect_phy_read(struct lan9303 *chip, int addr, int regnum) { int ret; u32 val; @@ -278,7 +278,7 @@ static int lan9303_port_phy_reg_read(struct lan9303 *chip, int addr, int regnum) mutex_lock(&chip->indirect_mutex); - ret = lan9303_port_phy_reg_wait_for_completion(chip); + ret = lan9303_indirect_phy_wait_for_completion(chip); if (ret) goto on_error; @@ -287,7 +287,7 @@ static int lan9303_port_phy_reg_read(struct lan9303 *chip, int addr, int regnum) if (ret) goto on_error; - ret = lan9303_port_phy_reg_wait_for_completion(chip); + ret = lan9303_indirect_phy_wait_for_completion(chip); if (ret) goto on_error; @@ -305,8 +305,8 @@ on_error: return ret; } -static int lan9303_phy_reg_write(struct lan9303 *chip, int addr, int regnum, - unsigned int val) +static int lan9303_indirect_phy_write(struct lan9303 *chip, int addr, + int regnum, u16 val) { int ret; u32 reg; @@ -317,7 +317,7 @@ static int lan9303_phy_reg_write(struct lan9303 *chip, int addr, int regnum, mutex_lock(&chip->indirect_mutex); - ret = lan9303_port_phy_reg_wait_for_completion(chip); + ret = lan9303_indirect_phy_wait_for_completion(chip); if (ret) goto on_error; @@ -435,7 +435,7 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip) * 0x0000, which means 'phy_addr_sel_strap' is 1 and the IDs are 1-2-3. * 0xffff is returned on MDIO read with no response. */ - reg = lan9303_port_phy_reg_read(chip, 3, MII_LAN911X_SPECIAL_MODES); + reg = lan9303_indirect_phy_read(chip, 3, MII_LAN911X_SPECIAL_MODES); if (reg < 0) { dev_err(chip->dev, "Failed to detect phy config: %d\n", reg); return reg; @@ -726,7 +726,7 @@ static int lan9303_phy_read(struct dsa_switch *ds, int phy, int regnum) if (phy > phy_base + 2) return -ENODEV; - return lan9303_port_phy_reg_read(chip, phy, regnum); + return lan9303_indirect_phy_read(chip, phy, regnum); } static int lan9303_phy_write(struct dsa_switch *ds, int phy, int regnum, @@ -740,7 +740,7 @@ static int lan9303_phy_write(struct dsa_switch *ds, int phy, int regnum, if (phy > phy_base + 2) return -ENODEV; - return lan9303_phy_reg_write(chip, phy, regnum, val); + return lan9303_indirect_phy_write(chip, phy, regnum, val); } static int lan9303_port_enable(struct dsa_switch *ds, int port, @@ -773,13 +773,13 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port, switch (port) { case 1: lan9303_disable_packet_processing(chip, LAN9303_PORT_1_OFFSET); - lan9303_phy_reg_write(chip, chip->phy_addr_sel_strap + 1, - MII_BMCR, BMCR_PDOWN); + lan9303_indirect_phy_write(chip, chip->phy_addr_sel_strap + 1, + MII_BMCR, BMCR_PDOWN); break; case 2: lan9303_disable_packet_processing(chip, LAN9303_PORT_2_OFFSET); - lan9303_phy_reg_write(chip, chip->phy_addr_sel_strap + 2, - MII_BMCR, BMCR_PDOWN); + lan9303_indirect_phy_write(chip, chip->phy_addr_sel_strap + 2, + MII_BMCR, BMCR_PDOWN); break; default: dev_dbg(chip->dev, -- cgit v1.2.3 From 2c3408986c07515abcbad5dc584a33892e3621da Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Sun, 30 Jul 2017 19:58:56 +0200 Subject: net: dsa: lan9303: MDIO access phy registers directly Indirect access (PMI) to phy register only work in I2C mode. In MDIO mode phy registers must be accessed directly. Introduced struct lan9303_phy_ops to handle the two modes. Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 20 +++++++++++++------- drivers/net/dsa/lan9303.h | 11 +++++++++++ drivers/net/dsa/lan9303_i2c.c | 2 ++ drivers/net/dsa/lan9303_mdio.c | 21 +++++++++++++++++++++ 4 files changed, 47 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 9427c3b0ced2..8e430d1ee297 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -334,6 +334,12 @@ on_error: return ret; } +const struct lan9303_phy_ops lan9303_indirect_phy_ops = { + .phy_read = lan9303_indirect_phy_read, + .phy_write = lan9303_indirect_phy_write, +}; +EXPORT_SYMBOL_GPL(lan9303_indirect_phy_ops); + static int lan9303_switch_wait_for_completion(struct lan9303 *chip) { int ret, i; @@ -435,7 +441,7 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip) * 0x0000, which means 'phy_addr_sel_strap' is 1 and the IDs are 1-2-3. * 0xffff is returned on MDIO read with no response. */ - reg = lan9303_indirect_phy_read(chip, 3, MII_LAN911X_SPECIAL_MODES); + reg = chip->ops->phy_read(chip, 3, MII_LAN911X_SPECIAL_MODES); if (reg < 0) { dev_err(chip->dev, "Failed to detect phy config: %d\n", reg); return reg; @@ -726,7 +732,7 @@ static int lan9303_phy_read(struct dsa_switch *ds, int phy, int regnum) if (phy > phy_base + 2) return -ENODEV; - return lan9303_indirect_phy_read(chip, phy, regnum); + return chip->ops->phy_read(chip, phy, regnum); } static int lan9303_phy_write(struct dsa_switch *ds, int phy, int regnum, @@ -740,7 +746,7 @@ static int lan9303_phy_write(struct dsa_switch *ds, int phy, int regnum, if (phy > phy_base + 2) return -ENODEV; - return lan9303_indirect_phy_write(chip, phy, regnum, val); + return chip->ops->phy_write(chip, phy, regnum, val); } static int lan9303_port_enable(struct dsa_switch *ds, int port, @@ -773,13 +779,13 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port, switch (port) { case 1: lan9303_disable_packet_processing(chip, LAN9303_PORT_1_OFFSET); - lan9303_indirect_phy_write(chip, chip->phy_addr_sel_strap + 1, - MII_BMCR, BMCR_PDOWN); + lan9303_phy_write(ds, chip->phy_addr_sel_strap + 1, + MII_BMCR, BMCR_PDOWN); break; case 2: lan9303_disable_packet_processing(chip, LAN9303_PORT_2_OFFSET); - lan9303_indirect_phy_write(chip, chip->phy_addr_sel_strap + 2, - MII_BMCR, BMCR_PDOWN); + lan9303_phy_write(ds, chip->phy_addr_sel_strap + 2, + MII_BMCR, BMCR_PDOWN); break; default: dev_dbg(chip->dev, diff --git a/drivers/net/dsa/lan9303.h b/drivers/net/dsa/lan9303.h index d1512dad2d90..4d8be555ff4d 100644 --- a/drivers/net/dsa/lan9303.h +++ b/drivers/net/dsa/lan9303.h @@ -2,6 +2,15 @@ #include #include +struct lan9303; + +struct lan9303_phy_ops { + /* PHY 1 and 2 access*/ + int (*phy_read)(struct lan9303 *chip, int port, int regnum); + int (*phy_write)(struct lan9303 *chip, int port, + int regnum, u16 val); +}; + struct lan9303 { struct device *dev; struct regmap *regmap; @@ -11,9 +20,11 @@ struct lan9303 { bool phy_addr_sel_strap; struct dsa_switch *ds; struct mutex indirect_mutex; /* protect indexed register access */ + const struct lan9303_phy_ops *ops; }; extern const struct regmap_access_table lan9303_register_set; +extern const struct lan9303_phy_ops lan9303_indirect_phy_ops; int lan9303_probe(struct lan9303 *chip, struct device_node *np); int lan9303_remove(struct lan9303 *chip); diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c index ab3ce0da5071..24ec20f7f444 100644 --- a/drivers/net/dsa/lan9303_i2c.c +++ b/drivers/net/dsa/lan9303_i2c.c @@ -63,6 +63,8 @@ static int lan9303_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, sw_dev); sw_dev->chip.dev = &client->dev; + sw_dev->chip.ops = &lan9303_indirect_phy_ops; + ret = lan9303_probe(&sw_dev->chip, client->dev.of_node); if (ret != 0) return ret; diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c index 2db7970fc88c..fc16668a487f 100644 --- a/drivers/net/dsa/lan9303_mdio.c +++ b/drivers/net/dsa/lan9303_mdio.c @@ -67,6 +67,25 @@ static int lan9303_mdio_read(void *ctx, uint32_t reg, uint32_t *val) return 0; } +int lan9303_mdio_phy_write(struct lan9303 *chip, int phy, int reg, u16 val) +{ + struct lan9303_mdio *sw_dev = dev_get_drvdata(chip->dev); + + return mdiobus_write_nested(sw_dev->device->bus, phy, reg, val); +} + +int lan9303_mdio_phy_read(struct lan9303 *chip, int phy, int reg) +{ + struct lan9303_mdio *sw_dev = dev_get_drvdata(chip->dev); + + return mdiobus_read_nested(sw_dev->device->bus, phy, reg); +} + +static const struct lan9303_phy_ops lan9303_mdio_phy_ops = { + .phy_read = lan9303_mdio_phy_read, + .phy_write = lan9303_mdio_phy_write, +}; + static const struct regmap_config lan9303_mdio_regmap_config = { .reg_bits = 8, .val_bits = 32, @@ -108,6 +127,8 @@ static int lan9303_mdio_probe(struct mdio_device *mdiodev) dev_set_drvdata(&mdiodev->dev, sw_dev); sw_dev->chip.dev = &mdiodev->dev; + sw_dev->chip.ops = &lan9303_mdio_phy_ops; + ret = lan9303_probe(&sw_dev->chip, mdiodev->dev.of_node); if (ret != 0) return ret; -- cgit v1.2.3 From 9558df3a8251f1c636eaf3d2222b6da2eb5d1086 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 30 Jul 2017 22:11:06 +0200 Subject: net: fec: Issue error for missing but expected PHY If the PHY is missing but expected, e.g. because of a typ0 in the dt file, it is not possible to open the interface. ip link returns: RTNETLINK answers: No such device It is not very obvious what the problem is. Add a netdev_err() in this case to make it easier to debug the issue. [ 21.409385] fec 2188000.ethernet eth0: Unable to connect to phy RTNETLINK answers: No such device Signed-off-by: Andrew Lunn Acked-by: Fugang Duan Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index a6e323f15637..faa36f6ddf29 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1904,8 +1904,10 @@ static int fec_enet_mii_probe(struct net_device *ndev) phy_dev = of_phy_connect(ndev, fep->phy_node, &fec_enet_adjust_link, 0, fep->phy_interface); - if (!phy_dev) + if (!phy_dev) { + netdev_err(ndev, "Unable to connect to phy\n"); return -ENODEV; + } } else { /* check for attached phy */ for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { -- cgit v1.2.3 From fbbeefdd21049fcf9437c809da3828b210577f36 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 30 Jul 2017 19:36:05 +0200 Subject: net: fec: Allow reception of frames bigger than 1522 bytes The FEC Receive Control Register has a 14 bit field indicating the longest frame that may be received. It is being set to 1522. Frames longer than this are discarded, but counted as being in error. When using DSA, frames from the switch has an additional header, either 4 or 8 bytes if a Marvell switch is used. Thus a full MTU frame of 1522 bytes received by the switch on a port becomes 1530 bytes when passed to the host via the FEC interface. Change the maximum receive size to 2048 - 64, where 64 is the maximum rx_alignment applied on the receive buffer for AVB capable FEC cores. Use this value also for the maximum receive buffer size. The driver is already allocating a receive SKB of 2048 bytes, so this change should not have any significant effects. Tested on imx51, imx6, vf610. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index faa36f6ddf29..df09b254553d 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -173,10 +173,12 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #endif /* CONFIG_M5272 */ /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. + * + * 2048 byte skbufs are allocated. However, alignment requirements + * varies between FEC variants. Worst case is 64, so round down by 64. */ -#define PKT_MAXBUF_SIZE 1522 +#define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) #define PKT_MINBUF_SIZE 64 -#define PKT_MAXBLR_SIZE 1536 /* FEC receive acceleration */ #define FEC_RACC_IPDIS (1 << 1) @@ -851,7 +853,7 @@ static void fec_enet_enable_ring(struct net_device *ndev) for (i = 0; i < fep->num_rx_queues; i++) { rxq = fep->rx_queue[i]; writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); - writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); + writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); /* enable DMA1/2 */ if (i) -- cgit v1.2.3 From 591bb2789bc2a93f379b13d277f441f1b427102d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 26 Jul 2017 11:40:52 +0200 Subject: netfilter: nf_hook_ops structs can be const We no longer place these on a list so they can be const. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- drivers/net/ipvlan/ipvlan_main.c | 2 +- net/bridge/br_netfilter_hooks.c | 2 +- net/bridge/netfilter/ebtable_filter.c | 2 +- net/bridge/netfilter/ebtable_nat.c | 2 +- net/decnet/netfilter/dn_rtmsg.c | 2 +- net/ipv4/netfilter/ipt_CLUSTERIP.c | 2 +- net/ipv4/netfilter/ipt_SYNPROXY.c | 2 +- net/ipv4/netfilter/iptable_nat.c | 2 +- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 2 +- net/ipv4/netfilter/nf_defrag_ipv4.c | 2 +- net/ipv6/ila/ila_xlat.c | 2 +- net/ipv6/netfilter/ip6t_SYNPROXY.c | 2 +- net/ipv6/netfilter/ip6table_nat.c | 2 +- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 2 +- net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | 2 +- net/netfilter/ipvs/ip_vs_core.c | 2 +- security/selinux/hooks.c | 2 +- security/smack/smack_netfilter.c | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index fdde20735416..943e6907dc19 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -15,7 +15,7 @@ struct ipvlan_netns { unsigned int ipvl_nf_hook_refcnt; }; -static struct nf_hook_ops ipvl_nfops[] __read_mostly = { +static const struct nf_hook_ops ipvl_nfops[] = { { .hook = ipvlan_nf_input, .pf = NFPROTO_IPV4, diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 2261e5194c82..626f4b2cef16 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -887,7 +887,7 @@ EXPORT_SYMBOL_GPL(br_netfilter_enable); /* For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because * br_dev_queue_push_xmit is called afterwards */ -static struct nf_hook_ops br_nf_ops[] __read_mostly = { +static const struct nf_hook_ops br_nf_ops[] = { { .hook = br_nf_pre_routing, .pf = NFPROTO_BRIDGE, diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index f22ef7c21913..45a00dbdbcad 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -70,7 +70,7 @@ ebt_out_hook(void *priv, struct sk_buff *skb, return ebt_do_table(skb, state, state->net->xt.frame_filter); } -static struct nf_hook_ops ebt_ops_filter[] __read_mostly = { +static const struct nf_hook_ops ebt_ops_filter[] = { { .hook = ebt_in_hook, .pf = NFPROTO_BRIDGE, diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 2f7a4f314406..4ecf50662b7d 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -70,7 +70,7 @@ ebt_nat_out(void *priv, struct sk_buff *skb, return ebt_do_table(skb, state, state->net->xt.frame_nat); } -static struct nf_hook_ops ebt_ops_nat[] __read_mostly = { +static const struct nf_hook_ops ebt_ops_nat[] = { { .hook = ebt_nat_out, .pf = NFPROTO_BRIDGE, diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index aa8ffecc46a4..ab395e55cd78 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -115,7 +115,7 @@ static inline void dnrmg_receive_user_skb(struct sk_buff *skb) RCV_SKB_FAIL(-EINVAL); } -static struct nf_hook_ops dnrmg_ops __read_mostly = { +static const struct nf_hook_ops dnrmg_ops = { .hook = dnrmg_hook, .pf = NFPROTO_DECNET, .hooknum = NF_DN_ROUTE, diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 7d72decb80f9..6637e8b37ee2 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -624,7 +624,7 @@ arp_mangle(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops cip_arp_ops __read_mostly = { +static const struct nf_hook_ops cip_arp_ops = { .hook = arp_mangle, .pf = NFPROTO_ARP, .hooknum = NF_ARP_OUT, diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index f1528f7175a8..811689e523c3 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -416,7 +416,7 @@ static unsigned int ipv4_synproxy_hook(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops ipv4_synproxy_ops[] __read_mostly = { +static const struct nf_hook_ops ipv4_synproxy_ops[] = { { .hook = ipv4_synproxy_hook, .pf = NFPROTO_IPV4, diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index 138a24bc76ad..a1a07b338ccf 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -67,7 +67,7 @@ static unsigned int iptable_nat_ipv4_local_fn(void *priv, return nf_nat_ipv4_local_fn(priv, skb, state, iptable_nat_do_chain); } -static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { +static const struct nf_hook_ops nf_nat_ipv4_ops[] = { /* Before packet filtering, change destination */ { .hook = iptable_nat_ipv4_in, diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 2e14ed11a35c..63e4ea0e01f8 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -174,7 +174,7 @@ static unsigned int ipv4_conntrack_local(void *priv, /* Connection tracking may drop packets, but never alters them, so make it the first hook. */ -static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = { +static const struct nf_hook_ops ipv4_conntrack_ops[] = { { .hook = ipv4_conntrack_in, .pf = NFPROTO_IPV4, diff --git a/net/ipv4/netfilter/nf_defrag_ipv4.c b/net/ipv4/netfilter/nf_defrag_ipv4.c index 346bf7ccac08..37fe1616ca0b 100644 --- a/net/ipv4/netfilter/nf_defrag_ipv4.c +++ b/net/ipv4/netfilter/nf_defrag_ipv4.c @@ -90,7 +90,7 @@ static unsigned int ipv4_conntrack_defrag(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops ipv4_defrag_ops[] = { +static const struct nf_hook_ops ipv4_defrag_ops[] = { { .hook = ipv4_conntrack_defrag, .pf = NFPROTO_IPV4, diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index 77f7f8c7d93d..5bd419c1abc8 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c @@ -208,7 +208,7 @@ ila_nf_input(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops ila_nf_hook_ops[] __read_mostly = { +static const struct nf_hook_ops ila_nf_hook_ops[] = { { .hook = ila_nf_input, .pf = NFPROTO_IPV6, diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index ce203dd729e0..a5cd43d75393 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -438,7 +438,7 @@ static unsigned int ipv6_synproxy_hook(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops ipv6_synproxy_ops[] __read_mostly = { +static const struct nf_hook_ops ipv6_synproxy_ops[] = { { .hook = ipv6_synproxy_hook, .pf = NFPROTO_IPV6, diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index 7d2bd940291f..991512576c8c 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -69,7 +69,7 @@ static unsigned int ip6table_nat_local_fn(void *priv, return nf_nat_ipv6_local_fn(priv, skb, state, ip6table_nat_do_chain); } -static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = { +static const struct nf_hook_ops nf_nat_ipv6_ops[] = { /* Before packet filtering, change destination */ { .hook = ip6table_nat_in, diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 4e3402486833..f2d2f4a9294b 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -191,7 +191,7 @@ static unsigned int ipv6_conntrack_local(void *priv, return nf_conntrack_in(state->net, PF_INET6, state->hook, skb); } -static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { +static const struct nf_hook_ops ipv6_conntrack_ops[] = { { .hook = ipv6_conntrack_in, .pf = NFPROTO_IPV6, diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c index ada60d1a991b..b326da59257f 100644 --- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c @@ -74,7 +74,7 @@ static unsigned int ipv6_defrag(void *priv, return err == 0 ? NF_ACCEPT : NF_DROP; } -static struct nf_hook_ops ipv6_defrag_ops[] = { +static const struct nf_hook_ops ipv6_defrag_ops[] = { { .hook = ipv6_defrag, .pf = NFPROTO_IPV6, diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 2ff9d9070c95..5cb7cac9177d 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -2101,7 +2101,7 @@ ip_vs_forward_icmp_v6(void *priv, struct sk_buff *skb, #endif -static struct nf_hook_ops ip_vs_ops[] __read_mostly = { +static const struct nf_hook_ops ip_vs_ops[] = { /* After packet filtering, change source only for VS/NAT */ { .hook = ip_vs_reply4, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 33fd061305c4..2f2e1338cd3d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6530,7 +6530,7 @@ security_initcall(selinux_init); #if defined(CONFIG_NETFILTER) -static struct nf_hook_ops selinux_nf_ops[] = { +static const struct nf_hook_ops selinux_nf_ops[] = { { .hook = selinux_ipv4_postroute, .pf = NFPROTO_IPV4, diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c index cdeb0f3243dd..e36d17835d4f 100644 --- a/security/smack/smack_netfilter.c +++ b/security/smack/smack_netfilter.c @@ -58,7 +58,7 @@ static unsigned int smack_ipv4_output(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops smack_nf_ops[] = { +static const struct nf_hook_ops smack_nf_ops[] = { { .hook = smack_ipv4_output, .pf = NFPROTO_IPV4, -- cgit v1.2.3 From 69a60b0579a4bf63871dfcfaca44a4e20c7d05f8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 31 Jul 2017 12:04:22 -0700 Subject: net: phy: mdio-bcm-unimac: factor busy polling loop Factor the code that does the busy polling on the MDIO_BUSY bit since we will have different code-paths for for completion depending on whether we are using interrupts or polling. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio-bcm-unimac.c | 43 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 34395230ce70..226fdccfa1a8 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -57,10 +57,26 @@ static inline unsigned int unimac_mdio_busy(struct unimac_mdio_priv *priv) return __raw_readl(priv->base + MDIO_CMD) & MDIO_START_BUSY; } +static int unimac_mdio_poll(struct unimac_mdio_priv *priv) +{ + unsigned int timeout = 1000; + + do { + if (!unimac_mdio_busy(priv)) + return 0; + + usleep_range(1000, 2000); + } while (timeout--); + + if (!timeout) + return -ETIMEDOUT; + + return 0; +} + static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) { struct unimac_mdio_priv *priv = bus->priv; - unsigned int timeout = 1000; u32 cmd; /* Prepare the read operation */ @@ -70,15 +86,9 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) /* Start MDIO transaction */ unimac_mdio_start(priv); - do { - if (!unimac_mdio_busy(priv)) - break; - - usleep_range(1000, 2000); - } while (timeout--); - - if (!timeout) - return -ETIMEDOUT; + ret = unimac_mdio_poll(priv); + if (ret) + return ret; cmd = __raw_readl(priv->base + MDIO_CMD); @@ -97,7 +107,6 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) { struct unimac_mdio_priv *priv = bus->priv; - unsigned int timeout = 1000; u32 cmd; /* Prepare the write operation */ @@ -107,17 +116,7 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id, unimac_mdio_start(priv); - do { - if (!unimac_mdio_busy(priv)) - break; - - usleep_range(1000, 2000); - } while (timeout--); - - if (!timeout) - return -ETIMEDOUT; - - return 0; + return unimac_mdio_poll(priv); } /* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with -- cgit v1.2.3 From d782f7c935123ad99582af15c862bc7b0331d205 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 31 Jul 2017 12:04:23 -0700 Subject: net: phy: mdio-bcm-unimac: create unique bus names In preparation for having multiple GENET instances in a system (up to 3), make sure that we do include the bus instance number in the name of the MDIO bus such that we change it from "unimac-mdio" to "unimac-mdio-0" for instance. So far, the only user of this driver is using Device Tree, which uses a lookup/parenting based technique to map PHY devices to their respective MDIO bus controllers, hence causing no additional changes. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio-bcm-unimac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 226fdccfa1a8..97cac10bd0d5 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -197,7 +197,7 @@ static int unimac_mdio_probe(struct platform_device *pdev) bus->read = unimac_mdio_read; bus->write = unimac_mdio_write; bus->reset = unimac_mdio_reset; - snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); ret = of_mdiobus_register(bus, np); if (ret) { -- cgit v1.2.3 From e23597f7524ba11ddecc6bb3d9b31e5285fd3b45 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 31 Jul 2017 12:04:24 -0700 Subject: net: phy: mdio-bcm-unimac: Add debug print for PHY workaround In order to be stricly identical to what bcmgenet does, add a debug print when a PHY workaround during bus->reset() is executed. Preliminary change to moving bcmgenet towards mdio-bcm-unimac. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio-bcm-unimac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 97cac10bd0d5..4e52692f9eea 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -154,8 +154,10 @@ static int unimac_mdio_reset(struct mii_bus *bus) } for (addr = 0; addr < PHY_MAX_ADDR; addr++) { - if (read_mask & 1 << addr) + if (read_mask & 1 << addr) { + dev_dbg(&bus->dev, "Workaround for PHY @ %d\n", addr); mdiobus_read(bus, addr, MII_BMSR); + } } return 0; -- cgit v1.2.3 From f248aff86d1fd6e60b656c2af278f1723c3b84c2 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 31 Jul 2017 12:04:25 -0700 Subject: net: phy: mdio-bcm-unimac: Allow specifying platform data In preparation for having the bcmgenet driver migrate over the mdio-bcm-unimac driver, add a platform data structure which allows passing integrating specific details like bus name, wait function to complete MDIO operations and PHY mask. We also define what the platform device name contract is by defining UNIMAC_MDIO_DRV_NAME and moving it to the platform_data header. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- MAINTAINERS | 1 + drivers/net/phy/mdio-bcm-unimac.c | 28 +++++++++++++++++++++------ include/linux/platform_data/mdio-bcm-unimac.h | 13 +++++++++++++ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 include/linux/platform_data/mdio-bcm-unimac.h (limited to 'drivers/net') diff --git a/MAINTAINERS b/MAINTAINERS index 297e610c9163..7d72fdbed6e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5140,6 +5140,7 @@ L: netdev@vger.kernel.org S: Maintained F: include/linux/phy.h F: include/linux/phy_fixed.h +F: include/linux/platform_data/mdio-bcm-unimac.h F: drivers/net/phy/ F: Documentation/networking/phy.txt F: drivers/of/of_mdio.c diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 4e52692f9eea..89425ca48412 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -21,6 +21,8 @@ #include #include +#include + #define MDIO_CMD 0x00 #define MDIO_START_BUSY (1 << 29) #define MDIO_READ_FAIL (1 << 28) @@ -41,6 +43,8 @@ struct unimac_mdio_priv { struct mii_bus *mii_bus; void __iomem *base; + int (*wait_func) (void *wait_func_data); + void *wait_func_data; }; static inline void unimac_mdio_start(struct unimac_mdio_priv *priv) @@ -57,8 +61,9 @@ static inline unsigned int unimac_mdio_busy(struct unimac_mdio_priv *priv) return __raw_readl(priv->base + MDIO_CMD) & MDIO_START_BUSY; } -static int unimac_mdio_poll(struct unimac_mdio_priv *priv) +static int unimac_mdio_poll(void *wait_func_data) { + struct unimac_mdio_priv *priv = wait_func_data; unsigned int timeout = 1000; do { @@ -77,6 +82,7 @@ static int unimac_mdio_poll(struct unimac_mdio_priv *priv) static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) { struct unimac_mdio_priv *priv = bus->priv; + int ret; u32 cmd; /* Prepare the read operation */ @@ -86,7 +92,7 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) /* Start MDIO transaction */ unimac_mdio_start(priv); - ret = unimac_mdio_poll(priv); + ret = priv->wait_func(priv->wait_func_data); if (ret) return ret; @@ -116,7 +122,7 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id, unimac_mdio_start(priv); - return unimac_mdio_poll(priv); + return priv->wait_func(priv->wait_func_data); } /* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with @@ -165,6 +171,7 @@ static int unimac_mdio_reset(struct mii_bus *bus) static int unimac_mdio_probe(struct platform_device *pdev) { + struct unimac_mdio_pdata *pdata = pdev->dev.platform_data; struct unimac_mdio_priv *priv; struct device_node *np; struct mii_bus *bus; @@ -194,7 +201,16 @@ static int unimac_mdio_probe(struct platform_device *pdev) bus = priv->mii_bus; bus->priv = priv; - bus->name = "unimac MII bus"; + if (pdata) { + bus->name = pdata->bus_name; + priv->wait_func = pdata->wait_func; + priv->wait_func_data = pdata->wait_func_data; + bus->phy_mask = ~pdata->phy_mask; + } else { + bus->name = "unimac MII bus"; + priv->wait_func_data = priv; + priv->wait_func = unimac_mdio_poll; + } bus->parent = &pdev->dev; bus->read = unimac_mdio_read; bus->write = unimac_mdio_write; @@ -241,7 +257,7 @@ MODULE_DEVICE_TABLE(of, unimac_mdio_ids); static struct platform_driver unimac_mdio_driver = { .driver = { - .name = "unimac-mdio", + .name = UNIMAC_MDIO_DRV_NAME, .of_match_table = unimac_mdio_ids, }, .probe = unimac_mdio_probe, @@ -252,4 +268,4 @@ module_platform_driver(unimac_mdio_driver); MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom UniMAC MDIO bus controller"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:unimac-mdio"); +MODULE_ALIAS("platform:" UNIMAC_MDIO_DRV_NAME); diff --git a/include/linux/platform_data/mdio-bcm-unimac.h b/include/linux/platform_data/mdio-bcm-unimac.h new file mode 100644 index 000000000000..8a5f9f0b2c52 --- /dev/null +++ b/include/linux/platform_data/mdio-bcm-unimac.h @@ -0,0 +1,13 @@ +#ifndef __MDIO_BCM_UNIMAC_PDATA_H +#define __MDIO_BCM_UNIMAC_PDATA_H + +struct unimac_mdio_pdata { + u32 phy_mask; + int (*wait_func)(void *data); + void *wait_func_data; + const char *bus_name; +}; + +#define UNIMAC_MDIO_DRV_NAME "unimac-mdio" + +#endif /* __MDIO_BCM_UNIMAC_PDATA_H */ -- cgit v1.2.3 From 9a4e79697009ddd0d1af52053c830f6e60e1c771 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 31 Jul 2017 12:04:26 -0700 Subject: net: bcmgenet: utilize generic Broadcom UniMAC MDIO controller driver Update the GENET driver to register an UniMAC MDIO bus controller for the GENET internal MDIO bus, update the platform data code to attach the PHY to the correct MDIO bus controller. The Device Tree portion of the code is mostly left unmodified since the lookup/binding is done via phandles and Device Tree nodes which are much more flexible in locating and binding PHYs to their respective MDIO bus controllers. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/Kconfig | 1 + drivers/net/ethernet/broadcom/genet/bcmgenet.h | 1 + drivers/net/ethernet/broadcom/genet/bcmmii.c | 148 +++++++++++++++++++------ 3 files changed, 116 insertions(+), 34 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 285f8bc25682..ec7a798c6bd1 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -65,6 +65,7 @@ config BCMGENET select PHYLIB select FIXED_PHY select BCM7XXX_PHY + select MDIO_BCM_UNIMAC help This driver supports the built-in Ethernet MACs found in the Broadcom BCM7xxx Set Top Box family chipset. diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index b9344de669f8..4775999ee016 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -657,6 +657,7 @@ struct bcmgenet_priv { struct clk *clk; struct platform_device *pdev; + struct platform_device *mii_pdev; /* WOL */ struct clk *clk_wol; diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 071fcbd14e6a..368d5eab306b 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "bcmgenet.h" @@ -464,31 +465,120 @@ static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv) return 0; } -static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) +static struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv) { struct device_node *dn = priv->pdev->dev.of_node; struct device *kdev = &priv->pdev->dev; - struct phy_device *phydev = NULL; char *compat; - int phy_mode; - int ret; compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version); if (!compat) - return -ENOMEM; + return NULL; priv->mdio_dn = of_find_compatible_node(dn, NULL, compat); kfree(compat); if (!priv->mdio_dn) { dev_err(kdev, "unable to find MDIO bus node\n"); - return -ENODEV; + return NULL; } - ret = of_mdiobus_register(priv->mii_bus, priv->mdio_dn); - if (ret) { - dev_err(kdev, "failed to register MDIO bus\n"); - return ret; + return priv->mdio_dn; +} + +static void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv, + struct unimac_mdio_pdata *ppd) +{ + struct device *kdev = &priv->pdev->dev; + struct bcmgenet_platform_data *pd = kdev->platform_data; + + if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) { + /* + * Internal or external PHY with MDIO access + */ + if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR) + ppd->phy_mask = 1 << pd->phy_address; + else + ppd->phy_mask = 0; } +} + +static int bcmgenet_mii_wait(void *wait_func_data) +{ + struct bcmgenet_priv *priv = wait_func_data; + + wait_event_timeout(priv->wq, + !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) + & MDIO_START_BUSY), + HZ / 100); + return 0; +} + +static int bcmgenet_mii_register(struct bcmgenet_priv *priv) +{ + struct platform_device *pdev = priv->pdev; + struct bcmgenet_platform_data *pdata = pdev->dev.platform_data; + struct device_node *dn = pdev->dev.of_node; + struct unimac_mdio_pdata ppd; + struct platform_device *ppdev; + struct resource *pres, res; + int id, ret; + + pres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + memset(&res, 0, sizeof(res)); + memset(&ppd, 0, sizeof(ppd)); + + ppd.wait_func = bcmgenet_mii_wait; + ppd.wait_func_data = priv; + ppd.bus_name = "bcmgenet MII bus"; + + /* Unimac MDIO bus controller starts at UniMAC offset + MDIO_CMD + * and is 2 * 32-bits word long, 8 bytes total. + */ + res.start = pres->start + GENET_UMAC_OFF + UMAC_MDIO_CMD; + res.end = res.start + 8; + res.flags = IORESOURCE_MEM; + + if (dn) + id = of_alias_get_id(dn, "eth"); + else + id = pdev->id; + + ppdev = platform_device_alloc(UNIMAC_MDIO_DRV_NAME, id); + if (!ppdev) + return -ENOMEM; + + /* Retain this platform_device pointer for later cleanup */ + priv->mii_pdev = ppdev; + ppdev->dev.parent = &pdev->dev; + ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv); + if (pdata) + bcmgenet_mii_pdata_init(priv, &ppd); + + ret = platform_device_add_resources(ppdev, &res, 1); + if (ret) + goto out; + + ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd)); + if (ret) + goto out; + + ret = platform_device_add(ppdev); + if (ret) + goto out; + + return 0; +out: + platform_device_put(ppdev); + return ret; +} + +static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) +{ + struct device_node *dn = priv->pdev->dev.of_node; + struct device *kdev = &priv->pdev->dev; + struct phy_device *phydev; + int phy_mode; + int ret; /* Fetch the PHY phandle */ priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0); @@ -536,33 +626,23 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv) { struct device *kdev = &priv->pdev->dev; struct bcmgenet_platform_data *pd = kdev->platform_data; - struct mii_bus *mdio = priv->mii_bus; + char phy_name[MII_BUS_ID_SIZE + 3]; + char mdio_bus_id[MII_BUS_ID_SIZE]; struct phy_device *phydev; - int ret; + + snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d", + UNIMAC_MDIO_DRV_NAME, priv->pdev->id); if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) { + snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, + mdio_bus_id, pd->phy_address); + /* * Internal or external PHY with MDIO access */ - if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR) - mdio->phy_mask = ~(1 << pd->phy_address); - else - mdio->phy_mask = 0; - - ret = mdiobus_register(mdio); - if (ret) { - dev_err(kdev, "failed to register MDIO bus\n"); - return ret; - } - - if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR) - phydev = mdiobus_get_phy(mdio, pd->phy_address); - else - phydev = phy_find_first(mdio); - + phydev = phy_attach(priv->dev, phy_name, pd->phy_interface); if (!phydev) { dev_err(kdev, "failed to register PHY device\n"); - mdiobus_unregister(mdio); return -ENODEV; } } else { @@ -611,7 +691,7 @@ int bcmgenet_mii_init(struct net_device *dev) struct device_node *dn = priv->pdev->dev.of_node; int ret; - ret = bcmgenet_mii_alloc(priv); + ret = bcmgenet_mii_register(priv); if (ret) return ret; @@ -625,8 +705,8 @@ out: if (of_phy_is_fixed_link(dn)) of_phy_deregister_fixed_link(dn); of_node_put(priv->phy_dn); - mdiobus_unregister(priv->mii_bus); - mdiobus_free(priv->mii_bus); + platform_device_unregister(priv->mii_pdev); + platform_device_put(priv->mii_pdev); return ret; } @@ -638,6 +718,6 @@ void bcmgenet_mii_exit(struct net_device *dev) if (of_phy_is_fixed_link(dn)) of_phy_deregister_fixed_link(dn); of_node_put(priv->phy_dn); - mdiobus_unregister(priv->mii_bus); - mdiobus_free(priv->mii_bus); + platform_device_unregister(priv->mii_pdev); + platform_device_put(priv->mii_pdev); } -- cgit v1.2.3 From 2b13c3ae098142b9a7da0b6d989081f0b563b0dd Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 31 Jul 2017 12:04:27 -0700 Subject: net: bcmgenet: Drop legacy MDIO code Now that we have fully migrated to the mdio-bcm-unimac driver, drop the legacy MDIO bus code which did duplicate a fair amount of code. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmmii.c | 125 --------------------------- 1 file changed, 125 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 368d5eab306b..7fdc352628f9 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -28,59 +28,6 @@ #include "bcmgenet.h" -/* read a value from the MII */ -static int bcmgenet_mii_read(struct mii_bus *bus, int phy_id, int location) -{ - int ret; - struct net_device *dev = bus->priv; - struct bcmgenet_priv *priv = netdev_priv(dev); - u32 reg; - - bcmgenet_umac_writel(priv, (MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | - (location << MDIO_REG_SHIFT)), UMAC_MDIO_CMD); - /* Start MDIO transaction*/ - reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); - reg |= MDIO_START_BUSY; - bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); - wait_event_timeout(priv->wq, - !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) - & MDIO_START_BUSY), - HZ / 100); - ret = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); - - /* Some broken devices are known not to release the line during - * turn-around, e.g: Broadcom BCM53125 external switches, so check for - * that condition here and ignore the MDIO controller read failure - * indication. - */ - if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (ret & MDIO_READ_FAIL)) - return -EIO; - - return ret & 0xffff; -} - -/* write a value to the MII */ -static int bcmgenet_mii_write(struct mii_bus *bus, int phy_id, - int location, u16 val) -{ - struct net_device *dev = bus->priv; - struct bcmgenet_priv *priv = netdev_priv(dev); - u32 reg; - - bcmgenet_umac_writel(priv, (MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | - (location << MDIO_REG_SHIFT) | (0xffff & val)), - UMAC_MDIO_CMD); - reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); - reg |= MDIO_START_BUSY; - bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); - wait_event_timeout(priv->wq, - !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) & - MDIO_START_BUSY), - HZ / 100); - - return 0; -} - /* setup netdev link state when PHY link status change and * update UMAC and RGMII block when link up */ @@ -393,78 +340,6 @@ int bcmgenet_mii_probe(struct net_device *dev) return 0; } -/* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with - * their internal MDIO management controller making them fail to successfully - * be read from or written to for the first transaction. We insert a dummy - * BMSR read here to make sure that phy_get_device() and get_phy_id() can - * correctly read the PHY MII_PHYSID1/2 registers and successfully register a - * PHY device for this peripheral. - * - * Once the PHY driver is registered, we can workaround subsequent reads from - * there (e.g: during system-wide power management). - * - * bus->reset is invoked before mdiobus_scan during mdiobus_register and is - * therefore the right location to stick that workaround. Since we do not want - * to read from non-existing PHYs, we either use bus->phy_mask or do a manual - * Device Tree scan to limit the search area. - */ -static int bcmgenet_mii_bus_reset(struct mii_bus *bus) -{ - struct net_device *dev = bus->priv; - struct bcmgenet_priv *priv = netdev_priv(dev); - struct device_node *np = priv->mdio_dn; - struct device_node *child = NULL; - u32 read_mask = 0; - int addr = 0; - - if (!np) { - read_mask = 1 << priv->phy_addr; - } else { - for_each_available_child_of_node(np, child) { - addr = of_mdio_parse_addr(&dev->dev, child); - if (addr < 0) - continue; - - read_mask |= 1 << addr; - } - } - - for (addr = 0; addr < PHY_MAX_ADDR; addr++) { - if (read_mask & 1 << addr) { - dev_dbg(&dev->dev, "Workaround for PHY @ %d\n", addr); - mdiobus_read(bus, addr, MII_BMSR); - } - } - - return 0; -} - -static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv) -{ - struct mii_bus *bus; - - if (priv->mii_bus) - return 0; - - priv->mii_bus = mdiobus_alloc(); - if (!priv->mii_bus) { - pr_err("failed to allocate\n"); - return -ENOMEM; - } - - bus = priv->mii_bus; - bus->priv = priv->dev; - bus->name = "bcmgenet MII bus"; - bus->parent = &priv->pdev->dev; - bus->read = bcmgenet_mii_read; - bus->write = bcmgenet_mii_write; - bus->reset = bcmgenet_mii_bus_reset; - snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", - priv->pdev->name, priv->pdev->id); - - return 0; -} - static struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv) { struct device_node *dn = priv->pdev->dev.of_node; -- cgit v1.2.3 From 6f24b85e265a29f570f83c1c3694444ca0e07a56 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 31 Jul 2017 12:04:28 -0700 Subject: net: bcmgenet: Utilize bcmgenet_mii_exit() for error path bcmgenet_mii_init() has an error path which is strictly identical to the unwinding that bcmgenet_mii_exit() does, so have bcmgenet_mii_init() utilize bcmgenet_mii_exit() for that. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmmii.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 7fdc352628f9..33d3f60ac74b 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -563,7 +563,6 @@ static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv) int bcmgenet_mii_init(struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); - struct device_node *dn = priv->pdev->dev.of_node; int ret; ret = bcmgenet_mii_register(priv); @@ -577,11 +576,7 @@ int bcmgenet_mii_init(struct net_device *dev) return 0; out: - if (of_phy_is_fixed_link(dn)) - of_phy_deregister_fixed_link(dn); - of_node_put(priv->phy_dn); - platform_device_unregister(priv->mii_pdev); - platform_device_put(priv->mii_pdev); + bcmgenet_mii_exit(dev); return ret; } -- cgit v1.2.3 From 4bb51bd64f65acfb6bb63fea69fec8cbb39a33ad Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 31 Jul 2017 09:27:23 +0200 Subject: mlxsw: spectrum_acl: Fix a typo Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 01a1501b56ca..508b5fcacd77 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -369,7 +369,7 @@ int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp, local_port = mlxsw_sp_port->local_port; in_port = false; } else { - /* If out_dev is NULL, the called wants to + /* If out_dev is NULL, the caller wants to * set forward to ingress port. */ local_port = 0; -- cgit v1.2.3 From 806a1c1ab1b9bba06e690b306f4db5fab0ebf3dd Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 31 Jul 2017 09:27:24 +0200 Subject: mlxsw: reg.h: Fix a typo Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index c6c508941d23..b4ea8cbc1f7d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4718,7 +4718,7 @@ MLXSW_ITEM32(reg, ralue, prefix_len, 0x08, 0, 8); /* reg_ralue_dip* * The prefix of the route or of the marker that the object of the LPM * is compared with. The most significant bits of the dip are the prefix. - * The list significant bits must be '0' if the prefix_len is smaller + * The least significant bits must be '0' if the prefix_len is smaller * than 128 for IPv6 or smaller than 32 for IPv4. * IPv4 address uses bits dip[31:0] and bits dip[127:32] are reserved. * Access: Index -- cgit v1.2.3 From 8de3c17819ef5b3ee1c6e75f7ad98e90ff421063 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 31 Jul 2017 09:27:25 +0200 Subject: mlxsw: spectrum_router: Fix a typo Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 8bf076d22fb6..b6df3c3b5290 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1804,7 +1804,7 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp, return 0; /* Take a reference of neigh here ensuring that neigh would - * not be detructed before the nexthop entry is finished. + * not be destructed before the nexthop entry is finished. * The reference is taken either in neigh_lookup() or * in neigh_create() in case n is not found. */ -- cgit v1.2.3 From 78676ad4fb4e4f6345385e09a315707d7c355a52 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 31 Jul 2017 09:27:26 +0200 Subject: mlxsw: Update specification of reg_ritr_type The comments really belong to the individual enumerators. The comment at the register should instead reference the enum. Signed-off-by: Petr Machata Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index b4ea8cbc1f7d..a671438562fd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3992,16 +3992,16 @@ MLXSW_ITEM32(reg, ritr, ipv4, 0x00, 29, 1); MLXSW_ITEM32(reg, ritr, ipv6, 0x00, 28, 1); enum mlxsw_reg_ritr_if_type { + /* VLAN interface. */ MLXSW_REG_RITR_VLAN_IF, + /* FID interface. */ MLXSW_REG_RITR_FID_IF, + /* Sub-port interface. */ MLXSW_REG_RITR_SP_IF, }; /* reg_ritr_type - * Router interface type. - * 0 - VLAN interface. - * 1 - FID interface. - * 2 - Sub-port interface. + * Router interface type as per enum mlxsw_reg_ritr_if_type. * Access: RW */ MLXSW_ITEM32(reg, ritr, type, 0x00, 23, 3); -- cgit v1.2.3 From 83930cd76abc81c23e355a7df8b1c0646a55df43 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 31 Jul 2017 09:27:27 +0200 Subject: mlxsw: reg.h: Namespace IP2ME registers This renames IP2ME-specific registers reg_ralue_v and reg_ralue_tunnel_ptr to reg_ralue_ip2me_*. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index a671438562fd..7e8ba546c3a4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4813,7 +4813,7 @@ MLXSW_ITEM32(reg, ralue, ecmp_size, 0x28, 0, 13); */ MLXSW_ITEM32(reg, ralue, local_erif, 0x24, 0, 16); -/* reg_ralue_v +/* reg_ralue_ip2me_v * Valid bit for the tunnel_ptr field. * If valid = 0 then trap to CPU as IP2ME trap ID. * If valid = 1 and the packet format allows NVE or IPinIP tunnel @@ -4823,15 +4823,15 @@ MLXSW_ITEM32(reg, ralue, local_erif, 0x24, 0, 16); * Only relevant in case of IP2ME action. * Access: RW */ -MLXSW_ITEM32(reg, ralue, v, 0x24, 31, 1); +MLXSW_ITEM32(reg, ralue, ip2me_v, 0x24, 31, 1); -/* reg_ralue_tunnel_ptr +/* reg_ralue_ip2me_tunnel_ptr * Tunnel Pointer for NVE or IPinIP tunnel decapsulation. * For Spectrum, pointer to KVD Linear. * Only relevant in case of IP2ME action. * Access: RW */ -MLXSW_ITEM32(reg, ralue, tunnel_ptr, 0x24, 0, 24); +MLXSW_ITEM32(reg, ralue, ip2me_tunnel_ptr, 0x24, 0, 24); static inline void mlxsw_reg_ralue_pack(char *payload, enum mlxsw_reg_ralxx_protocol protocol, -- cgit v1.2.3 From f1b1f273aec0478c17ead69c5f285edd495d3d58 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 31 Jul 2017 09:27:28 +0200 Subject: mlxsw: spectrum_router: Simplify a piece of code Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index b6df3c3b5290..600268cf4fa1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3124,9 +3124,7 @@ mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev, switch (event) { case NETDEV_UP: - if (!rif) - return true; - return false; + return rif == NULL; case NETDEV_DOWN: idev = __in_dev_get_rtnl(dev); if (idev && idev->ifa_list) -- cgit v1.2.3 From 56b8a9ed276bc1771deb4efd5f409749ababc961 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 31 Jul 2017 09:27:29 +0200 Subject: mlxsw: spectrum_router: Clarify a piece of code Prefer logical operator that expresses the intent to bitwise one that happens to give the same result. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 600268cf4fa1..c69efbd955c5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1668,7 +1668,7 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, for (i = 0; i < nh_grp->count; i++) { nh = &nh_grp->nexthops[i]; - if (nh->should_offload ^ nh->offloaded) { + if (nh->should_offload != nh->offloaded) { offload_change = true; if (nh->should_offload) nh->update = 1; -- cgit v1.2.3 From 213666a3563f71b4a168b1fdc8c64115218e7758 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 31 Jul 2017 09:27:30 +0200 Subject: mlxsw: spectrum_router: Simplify a piece of code Express the same logic more succinctly. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index c69efbd955c5..802f5b8d8761 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1752,9 +1752,9 @@ set_trap: static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh, bool removing) { - if (!removing && !nh->should_offload) + if (!removing) nh->should_offload = 1; - else if (removing && nh->offloaded) + else if (nh->offloaded) nh->should_offload = 0; nh->update = 1; } -- cgit v1.2.3 From 6a95befc8d0346d6cb3b4646c761e8b42e66a4df Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Fri, 28 Jul 2017 13:18:30 +0200 Subject: net: phy: Log only PHY state transitions In the current code, old and new PHY states are always logged. >From now on, log only PHY state transitions. Signed-off-by: Marc Gonzalez Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ac1dcf0289fa..3aedf415908b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1225,9 +1225,10 @@ void phy_state_machine(struct work_struct *work) if (err < 0) phy_error(phydev); - phydev_dbg(phydev, "PHY state change %s -> %s\n", - phy_state_to_str(old_state), - phy_state_to_str(phydev->state)); + if (old_state != phydev->state) + phydev_dbg(phydev, "PHY state change %s -> %s\n", + phy_state_to_str(old_state), + phy_state_to_str(phydev->state)); /* Only re-schedule a PHY state machine change if we are polling the * PHY, if PHY_IGNORE_INTERRUPT is set, then we will be moving -- cgit v1.2.3 From 5af74bb4fcf8935cff38757400c25ae10eed4fd6 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 31 Jul 2017 17:53:07 -0700 Subject: net: bcmgenet: Add dependency on HAS_IOMEM && OF The driver needs CONFIG_HAS_IOMEM and OF to be functional, but we still let it build with COMPILE_TEST. This fixes the unmet dependency after selecting MDIO_BCM_UNIMAC in commit mentioned below: warning: (NET_DSA_BCM_SF2 && BCMGENET) selects MDIO_BCM_UNIMAC which has unmet direct dependencies (NETDEVICES && MDIO_DEVICE && HAS_IOMEM && OF_MDIO) Fixes: 9a4e79697009 ("net: bcmgenet: utilize generic Broadcom UniMAC MDIO controller driver") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index ec7a798c6bd1..45775399cab6 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -61,6 +61,7 @@ config BCM63XX_ENET config BCMGENET tristate "Broadcom GENET internal MAC support" + depends on (OF && HAS_IOMEM) || COMPILE_TEST select MII select PHYLIB select FIXED_PHY -- cgit v1.2.3 From 8cf8b87b73993980d703d71bd84bd8ab7c2898e4 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 30 Jul 2017 22:41:44 +0200 Subject: net: phy: marvell: tabification Convert spaces to tabs where appropriate, and fix up some otherwise odd indentation. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 5d314f143aea..6a5256ceb11e 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -108,24 +108,24 @@ #define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6) /* Copper Specific Interrupt Enable Register */ -#define MII_88E1318S_PHY_CSIER 0x12 +#define MII_88E1318S_PHY_CSIER 0x12 /* WOL Event Interrupt Enable */ -#define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7) +#define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7) /* LED Timer Control Register */ -#define MII_88E1318S_PHY_LED_TCR 0x12 -#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) -#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) -#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11) +#define MII_88E1318S_PHY_LED_TCR 0x12 +#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) +#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) +#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11) /* Magic Packet MAC address registers */ -#define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17 -#define MII_88E1318S_PHY_MAGIC_PACKET_WORD1 0x18 -#define MII_88E1318S_PHY_MAGIC_PACKET_WORD0 0x19 +#define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17 +#define MII_88E1318S_PHY_MAGIC_PACKET_WORD1 0x18 +#define MII_88E1318S_PHY_MAGIC_PACKET_WORD0 0x19 -#define MII_88E1318S_PHY_WOL_CTRL 0x10 -#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12) -#define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14) +#define MII_88E1318S_PHY_WOL_CTRL 0x10 +#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12) +#define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14) #define MII_88E1121_PHY_LED_CTRL 16 #define MII_88E1121_PHY_LED_DEF 0x0030 @@ -152,7 +152,7 @@ #define LPA_FIBER_1000HALF 0x40 #define LPA_FIBER_1000FULL 0x20 -#define LPA_PAUSE_FIBER 0x180 +#define LPA_PAUSE_FIBER 0x180 #define LPA_PAUSE_ASYM_FIBER 0x100 #define ADVERTISE_FIBER_1000HALF 0x40 @@ -596,7 +596,7 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev) if (changed == 0) { /* Advertisement hasn't changed, but maybe aneg was never on to - * begin with? Or maybe phy was isolated? + * begin with? Or maybe phy was isolated? */ int ctl = phy_read(phydev, MII_BMCR); @@ -1515,7 +1515,7 @@ static void marvell_get_strings(struct phy_device *phydev, u8 *data) } #ifndef UINT64_MAX -#define UINT64_MAX (u64)(~((u64)0)) +#define UINT64_MAX (u64)(~((u64)0)) #endif static u64 marvell_get_stat(struct phy_device *phydev, int i) { -- cgit v1.2.3 From 3438634456c40985bc12eafbd7aacb648ac2d454 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 30 Jul 2017 22:41:45 +0200 Subject: net: phy: marvell: Use core genphy_soft_reset() Rather than using an open coded equivalent, use the core genphy_soft_reset() function. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 47 ++++++++++++----------------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 6a5256ceb11e..33a52532fac6 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -292,17 +292,11 @@ static int marvell_config_aneg(struct phy_device *phydev) return err; if (phydev->autoneg != AUTONEG_ENABLE) { - int bmcr; - /* A write to speed/duplex bits (that is performed by * genphy_config_aneg() call above) must be followed by * a software reset. Otherwise, the write has no effect. */ - bmcr = phy_read(phydev, MII_BMCR); - if (bmcr < 0) - return bmcr; - - err = phy_write(phydev, MII_BMCR, bmcr | BMCR_RESET); + err = genphy_soft_reset(phydev); if (err < 0) return err; } @@ -318,8 +312,7 @@ static int m88e1101_config_aneg(struct phy_device *phydev) * that certain registers get written in order * to restart autonegotiation */ - err = phy_write(phydev, MII_BMCR, BMCR_RESET); - + err = genphy_soft_reset(phydev); if (err < 0) return err; @@ -354,7 +347,7 @@ static int m88e1111_config_aneg(struct phy_device *phydev) * that certain registers get written in order * to restart autonegotiation */ - err = phy_write(phydev, MII_BMCR, BMCR_RESET); + err = genphy_soft_reset(phydev); err = marvell_set_polarity(phydev, phydev->mdix_ctrl); if (err < 0) @@ -370,17 +363,11 @@ static int m88e1111_config_aneg(struct phy_device *phydev) return err; if (phydev->autoneg != AUTONEG_ENABLE) { - int bmcr; - /* A write to speed/duplex bits (that is performed by * genphy_config_aneg() call above) must be followed by * a software reset. Otherwise, the write has no effect. */ - bmcr = phy_read(phydev, MII_BMCR); - if (bmcr < 0) - return bmcr; - - err = phy_write(phydev, MII_BMCR, bmcr | BMCR_RESET); + err = genphy_soft_reset(phydev); if (err < 0) return err; } @@ -493,7 +480,7 @@ static int m88e1121_config_aneg(struct phy_device *phydev) marvell_set_page(phydev, oldpage); - err = phy_write(phydev, MII_BMCR, BMCR_RESET); + err = genphy_soft_reset(phydev); if (err < 0) return err; @@ -656,9 +643,7 @@ static int m88e1116r_config_init(struct phy_device *phydev) int temp; int err; - temp = phy_read(phydev, MII_BMCR); - temp |= BMCR_RESET; - err = phy_write(phydev, MII_BMCR, temp); + err = genphy_soft_reset(phydev); if (err < 0) return err; @@ -689,14 +674,10 @@ static int m88e1116r_config_init(struct phy_device *phydev) if (err < 0) return err; - temp = phy_read(phydev, MII_BMCR); - temp |= BMCR_RESET; - err = phy_write(phydev, MII_BMCR, temp); + err = genphy_soft_reset(phydev); if (err < 0) return err; - mdelay(500); - return marvell_config_init(phydev); } @@ -804,14 +785,10 @@ static int m88e1111_config_init_rtbi(struct phy_device *phydev) return err; /* soft reset */ - err = phy_write(phydev, MII_BMCR, BMCR_RESET); + err = genphy_soft_reset(phydev); if (err < 0) return err; - do - temp = phy_read(phydev, MII_BMCR); - while (temp & BMCR_RESET); - temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); if (temp < 0) return temp; @@ -850,7 +827,7 @@ static int m88e1111_config_init(struct phy_device *phydev) if (err < 0) return err; - return phy_write(phydev, MII_BMCR, BMCR_RESET); + return genphy_soft_reset(phydev); } static int m88e1121_config_init(struct phy_device *phydev) @@ -912,7 +889,7 @@ static int m88e1118_config_aneg(struct phy_device *phydev) { int err; - err = phy_write(phydev, MII_BMCR, BMCR_RESET); + err = genphy_soft_reset(phydev); if (err < 0) return err; @@ -961,7 +938,7 @@ static int m88e1118_config_init(struct phy_device *phydev) if (err < 0) return err; - return phy_write(phydev, MII_BMCR, BMCR_RESET); + return genphy_soft_reset(phydev); } static int m88e1149_config_init(struct phy_device *phydev) @@ -987,7 +964,7 @@ static int m88e1149_config_init(struct phy_device *phydev) if (err < 0) return err; - return phy_write(phydev, MII_BMCR, BMCR_RESET); + return genphy_soft_reset(phydev); } static int m88e1145_config_init_rgmii(struct phy_device *phydev) -- cgit v1.2.3 From 61111598b08bcdc84688de81c59605d56b197fbf Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 30 Jul 2017 22:41:46 +0200 Subject: net: phy: marvell: consolidate RGMII delay code The same code is repeated for different PHY versions. Put it into a help and call when needed. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 54 +++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 33a52532fac6..c1b724ab5f25 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -61,13 +61,6 @@ #define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 #define MII_M1145_PHY_EXT_SR 0x1b -#define MII_M1145_PHY_EXT_CR 0x14 -#define MII_M1145_RGMII_RX_DELAY 0x0080 -#define MII_M1145_RGMII_TX_DELAY 0x0002 -#define MII_M1145_HWCFG_MODE_SGMII_NO_CLK 0x4 -#define MII_M1145_HWCFG_MODE_MASK 0xf -#define MII_M1145_HWCFG_FIBER_COPPER_AUTO 0x8000 - #define MII_M1145_HWCFG_MODE_SGMII_NO_CLK 0x4 #define MII_M1145_HWCFG_MODE_MASK 0xf #define MII_M1145_HWCFG_FIBER_COPPER_AUTO 0x8000 @@ -76,8 +69,8 @@ #define MII_M1111_PHY_LED_DIRECT 0x4100 #define MII_M1111_PHY_LED_COMBINE 0x411c #define MII_M1111_PHY_EXT_CR 0x14 -#define MII_M1111_RX_DELAY 0x80 -#define MII_M1111_TX_DELAY 0x2 +#define MII_M1111_RGMII_RX_DELAY BIT(7) +#define MII_M1111_RGMII_TX_DELAY BIT(1) #define MII_M1111_PHY_EXT_SR 0x1b #define MII_M1111_HWCFG_MODE_MASK 0xf @@ -700,9 +693,8 @@ static int m88e3016_config_init(struct phy_device *phydev) return marvell_config_init(phydev); } -static int m88e1111_config_init_rgmii(struct phy_device *phydev) +static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev) { - int err; int temp; temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); @@ -710,16 +702,24 @@ static int m88e1111_config_init_rgmii(struct phy_device *phydev) return temp; if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { - temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); + temp |= (MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY); } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { - temp &= ~MII_M1111_TX_DELAY; - temp |= MII_M1111_RX_DELAY; + temp &= ~MII_M1111_RGMII_TX_DELAY; + temp |= MII_M1111_RGMII_RX_DELAY; } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { - temp &= ~MII_M1111_RX_DELAY; - temp |= MII_M1111_TX_DELAY; + temp &= ~MII_M1111_RGMII_RX_DELAY; + temp |= MII_M1111_RGMII_TX_DELAY; } - err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); + return phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); +} + +static int m88e1111_config_init_rgmii(struct phy_device *phydev) +{ + int temp; + int err; + + err = m88e1111_config_init_rgmii_delays(phydev); if (err < 0) return err; @@ -760,16 +760,11 @@ static int m88e1111_config_init_sgmii(struct phy_device *phydev) static int m88e1111_config_init_rtbi(struct phy_device *phydev) { - int err; int temp; + int err; - temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); - if (temp < 0) - return temp; - - temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); - err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); - if (err < 0) + err = m88e1111_config_init_rgmii_delays(phydev); + if (err) return err; temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); @@ -969,15 +964,10 @@ static int m88e1149_config_init(struct phy_device *phydev) static int m88e1145_config_init_rgmii(struct phy_device *phydev) { + int temp; int err; - int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR); - - if (temp < 0) - return temp; - - temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY); - err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp); + err = m88e1111_config_init_rgmii_delays(phydev); if (err < 0) return err; -- cgit v1.2.3 From 865b813aa273cd411066d05c47154d5d1490eac7 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 30 Jul 2017 22:41:47 +0200 Subject: net: phy: marvell: Consolidate setting the phy-mode The same code is repeated a few times. Refactor into a helped. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 88 +++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 48 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index c1b724ab5f25..275647ebaa81 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -60,11 +60,6 @@ #define MII_M1011_PHY_SCR_MDI_X 0x0020 #define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 -#define MII_M1145_PHY_EXT_SR 0x1b -#define MII_M1145_HWCFG_MODE_SGMII_NO_CLK 0x4 -#define MII_M1145_HWCFG_MODE_MASK 0xf -#define MII_M1145_HWCFG_FIBER_COPPER_AUTO 0x8000 - #define MII_M1111_PHY_LED_CONTROL 0x18 #define MII_M1111_PHY_LED_DIRECT 0x4100 #define MII_M1111_PHY_LED_COMBINE 0x411c @@ -74,12 +69,13 @@ #define MII_M1111_PHY_EXT_SR 0x1b #define MII_M1111_HWCFG_MODE_MASK 0xf -#define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb #define MII_M1111_HWCFG_MODE_FIBER_RGMII 0x3 #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4 +#define MII_M1111_HWCFG_MODE_RTBI 0x7 #define MII_M1111_HWCFG_MODE_COPPER_RTBI 0x9 -#define MII_M1111_HWCFG_FIBER_COPPER_AUTO 0x8000 -#define MII_M1111_HWCFG_FIBER_COPPER_RES 0x2000 +#define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb +#define MII_M1111_HWCFG_FIBER_COPPER_RES BIT(13) +#define MII_M1111_HWCFG_FIBER_COPPER_AUTO BIT(15) #define MII_88E1121_PHY_MSCR_REG 21 #define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5) @@ -693,6 +689,27 @@ static int m88e3016_config_init(struct phy_device *phydev) return marvell_config_init(phydev); } +static int m88e1111_config_init_hwcfg_mode(struct phy_device *phydev, + u16 mode, + int fibre_copper_auto) +{ + int temp; + + temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); + if (temp < 0) + return temp; + + temp &= ~(MII_M1111_HWCFG_MODE_MASK | + MII_M1111_HWCFG_FIBER_COPPER_AUTO | + MII_M1111_HWCFG_FIBER_COPPER_RES); + temp |= mode; + + if (fibre_copper_auto) + temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO; + + return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); +} + static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev) { int temp; @@ -740,17 +757,11 @@ static int m88e1111_config_init_rgmii(struct phy_device *phydev) static int m88e1111_config_init_sgmii(struct phy_device *phydev) { int err; - int temp; - temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); - if (temp < 0) - return temp; - - temp &= ~(MII_M1111_HWCFG_MODE_MASK); - temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK; - temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO; - - err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); + err = m88e1111_config_init_hwcfg_mode( + phydev, + MII_M1111_HWCFG_MODE_SGMII_NO_CLK, + MII_M1111_HWCFG_FIBER_COPPER_AUTO); if (err < 0) return err; @@ -760,22 +771,16 @@ static int m88e1111_config_init_sgmii(struct phy_device *phydev) static int m88e1111_config_init_rtbi(struct phy_device *phydev) { - int temp; int err; err = m88e1111_config_init_rgmii_delays(phydev); if (err) return err; - temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); - if (temp < 0) - return temp; - - temp &= ~(MII_M1111_HWCFG_MODE_MASK | - MII_M1111_HWCFG_FIBER_COPPER_RES); - temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO; - - err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); + err = m88e1111_config_init_hwcfg_mode( + phydev, + MII_M1111_HWCFG_MODE_RTBI, + MII_M1111_HWCFG_FIBER_COPPER_AUTO); if (err < 0) return err; @@ -784,16 +789,10 @@ static int m88e1111_config_init_rtbi(struct phy_device *phydev) if (err < 0) return err; - temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); - if (temp < 0) - return temp; - - temp &= ~(MII_M1111_HWCFG_MODE_MASK | - MII_M1111_HWCFG_FIBER_COPPER_RES); - temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI | - MII_M1111_HWCFG_FIBER_COPPER_AUTO; - - return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); + return m88e1111_config_init_hwcfg_mode( + phydev, + MII_M1111_HWCFG_MODE_RTBI, + MII_M1111_HWCFG_FIBER_COPPER_AUTO); } static int m88e1111_config_init(struct phy_device *phydev) @@ -999,16 +998,9 @@ static int m88e1145_config_init_rgmii(struct phy_device *phydev) static int m88e1145_config_init_sgmii(struct phy_device *phydev) { - int temp = phy_read(phydev, MII_M1145_PHY_EXT_SR); - - if (temp < 0) - return temp; - - temp &= ~MII_M1145_HWCFG_MODE_MASK; - temp |= MII_M1145_HWCFG_MODE_SGMII_NO_CLK; - temp |= MII_M1145_HWCFG_FIBER_COPPER_AUTO; - - return phy_write(phydev, MII_M1145_PHY_EXT_SR, temp); + return m88e1111_config_init_hwcfg_mode( + phydev, MII_M1111_HWCFG_MODE_SGMII_NO_CLK, + MII_M1111_HWCFG_FIBER_COPPER_AUTO); } static int m88e1145_config_init(struct phy_device *phydev) -- cgit v1.2.3 From 864dc729d528560e8d204267d66dae59972f462c Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 30 Jul 2017 22:41:48 +0200 Subject: net: phy: marvell: Refactor m88e1121 RGMII delay configuration Turns out that MII_M1116R_CONTROL_REG_MAC is the same as MII_88E1121_PHY_MSCR_REG. Refactor the code to set the RGMII delays into a shared helper. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 62 +++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 29 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 275647ebaa81..408442bdef0a 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -80,7 +80,7 @@ #define MII_88E1121_PHY_MSCR_REG 21 #define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5) #define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4) -#define MII_88E1121_PHY_MSCR_DELAY_MASK (~(0x3 << 4)) +#define MII_88E1121_PHY_MSCR_DELAY_MASK (~(BIT(5) || BIT(4))) #define MII_88E1121_MISC_TEST 0x1a #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK 0x1f00 @@ -127,8 +127,6 @@ #define MII_M1011_PHY_STATUS_RESOLVED 0x0800 #define MII_M1011_PHY_STATUS_LINK 0x0400 -#define MII_M1116R_CONTROL_REG_MAC 21 - #define MII_88E3016_PHY_SPEC_CTRL 0x10 #define MII_88E3016_DISABLE_SCRAMBLER 0x0200 #define MII_88E3016_AUTO_MDIX_CROSSOVER 0x0030 @@ -442,7 +440,7 @@ static int marvell_of_reg_init(struct phy_device *phydev) } #endif /* CONFIG_OF_MDIO */ -static int m88e1121_config_aneg(struct phy_device *phydev) +static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev) { int err, oldpage, mscr; @@ -450,25 +448,40 @@ static int m88e1121_config_aneg(struct phy_device *phydev) if (oldpage < 0) return oldpage; - if (phy_interface_is_rgmii(phydev)) { - mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG) & - MII_88E1121_PHY_MSCR_DELAY_MASK; - - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) - mscr |= (MII_88E1121_PHY_MSCR_RX_DELAY | - MII_88E1121_PHY_MSCR_TX_DELAY); - else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) - mscr |= MII_88E1121_PHY_MSCR_RX_DELAY; - else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) - mscr |= MII_88E1121_PHY_MSCR_TX_DELAY; - - err = phy_write(phydev, MII_88E1121_PHY_MSCR_REG, mscr); - if (err < 0) - return err; + mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG); + if (mscr < 0) { + err = mscr; + goto out; } + mscr &= MII_88E1121_PHY_MSCR_DELAY_MASK; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + mscr |= (MII_88E1121_PHY_MSCR_RX_DELAY | + MII_88E1121_PHY_MSCR_TX_DELAY); + else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + mscr |= MII_88E1121_PHY_MSCR_RX_DELAY; + else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + mscr |= MII_88E1121_PHY_MSCR_TX_DELAY; + + err = phy_write(phydev, MII_88E1121_PHY_MSCR_REG, mscr); + +out: marvell_set_page(phydev, oldpage); + return err; +} + +static int m88e1121_config_aneg(struct phy_device *phydev) +{ + int err = 0; + + if (phy_interface_is_rgmii(phydev)) { + err = m88e1121_config_aneg_rgmii_delays(phydev); + if (err) + return err; + } + err = genphy_soft_reset(phydev); if (err < 0) return err; @@ -650,16 +663,7 @@ static int m88e1116r_config_init(struct phy_device *phydev) if (err < 0) return err; - err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE); - if (err < 0) - return err; - temp = phy_read(phydev, MII_M1116R_CONTROL_REG_MAC); - temp |= (1 << 5); - temp |= (1 << 4); - err = phy_write(phydev, MII_M1116R_CONTROL_REG_MAC, temp); - if (err < 0) - return err; - err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); + err = m88e1121_config_aneg_rgmii_delays(phydev); if (err < 0) return err; -- cgit v1.2.3 From fecd5e910ebed27fa68c34bf7ecc300a12950897 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 30 Jul 2017 22:41:49 +0200 Subject: net: phy: marvell: Use the set_polarity helper Some of the init functions unilaterally enable set auto cross over without using the helper. Make use of the helper, and respect the phydev MDI configuration. Clean up the #define used while setting polarity, and the other functions of the bits in the register. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 408442bdef0a..34fd15b904e7 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -55,10 +55,12 @@ #define MII_M1011_IMASK_INIT 0x6400 #define MII_M1011_IMASK_CLEAR 0x0000 -#define MII_M1011_PHY_SCR 0x10 -#define MII_M1011_PHY_SCR_MDI 0x0000 -#define MII_M1011_PHY_SCR_MDI_X 0x0020 -#define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 +#define MII_M1011_PHY_SCR 0x10 +#define MII_M1011_PHY_SCR_DOWNSHIFT_EN BIT(11) +#define MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT 12 +#define MII_M1011_PHY_SCR_MDI (0x0 << 5) +#define MII_M1011_PHY_SCR_MDI_X (0x1 << 5) +#define MII_M1011_PHY_SCR_AUTO_CROSS (0x3 << 5) #define MII_M1111_PHY_LED_CONTROL 0x18 #define MII_M1111_PHY_LED_DIRECT 0x4100 @@ -486,8 +488,7 @@ static int m88e1121_config_aneg(struct phy_device *phydev) if (err < 0) return err; - err = phy_write(phydev, MII_M1011_PHY_SCR, - MII_M1011_PHY_SCR_AUTO_CROSS); + err = marvell_set_polarity(phydev, phydev->mdix_ctrl); if (err < 0) return err; @@ -655,10 +656,13 @@ static int m88e1116r_config_init(struct phy_device *phydev) if (err < 0) return err; + err = marvell_set_polarity(phydev, phydev->mdix_ctrl); + if (err < 0) + return err; + temp = phy_read(phydev, MII_M1011_PHY_SCR); - temp |= (7 << 12); /* max number of gigabit attempts */ - temp |= (1 << 11); /* enable downshift */ - temp |= MII_M1011_PHY_SCR_AUTO_CROSS; + temp |= (7 << MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT); + temp |= MII_M1011_PHY_SCR_DOWNSHIFT_EN; err = phy_write(phydev, MII_M1011_PHY_SCR, temp); if (err < 0) return err; @@ -891,8 +895,7 @@ static int m88e1118_config_aneg(struct phy_device *phydev) if (err < 0) return err; - err = phy_write(phydev, MII_M1011_PHY_SCR, - MII_M1011_PHY_SCR_AUTO_CROSS); + err = marvell_set_polarity(phydev, phydev->mdix_ctrl); if (err < 0) return err; -- cgit v1.2.3 From 6ef05eb73c8f623aec11449c9c763f19fb8a8fb1 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 30 Jul 2017 22:41:50 +0200 Subject: net: phy: marvell: Refactor setting downshift into a helper The 1116r has code to set downshift. Refactor this into a helper, so in future other marvell PHYs can use it. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 34fd15b904e7..361fe9927ef2 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -58,6 +58,7 @@ #define MII_M1011_PHY_SCR 0x10 #define MII_M1011_PHY_SCR_DOWNSHIFT_EN BIT(11) #define MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT 12 +#define MII_M1011_PHY_SRC_DOWNSHIFT_MASK 0x7800 #define MII_M1011_PHY_SCR_MDI (0x0 << 5) #define MII_M1011_PHY_SCR_MDI_X (0x1 << 5) #define MII_M1011_PHY_SCR_AUTO_CROSS (0x3 << 5) @@ -263,6 +264,23 @@ static int marvell_set_polarity(struct phy_device *phydev, int polarity) return 0; } +static int marvell_set_downshift(struct phy_device *phydev, bool enable, + u8 retries) +{ + int reg; + + reg = phy_read(phydev, MII_M1011_PHY_SCR); + if (reg < 0) + return reg; + + reg &= MII_M1011_PHY_SRC_DOWNSHIFT_MASK; + reg |= ((retries - 1) << MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT); + if (enable) + reg |= MII_M1011_PHY_SCR_DOWNSHIFT_EN; + + return phy_write(phydev, MII_M1011_PHY_SCR, reg); +} + static int marvell_config_aneg(struct phy_device *phydev) { int err; @@ -643,7 +661,6 @@ static int marvell_config_init(struct phy_device *phydev) static int m88e1116r_config_init(struct phy_device *phydev) { - int temp; int err; err = genphy_soft_reset(phydev); @@ -660,10 +677,7 @@ static int m88e1116r_config_init(struct phy_device *phydev) if (err < 0) return err; - temp = phy_read(phydev, MII_M1011_PHY_SCR); - temp |= (7 << MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT); - temp |= MII_M1011_PHY_SCR_DOWNSHIFT_EN; - err = phy_write(phydev, MII_M1011_PHY_SCR, temp); + err = marvell_set_downshift(phydev, true, 8); if (err < 0) return err; -- cgit v1.2.3 From 235acb1894321f91c771464d90c4010a9bef90bb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 1 Jun 2017 12:10:32 +0200 Subject: iwlwifi: refactor out paging code Refactor the paging code from mvm to be used by different opmodes. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/Makefile | 1 + drivers/net/wireless/intel/iwlwifi/fw/api.h | 43 +++ drivers/net/wireless/intel/iwlwifi/fw/paging.c | 417 ++++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 99 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 44 --- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 12 +- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 344 +------------------ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 12 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 + 9 files changed, 572 insertions(+), 402 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/paging.c create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/runtime.h (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index 20bd261223af..186a5b2fb6e3 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -11,6 +11,7 @@ iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/a000.o iwlwifi-objs += iwl-trans.o iwlwifi-objs += fw/notif-wait.o +iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o iwlwifi-objs += $(iwlwifi-m) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api.h b/drivers/net/wireless/intel/iwlwifi/fw/api.h index 0e107f916ce3..f9bcbaf87ce2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api.h @@ -226,4 +226,47 @@ struct iwl_phy_db_cmd { u8 data[]; } __packed; +#define NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */ + +/** + * struct iwl_fw_paging_cmd - paging layout + * + * Send to FW the paging layout in the driver. + * + * @flags: various flags for the command + * @block_size: the block size in powers of 2 + * @block_num: number of blocks specified in the command. + * @device_phy_addr: virtual addresses from device side + */ +struct iwl_fw_paging_cmd { + __le32 flags; + __le32 block_size; + __le32 block_num; + __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS]; +} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */ + +/** + * enum iwl_fw_item_id - FW item IDs + * + * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload + * download + */ +enum iwl_fw_item_id { + IWL_FW_ITEM_ID_PAGING = 3, +}; + +/** + * struct iwl_fw_get_item_cmd - get an item from the fw + * @item_id: ID of item to obtain, see &enum iwl_fw_item_id + */ +struct iwl_fw_get_item_cmd { + __le32 item_id; +} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */ + +struct iwl_fw_get_item_resp { + __le32 item_id; + __le32 item_byte_cnt; + __le32 item_val; +} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */ + #endif /* __iwl_fw_api_h__*/ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/paging.c b/drivers/net/wireless/intel/iwlwifi/fw/paging.c new file mode 100644 index 000000000000..2f4044922be3 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/paging.c @@ -0,0 +1,417 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include "iwl-drv.h" +#include "runtime.h" + +/* FIXME */ +#define FW_PAGING_BLOCK_CMD 0x4f +#define FW_GET_ITEM_CMD 0x1a + +void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt) +{ + int i; + + if (!fwrt->fw_paging_db[0].fw_paging_block) + return; + + for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) { + struct iwl_fw_paging *paging = &fwrt->fw_paging_db[i]; + + if (!paging->fw_paging_block) { + IWL_DEBUG_FW(fwrt, + "Paging: block %d already freed, continue to next page\n", + i); + + continue; + } + dma_unmap_page(fwrt->trans->dev, paging->fw_paging_phys, + paging->fw_paging_size, DMA_BIDIRECTIONAL); + + __free_pages(paging->fw_paging_block, + get_order(paging->fw_paging_size)); + paging->fw_paging_block = NULL; + } + kfree(fwrt->trans->paging_download_buf); + fwrt->trans->paging_download_buf = NULL; + fwrt->trans->paging_db = NULL; + + memset(fwrt->fw_paging_db, 0, sizeof(fwrt->fw_paging_db)); +} +IWL_EXPORT_SYMBOL(iwl_free_fw_paging); + +static int iwl_alloc_fw_paging_mem(struct iwl_fw_runtime *fwrt, + const struct fw_img *image) +{ + struct page *block; + dma_addr_t phys = 0; + int blk_idx, order, num_of_pages, size, dma_enabled; + + if (fwrt->fw_paging_db[0].fw_paging_block) + return 0; + + dma_enabled = is_device_dma_capable(fwrt->trans->dev); + + /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */ + BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE); + + num_of_pages = image->paging_mem_size / FW_PAGING_SIZE; + fwrt->num_of_paging_blk = + DIV_ROUND_UP(num_of_pages, NUM_OF_PAGE_PER_GROUP); + fwrt->num_of_pages_in_last_blk = + num_of_pages - + NUM_OF_PAGE_PER_GROUP * (fwrt->num_of_paging_blk - 1); + + IWL_DEBUG_FW(fwrt, + "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n", + fwrt->num_of_paging_blk, + fwrt->num_of_pages_in_last_blk); + + /* + * Allocate CSS and paging blocks in dram. + */ + for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) { + /* For CSS allocate 4KB, for others PAGING_BLOCK_SIZE (32K) */ + size = blk_idx ? PAGING_BLOCK_SIZE : FW_PAGING_SIZE; + order = get_order(size); + block = alloc_pages(GFP_KERNEL, order); + if (!block) { + /* free all the previous pages since we failed */ + iwl_free_fw_paging(fwrt); + return -ENOMEM; + } + + fwrt->fw_paging_db[blk_idx].fw_paging_block = block; + fwrt->fw_paging_db[blk_idx].fw_paging_size = size; + + if (dma_enabled) { + phys = dma_map_page(fwrt->trans->dev, block, 0, + PAGE_SIZE << order, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(fwrt->trans->dev, phys)) { + /* + * free the previous pages and the current one + * since we failed to map_page. + */ + iwl_free_fw_paging(fwrt); + return -ENOMEM; + } + fwrt->fw_paging_db[blk_idx].fw_paging_phys = phys; + } else { + fwrt->fw_paging_db[blk_idx].fw_paging_phys = + PAGING_ADDR_SIG | + blk_idx << BLOCK_2_EXP_SIZE; + } + + if (!blk_idx) + IWL_DEBUG_FW(fwrt, + "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n", + order); + else + IWL_DEBUG_FW(fwrt, + "Paging: allocated 32K bytes (order %d) for firmware paging.\n", + order); + } + + return 0; +} + +static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt, + const struct fw_img *image) +{ + int sec_idx, idx; + u32 offset = 0; + + /* + * find where is the paging image start point: + * if CPU2 exist and it's in paging format, then the image looks like: + * CPU1 sections (2 or more) + * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2 + * CPU2 sections (not paged) + * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2 + * non paged to CPU2 paging sec + * CPU2 paging CSS + * CPU2 paging image (including instruction and data) + */ + for (sec_idx = 0; sec_idx < image->num_sec; sec_idx++) { + if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) { + sec_idx++; + break; + } + } + + /* + * If paging is enabled there should be at least 2 more sections left + * (one for CSS and one for Paging data) + */ + if (sec_idx >= image->num_sec - 1) { + IWL_ERR(fwrt, "Paging: Missing CSS and/or paging sections\n"); + iwl_free_fw_paging(fwrt); + return -EINVAL; + } + + /* copy the CSS block to the dram */ + IWL_DEBUG_FW(fwrt, "Paging: load paging CSS to FW, sec = %d\n", + sec_idx); + + memcpy(page_address(fwrt->fw_paging_db[0].fw_paging_block), + image->sec[sec_idx].data, + fwrt->fw_paging_db[0].fw_paging_size); + dma_sync_single_for_device(fwrt->trans->dev, + fwrt->fw_paging_db[0].fw_paging_phys, + fwrt->fw_paging_db[0].fw_paging_size, + DMA_BIDIRECTIONAL); + + IWL_DEBUG_FW(fwrt, + "Paging: copied %d CSS bytes to first block\n", + fwrt->fw_paging_db[0].fw_paging_size); + + sec_idx++; + + /* + * copy the paging blocks to the dram + * loop index start from 1 since that CSS block already copied to dram + * and CSS index is 0. + * loop stop at num_of_paging_blk since that last block is not full. + */ + for (idx = 1; idx < fwrt->num_of_paging_blk; idx++) { + struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx]; + + memcpy(page_address(block->fw_paging_block), + image->sec[sec_idx].data + offset, + block->fw_paging_size); + dma_sync_single_for_device(fwrt->trans->dev, + block->fw_paging_phys, + block->fw_paging_size, + DMA_BIDIRECTIONAL); + + IWL_DEBUG_FW(fwrt, + "Paging: copied %d paging bytes to block %d\n", + fwrt->fw_paging_db[idx].fw_paging_size, + idx); + + offset += fwrt->fw_paging_db[idx].fw_paging_size; + } + + /* copy the last paging block */ + if (fwrt->num_of_pages_in_last_blk > 0) { + struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx]; + + memcpy(page_address(block->fw_paging_block), + image->sec[sec_idx].data + offset, + FW_PAGING_SIZE * fwrt->num_of_pages_in_last_blk); + dma_sync_single_for_device(fwrt->trans->dev, + block->fw_paging_phys, + block->fw_paging_size, + DMA_BIDIRECTIONAL); + + IWL_DEBUG_FW(fwrt, + "Paging: copied %d pages in the last block %d\n", + fwrt->num_of_pages_in_last_blk, idx); + } + + return 0; +} + +static int iwl_save_fw_paging(struct iwl_fw_runtime *fwrt, + const struct fw_img *fw) +{ + int ret; + + ret = iwl_alloc_fw_paging_mem(fwrt, fw); + if (ret) + return ret; + + return iwl_fill_paging_mem(fwrt, fw); +} + +/* send paging cmd to FW in case CPU2 has paging image */ +static int iwl_send_paging_cmd(struct iwl_fw_runtime *fwrt, + const struct fw_img *fw) +{ + struct iwl_fw_paging_cmd paging_cmd = { + .flags = cpu_to_le32(PAGING_CMD_IS_SECURED | + PAGING_CMD_IS_ENABLED | + (fwrt->num_of_pages_in_last_blk << + PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)), + .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE), + .block_num = cpu_to_le32(fwrt->num_of_paging_blk), + }; + struct iwl_host_cmd hcmd = { + .id = iwl_cmd_id(FW_PAGING_BLOCK_CMD, IWL_ALWAYS_LONG_GROUP, 0), + .len = { sizeof(paging_cmd), }, + .data = { &paging_cmd, }, + }; + int blk_idx; + + /* loop for for all paging blocks + CSS block */ + for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) { + dma_addr_t addr = fwrt->fw_paging_db[blk_idx].fw_paging_phys; + __le32 phy_addr; + + addr = addr >> PAGE_2_EXP_SIZE; + phy_addr = cpu_to_le32(addr); + paging_cmd.device_phy_addr[blk_idx] = phy_addr; + } + + return iwl_trans_send_cmd(fwrt->trans, &hcmd); +} + +/* + * Send paging item cmd to FW in case CPU2 has paging image + */ +static int iwl_trans_get_paging_item(struct iwl_fw_runtime *fwrt) +{ + int ret; + struct iwl_fw_get_item_cmd fw_get_item_cmd = { + .item_id = cpu_to_le32(IWL_FW_ITEM_ID_PAGING), + }; + struct iwl_fw_get_item_resp *item_resp; + struct iwl_host_cmd cmd = { + .id = iwl_cmd_id(FW_GET_ITEM_CMD, IWL_ALWAYS_LONG_GROUP, 0), + .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, + .data = { &fw_get_item_cmd, }, + .len = { sizeof(fw_get_item_cmd), }, + }; + + ret = iwl_trans_send_cmd(fwrt->trans, &cmd); + if (ret) { + IWL_ERR(fwrt, + "Paging: Failed to send FW_GET_ITEM_CMD cmd (err = %d)\n", + ret); + return ret; + } + + item_resp = (void *)((struct iwl_rx_packet *)cmd.resp_pkt)->data; + if (item_resp->item_id != cpu_to_le32(IWL_FW_ITEM_ID_PAGING)) { + IWL_ERR(fwrt, + "Paging: got wrong item in FW_GET_ITEM_CMD resp (item_id = %u)\n", + le32_to_cpu(item_resp->item_id)); + ret = -EIO; + goto exit; + } + + /* Add an extra page for headers */ + fwrt->trans->paging_download_buf = kzalloc(PAGING_BLOCK_SIZE + + FW_PAGING_SIZE, + GFP_KERNEL); + if (!fwrt->trans->paging_download_buf) { + ret = -ENOMEM; + goto exit; + } + fwrt->trans->paging_req_addr = le32_to_cpu(item_resp->item_val); + fwrt->trans->paging_db = fwrt->fw_paging_db; + IWL_DEBUG_FW(fwrt, + "Paging: got paging request address (paging_req_addr 0x%08x)\n", + fwrt->trans->paging_req_addr); + +exit: + iwl_free_resp(&cmd); + + return ret; +} + +int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type) +{ + const struct fw_img *fw = &fwrt->fw->img[type]; + int ret; + + if (fwrt->trans->cfg->gen2) + return 0; + + /* + * Configure and operate fw paging mechanism. + * The driver configures the paging flow only once. + * The CPU2 paging image is included in the IWL_UCODE_INIT image. + */ + if (!fw->paging_mem_size) + return 0; + + /* + * When dma is not enabled, the driver needs to copy / write + * the downloaded / uploaded page to / from the smem. + * This gets the location of the place were the pages are + * stored. + */ + if (!is_device_dma_capable(fwrt->trans->dev)) { + ret = iwl_trans_get_paging_item(fwrt); + if (ret) { + IWL_ERR(fwrt, "failed to get FW paging item\n"); + return ret; + } + } + + ret = iwl_save_fw_paging(fwrt, fw); + if (ret) { + IWL_ERR(fwrt, "failed to save the FW paging image\n"); + return ret; + } + + ret = iwl_send_paging_cmd(fwrt, fw); + if (ret) { + IWL_ERR(fwrt, "failed to send the paging cmd\n"); + iwl_free_fw_paging(fwrt); + return ret; + } + + return 0; +} +IWL_EXPORT_SYMBOL(iwl_init_paging); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h new file mode 100644 index 000000000000..c5d564305d30 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -0,0 +1,99 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_fw_runtime_h__ +#define __iwl_fw_runtime_h__ + +#include "iwl-config.h" +#include "iwl-trans.h" +#include "img.h" +#include "api.h" + +/** + * struct iwl_fw_runtime - runtime data for firmware + * @fw: firmware image + * @cfg: NIC configuration + * @dev: device pointer + * @fw_paging_db: paging database + * @num_of_paging_blk: number of paging blocks + * @num_of_pages_in_last_blk: number of pages in the last block + */ +struct iwl_fw_runtime { + struct iwl_trans *trans; + const struct iwl_fw *fw; + struct device *dev; + + /* Paging */ + struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS]; + u16 num_of_paging_blk; + u16 num_of_pages_in_last_blk; +}; + +static inline void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, + struct iwl_trans *trans, + const struct iwl_fw *fw) +{ + memset(fwrt, 0, sizeof(*fwrt)); + fwrt->trans = trans; + fwrt->fw = fw; + fwrt->dev = trans->dev; +} + +int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type); +void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt); + +#endif /* __iwl_fw_runtime_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index aad265dcfaf5..e1a29cde92d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -1000,44 +1000,6 @@ struct iwl_nvm_access_cmd { u8 data[]; } __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */ -#define NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */ - -/** - * struct iwl_fw_paging_cmd - paging layout - * - * (FW_PAGING_BLOCK_CMD = 0x4f) - * - * Send to FW the paging layout in the driver. - * - * @flags: various flags for the command - * @block_size: the block size in powers of 2 - * @block_num: number of blocks specified in the command. - * @device_phy_addr: virtual addresses from device side - */ -struct iwl_fw_paging_cmd { - __le32 flags; - __le32 block_size; - __le32 block_num; - __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS]; -} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */ - -/* - * Fw items ID's - * - * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload - * download - */ -enum iwl_fw_item_id { - IWL_FW_ITEM_ID_PAGING = 3, -}; - -/* - * struct iwl_fw_get_item_cmd - get an item from the fw - */ -struct iwl_fw_get_item_cmd { - __le32 item_id; -} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */ - #define CONT_REC_COMMAND_SIZE 80 #define ENABLE_CONT_RECORDING 0x15 #define DISABLE_CONT_RECORDING 0x16 @@ -1058,12 +1020,6 @@ struct iwl_continuous_record_cmd { sizeof(struct iwl_continuous_record_mode)]; } __packed; -struct iwl_fw_get_item_resp { - __le32 item_id; - __le32 item_byte_cnt; - __le32 item_val; -} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */ - /** * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD * @offset: offset in bytes into the section diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 1602b360353c..ba32753fda5a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -694,8 +694,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* Make room for fw's virtual image pages, if it exists */ if (!mvm->trans->cfg->gen2 && mvm->fw->img[mvm->cur_ucode].paging_mem_size && - mvm->fw_paging_db[0].fw_paging_block) - file_len += mvm->num_of_paging_blk * + mvm->fwrt.fw_paging_db[0].fw_paging_block) + file_len += mvm->fwrt.num_of_paging_blk * (sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_paging) + PAGING_BLOCK_SIZE); @@ -833,12 +833,12 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* Dump fw's virtual image */ if (!mvm->trans->cfg->gen2 && mvm->fw->img[mvm->cur_ucode].paging_mem_size && - mvm->fw_paging_db[0].fw_paging_block) { - for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { + mvm->fwrt.fw_paging_db[0].fw_paging_block) { + for (i = 1; i < mvm->fwrt.num_of_paging_blk + 1; i++) { struct iwl_fw_error_dump_paging *paging; struct page *pages = - mvm->fw_paging_db[i].fw_paging_block; - dma_addr_t addr = mvm->fw_paging_db[i].fw_paging_phys; + mvm->fwrt.fw_paging_db[i].fw_paging_block; + dma_addr_t addr = mvm->fwrt.fw_paging_db[i].fw_paging_phys; dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); dump_data->len = cpu_to_le32(sizeof(*paging) + diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 79e7a7a285dc..47715eec22e6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -144,134 +144,6 @@ static int iwl_mvm_send_dqa_cmd(struct iwl_mvm *mvm) return ret; } -void iwl_free_fw_paging(struct iwl_mvm *mvm) -{ - int i; - - if (!mvm->fw_paging_db[0].fw_paging_block) - return; - - for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) { - struct iwl_fw_paging *paging = &mvm->fw_paging_db[i]; - - if (!paging->fw_paging_block) { - IWL_DEBUG_FW(mvm, - "Paging: block %d already freed, continue to next page\n", - i); - - continue; - } - dma_unmap_page(mvm->trans->dev, paging->fw_paging_phys, - paging->fw_paging_size, DMA_BIDIRECTIONAL); - - __free_pages(paging->fw_paging_block, - get_order(paging->fw_paging_size)); - paging->fw_paging_block = NULL; - } - kfree(mvm->trans->paging_download_buf); - mvm->trans->paging_download_buf = NULL; - mvm->trans->paging_db = NULL; - - memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db)); -} - -static int iwl_fill_paging_mem(struct iwl_mvm *mvm, const struct fw_img *image) -{ - int sec_idx, idx; - u32 offset = 0; - - /* - * find where is the paging image start point: - * if CPU2 exist and it's in paging format, then the image looks like: - * CPU1 sections (2 or more) - * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2 - * CPU2 sections (not paged) - * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2 - * non paged to CPU2 paging sec - * CPU2 paging CSS - * CPU2 paging image (including instruction and data) - */ - for (sec_idx = 0; sec_idx < image->num_sec; sec_idx++) { - if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) { - sec_idx++; - break; - } - } - - /* - * If paging is enabled there should be at least 2 more sections left - * (one for CSS and one for Paging data) - */ - if (sec_idx >= image->num_sec - 1) { - IWL_ERR(mvm, "Paging: Missing CSS and/or paging sections\n"); - iwl_free_fw_paging(mvm); - return -EINVAL; - } - - /* copy the CSS block to the dram */ - IWL_DEBUG_FW(mvm, "Paging: load paging CSS to FW, sec = %d\n", - sec_idx); - - memcpy(page_address(mvm->fw_paging_db[0].fw_paging_block), - image->sec[sec_idx].data, - mvm->fw_paging_db[0].fw_paging_size); - dma_sync_single_for_device(mvm->trans->dev, - mvm->fw_paging_db[0].fw_paging_phys, - mvm->fw_paging_db[0].fw_paging_size, - DMA_BIDIRECTIONAL); - - IWL_DEBUG_FW(mvm, - "Paging: copied %d CSS bytes to first block\n", - mvm->fw_paging_db[0].fw_paging_size); - - sec_idx++; - - /* - * copy the paging blocks to the dram - * loop index start from 1 since that CSS block already copied to dram - * and CSS index is 0. - * loop stop at num_of_paging_blk since that last block is not full. - */ - for (idx = 1; idx < mvm->num_of_paging_blk; idx++) { - struct iwl_fw_paging *block = &mvm->fw_paging_db[idx]; - - memcpy(page_address(block->fw_paging_block), - image->sec[sec_idx].data + offset, - block->fw_paging_size); - dma_sync_single_for_device(mvm->trans->dev, - block->fw_paging_phys, - block->fw_paging_size, - DMA_BIDIRECTIONAL); - - - IWL_DEBUG_FW(mvm, - "Paging: copied %d paging bytes to block %d\n", - mvm->fw_paging_db[idx].fw_paging_size, - idx); - - offset += mvm->fw_paging_db[idx].fw_paging_size; - } - - /* copy the last paging block */ - if (mvm->num_of_pages_in_last_blk > 0) { - struct iwl_fw_paging *block = &mvm->fw_paging_db[idx]; - - memcpy(page_address(block->fw_paging_block), - image->sec[sec_idx].data + offset, - FW_PAGING_SIZE * mvm->num_of_pages_in_last_blk); - dma_sync_single_for_device(mvm->trans->dev, - block->fw_paging_phys, - block->fw_paging_size, - DMA_BIDIRECTIONAL); - - IWL_DEBUG_FW(mvm, - "Paging: copied %d pages in the last block %d\n", - mvm->num_of_pages_in_last_blk, idx); - } - - return 0; -} - void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { @@ -293,178 +165,6 @@ void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm, le32_to_cpu(dump_data[i])); } -static int iwl_alloc_fw_paging_mem(struct iwl_mvm *mvm, - const struct fw_img *image) -{ - struct page *block; - dma_addr_t phys = 0; - int blk_idx, order, num_of_pages, size, dma_enabled; - - if (mvm->fw_paging_db[0].fw_paging_block) - return 0; - - dma_enabled = is_device_dma_capable(mvm->trans->dev); - - /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */ - BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE); - - num_of_pages = image->paging_mem_size / FW_PAGING_SIZE; - mvm->num_of_paging_blk = - DIV_ROUND_UP(num_of_pages, NUM_OF_PAGE_PER_GROUP); - mvm->num_of_pages_in_last_blk = - num_of_pages - - NUM_OF_PAGE_PER_GROUP * (mvm->num_of_paging_blk - 1); - - IWL_DEBUG_FW(mvm, - "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n", - mvm->num_of_paging_blk, - mvm->num_of_pages_in_last_blk); - - /* - * Allocate CSS and paging blocks in dram. - */ - for (blk_idx = 0; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) { - /* For CSS allocate 4KB, for others PAGING_BLOCK_SIZE (32K) */ - size = blk_idx ? PAGING_BLOCK_SIZE : FW_PAGING_SIZE; - order = get_order(size); - block = alloc_pages(GFP_KERNEL, order); - if (!block) { - /* free all the previous pages since we failed */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - - mvm->fw_paging_db[blk_idx].fw_paging_block = block; - mvm->fw_paging_db[blk_idx].fw_paging_size = size; - - if (dma_enabled) { - phys = dma_map_page(mvm->trans->dev, block, 0, - PAGE_SIZE << order, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(mvm->trans->dev, phys)) { - /* - * free the previous pages and the current one - * since we failed to map_page. - */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - mvm->fw_paging_db[blk_idx].fw_paging_phys = phys; - } else { - mvm->fw_paging_db[blk_idx].fw_paging_phys = - PAGING_ADDR_SIG | - blk_idx << BLOCK_2_EXP_SIZE; - } - - if (!blk_idx) - IWL_DEBUG_FW(mvm, - "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n", - order); - else - IWL_DEBUG_FW(mvm, - "Paging: allocated 32K bytes (order %d) for firmware paging.\n", - order); - } - - return 0; -} - -static int iwl_save_fw_paging(struct iwl_mvm *mvm, - const struct fw_img *fw) -{ - int ret; - - ret = iwl_alloc_fw_paging_mem(mvm, fw); - if (ret) - return ret; - - return iwl_fill_paging_mem(mvm, fw); -} - -/* send paging cmd to FW in case CPU2 has paging image */ -static int iwl_send_paging_cmd(struct iwl_mvm *mvm, const struct fw_img *fw) -{ - struct iwl_fw_paging_cmd paging_cmd = { - .flags = cpu_to_le32(PAGING_CMD_IS_SECURED | - PAGING_CMD_IS_ENABLED | - (mvm->num_of_pages_in_last_blk << - PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)), - .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE), - .block_num = cpu_to_le32(mvm->num_of_paging_blk), - }; - int blk_idx; - - /* loop for for all paging blocks + CSS block */ - for (blk_idx = 0; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) { - dma_addr_t addr = mvm->fw_paging_db[blk_idx].fw_paging_phys; - __le32 phy_addr; - - addr = addr >> PAGE_2_EXP_SIZE; - phy_addr = cpu_to_le32(addr); - paging_cmd.device_phy_addr[blk_idx] = phy_addr; - } - - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(FW_PAGING_BLOCK_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(paging_cmd), &paging_cmd); -} - -/* - * Send paging item cmd to FW in case CPU2 has paging image - */ -static int iwl_trans_get_paging_item(struct iwl_mvm *mvm) -{ - int ret; - struct iwl_fw_get_item_cmd fw_get_item_cmd = { - .item_id = cpu_to_le32(IWL_FW_ITEM_ID_PAGING), - }; - - struct iwl_fw_get_item_resp *item_resp; - struct iwl_host_cmd cmd = { - .id = iwl_cmd_id(FW_GET_ITEM_CMD, IWL_ALWAYS_LONG_GROUP, 0), - .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, - .data = { &fw_get_item_cmd, }, - }; - - cmd.len[0] = sizeof(struct iwl_fw_get_item_cmd); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) { - IWL_ERR(mvm, - "Paging: Failed to send FW_GET_ITEM_CMD cmd (err = %d)\n", - ret); - return ret; - } - - item_resp = (void *)((struct iwl_rx_packet *)cmd.resp_pkt)->data; - if (item_resp->item_id != cpu_to_le32(IWL_FW_ITEM_ID_PAGING)) { - IWL_ERR(mvm, - "Paging: got wrong item in FW_GET_ITEM_CMD resp (item_id = %u)\n", - le32_to_cpu(item_resp->item_id)); - ret = -EIO; - goto exit; - } - - /* Add an extra page for headers */ - mvm->trans->paging_download_buf = kzalloc(PAGING_BLOCK_SIZE + - FW_PAGING_SIZE, - GFP_KERNEL); - if (!mvm->trans->paging_download_buf) { - ret = -ENOMEM; - goto exit; - } - mvm->trans->paging_req_addr = le32_to_cpu(item_resp->item_val); - mvm->trans->paging_db = mvm->fw_paging_db; - IWL_DEBUG_FW(mvm, - "Paging: got paging request address (paging_req_addr 0x%08x)\n", - mvm->trans->paging_req_addr); - -exit: - iwl_free_resp(&cmd); - - return ret; -} - static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt, void *data) { @@ -544,48 +244,6 @@ static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait, return false; } -static int iwl_mvm_init_paging(struct iwl_mvm *mvm) -{ - const struct fw_img *fw = &mvm->fw->img[mvm->cur_ucode]; - int ret; - - /* - * Configure and operate fw paging mechanism. - * The driver configures the paging flow only once. - * The CPU2 paging image is included in the IWL_UCODE_INIT image. - */ - if (!fw->paging_mem_size) - return 0; - - /* - * When dma is not enabled, the driver needs to copy / write - * the downloaded / uploaded page to / from the smem. - * This gets the location of the place were the pages are - * stored. - */ - if (!is_device_dma_capable(mvm->trans->dev)) { - ret = iwl_trans_get_paging_item(mvm); - if (ret) { - IWL_ERR(mvm, "failed to get FW paging item\n"); - return ret; - } - } - - ret = iwl_save_fw_paging(mvm, fw); - if (ret) { - IWL_ERR(mvm, "failed to save the FW paging image\n"); - return ret; - } - - ret = iwl_send_paging_cmd(mvm, fw); - if (ret) { - IWL_ERR(mvm, "failed to send the paging cmd\n"); - iwl_free_fw_paging(mvm); - return ret; - } - - return 0; -} static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type) { @@ -1495,7 +1153,7 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) if (ret) return ret; - return iwl_mvm_init_paging(mvm); + return iwl_init_paging(&mvm->fwrt, mvm->cur_ucode); } int iwl_mvm_up(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index eaacfaf37206..f8fe15cd80c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -87,6 +87,7 @@ #include "fw-api.h" #include "constants.h" #include "tof.h" +#include "fw/runtime.h" #define IWL_MVM_MAX_ADDRESSES 5 /* RSSI offset for WkP */ @@ -815,10 +816,7 @@ struct iwl_mvm { /* NVM sections */ struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS]; - /* Paging section */ - struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS]; - u16 num_of_paging_blk; - u16 num_of_pages_in_last_blk; + struct iwl_fw_runtime fwrt; /* EEPROM MAC addresses */ struct mac_address addresses[IWL_MVM_MAX_ADDRESSES]; @@ -1571,9 +1569,6 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -/* Paging */ -void iwl_free_fw_paging(struct iwl_mvm *mvm); - /* MVM debugfs */ #ifdef CONFIG_IWLWIFI_DEBUGFS int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir); @@ -1798,8 +1793,7 @@ void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm) { - if (!iwl_mvm_has_new_tx_api(mvm)) - iwl_free_fw_paging(mvm); + iwl_free_fw_paging(&mvm->fwrt); clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); mvm->fw_dbg_conf = FW_DBG_INVALID; iwl_trans_stop_device(mvm->trans); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 4d1188b8736a..8c49ac903c8c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -580,6 +580,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->fw = fw; mvm->hw = hw; + iwl_fw_runtime_init(&mvm->fwrt, trans, fw); + mvm->init_status = 0; if (iwl_mvm_has_new_rx_api(mvm)) { -- cgit v1.2.3 From d0b813fcdc22d091f8c9eee3a1484dca162667c8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 1 Jun 2017 14:44:40 +0200 Subject: iwlwifi: refactor shared mem parsing Refactor the shared memory command parsing into common code. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/fw/api.h | 87 +++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 19 +++ drivers/net/wireless/intel/iwlwifi/fw/smem.c | 156 ++++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 87 ------------- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 18 +-- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 91 +------------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 14 --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 3 +- 9 files changed, 275 insertions(+), 202 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/smem.c (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index 186a5b2fb6e3..ddc2bfb501c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -11,7 +11,7 @@ iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/a000.o iwlwifi-objs += iwl-trans.o iwlwifi-objs += fw/notif-wait.o -iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o +iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o iwlwifi-objs += $(iwlwifi-m) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api.h b/drivers/net/wireless/intel/iwlwifi/fw/api.h index f9bcbaf87ce2..afd333e57790 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api.h @@ -269,4 +269,91 @@ struct iwl_fw_get_item_resp { __le32 item_val; } __packed; /* FW_GET_ITEM_RSP_S_VER_1 */ +#define TX_FIFO_MAX_NUM_9000 8 +#define TX_FIFO_MAX_NUM 15 +#define RX_FIFO_MAX_NUM 2 +#define TX_FIFO_INTERNAL_MAX_NUM 6 + +/** + * struct iwl_shared_mem_cfg_v2 - Shared memory configuration information + * + * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not + * accessible) + * @shared_mem_size: shared memory size + * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to + * 0x0 as accessible only via DBGM RDAT) + * @sample_buff_size: internal sample buff size + * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre + * 8000 HW set to 0x0 as not accessible) + * @txfifo_size: size of TXF0 ... TXF7 + * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0 + * @page_buff_addr: used by UMAC and performance debug (page miss analysis), + * when paging is not supported this should be 0 + * @page_buff_size: size of %page_buff_addr + * @rxfifo_addr: Start address of rxFifo + * @internal_txfifo_addr: start address of internalFifo + * @internal_txfifo_size: internal fifos' size + * + * NOTE: on firmware that don't have IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG + * set, the last 3 members don't exist. + */ +struct iwl_shared_mem_cfg_v2 { + __le32 shared_mem_addr; + __le32 shared_mem_size; + __le32 sample_buff_addr; + __le32 sample_buff_size; + __le32 txfifo_addr; + __le32 txfifo_size[TX_FIFO_MAX_NUM_9000]; + __le32 rxfifo_size[RX_FIFO_MAX_NUM]; + __le32 page_buff_addr; + __le32 page_buff_size; + __le32 rxfifo_addr; + __le32 internal_txfifo_addr; + __le32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; +} __packed; /* SHARED_MEM_ALLOC_API_S_VER_2 */ + +/** + * struct iwl_shared_mem_lmac_cfg - LMAC shared memory configuration + * + * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB) + * @txfifo_size: size of TX FIFOs + * @rxfifo1_addr: RXF1 addr + * @rxfifo1_size: RXF1 size + */ +struct iwl_shared_mem_lmac_cfg { + __le32 txfifo_addr; + __le32 txfifo_size[TX_FIFO_MAX_NUM]; + __le32 rxfifo1_addr; + __le32 rxfifo1_size; + +} __packed; /* SHARED_MEM_ALLOC_LMAC_API_S_VER_1 */ + +/** + * struct iwl_shared_mem_cfg - Shared memory configuration information + * + * @shared_mem_addr: shared memory address + * @shared_mem_size: shared memory size + * @sample_buff_addr: internal sample (mon/adc) buff addr + * @sample_buff_size: internal sample buff size + * @rxfifo2_addr: start addr of RXF2 + * @rxfifo2_size: size of RXF2 + * @page_buff_addr: used by UMAC and performance debug (page miss analysis), + * when paging is not supported this should be 0 + * @page_buff_size: size of %page_buff_addr + * @lmac_num: number of LMACs (1 or 2) + * @lmac_smem: per - LMAC smem data + */ +struct iwl_shared_mem_cfg { + __le32 shared_mem_addr; + __le32 shared_mem_size; + __le32 sample_buff_addr; + __le32 sample_buff_size; + __le32 rxfifo2_addr; + __le32 rxfifo2_size; + __le32 page_buff_addr; + __le32 page_buff_size; + __le32 lmac_num; + struct iwl_shared_mem_lmac_cfg lmac_smem[2]; +} __packed; /* SHARED_MEM_ALLOC_API_S_VER_3 */ + #endif /* __iwl_fw_api_h__*/ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index c5d564305d30..185902ce7e13 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -63,6 +63,19 @@ #include "img.h" #include "api.h" +#define MAX_NUM_LMAC 2 +struct iwl_fwrt_shared_mem_cfg { + int num_lmacs; + int num_txfifo_entries; + struct { + u32 txfifo_size[TX_FIFO_MAX_NUM]; + u32 rxfifo1_size; + } lmac[MAX_NUM_LMAC]; + u32 rxfifo2_size; + u32 internal_txfifo_addr; + u32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; +}; + /** * struct iwl_fw_runtime - runtime data for firmware * @fw: firmware image @@ -71,6 +84,7 @@ * @fw_paging_db: paging database * @num_of_paging_blk: number of paging blocks * @num_of_pages_in_last_blk: number of pages in the last block + * @smem_cfg: saved firmware SMEM configuration */ struct iwl_fw_runtime { struct iwl_trans *trans; @@ -81,6 +95,9 @@ struct iwl_fw_runtime { struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS]; u16 num_of_paging_blk; u16 num_of_pages_in_last_blk; + + /* memory configuration */ + struct iwl_fwrt_shared_mem_cfg smem_cfg; }; static inline void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, @@ -96,4 +113,6 @@ static inline void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type); void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt); +void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt); + #endif /* __iwl_fw_runtime_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c new file mode 100644 index 000000000000..053993bf00f9 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c @@ -0,0 +1,156 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include "iwl-drv.h" +#include "runtime.h" + +/* FIXME */ +#define SHARED_MEM_CFG_CMD 0x00 +#define SYSTEM_GROUP 0x2 +#define SHARED_MEM_CFG 0x25 + +static void iwl_parse_shared_mem_a000(struct iwl_fw_runtime *fwrt, + struct iwl_rx_packet *pkt) +{ + struct iwl_shared_mem_cfg *mem_cfg = (void *)pkt->data; + int i, lmac; + int lmac_num = le32_to_cpu(mem_cfg->lmac_num); + + if (WARN_ON(lmac_num > ARRAY_SIZE(mem_cfg->lmac_smem))) + return; + + fwrt->smem_cfg.num_lmacs = lmac_num; + fwrt->smem_cfg.num_txfifo_entries = + ARRAY_SIZE(mem_cfg->lmac_smem[0].txfifo_size); + fwrt->smem_cfg.rxfifo2_size = le32_to_cpu(mem_cfg->rxfifo2_size); + + for (lmac = 0; lmac < lmac_num; lmac++) { + struct iwl_shared_mem_lmac_cfg *lmac_cfg = + &mem_cfg->lmac_smem[lmac]; + + for (i = 0; i < ARRAY_SIZE(lmac_cfg->txfifo_size); i++) + fwrt->smem_cfg.lmac[lmac].txfifo_size[i] = + le32_to_cpu(lmac_cfg->txfifo_size[i]); + fwrt->smem_cfg.lmac[lmac].rxfifo1_size = + le32_to_cpu(lmac_cfg->rxfifo1_size); + } +} + +static void iwl_parse_shared_mem(struct iwl_fw_runtime *fwrt, + struct iwl_rx_packet *pkt) +{ + struct iwl_shared_mem_cfg_v2 *mem_cfg = (void *)pkt->data; + int i; + + fwrt->smem_cfg.num_lmacs = 1; + + fwrt->smem_cfg.num_txfifo_entries = ARRAY_SIZE(mem_cfg->txfifo_size); + for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) + fwrt->smem_cfg.lmac[0].txfifo_size[i] = + le32_to_cpu(mem_cfg->txfifo_size[i]); + + fwrt->smem_cfg.lmac[0].rxfifo1_size = + le32_to_cpu(mem_cfg->rxfifo_size[0]); + fwrt->smem_cfg.rxfifo2_size = le32_to_cpu(mem_cfg->rxfifo_size[1]); + + /* new API has more data, from rxfifo_addr field and on */ + if (fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { + BUILD_BUG_ON(sizeof(fwrt->smem_cfg.internal_txfifo_size) != + sizeof(mem_cfg->internal_txfifo_size)); + + for (i = 0; + i < ARRAY_SIZE(fwrt->smem_cfg.internal_txfifo_size); + i++) + fwrt->smem_cfg.internal_txfifo_size[i] = + le32_to_cpu(mem_cfg->internal_txfifo_size[i]); + } +} + +void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt) +{ + struct iwl_host_cmd cmd = { + .flags = CMD_WANT_SKB, + .data = { NULL, }, + .len = { 0, }, + }; + struct iwl_rx_packet *pkt; + + if (fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) + cmd.id = iwl_cmd_id(SHARED_MEM_CFG_CMD, SYSTEM_GROUP, 0); + else + cmd.id = SHARED_MEM_CFG; + + if (WARN_ON(iwl_trans_send_cmd(fwrt->trans, &cmd))) + return; + + pkt = cmd.resp_pkt; + if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_A000) + iwl_parse_shared_mem_a000(fwrt, pkt); + else + iwl_parse_shared_mem(fwrt, pkt); + + IWL_DEBUG_INFO(fwrt, "SHARED MEM CFG: got memory offsets/sizes\n"); + + iwl_free_resp(&cmd); +} +IWL_EXPORT_SYMBOL(iwl_get_shared_mem_conf); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index e1a29cde92d9..bfc865ad8904 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -2481,93 +2481,6 @@ struct iwl_tdls_config_res { struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT]; } __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */ -#define TX_FIFO_MAX_NUM_9000 8 -#define TX_FIFO_MAX_NUM 15 -#define RX_FIFO_MAX_NUM 2 -#define TX_FIFO_INTERNAL_MAX_NUM 6 - -/** - * struct iwl_shared_mem_cfg_v2 - Shared memory configuration information - * - * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not - * accessible) - * @shared_mem_size: shared memory size - * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to - * 0x0 as accessible only via DBGM RDAT) - * @sample_buff_size: internal sample buff size - * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre - * 8000 HW set to 0x0 as not accessible) - * @txfifo_size: size of TXF0 ... TXF7 - * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0 - * @page_buff_addr: used by UMAC and performance debug (page miss analysis), - * when paging is not supported this should be 0 - * @page_buff_size: size of %page_buff_addr - * @rxfifo_addr: Start address of rxFifo - * @internal_txfifo_addr: start address of internalFifo - * @internal_txfifo_size: internal fifos' size - * - * NOTE: on firmware that don't have IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG - * set, the last 3 members don't exist. - */ -struct iwl_shared_mem_cfg_v2 { - __le32 shared_mem_addr; - __le32 shared_mem_size; - __le32 sample_buff_addr; - __le32 sample_buff_size; - __le32 txfifo_addr; - __le32 txfifo_size[TX_FIFO_MAX_NUM_9000]; - __le32 rxfifo_size[RX_FIFO_MAX_NUM]; - __le32 page_buff_addr; - __le32 page_buff_size; - __le32 rxfifo_addr; - __le32 internal_txfifo_addr; - __le32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; -} __packed; /* SHARED_MEM_ALLOC_API_S_VER_2 */ - -/** - * struct iwl_shared_mem_lmac_cfg - LMAC shared memory configuration - * - * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB) - * @txfifo_size: size of TX FIFOs - * @rxfifo1_addr: RXF1 addr - * @rxfifo1_size: RXF1 size - */ -struct iwl_shared_mem_lmac_cfg { - __le32 txfifo_addr; - __le32 txfifo_size[TX_FIFO_MAX_NUM]; - __le32 rxfifo1_addr; - __le32 rxfifo1_size; - -} __packed; /* SHARED_MEM_ALLOC_LMAC_API_S_VER_1 */ - -/** - * struct iwl_shared_mem_cfg - Shared memory configuration information - * - * @shared_mem_addr: shared memory address - * @shared_mem_size: shared memory size - * @sample_buff_addr: internal sample (mon/adc) buff addr - * @sample_buff_size: internal sample buff size - * @rxfifo2_addr: start addr of RXF2 - * @rxfifo2_size: size of RXF2 - * @page_buff_addr: used by UMAC and performance debug (page miss analysis), - * when paging is not supported this should be 0 - * @page_buff_size: size of %page_buff_addr - * @lmac_num: number of LMACs (1 or 2) - * @lmac_smem: per - LMAC smem data - */ -struct iwl_shared_mem_cfg { - __le32 shared_mem_addr; - __le32 shared_mem_size; - __le32 sample_buff_addr; - __le32 sample_buff_size; - __le32 rxfifo2_addr; - __le32 rxfifo2_size; - __le32 page_buff_addr; - __le32 page_buff_size; - __le32 lmac_num; - struct iwl_shared_mem_lmac_cfg lmac_smem[2]; -} __packed; /* SHARED_MEM_ALLOC_API_S_VER_3 */ - /** * struct iwl_mu_group_mgmt_cmd - VHT MU-MIMO group configuration * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index ba32753fda5a..9d54005b6d50 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -212,7 +212,7 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, struct iwl_fw_error_dump_data **dump_data) { struct iwl_fw_error_dump_fifo *fifo_hdr; - struct iwl_mvm_shared_mem_cfg *cfg = &mvm->smem_cfg; + struct iwl_fwrt_shared_mem_cfg *cfg = &mvm->fwrt.smem_cfg; u32 *fifo_data; u32 fifo_len; unsigned long flags; @@ -227,12 +227,12 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, iwl_mvm_dump_rxf(mvm, dump_data, cfg->rxfifo2_size, RXF_DIFF_FROM_PREV, 1); /* Pull LMAC2 RXF1 */ - if (mvm->smem_cfg.num_lmacs > 1) + if (mvm->fwrt.smem_cfg.num_lmacs > 1) iwl_mvm_dump_rxf(mvm, dump_data, cfg->lmac[1].rxfifo1_size, LMAC2_PRPH_OFFSET, 2); /* Pull TXF data from LMAC1 */ - for (i = 0; i < mvm->smem_cfg.num_txfifo_entries; i++) { + for (i = 0; i < mvm->fwrt.smem_cfg.num_txfifo_entries; i++) { /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); iwl_mvm_dump_txf(mvm, dump_data, cfg->lmac[0].txfifo_size[i], @@ -240,8 +240,8 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, } /* Pull TXF data from LMAC2 */ - if (mvm->smem_cfg.num_lmacs > 1) { - for (i = 0; i < mvm->smem_cfg.num_txfifo_entries; i++) { + if (mvm->fwrt.smem_cfg.num_lmacs > 1) { + for (i = 0; i < mvm->fwrt.smem_cfg.num_txfifo_entries; i++) { /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM + LMAC2_PRPH_OFFSET, @@ -257,11 +257,11 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { /* Pull UMAC internal TXF data from all TXFs */ for (i = 0; - i < ARRAY_SIZE(mvm->smem_cfg.internal_txfifo_size); + i < ARRAY_SIZE(mvm->fwrt.smem_cfg.internal_txfifo_size); i++) { fifo_hdr = (void *)(*dump_data)->data; fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->smem_cfg.internal_txfifo_size[i]; + fifo_len = mvm->fwrt.smem_cfg.internal_txfifo_size[i]; /* No need to try to read the data if the length is 0 */ if (fifo_len == 0) @@ -277,7 +277,7 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_CPU2_NUM, i + - mvm->smem_cfg.num_txfifo_entries); + mvm->fwrt.smem_cfg.num_txfifo_entries); fifo_hdr->available_bytes = cpu_to_le32(iwl_trans_read_prph(mvm->trans, @@ -582,7 +582,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* reading RXF/TXF sizes */ if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { - struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->smem_cfg; + struct iwl_fwrt_shared_mem_cfg *mem_cfg = &mvm->fwrt.smem_cfg; fifo_data_len = 0; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 47715eec22e6..161b7452baa2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -568,95 +568,6 @@ out: return ret; } -static void iwl_mvm_parse_shared_mem_a000(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_shared_mem_cfg *mem_cfg = (void *)pkt->data; - int i, lmac; - int lmac_num = le32_to_cpu(mem_cfg->lmac_num); - - if (WARN_ON(lmac_num > ARRAY_SIZE(mem_cfg->lmac_smem))) - return; - - mvm->smem_cfg.num_lmacs = lmac_num; - mvm->smem_cfg.num_txfifo_entries = - ARRAY_SIZE(mem_cfg->lmac_smem[0].txfifo_size); - mvm->smem_cfg.rxfifo2_size = le32_to_cpu(mem_cfg->rxfifo2_size); - - for (lmac = 0; lmac < lmac_num; lmac++) { - struct iwl_shared_mem_lmac_cfg *lmac_cfg = - &mem_cfg->lmac_smem[lmac]; - - for (i = 0; i < ARRAY_SIZE(lmac_cfg->txfifo_size); i++) - mvm->smem_cfg.lmac[lmac].txfifo_size[i] = - le32_to_cpu(lmac_cfg->txfifo_size[i]); - mvm->smem_cfg.lmac[lmac].rxfifo1_size = - le32_to_cpu(lmac_cfg->rxfifo1_size); - } -} - -static void iwl_mvm_parse_shared_mem(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_shared_mem_cfg_v2 *mem_cfg = (void *)pkt->data; - int i; - - mvm->smem_cfg.num_lmacs = 1; - - mvm->smem_cfg.num_txfifo_entries = ARRAY_SIZE(mem_cfg->txfifo_size); - for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) - mvm->smem_cfg.lmac[0].txfifo_size[i] = - le32_to_cpu(mem_cfg->txfifo_size[i]); - - mvm->smem_cfg.lmac[0].rxfifo1_size = - le32_to_cpu(mem_cfg->rxfifo_size[0]); - mvm->smem_cfg.rxfifo2_size = le32_to_cpu(mem_cfg->rxfifo_size[1]); - - /* new API has more data, from rxfifo_addr field and on */ - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { - BUILD_BUG_ON(sizeof(mvm->smem_cfg.internal_txfifo_size) != - sizeof(mem_cfg->internal_txfifo_size)); - - for (i = 0; - i < ARRAY_SIZE(mvm->smem_cfg.internal_txfifo_size); - i++) - mvm->smem_cfg.internal_txfifo_size[i] = - le32_to_cpu(mem_cfg->internal_txfifo_size[i]); - } -} - -static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) -{ - struct iwl_host_cmd cmd = { - .flags = CMD_WANT_SKB, - .data = { NULL, }, - .len = { 0, }, - }; - struct iwl_rx_packet *pkt; - - lockdep_assert_held(&mvm->mutex); - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) - cmd.id = iwl_cmd_id(SHARED_MEM_CFG_CMD, SYSTEM_GROUP, 0); - else - cmd.id = SHARED_MEM_CFG; - - if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd))) - return; - - pkt = cmd.resp_pkt; - if (iwl_mvm_has_new_tx_api(mvm)) - iwl_mvm_parse_shared_mem_a000(mvm, pkt); - else - iwl_mvm_parse_shared_mem(mvm, pkt); - - IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n"); - - iwl_free_resp(&cmd); -} - static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) { struct iwl_ltr_config_cmd cmd = { @@ -1174,7 +1085,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) goto error; } - iwl_mvm_get_shared_mem_conf(mvm); + iwl_get_shared_mem_conf(&mvm->fwrt); ret = iwl_mvm_sf_update(mvm, NULL, false); if (ret) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index f8fe15cd80c5..739e7cbea82f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -607,19 +607,6 @@ enum iwl_mvm_tdls_cs_state { IWL_MVM_TDLS_SW_ACTIVE, }; -#define MAX_NUM_LMAC 2 -struct iwl_mvm_shared_mem_cfg { - int num_lmacs; - int num_txfifo_entries; - struct { - u32 txfifo_size[TX_FIFO_MAX_NUM]; - u32 rxfifo1_size; - } lmac[MAX_NUM_LMAC]; - u32 rxfifo2_size; - u32 internal_txfifo_addr; - u32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; -}; - /** * struct iwl_mvm_reorder_buffer - per ra/tid/queue reorder buffer * @head_sn: reorder window head sn @@ -1053,7 +1040,6 @@ struct iwl_mvm { } peer; } tdls_cs; - struct iwl_mvm_shared_mem_cfg smem_cfg; u32 ciphers[IWL_MVM_NUM_CIPHERS]; struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 60360ed73f26..c7ca6bd3129c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -761,7 +761,8 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, * fifo to be able to send bursts. */ max_amsdu_len = min_t(unsigned int, max_amsdu_len, - mvm->smem_cfg.lmac[0].txfifo_size[txf] - 256); + mvm->fwrt.smem_cfg.lmac[0].txfifo_size[txf] - + 256); if (unlikely(dbg_max_amsdu_len)) max_amsdu_len = min_t(unsigned int, max_amsdu_len, -- cgit v1.2.3 From 702e975d6a60027968423a4fdaaf0831da87b73b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 2 Jun 2017 11:56:58 +0200 Subject: iwlwifi: track current firmware image in common code Track the current firmware image in the common code instead of in the opmode so that later patches can access it there in a common way. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 10 ++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 12 ++++++------ drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 14 +++++++------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 12 ++++++------ drivers/net/wireless/intel/iwlwifi/mvm/power.c | 23 ++++++++++++----------- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 10 +++++----- 9 files changed, 52 insertions(+), 42 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 185902ce7e13..c483a76cb0e5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -85,6 +85,8 @@ struct iwl_fwrt_shared_mem_cfg { * @num_of_paging_blk: number of paging blocks * @num_of_pages_in_last_blk: number of pages in the last block * @smem_cfg: saved firmware SMEM configuration + * @cur_fw_img: current firmware image, must be maintained by + * the driver by calling &iwl_fw_set_current_image() */ struct iwl_fw_runtime { struct iwl_trans *trans; @@ -96,6 +98,8 @@ struct iwl_fw_runtime { u16 num_of_paging_blk; u16 num_of_pages_in_last_blk; + enum iwl_ucode_type cur_fw_img; + /* memory configuration */ struct iwl_fwrt_shared_mem_cfg smem_cfg; }; @@ -110,6 +114,12 @@ static inline void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, fwrt->dev = trans->dev; } +static inline void iwl_fw_set_current_image(struct iwl_fw_runtime *fwrt, + enum iwl_ucode_type cur_fw_img) +{ + fwrt->cur_fw_img = cur_fw_img; +} + int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type); void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index c1c9c489edc9..9991494314e2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -84,7 +84,7 @@ static ssize_t iwl_dbgfs_ctdp_budget_read(struct file *file, int pos, budget; if (!iwl_mvm_firmware_running(mvm) || - mvm->cur_ucode != IWL_UCODE_REGULAR) + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) return -EIO; mutex_lock(&mvm->mutex); @@ -105,7 +105,7 @@ static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mvm *mvm, char *buf, int ret; if (!iwl_mvm_firmware_running(mvm) || - mvm->cur_ucode != IWL_UCODE_REGULAR) + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) return -EIO; mutex_lock(&mvm->mutex); @@ -122,7 +122,7 @@ static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, u32 flush_arg; if (!iwl_mvm_firmware_running(mvm) || - mvm->cur_ucode != IWL_UCODE_REGULAR) + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) return -EIO; if (kstrtou32(buf, 0, &flush_arg)) @@ -155,7 +155,7 @@ static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf, int sta_id, drain, ret; if (!iwl_mvm_firmware_running(mvm) || - mvm->cur_ucode != IWL_UCODE_REGULAR) + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) return -EIO; if (sscanf(buf, "%d %d", &sta_id, &drain) != 2) @@ -192,7 +192,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, return -EINVAL; /* default is to dump the entire data segment */ - img = &mvm->fw->img[mvm->cur_ucode]; + img = &mvm->fw->img[mvm->fwrt.cur_fw_img]; ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; len = img->sec[IWL_UCODE_SECTION_DATA].len; @@ -224,7 +224,7 @@ static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf, if (!iwl_mvm_firmware_running(mvm)) return -EINVAL; - img = &mvm->fw->img[mvm->cur_ucode]; + img = &mvm->fw->img[mvm->fwrt.cur_fw_img]; img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset; img_len = img->sec[IWL_UCODE_SECTION_DATA].len; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 9d54005b6d50..0c12e604f22b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -572,7 +572,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { const struct fw_img *img; - img = &mvm->fw->img[mvm->cur_ucode]; + img = &mvm->fw->img[mvm->fwrt.cur_fw_img]; sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; } else { @@ -693,7 +693,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* Make room for fw's virtual image pages, if it exists */ if (!mvm->trans->cfg->gen2 && - mvm->fw->img[mvm->cur_ucode].paging_mem_size && + mvm->fw->img[mvm->fwrt.cur_fw_img].paging_mem_size && mvm->fwrt.fw_paging_db[0].fw_paging_block) file_len += mvm->fwrt.num_of_paging_blk * (sizeof(*dump_data) + @@ -832,7 +832,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* Dump fw's virtual image */ if (!mvm->trans->cfg->gen2 && - mvm->fw->img[mvm->cur_ucode].paging_mem_size && + mvm->fw->img[mvm->fwrt.cur_fw_img].paging_mem_size && mvm->fwrt.fw_paging_db[0].fw_paging_block) { for (i = 1; i < mvm->fwrt.num_of_paging_blk + 1; i++) { struct iwl_fw_error_dump_paging *paging; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 161b7452baa2..08108620b977 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -251,7 +251,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, struct iwl_mvm_alive_data alive_data; const struct fw_img *fw; int ret, i; - enum iwl_ucode_type old_type = mvm->cur_ucode; + enum iwl_ucode_type old_type = mvm->fwrt.cur_fw_img; static const u16 alive_cmd[] = { MVM_ALIVE }; struct iwl_sf_region st_fwrd_space; @@ -264,7 +264,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, fw = iwl_get_ucode_image(mvm->fw, ucode_type); if (WARN_ON(!fw)) return -EINVAL; - mvm->cur_ucode = ucode_type; + iwl_fw_set_current_image(&mvm->fwrt, ucode_type); clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); iwl_init_notification_wait(&mvm->notif_wait, &alive_wait, @@ -273,7 +273,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT); if (ret) { - mvm->cur_ucode = old_type; + iwl_fw_set_current_image(&mvm->fwrt, old_type); iwl_remove_notification(&mvm->notif_wait, &alive_wait); return ret; } @@ -297,13 +297,13 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", iwl_read_prph(trans, SB_CPU_1_STATUS), iwl_read_prph(trans, SB_CPU_2_STATUS)); - mvm->cur_ucode = old_type; + iwl_fw_set_current_image(&mvm->fwrt, old_type); return ret; } if (!alive_data.valid) { IWL_ERR(mvm, "Loaded ucode is not valid!\n"); - mvm->cur_ucode = old_type; + iwl_fw_set_current_image(&mvm->fwrt, old_type); return -EIO; } @@ -432,7 +432,7 @@ error: static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) { struct iwl_phy_cfg_cmd phy_cfg_cmd; - enum iwl_ucode_type ucode_type = mvm->cur_ucode; + enum iwl_ucode_type ucode_type = mvm->fwrt.cur_fw_img; /* Set parameters */ phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm)); @@ -1064,7 +1064,7 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) if (ret) return ret; - return iwl_init_paging(&mvm->fwrt, mvm->cur_ucode); + return iwl_init_paging(&mvm->fwrt, mvm->fwrt.cur_fw_img); } int iwl_mvm_up(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 739e7cbea82f..988f4c331d15 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -754,7 +754,6 @@ struct iwl_mvm { */ struct iwl_mvm_vif *bf_allowed_vif; - enum iwl_ucode_type cur_ucode; bool hw_registered; bool calibrating; u32 error_event_table[2]; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 8c49ac903c8c..60f0c9975538 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -621,9 +621,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, } mvm->sf_state = SF_UNINIT; if (iwl_mvm_has_new_tx_api(mvm)) - mvm->cur_ucode = IWL_UCODE_REGULAR; + iwl_fw_set_current_image(&mvm->fwrt, IWL_UCODE_REGULAR); else - mvm->cur_ucode = IWL_UCODE_INIT; + iwl_fw_set_current_image(&mvm->fwrt, IWL_UCODE_INIT); mvm->drop_bcn_ap_mode = true; mutex_init(&mvm->mutex); @@ -1133,7 +1133,7 @@ static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) * Stop the device if we run OPERATIONAL firmware or if we are in the * middle of the calibrations. */ - return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating); + return state && (mvm->fwrt.cur_fw_img != IWL_UCODE_INIT || calibrating); } static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) @@ -1263,7 +1263,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) reprobe->dev = mvm->trans->dev; INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); schedule_work(&reprobe->work); - } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && + } else if (mvm->fwrt.cur_fw_img == IWL_UCODE_REGULAR && mvm->hw_registered) { /* don't let the transport/FW power down */ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); @@ -1441,7 +1441,7 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); - if (WARN_ON_ONCE(mvm->cur_ucode != IWL_UCODE_REGULAR)) + if (WARN_ON_ONCE(mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)) return -EINVAL; set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); @@ -1667,7 +1667,7 @@ int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm) IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n"); - if (WARN_ON_ONCE(mvm->cur_ucode != IWL_UCODE_REGULAR)) + if (WARN_ON_ONCE(mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)) return -EINVAL; mutex_lock(&mvm->d0i3_suspend_mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index e684811f8e8b..c05e5ac565ae 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -186,7 +186,7 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, if (!mvmvif->queue_params[ac].uapsd) continue; - if (mvm->cur_ucode != IWL_UCODE_WOWLAN) + if (mvm->fwrt.cur_fw_img != IWL_UCODE_WOWLAN) cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); @@ -220,14 +220,15 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, BIT(IEEE80211_AC_BK))) { cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); - cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ? - cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : - cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); + cmd->snooze_window = + (mvm->fwrt.cur_fw_img == IWL_UCODE_WOWLAN) ? + cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : + cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); } cmd->uapsd_max_sp = mvm->hw->uapsd_max_sp_len; - if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags & + if (mvm->fwrt.cur_fw_img == IWL_UCODE_WOWLAN || cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { cmd->rx_data_timeout_uapsd = cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); @@ -502,7 +503,7 @@ static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm, struct iwl_mac_power_cmd cmd = {}; iwl_mvm_power_build_cmd(mvm, vif, &cmd, - mvm->cur_ucode != IWL_UCODE_WOWLAN); + mvm->fwrt.cur_fw_img != IWL_UCODE_WOWLAN); iwl_mvm_power_log(mvm, &cmd); #ifdef CONFIG_IWLWIFI_DEBUGFS memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd)); @@ -525,8 +526,8 @@ int iwl_mvm_power_update_device(struct iwl_mvm *mvm) cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); #ifdef CONFIG_IWLWIFI_DEBUGFS - if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 : - mvm->disable_power_off) + if ((mvm->fwrt.cur_fw_img == IWL_UCODE_WOWLAN) ? + mvm->disable_power_off_d3 : mvm->disable_power_off) cmd.flags &= cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); #endif @@ -933,7 +934,7 @@ static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm, if (!mvmvif->bf_data.bf_enabled) return 0; - if (mvm->cur_ucode == IWL_UCODE_WOWLAN) + if (mvm->fwrt.cur_fw_img == IWL_UCODE_WOWLAN) cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); mvmvif->bf_data.ba_enabled = !(!mvmvif->pm_enabled || diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 453a785a3ea5..a638bd69a1f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -629,7 +629,7 @@ static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device, mutex_lock(&mvm->mutex); if (!iwl_mvm_firmware_running(mvm) || - mvm->cur_ucode != IWL_UCODE_REGULAR) { + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) { ret = -EIO; goto out; } @@ -680,7 +680,7 @@ static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device, mutex_lock(&mvm->mutex); if (!iwl_mvm_firmware_running(mvm) || - mvm->cur_ucode != IWL_UCODE_REGULAR) { + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) { ret = -EIO; goto out; } @@ -795,7 +795,7 @@ static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev, mutex_lock(&mvm->mutex); if (!iwl_mvm_firmware_running(mvm) || - mvm->cur_ucode != IWL_UCODE_REGULAR) { + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) { ret = -EIO; goto unlock; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index fc5a490880d0..4e80c8fa4741 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -464,8 +464,8 @@ static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) IWL_ERR(mvm, "Not valid error log pointer 0x%08X for %s uCode\n", base, - (mvm->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); + (mvm->fwrt.cur_fw_img == IWL_UCODE_INIT) + ? "Init" : "RT"); return; } @@ -500,7 +500,7 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base) struct iwl_error_event_table table; u32 val; - if (mvm->cur_ucode == IWL_UCODE_INIT) { + if (mvm->fwrt.cur_fw_img == IWL_UCODE_INIT) { if (!base) base = mvm->fw->init_errlog_ptr; } else { @@ -512,8 +512,8 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base) IWL_ERR(mvm, "Not valid error log pointer 0x%08X for %s uCode\n", base, - (mvm->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); + (mvm->fwrt.cur_fw_img == IWL_UCODE_INIT) + ? "Init" : "RT"); return; } -- cgit v1.2.3 From 7174beb60c53209c6e98c9cee20efdee97ef880f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 1 Jun 2017 16:03:19 +0200 Subject: iwlwifi: refactor firmware debug code Split out the firmware debug code to be more general, so that it can be used by different subdrivers. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 1099 ++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/dbg.h | 214 ++++ drivers/net/wireless/intel/iwlwifi/fw/init.c | 75 ++ drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 42 +- drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/constants.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 9 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 1031 ------------------ drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h | 175 ---- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 8 +- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 7 +- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 72 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 40 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 99 +- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 11 +- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 7 +- .../net/wireless/intel/iwlwifi/mvm/time-event.c | 15 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 17 +- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 6 +- 20 files changed, 1541 insertions(+), 1391 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/dbg.c create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/dbg.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/init.c delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index ddc2bfb501c1..fd12b7394c5c 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -11,7 +11,7 @@ iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/a000.o iwlwifi-objs += iwl-trans.o iwlwifi-objs += fw/notif-wait.o -iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o +iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o fw/dbg.o iwlwifi-objs += $(iwlwifi-m) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c new file mode 100644 index 000000000000..77245fcba996 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -0,0 +1,1099 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include "iwl-drv.h" +#include "runtime.h" +#include "dbg.h" +#include "iwl-io.h" +#include "iwl-prph.h" +#include "iwl-csr.h" + +/** + * struct iwl_fw_dump_ptrs - set of pointers needed for the fw-error-dump + * + * @fwrt_ptr: pointer to the buffer coming from fwrt + * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the + * transport's data. + * @trans_len: length of the valid data in trans_ptr + * @fwrt_len: length of the valid data in fwrt_ptr + */ +struct iwl_fw_dump_ptrs { + struct iwl_trans_dump_data *trans_ptr; + void *fwrt_ptr; + u32 fwrt_len; +}; + +#define RADIO_REG_MAX_READ 0x2ad +static void iwl_read_radio_regs(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data) +{ + u8 *pos = (void *)(*dump_data)->data; + unsigned long flags; + int i; + + if (!iwl_trans_grab_nic_access(fwrt->trans, &flags)) + return; + + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG); + (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ); + + for (i = 0; i < RADIO_REG_MAX_READ; i++) { + u32 rd_cmd = RADIO_RSP_RD_CMD; + + rd_cmd |= i << RADIO_RSP_ADDR_POS; + iwl_write_prph_no_grab(fwrt->trans, RSP_RADIO_CMD, rd_cmd); + *pos = (u8)iwl_read_prph_no_grab(fwrt->trans, RSP_RADIO_RDDAT); + + pos++; + } + + *dump_data = iwl_fw_error_next_data(*dump_data); + + iwl_trans_release_nic_access(fwrt->trans, &flags); +} + +static void iwl_fwrt_dump_rxf(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data, + int size, u32 offset, int fifo_num) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + u32 *fifo_data; + u32 fifo_len; + int i; + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = size; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + return; + + /* Add a TLV for the RXF */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(fifo_num); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_RD_D_SPACE + offset)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_RD_WR_PTR + offset)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_RD_RD_PTR + offset)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_RD_FENCE_PTR + offset)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_SET_FENCE_MODE + offset)); + + /* Lock fence */ + iwl_trans_write_prph(fwrt->trans, RXF_SET_FENCE_MODE + offset, 0x1); + /* Set fence pointer to the same place like WR pointer */ + iwl_trans_write_prph(fwrt->trans, RXF_LD_WR2FENCE + offset, 0x1); + /* Set fence offset */ + iwl_trans_write_prph(fwrt->trans, + RXF_LD_FENCE_OFFSET_ADDR + offset, 0x0); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (i = 0; i < fifo_len; i++) + fifo_data[i] = iwl_trans_read_prph(fwrt->trans, + RXF_FIFO_RD_FENCE_INC + + offset); + *dump_data = iwl_fw_error_next_data(*dump_data); +} + +static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data, + int size, u32 offset, int fifo_num) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + u32 *fifo_data; + u32 fifo_len; + int i; + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = size; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + return; + + /* Add a TLV for the FIFO */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(fifo_num); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_FIFO_ITEM_CNT + offset)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_WR_PTR + offset)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_RD_PTR + offset)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_FENCE_PTR + offset)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_LOCK_FENCE + offset)); + + /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ + iwl_trans_write_prph(fwrt->trans, TXF_READ_MODIFY_ADDR + offset, + TXF_WR_PTR + offset); + + /* Dummy-read to advance the read pointer to the head */ + iwl_trans_read_prph(fwrt->trans, TXF_READ_MODIFY_DATA + offset); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (i = 0; i < fifo_len; i++) + fifo_data[i] = iwl_trans_read_prph(fwrt->trans, + TXF_READ_MODIFY_DATA + + offset); + *dump_data = iwl_fw_error_next_data(*dump_data); +} + +static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg; + u32 *fifo_data; + u32 fifo_len; + unsigned long flags; + int i, j; + + if (!iwl_trans_grab_nic_access(fwrt->trans, &flags)) + return; + + /* Pull RXF1 */ + iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->lmac[0].rxfifo1_size, 0, 0); + /* Pull RXF2 */ + iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->rxfifo2_size, + RXF_DIFF_FROM_PREV, 1); + /* Pull LMAC2 RXF1 */ + if (fwrt->smem_cfg.num_lmacs > 1) + iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->lmac[1].rxfifo1_size, + LMAC2_PRPH_OFFSET, 2); + + /* Pull TXF data from LMAC1 */ + for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) { + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(fwrt->trans, TXF_LARC_NUM, i); + iwl_fwrt_dump_txf(fwrt, dump_data, cfg->lmac[0].txfifo_size[i], + 0, i); + } + + /* Pull TXF data from LMAC2 */ + if (fwrt->smem_cfg.num_lmacs > 1) { + for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) { + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(fwrt->trans, + TXF_LARC_NUM + LMAC2_PRPH_OFFSET, + i); + iwl_fwrt_dump_txf(fwrt, dump_data, + cfg->lmac[1].txfifo_size[i], + LMAC2_PRPH_OFFSET, + i + cfg->num_txfifo_entries); + } + } + + if (fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { + /* Pull UMAC internal TXF data from all TXFs */ + for (i = 0; + i < ARRAY_SIZE(fwrt->smem_cfg.internal_txfifo_size); + i++) { + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = fwrt->smem_cfg.internal_txfifo_size[i]; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + continue; + + /* Add a TLV for the internal FIFOs */ + (*dump_data)->type = + cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF); + (*dump_data)->len = + cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(i); + + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(fwrt->trans, TXF_CPU2_NUM, i + + fwrt->smem_cfg.num_txfifo_entries); + + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_FIFO_ITEM_CNT)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_WR_PTR)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_RD_PTR)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_FENCE_PTR)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_LOCK_FENCE)); + + /* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */ + iwl_trans_write_prph(fwrt->trans, + TXF_CPU2_READ_MODIFY_ADDR, + TXF_CPU2_WR_PTR); + + /* Dummy-read to advance the read pointer to head */ + iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_READ_MODIFY_DATA); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (j = 0; j < fifo_len; j++) + fifo_data[j] = + iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_READ_MODIFY_DATA); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + } + + iwl_trans_release_nic_access(fwrt->trans, &flags); +} + +#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ +#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ + +struct iwl_prph_range { + u32 start, end; +}; + +static const struct iwl_prph_range iwl_prph_dump_addr_comm[] = { + { .start = 0x00a00000, .end = 0x00a00000 }, + { .start = 0x00a0000c, .end = 0x00a00024 }, + { .start = 0x00a0002c, .end = 0x00a0003c }, + { .start = 0x00a00410, .end = 0x00a00418 }, + { .start = 0x00a00420, .end = 0x00a00420 }, + { .start = 0x00a00428, .end = 0x00a00428 }, + { .start = 0x00a00430, .end = 0x00a0043c }, + { .start = 0x00a00444, .end = 0x00a00444 }, + { .start = 0x00a004c0, .end = 0x00a004cc }, + { .start = 0x00a004d8, .end = 0x00a004d8 }, + { .start = 0x00a004e0, .end = 0x00a004f0 }, + { .start = 0x00a00840, .end = 0x00a00840 }, + { .start = 0x00a00850, .end = 0x00a00858 }, + { .start = 0x00a01004, .end = 0x00a01008 }, + { .start = 0x00a01010, .end = 0x00a01010 }, + { .start = 0x00a01018, .end = 0x00a01018 }, + { .start = 0x00a01024, .end = 0x00a01024 }, + { .start = 0x00a0102c, .end = 0x00a01034 }, + { .start = 0x00a0103c, .end = 0x00a01040 }, + { .start = 0x00a01048, .end = 0x00a01094 }, + { .start = 0x00a01c00, .end = 0x00a01c20 }, + { .start = 0x00a01c58, .end = 0x00a01c58 }, + { .start = 0x00a01c7c, .end = 0x00a01c7c }, + { .start = 0x00a01c28, .end = 0x00a01c54 }, + { .start = 0x00a01c5c, .end = 0x00a01c5c }, + { .start = 0x00a01c60, .end = 0x00a01cdc }, + { .start = 0x00a01ce0, .end = 0x00a01d0c }, + { .start = 0x00a01d18, .end = 0x00a01d20 }, + { .start = 0x00a01d2c, .end = 0x00a01d30 }, + { .start = 0x00a01d40, .end = 0x00a01d5c }, + { .start = 0x00a01d80, .end = 0x00a01d80 }, + { .start = 0x00a01d98, .end = 0x00a01d9c }, + { .start = 0x00a01da8, .end = 0x00a01da8 }, + { .start = 0x00a01db8, .end = 0x00a01df4 }, + { .start = 0x00a01dc0, .end = 0x00a01dfc }, + { .start = 0x00a01e00, .end = 0x00a01e2c }, + { .start = 0x00a01e40, .end = 0x00a01e60 }, + { .start = 0x00a01e68, .end = 0x00a01e6c }, + { .start = 0x00a01e74, .end = 0x00a01e74 }, + { .start = 0x00a01e84, .end = 0x00a01e90 }, + { .start = 0x00a01e9c, .end = 0x00a01ec4 }, + { .start = 0x00a01ed0, .end = 0x00a01ee0 }, + { .start = 0x00a01f00, .end = 0x00a01f1c }, + { .start = 0x00a01f44, .end = 0x00a01ffc }, + { .start = 0x00a02000, .end = 0x00a02048 }, + { .start = 0x00a02068, .end = 0x00a020f0 }, + { .start = 0x00a02100, .end = 0x00a02118 }, + { .start = 0x00a02140, .end = 0x00a0214c }, + { .start = 0x00a02168, .end = 0x00a0218c }, + { .start = 0x00a021c0, .end = 0x00a021c0 }, + { .start = 0x00a02400, .end = 0x00a02410 }, + { .start = 0x00a02418, .end = 0x00a02420 }, + { .start = 0x00a02428, .end = 0x00a0242c }, + { .start = 0x00a02434, .end = 0x00a02434 }, + { .start = 0x00a02440, .end = 0x00a02460 }, + { .start = 0x00a02468, .end = 0x00a024b0 }, + { .start = 0x00a024c8, .end = 0x00a024cc }, + { .start = 0x00a02500, .end = 0x00a02504 }, + { .start = 0x00a0250c, .end = 0x00a02510 }, + { .start = 0x00a02540, .end = 0x00a02554 }, + { .start = 0x00a02580, .end = 0x00a025f4 }, + { .start = 0x00a02600, .end = 0x00a0260c }, + { .start = 0x00a02648, .end = 0x00a02650 }, + { .start = 0x00a02680, .end = 0x00a02680 }, + { .start = 0x00a026c0, .end = 0x00a026d0 }, + { .start = 0x00a02700, .end = 0x00a0270c }, + { .start = 0x00a02804, .end = 0x00a02804 }, + { .start = 0x00a02818, .end = 0x00a0281c }, + { .start = 0x00a02c00, .end = 0x00a02db4 }, + { .start = 0x00a02df4, .end = 0x00a02fb0 }, + { .start = 0x00a03000, .end = 0x00a03014 }, + { .start = 0x00a0301c, .end = 0x00a0302c }, + { .start = 0x00a03034, .end = 0x00a03038 }, + { .start = 0x00a03040, .end = 0x00a03048 }, + { .start = 0x00a03060, .end = 0x00a03068 }, + { .start = 0x00a03070, .end = 0x00a03074 }, + { .start = 0x00a0307c, .end = 0x00a0307c }, + { .start = 0x00a03080, .end = 0x00a03084 }, + { .start = 0x00a0308c, .end = 0x00a03090 }, + { .start = 0x00a03098, .end = 0x00a03098 }, + { .start = 0x00a030a0, .end = 0x00a030a0 }, + { .start = 0x00a030a8, .end = 0x00a030b4 }, + { .start = 0x00a030bc, .end = 0x00a030bc }, + { .start = 0x00a030c0, .end = 0x00a0312c }, + { .start = 0x00a03c00, .end = 0x00a03c5c }, + { .start = 0x00a04400, .end = 0x00a04454 }, + { .start = 0x00a04460, .end = 0x00a04474 }, + { .start = 0x00a044c0, .end = 0x00a044ec }, + { .start = 0x00a04500, .end = 0x00a04504 }, + { .start = 0x00a04510, .end = 0x00a04538 }, + { .start = 0x00a04540, .end = 0x00a04548 }, + { .start = 0x00a04560, .end = 0x00a0457c }, + { .start = 0x00a04590, .end = 0x00a04598 }, + { .start = 0x00a045c0, .end = 0x00a045f4 }, +}; + +static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = { + { .start = 0x00a05c00, .end = 0x00a05c18 }, + { .start = 0x00a05400, .end = 0x00a056e8 }, + { .start = 0x00a08000, .end = 0x00a098bc }, + { .start = 0x00a02400, .end = 0x00a02758 }, +}; + +static void _iwl_read_prph_block(struct iwl_trans *trans, u32 start, + u32 len_bytes, __le32 *data) +{ + u32 i; + + for (i = 0; i < len_bytes; i += 4) + *data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i)); +} + +static bool iwl_read_prph_block(struct iwl_trans *trans, u32 start, + u32 len_bytes, __le32 *data) +{ + unsigned long flags; + bool success = false; + + if (iwl_trans_grab_nic_access(trans, &flags)) { + success = true; + _iwl_read_prph_block(trans, start, len_bytes, data); + iwl_trans_release_nic_access(trans, &flags); + } + + return success; +} + +static void iwl_dump_prph(struct iwl_trans *trans, + struct iwl_fw_error_dump_data **data, + const struct iwl_prph_range *iwl_prph_dump_addr, + u32 range_len) +{ + struct iwl_fw_error_dump_prph *prph; + unsigned long flags; + u32 i; + + if (!iwl_trans_grab_nic_access(trans, &flags)) + return; + + for (i = 0; i < range_len; i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); + (*data)->len = cpu_to_le32(sizeof(*prph) + + num_bytes_in_chunk); + prph = (void *)(*data)->data; + prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); + + _iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start, + /* our range is inclusive, hence + 4 */ + iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4, + (void *)prph->data); + + *data = iwl_fw_error_next_data(*data); + } + + iwl_trans_release_nic_access(trans, &flags); +} + +/* + * alloc_sgtable - allocates scallerlist table in the given size, + * fills it with pages and returns it + * @size: the size (in bytes) of the table +*/ +static struct scatterlist *alloc_sgtable(int size) +{ + int alloc_size, nents, i; + struct page *new_page; + struct scatterlist *iter; + struct scatterlist *table; + + nents = DIV_ROUND_UP(size, PAGE_SIZE); + table = kcalloc(nents, sizeof(*table), GFP_KERNEL); + if (!table) + return NULL; + sg_init_table(table, nents); + iter = table; + for_each_sg(table, iter, sg_nents(table), i) { + new_page = alloc_page(GFP_KERNEL); + if (!new_page) { + /* release all previous allocated pages in the table */ + iter = table; + for_each_sg(table, iter, sg_nents(table), i) { + new_page = sg_page(iter); + if (new_page) + __free_page(new_page); + } + return NULL; + } + alloc_size = min_t(int, size, PAGE_SIZE); + size -= PAGE_SIZE; + sg_set_page(iter, new_page, alloc_size, 0); + } + return table; +} + +void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) +{ + struct iwl_fw_error_dump_file *dump_file; + struct iwl_fw_error_dump_data *dump_data; + struct iwl_fw_error_dump_info *dump_info; + struct iwl_fw_error_dump_mem *dump_mem; + struct iwl_fw_error_dump_trigger_desc *dump_trig; + struct iwl_fw_dump_ptrs *fw_error_dump; + struct scatterlist *sg_dump_data; + u32 sram_len, sram_ofs; + const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem = fwrt->fw->dbg_mem_tlv; + u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0; + u32 smem_len = fwrt->fw->n_dbg_mem_tlv ? 0 : fwrt->trans->cfg->smem_len; + u32 sram2_len = fwrt->fw->n_dbg_mem_tlv ? + 0 : fwrt->trans->cfg->dccm2_len; + bool monitor_dump_only = false; + int i; + + /* there's no point in fw dump if the bus is dead */ + if (test_bit(STATUS_TRANS_DEAD, &fwrt->trans->status)) { + IWL_ERR(fwrt, "Skip fw error dump since bus is dead\n"); + goto out; + } + + if (fwrt->dump.trig && + fwrt->dump.trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) + monitor_dump_only = true; + + fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); + if (!fw_error_dump) + goto out; + + /* SRAM - include stack CCM if driver knows the values for it */ + if (!fwrt->trans->cfg->dccm_offset || !fwrt->trans->cfg->dccm_len) { + const struct fw_img *img; + + img = &fwrt->fw->img[fwrt->cur_fw_img]; + sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + } else { + sram_ofs = fwrt->trans->cfg->dccm_offset; + sram_len = fwrt->trans->cfg->dccm_len; + } + + /* reading RXF/TXF sizes */ + if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) { + struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg; + + fifo_data_len = 0; + + /* Count RXF2 size */ + if (mem_cfg->rxfifo2_size) { + /* Add header info */ + fifo_data_len += mem_cfg->rxfifo2_size + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + + /* Count RXF1 sizes */ + for (i = 0; i < mem_cfg->num_lmacs; i++) { + if (!mem_cfg->lmac[i].rxfifo1_size) + continue; + + /* Add header info */ + fifo_data_len += mem_cfg->lmac[i].rxfifo1_size + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + + /* Count TXF sizes */ + for (i = 0; i < mem_cfg->num_lmacs; i++) { + int j; + + for (j = 0; j < mem_cfg->num_txfifo_entries; j++) { + if (!mem_cfg->lmac[i].txfifo_size[j]) + continue; + + /* Add header info */ + fifo_data_len += + mem_cfg->lmac[i].txfifo_size[j] + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + } + + if (fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { + for (i = 0; + i < ARRAY_SIZE(mem_cfg->internal_txfifo_size); + i++) { + if (!mem_cfg->internal_txfifo_size[i]) + continue; + + /* Add header info */ + fifo_data_len += + mem_cfg->internal_txfifo_size[i] + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + } + + /* Make room for PRPH registers */ + if (!fwrt->trans->cfg->gen2) { + for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr_comm); + i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = + iwl_prph_dump_addr_comm[i].end - + iwl_prph_dump_addr_comm[i].start + 4; + + prph_len += sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_prph) + + num_bytes_in_chunk; + } + } + + if (!fwrt->trans->cfg->gen2 && + fwrt->trans->cfg->mq_rx_supported) { + for (i = 0; i < + ARRAY_SIZE(iwl_prph_dump_addr_9000); i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = + iwl_prph_dump_addr_9000[i].end - + iwl_prph_dump_addr_9000[i].start + 4; + + prph_len += sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_prph) + + num_bytes_in_chunk; + } + } + + if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) + radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ; + } + + file_len = sizeof(*dump_file) + + sizeof(*dump_data) * 2 + + fifo_data_len + + prph_len + + radio_len + + sizeof(*dump_info); + + /* Make room for the SMEM, if it exists */ + if (smem_len) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; + + /* Make room for the secondary SRAM, if it exists */ + if (sram2_len) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; + + /* Make room for MEM segments */ + for (i = 0; i < fwrt->fw->n_dbg_mem_tlv; i++) { + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + + le32_to_cpu(fw_dbg_mem[i].len); + } + + /* Make room for fw's virtual image pages, if it exists */ + if (!fwrt->trans->cfg->gen2 && + fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size && + fwrt->fw_paging_db[0].fw_paging_block) + file_len += fwrt->num_of_paging_blk * + (sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_paging) + + PAGING_BLOCK_SIZE); + + /* If we only want a monitor dump, reset the file length */ + if (monitor_dump_only) { + file_len = sizeof(*dump_file) + sizeof(*dump_data) + + sizeof(*dump_info); + } + + if (fwrt->dump.desc) + file_len += sizeof(*dump_data) + sizeof(*dump_trig) + + fwrt->dump.desc->len; + + if (!fwrt->fw->n_dbg_mem_tlv) + file_len += sram_len + sizeof(*dump_mem); + + dump_file = vzalloc(file_len); + if (!dump_file) { + kfree(fw_error_dump); + goto out; + } + + fw_error_dump->fwrt_ptr = dump_file; + + dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); + dump_data = (void *)dump_file->data; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_info)); + dump_info = (void *)dump_data->data; + dump_info->device_family = + fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); + dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(fwrt->trans->hw_rev)); + memcpy(dump_info->fw_human_readable, fwrt->fw->human_readable, + sizeof(dump_info->fw_human_readable)); + strncpy(dump_info->dev_human_readable, fwrt->trans->cfg->name, + sizeof(dump_info->dev_human_readable)); + strncpy(dump_info->bus_human_readable, fwrt->dev->bus->name, + sizeof(dump_info->bus_human_readable)); + + dump_data = iwl_fw_error_next_data(dump_data); + /* We only dump the FIFOs if the FW is in error state */ + if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) { + iwl_fw_dump_fifos(fwrt, &dump_data); + if (radio_len) + iwl_read_radio_regs(fwrt, &dump_data); + } + + if (fwrt->dump.desc) { + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_trig) + + fwrt->dump.desc->len); + dump_trig = (void *)dump_data->data; + memcpy(dump_trig, &fwrt->dump.desc->trig_desc, + sizeof(*dump_trig) + fwrt->dump.desc->len); + + dump_data = iwl_fw_error_next_data(dump_data); + } + + /* In case we only want monitor dump, skip to dump trasport data */ + if (monitor_dump_only) + goto dump_trans_data; + + if (!fwrt->fw->n_dbg_mem_tlv) { + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(sram_ofs); + iwl_trans_read_mem_bytes(fwrt->trans, sram_ofs, dump_mem->data, + sram_len); + dump_data = iwl_fw_error_next_data(dump_data); + } + + for (i = 0; i < fwrt->fw->n_dbg_mem_tlv; i++) { + u32 len = le32_to_cpu(fw_dbg_mem[i].len); + u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs); + bool success; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = fw_dbg_mem[i].data_type; + dump_mem->offset = cpu_to_le32(ofs); + + switch (dump_mem->type & cpu_to_le32(FW_DBG_MEM_TYPE_MASK)) { + case cpu_to_le32(FW_DBG_MEM_TYPE_REGULAR): + iwl_trans_read_mem_bytes(fwrt->trans, ofs, + dump_mem->data, + len); + success = true; + break; + case cpu_to_le32(FW_DBG_MEM_TYPE_PRPH): + success = iwl_read_prph_block(fwrt->trans, ofs, len, + (void *)dump_mem->data); + break; + default: + /* + * shouldn't get here, we ignored this kind + * of TLV earlier during the TLV parsing?! + */ + WARN_ON(1); + success = false; + } + + if (success) + dump_data = iwl_fw_error_next_data(dump_data); + } + + if (smem_len) { + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); + dump_mem->offset = cpu_to_le32(fwrt->trans->cfg->smem_offset); + iwl_trans_read_mem_bytes(fwrt->trans, + fwrt->trans->cfg->smem_offset, + dump_mem->data, smem_len); + dump_data = iwl_fw_error_next_data(dump_data); + } + + if (sram2_len) { + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(fwrt->trans->cfg->dccm2_offset); + iwl_trans_read_mem_bytes(fwrt->trans, + fwrt->trans->cfg->dccm2_offset, + dump_mem->data, sram2_len); + dump_data = iwl_fw_error_next_data(dump_data); + } + + /* Dump fw's virtual image */ + if (!fwrt->trans->cfg->gen2 && + fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size && + fwrt->fw_paging_db[0].fw_paging_block) { + for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) { + struct iwl_fw_error_dump_paging *paging; + struct page *pages = + fwrt->fw_paging_db[i].fw_paging_block; + dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); + dump_data->len = cpu_to_le32(sizeof(*paging) + + PAGING_BLOCK_SIZE); + paging = (void *)dump_data->data; + paging->index = cpu_to_le32(i); + dma_sync_single_for_cpu(fwrt->trans->dev, addr, + PAGING_BLOCK_SIZE, + DMA_BIDIRECTIONAL); + memcpy(paging->data, page_address(pages), + PAGING_BLOCK_SIZE); + dump_data = iwl_fw_error_next_data(dump_data); + } + } + + if (prph_len) { + iwl_dump_prph(fwrt->trans, &dump_data, + iwl_prph_dump_addr_comm, + ARRAY_SIZE(iwl_prph_dump_addr_comm)); + + if (fwrt->trans->cfg->mq_rx_supported) + iwl_dump_prph(fwrt->trans, &dump_data, + iwl_prph_dump_addr_9000, + ARRAY_SIZE(iwl_prph_dump_addr_9000)); + } + +dump_trans_data: + fw_error_dump->trans_ptr = iwl_trans_dump_data(fwrt->trans, + fwrt->dump.trig); + fw_error_dump->fwrt_len = file_len; + if (fw_error_dump->trans_ptr) + file_len += fw_error_dump->trans_ptr->len; + dump_file->file_len = cpu_to_le32(file_len); + + sg_dump_data = alloc_sgtable(file_len); + if (sg_dump_data) { + sg_pcopy_from_buffer(sg_dump_data, + sg_nents(sg_dump_data), + fw_error_dump->fwrt_ptr, + fw_error_dump->fwrt_len, 0); + if (fw_error_dump->trans_ptr) + sg_pcopy_from_buffer(sg_dump_data, + sg_nents(sg_dump_data), + fw_error_dump->trans_ptr->data, + fw_error_dump->trans_ptr->len, + fw_error_dump->fwrt_len); + dev_coredumpsg(fwrt->trans->dev, sg_dump_data, file_len, + GFP_KERNEL); + } + vfree(fw_error_dump->fwrt_ptr); + vfree(fw_error_dump->trans_ptr); + kfree(fw_error_dump); + +out: + iwl_fw_free_dump_desc(fwrt); + fwrt->dump.trig = NULL; + clear_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status); +} +IWL_EXPORT_SYMBOL(iwl_fw_error_dump); + +const struct iwl_fw_dump_desc iwl_dump_desc_assert = { + .trig_desc = { + .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), + }, +}; +IWL_EXPORT_SYMBOL(iwl_dump_desc_assert); + +int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, + const struct iwl_fw_dump_desc *desc, + const struct iwl_fw_dbg_trigger_tlv *trigger) +{ + unsigned int delay = 0; + + if (trigger) + delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); + + if (WARN(fwrt->trans->state == IWL_TRANS_NO_FW, + "Can't collect dbg data when FW isn't alive\n")) + return -EIO; + + if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status)) + return -EBUSY; + + if (WARN_ON(fwrt->dump.desc)) + iwl_fw_free_dump_desc(fwrt); + + IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n", + le32_to_cpu(desc->trig_desc.type)); + + fwrt->dump.desc = desc; + fwrt->dump.trig = trigger; + + schedule_delayed_work(&fwrt->dump.wk, delay); + + return 0; +} +IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_desc); + +int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, + enum iwl_fw_dbg_trigger trig, + const char *str, size_t len, + const struct iwl_fw_dbg_trigger_tlv *trigger) +{ + struct iwl_fw_dump_desc *desc; + + desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); + if (!desc) + return -ENOMEM; + + desc->len = len; + desc->trig_desc.type = cpu_to_le32(trig); + memcpy(desc->trig_desc.data, str, len); + + return iwl_fw_dbg_collect_desc(fwrt, desc, trigger); +} +IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect); + +int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt, + struct iwl_fw_dbg_trigger_tlv *trigger, + const char *fmt, ...) +{ + u16 occurrences = le16_to_cpu(trigger->occurrences); + int ret, len = 0; + char buf[64]; + + if (!occurrences) + return 0; + + if (fmt) { + va_list ap; + + buf[sizeof(buf) - 1] = '\0'; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + /* check for truncation */ + if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) + buf[sizeof(buf) - 1] = '\0'; + + len = strlen(buf) + 1; + } + + ret = iwl_fw_dbg_collect(fwrt, le32_to_cpu(trigger->id), buf, len, + trigger); + + if (ret) + return ret; + + trigger->occurrences = cpu_to_le16(occurrences - 1); + return 0; +} +IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_trig); + +int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id) +{ + u8 *ptr; + int ret; + int i; + + if (WARN_ONCE(conf_id >= ARRAY_SIZE(fwrt->fw->dbg_conf_tlv), + "Invalid configuration %d\n", conf_id)) + return -EINVAL; + + /* EARLY START - firmware's configuration is hard coded */ + if ((!fwrt->fw->dbg_conf_tlv[conf_id] || + !fwrt->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && + conf_id == FW_DBG_START_FROM_ALIVE) + return 0; + + if (!fwrt->fw->dbg_conf_tlv[conf_id]) + return -EINVAL; + + if (fwrt->dump.conf != FW_DBG_INVALID) + IWL_WARN(fwrt, "FW already configured (%d) - re-configuring\n", + fwrt->dump.conf); + + /* Send all HCMDs for configuring the FW debug */ + ptr = (void *)&fwrt->fw->dbg_conf_tlv[conf_id]->hcmd; + for (i = 0; i < fwrt->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { + struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; + struct iwl_host_cmd hcmd = { + .id = cmd->id, + .len = { le16_to_cpu(cmd->len), }, + .data = { cmd->data, }, + }; + + ret = iwl_trans_send_cmd(fwrt->trans, &hcmd); + if (ret) + return ret; + + ptr += sizeof(*cmd); + ptr += le16_to_cpu(cmd->len); + } + + fwrt->dump.conf = conf_id; + + return 0; +} +IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf); + +void iwl_fw_error_dump_wk(struct work_struct *work) +{ + struct iwl_fw_runtime *fwrt = + container_of(work, struct iwl_fw_runtime, dump.wk.work); + + if (fwrt->ops && fwrt->ops->dump_start && + fwrt->ops->dump_start(fwrt->ops_ctx)) + return; + + if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { + /* stop recording */ + iwl_set_bits_prph(fwrt->trans, MON_BUFF_SAMPLE_CTL, 0x100); + + iwl_fw_error_dump(fwrt); + + /* start recording again if the firmware is not crashed */ + if (!test_bit(STATUS_FW_ERROR, &fwrt->trans->status) && + fwrt->fw->dbg_dest_tlv) { + iwl_clear_bits_prph(fwrt->trans, + MON_BUFF_SAMPLE_CTL, 0x100); + iwl_clear_bits_prph(fwrt->trans, + MON_BUFF_SAMPLE_CTL, 0x1); + iwl_set_bits_prph(fwrt->trans, + MON_BUFF_SAMPLE_CTL, 0x1); + } + } else { + u32 in_sample = iwl_read_prph(fwrt->trans, DBGC_IN_SAMPLE); + u32 out_ctrl = iwl_read_prph(fwrt->trans, DBGC_OUT_CTRL); + + /* stop recording */ + iwl_write_prph(fwrt->trans, DBGC_IN_SAMPLE, 0); + udelay(100); + iwl_write_prph(fwrt->trans, DBGC_OUT_CTRL, 0); + /* wait before we collect the data till the DBGC stop */ + udelay(500); + + iwl_fw_error_dump(fwrt); + + /* start recording again if the firmware is not crashed */ + if (!test_bit(STATUS_FW_ERROR, &fwrt->trans->status) && + fwrt->fw->dbg_dest_tlv) { + iwl_write_prph(fwrt->trans, DBGC_IN_SAMPLE, in_sample); + iwl_write_prph(fwrt->trans, DBGC_OUT_CTRL, out_ctrl); + } + } + + if (fwrt->ops && fwrt->ops->dump_end) + fwrt->ops->dump_end(fwrt->ops_ctx); +} + diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h new file mode 100644 index 000000000000..0f810ea89d31 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -0,0 +1,214 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_dbg_h__ +#define __iwl_fw_dbg_h__ +#include +#include +#include "runtime.h" +#include "file.h" +#include "error-dump.h" + +/** + * struct iwl_fw_dump_desc - describes the dump + * @len: length of trig_desc->data + * @trig_desc: the description of the dump + */ +struct iwl_fw_dump_desc { + size_t len; + /* must be last */ + struct iwl_fw_error_dump_trigger_desc trig_desc; +}; + +extern const struct iwl_fw_dump_desc iwl_dump_desc_assert; + +static inline void iwl_fw_free_dump_desc(struct iwl_fw_runtime *fwrt) +{ + if (fwrt->dump.desc != &iwl_dump_desc_assert) + kfree(fwrt->dump.desc); + fwrt->dump.desc = NULL; +} + +void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt); +int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, + const struct iwl_fw_dump_desc *desc, + const struct iwl_fw_dbg_trigger_tlv *trigger); +int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, + enum iwl_fw_dbg_trigger trig, + const char *str, size_t len, + const struct iwl_fw_dbg_trigger_tlv *trigger); +int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt, + struct iwl_fw_dbg_trigger_tlv *trigger, + const char *fmt, ...) __printf(3, 4); +int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 id); + +#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ + void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \ + unlikely(__dbg_trigger); \ +}) + +static inline struct iwl_fw_dbg_trigger_tlv* +_iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id) +{ + return fw->dbg_trigger_tlv[id]; +} + +#define iwl_fw_dbg_get_trigger(fw, id) ({ \ + BUILD_BUG_ON(!__builtin_constant_p(id)); \ + BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ + _iwl_fw_dbg_get_trigger((fw), (id)); \ +}) + +static inline bool +iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, + struct wireless_dev *wdev) +{ + u32 trig_vif = le32_to_cpu(trig->vif_type); + + return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || + wdev->iftype == trig_vif; +} + +static inline bool +iwl_fw_dbg_trigger_stop_conf_match(struct iwl_fw_runtime *fwrt, + struct iwl_fw_dbg_trigger_tlv *trig) +{ + return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && + (fwrt->dump.conf == FW_DBG_INVALID || + (BIT(fwrt->dump.conf) & le32_to_cpu(trig->stop_conf_ids)))); +} + +static inline bool +iwl_fw_dbg_no_trig_window(struct iwl_fw_runtime *fwrt, + struct iwl_fw_dbg_trigger_tlv *trig) +{ + unsigned long wind_jiff = + msecs_to_jiffies(le16_to_cpu(trig->trig_dis_ms)); + u32 id = le32_to_cpu(trig->id); + + /* If this is the first event checked, jump to update start ts */ + if (fwrt->dump.non_collect_ts_start[id] && + (time_after(fwrt->dump.non_collect_ts_start[id] + wind_jiff, + jiffies))) + return true; + + fwrt->dump.non_collect_ts_start[id] = jiffies; + return false; +} + +static inline bool +iwl_fw_dbg_trigger_check_stop(struct iwl_fw_runtime *fwrt, + struct wireless_dev *wdev, + struct iwl_fw_dbg_trigger_tlv *trig) +{ + if (wdev && !iwl_fw_dbg_trigger_vif_match(trig, wdev)) + return false; + + if (iwl_fw_dbg_no_trig_window(fwrt, trig)) { + IWL_WARN(fwrt, "Trigger %d occurred while no-collect window.\n", + trig->id); + return false; + } + + return iwl_fw_dbg_trigger_stop_conf_match(fwrt, trig); +} + +static inline void +_iwl_fw_dbg_trigger_simple_stop(struct iwl_fw_runtime *fwrt, + struct wireless_dev *wdev, + struct iwl_fw_dbg_trigger_tlv *trigger) +{ + if (!trigger) + return; + + if (!iwl_fw_dbg_trigger_check_stop(fwrt, wdev, trigger)) + return; + + iwl_fw_dbg_collect_trig(fwrt, trigger, NULL); +} + +#define iwl_fw_dbg_trigger_simple_stop(fwrt, wdev, trig) \ + _iwl_fw_dbg_trigger_simple_stop((fwrt), (wdev), \ + iwl_fw_dbg_get_trigger((fwrt)->fw,\ + (trig))) + +static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt) +{ + fwrt->dump.conf = FW_DBG_INVALID; +} + +void iwl_fw_error_dump_wk(struct work_struct *work); + +static inline void iwl_fw_flush_dump(struct iwl_fw_runtime *fwrt) +{ + flush_delayed_work(&fwrt->dump.wk); +} + +static inline void iwl_fw_cancel_dump(struct iwl_fw_runtime *fwrt) +{ + cancel_delayed_work_sync(&fwrt->dump.wk); +} + +#endif /* __iwl_fw_dbg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/init.c b/drivers/net/wireless/intel/iwlwifi/fw/init.c new file mode 100644 index 000000000000..bfe5316bbb6a --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/init.c @@ -0,0 +1,75 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include "iwl-drv.h" +#include "runtime.h" +#include "dbg.h" + +void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, + const struct iwl_fw *fw, + const struct iwl_fw_runtime_ops *ops, void *ops_ctx) +{ + memset(fwrt, 0, sizeof(*fwrt)); + fwrt->trans = trans; + fwrt->fw = fw; + fwrt->dev = trans->dev; + fwrt->dump.conf = FW_DBG_INVALID; + fwrt->ops = ops; + fwrt->ops_ctx = ops_ctx; + INIT_DELAYED_WORK(&fwrt->dump.wk, iwl_fw_error_dump_wk); +} +IWL_EXPORT_SYMBOL(iwl_fw_runtime_init); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index c483a76cb0e5..02f1bc985383 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -63,6 +63,11 @@ #include "img.h" #include "api.h" +struct iwl_fw_runtime_ops { + int (*dump_start)(void *ctx); + void (*dump_end)(void *ctx); +}; + #define MAX_NUM_LMAC 2 struct iwl_fwrt_shared_mem_cfg { int num_lmacs; @@ -76,23 +81,36 @@ struct iwl_fwrt_shared_mem_cfg { u32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; }; +enum iwl_fw_runtime_status { + IWL_FWRT_STATUS_DUMPING = 0, +}; + /** * struct iwl_fw_runtime - runtime data for firmware * @fw: firmware image * @cfg: NIC configuration * @dev: device pointer + * @ops: user ops + * @ops_ctx: user ops context + * @status: status flags * @fw_paging_db: paging database * @num_of_paging_blk: number of paging blocks * @num_of_pages_in_last_blk: number of pages in the last block * @smem_cfg: saved firmware SMEM configuration * @cur_fw_img: current firmware image, must be maintained by * the driver by calling &iwl_fw_set_current_image() + * @dump: debug dump data */ struct iwl_fw_runtime { struct iwl_trans *trans; const struct iwl_fw *fw; struct device *dev; + const struct iwl_fw_runtime_ops *ops; + void *ops_ctx; + + unsigned long status; + /* Paging */ struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS]; u16 num_of_paging_blk; @@ -102,17 +120,23 @@ struct iwl_fw_runtime { /* memory configuration */ struct iwl_fwrt_shared_mem_cfg smem_cfg; + + /* debug */ + struct { + const struct iwl_fw_dump_desc *desc; + const struct iwl_fw_dbg_trigger_tlv *trig; + struct delayed_work wk; + + u8 conf; + + /* ts of the beginning of a non-collect fw dbg data period */ + unsigned long non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1]; + } dump; }; -static inline void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, - struct iwl_trans *trans, - const struct iwl_fw *fw) -{ - memset(fwrt, 0, sizeof(*fwrt)); - fwrt->trans = trans; - fwrt->fw = fw; - fwrt->dev = trans->dev; -} +void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, + const struct iwl_fw *fw, + const struct iwl_fw_runtime_ops *ops, void *ops_ctx); static inline void iwl_fw_set_current_image(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type cur_fw_img) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index 83ac807e547d..00e6737dda72 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -6,7 +6,7 @@ iwlmvm-y += power.o coex.o iwlmvm-y += tt.o offloading.o tdls.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o -iwlmvm-y += tof.o fw-dbg.o +iwlmvm-y += tof.o iwlmvm-$(CONFIG_PM) += d3.o ccflags-y += -I$(src)/../ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index 6fda8627b726..21845034d80d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -111,7 +111,6 @@ #define IWL_MVM_SW_TX_CSUM_OFFLOAD 0 #define IWL_MVM_HW_CSUM_DISABLE 0 #define IWL_MVM_PARSE_NVM 0 -#define IWL_MVM_COLLECT_FW_ERR_DUMP 1 #define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 9991494314e2..29f1d1807415 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -69,7 +69,6 @@ #include #include "mvm.h" -#include "fw-dbg.h" #include "sta.h" #include "iwl-io.h" #include "debugfs.h" @@ -1123,7 +1122,7 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file, int pos = 0; mutex_lock(&mvm->mutex); - conf = mvm->fw_dbg_conf; + conf = mvm->fwrt.dump.conf; mutex_unlock(&mvm->mutex); pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf); @@ -1190,7 +1189,7 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm, return -EINVAL; mutex_lock(&mvm->mutex); - ret = iwl_mvm_start_fw_dbg_conf(mvm, conf_id); + ret = iwl_fw_start_dbg_conf(&mvm->fwrt, conf_id); mutex_unlock(&mvm->mutex); return ret ?: count; @@ -1211,8 +1210,8 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, if (count == 0) return 0; - iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, buf, - (count - 1), NULL); + iwl_fw_dbg_collect(&mvm->fwrt, FW_DBG_TRIGGER_USER, buf, + (count - 1), NULL); iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c deleted file mode 100644 index 0c12e604f22b..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ /dev/null @@ -1,1031 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include - -#include "fw-dbg.h" -#include "iwl-io.h" -#include "mvm.h" -#include "iwl-prph.h" -#include "iwl-csr.h" - -#define RADIO_REG_MAX_READ 0x2ad -static void iwl_mvm_read_radio_reg(struct iwl_mvm *mvm, - struct iwl_fw_error_dump_data **dump_data) -{ - u8 *pos = (void *)(*dump_data)->data; - unsigned long flags; - int i; - - if (!iwl_trans_grab_nic_access(mvm->trans, &flags)) - return; - - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG); - (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ); - - for (i = 0; i < RADIO_REG_MAX_READ; i++) { - u32 rd_cmd = RADIO_RSP_RD_CMD; - - rd_cmd |= i << RADIO_RSP_ADDR_POS; - iwl_write_prph_no_grab(mvm->trans, RSP_RADIO_CMD, rd_cmd); - *pos = (u8)iwl_read_prph_no_grab(mvm->trans, RSP_RADIO_RDDAT); - - pos++; - } - - *dump_data = iwl_fw_error_next_data(*dump_data); - - iwl_trans_release_nic_access(mvm->trans, &flags); -} - -static void iwl_mvm_dump_rxf(struct iwl_mvm *mvm, - struct iwl_fw_error_dump_data **dump_data, - int size, u32 offset, int fifo_num) -{ - struct iwl_fw_error_dump_fifo *fifo_hdr; - u32 *fifo_data; - u32 fifo_len; - int i; - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = size; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - return; - - /* Add a TLV for the RXF */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(fifo_num); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_D_SPACE + offset)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_WR_PTR + offset)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_RD_PTR + offset)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_FENCE_PTR + offset)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_SET_FENCE_MODE + offset)); - - /* Lock fence */ - iwl_trans_write_prph(mvm->trans, RXF_SET_FENCE_MODE + offset, 0x1); - /* Set fence pointer to the same place like WR pointer */ - iwl_trans_write_prph(mvm->trans, RXF_LD_WR2FENCE + offset, 0x1); - /* Set fence offset */ - iwl_trans_write_prph(mvm->trans, - RXF_LD_FENCE_OFFSET_ADDR + offset, 0x0); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (i = 0; i < fifo_len; i++) - fifo_data[i] = iwl_trans_read_prph(mvm->trans, - RXF_FIFO_RD_FENCE_INC + - offset); - *dump_data = iwl_fw_error_next_data(*dump_data); -} - -static void iwl_mvm_dump_txf(struct iwl_mvm *mvm, - struct iwl_fw_error_dump_data **dump_data, - int size, u32 offset, int fifo_num) -{ - struct iwl_fw_error_dump_fifo *fifo_hdr; - u32 *fifo_data; - u32 fifo_len; - int i; - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = size; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - return; - - /* Add a TLV for the FIFO */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(fifo_num); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FIFO_ITEM_CNT + offset)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_WR_PTR + offset)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_RD_PTR + offset)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FENCE_PTR + offset)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_LOCK_FENCE + offset)); - - /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ - iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR + offset, - TXF_WR_PTR + offset); - - /* Dummy-read to advance the read pointer to the head */ - iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA + offset); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (i = 0; i < fifo_len; i++) - fifo_data[i] = iwl_trans_read_prph(mvm->trans, - TXF_READ_MODIFY_DATA + - offset); - *dump_data = iwl_fw_error_next_data(*dump_data); -} - -static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, - struct iwl_fw_error_dump_data **dump_data) -{ - struct iwl_fw_error_dump_fifo *fifo_hdr; - struct iwl_fwrt_shared_mem_cfg *cfg = &mvm->fwrt.smem_cfg; - u32 *fifo_data; - u32 fifo_len; - unsigned long flags; - int i, j; - - if (!iwl_trans_grab_nic_access(mvm->trans, &flags)) - return; - - /* Pull RXF1 */ - iwl_mvm_dump_rxf(mvm, dump_data, cfg->lmac[0].rxfifo1_size, 0, 0); - /* Pull RXF2 */ - iwl_mvm_dump_rxf(mvm, dump_data, cfg->rxfifo2_size, - RXF_DIFF_FROM_PREV, 1); - /* Pull LMAC2 RXF1 */ - if (mvm->fwrt.smem_cfg.num_lmacs > 1) - iwl_mvm_dump_rxf(mvm, dump_data, cfg->lmac[1].rxfifo1_size, - LMAC2_PRPH_OFFSET, 2); - - /* Pull TXF data from LMAC1 */ - for (i = 0; i < mvm->fwrt.smem_cfg.num_txfifo_entries; i++) { - /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); - iwl_mvm_dump_txf(mvm, dump_data, cfg->lmac[0].txfifo_size[i], - 0, i); - } - - /* Pull TXF data from LMAC2 */ - if (mvm->fwrt.smem_cfg.num_lmacs > 1) { - for (i = 0; i < mvm->fwrt.smem_cfg.num_txfifo_entries; i++) { - /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(mvm->trans, - TXF_LARC_NUM + LMAC2_PRPH_OFFSET, - i); - iwl_mvm_dump_txf(mvm, dump_data, - cfg->lmac[1].txfifo_size[i], - LMAC2_PRPH_OFFSET, - i + cfg->num_txfifo_entries); - } - } - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { - /* Pull UMAC internal TXF data from all TXFs */ - for (i = 0; - i < ARRAY_SIZE(mvm->fwrt.smem_cfg.internal_txfifo_size); - i++) { - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->fwrt.smem_cfg.internal_txfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the internal FIFOs */ - (*dump_data)->type = - cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF); - (*dump_data)->len = - cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - - /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(mvm->trans, TXF_CPU2_NUM, i + - mvm->fwrt.smem_cfg.num_txfifo_entries); - - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_CPU2_FIFO_ITEM_CNT)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_CPU2_WR_PTR)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_CPU2_RD_PTR)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_CPU2_FENCE_PTR)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_CPU2_LOCK_FENCE)); - - /* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */ - iwl_trans_write_prph(mvm->trans, - TXF_CPU2_READ_MODIFY_ADDR, - TXF_CPU2_WR_PTR); - - /* Dummy-read to advance the read pointer to head */ - iwl_trans_read_prph(mvm->trans, - TXF_CPU2_READ_MODIFY_DATA); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = - iwl_trans_read_prph(mvm->trans, - TXF_CPU2_READ_MODIFY_DATA); - *dump_data = iwl_fw_error_next_data(*dump_data); - } - } - - iwl_trans_release_nic_access(mvm->trans, &flags); -} - -void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) -{ - if (mvm->fw_dump_desc != &iwl_mvm_dump_desc_assert) - kfree(mvm->fw_dump_desc); - mvm->fw_dump_desc = NULL; -} - -#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ -#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ - -struct iwl_prph_range { - u32 start, end; -}; - -static const struct iwl_prph_range iwl_prph_dump_addr_comm[] = { - { .start = 0x00a00000, .end = 0x00a00000 }, - { .start = 0x00a0000c, .end = 0x00a00024 }, - { .start = 0x00a0002c, .end = 0x00a0003c }, - { .start = 0x00a00410, .end = 0x00a00418 }, - { .start = 0x00a00420, .end = 0x00a00420 }, - { .start = 0x00a00428, .end = 0x00a00428 }, - { .start = 0x00a00430, .end = 0x00a0043c }, - { .start = 0x00a00444, .end = 0x00a00444 }, - { .start = 0x00a004c0, .end = 0x00a004cc }, - { .start = 0x00a004d8, .end = 0x00a004d8 }, - { .start = 0x00a004e0, .end = 0x00a004f0 }, - { .start = 0x00a00840, .end = 0x00a00840 }, - { .start = 0x00a00850, .end = 0x00a00858 }, - { .start = 0x00a01004, .end = 0x00a01008 }, - { .start = 0x00a01010, .end = 0x00a01010 }, - { .start = 0x00a01018, .end = 0x00a01018 }, - { .start = 0x00a01024, .end = 0x00a01024 }, - { .start = 0x00a0102c, .end = 0x00a01034 }, - { .start = 0x00a0103c, .end = 0x00a01040 }, - { .start = 0x00a01048, .end = 0x00a01094 }, - { .start = 0x00a01c00, .end = 0x00a01c20 }, - { .start = 0x00a01c58, .end = 0x00a01c58 }, - { .start = 0x00a01c7c, .end = 0x00a01c7c }, - { .start = 0x00a01c28, .end = 0x00a01c54 }, - { .start = 0x00a01c5c, .end = 0x00a01c5c }, - { .start = 0x00a01c60, .end = 0x00a01cdc }, - { .start = 0x00a01ce0, .end = 0x00a01d0c }, - { .start = 0x00a01d18, .end = 0x00a01d20 }, - { .start = 0x00a01d2c, .end = 0x00a01d30 }, - { .start = 0x00a01d40, .end = 0x00a01d5c }, - { .start = 0x00a01d80, .end = 0x00a01d80 }, - { .start = 0x00a01d98, .end = 0x00a01d9c }, - { .start = 0x00a01da8, .end = 0x00a01da8 }, - { .start = 0x00a01db8, .end = 0x00a01df4 }, - { .start = 0x00a01dc0, .end = 0x00a01dfc }, - { .start = 0x00a01e00, .end = 0x00a01e2c }, - { .start = 0x00a01e40, .end = 0x00a01e60 }, - { .start = 0x00a01e68, .end = 0x00a01e6c }, - { .start = 0x00a01e74, .end = 0x00a01e74 }, - { .start = 0x00a01e84, .end = 0x00a01e90 }, - { .start = 0x00a01e9c, .end = 0x00a01ec4 }, - { .start = 0x00a01ed0, .end = 0x00a01ee0 }, - { .start = 0x00a01f00, .end = 0x00a01f1c }, - { .start = 0x00a01f44, .end = 0x00a01ffc }, - { .start = 0x00a02000, .end = 0x00a02048 }, - { .start = 0x00a02068, .end = 0x00a020f0 }, - { .start = 0x00a02100, .end = 0x00a02118 }, - { .start = 0x00a02140, .end = 0x00a0214c }, - { .start = 0x00a02168, .end = 0x00a0218c }, - { .start = 0x00a021c0, .end = 0x00a021c0 }, - { .start = 0x00a02400, .end = 0x00a02410 }, - { .start = 0x00a02418, .end = 0x00a02420 }, - { .start = 0x00a02428, .end = 0x00a0242c }, - { .start = 0x00a02434, .end = 0x00a02434 }, - { .start = 0x00a02440, .end = 0x00a02460 }, - { .start = 0x00a02468, .end = 0x00a024b0 }, - { .start = 0x00a024c8, .end = 0x00a024cc }, - { .start = 0x00a02500, .end = 0x00a02504 }, - { .start = 0x00a0250c, .end = 0x00a02510 }, - { .start = 0x00a02540, .end = 0x00a02554 }, - { .start = 0x00a02580, .end = 0x00a025f4 }, - { .start = 0x00a02600, .end = 0x00a0260c }, - { .start = 0x00a02648, .end = 0x00a02650 }, - { .start = 0x00a02680, .end = 0x00a02680 }, - { .start = 0x00a026c0, .end = 0x00a026d0 }, - { .start = 0x00a02700, .end = 0x00a0270c }, - { .start = 0x00a02804, .end = 0x00a02804 }, - { .start = 0x00a02818, .end = 0x00a0281c }, - { .start = 0x00a02c00, .end = 0x00a02db4 }, - { .start = 0x00a02df4, .end = 0x00a02fb0 }, - { .start = 0x00a03000, .end = 0x00a03014 }, - { .start = 0x00a0301c, .end = 0x00a0302c }, - { .start = 0x00a03034, .end = 0x00a03038 }, - { .start = 0x00a03040, .end = 0x00a03048 }, - { .start = 0x00a03060, .end = 0x00a03068 }, - { .start = 0x00a03070, .end = 0x00a03074 }, - { .start = 0x00a0307c, .end = 0x00a0307c }, - { .start = 0x00a03080, .end = 0x00a03084 }, - { .start = 0x00a0308c, .end = 0x00a03090 }, - { .start = 0x00a03098, .end = 0x00a03098 }, - { .start = 0x00a030a0, .end = 0x00a030a0 }, - { .start = 0x00a030a8, .end = 0x00a030b4 }, - { .start = 0x00a030bc, .end = 0x00a030bc }, - { .start = 0x00a030c0, .end = 0x00a0312c }, - { .start = 0x00a03c00, .end = 0x00a03c5c }, - { .start = 0x00a04400, .end = 0x00a04454 }, - { .start = 0x00a04460, .end = 0x00a04474 }, - { .start = 0x00a044c0, .end = 0x00a044ec }, - { .start = 0x00a04500, .end = 0x00a04504 }, - { .start = 0x00a04510, .end = 0x00a04538 }, - { .start = 0x00a04540, .end = 0x00a04548 }, - { .start = 0x00a04560, .end = 0x00a0457c }, - { .start = 0x00a04590, .end = 0x00a04598 }, - { .start = 0x00a045c0, .end = 0x00a045f4 }, -}; - -static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = { - { .start = 0x00a05c00, .end = 0x00a05c18 }, - { .start = 0x00a05400, .end = 0x00a056e8 }, - { .start = 0x00a08000, .end = 0x00a098bc }, - { .start = 0x00a02400, .end = 0x00a02758 }, -}; - -static void _iwl_read_prph_block(struct iwl_trans *trans, u32 start, - u32 len_bytes, __le32 *data) -{ - u32 i; - - for (i = 0; i < len_bytes; i += 4) - *data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i)); -} - -static bool iwl_read_prph_block(struct iwl_trans *trans, u32 start, - u32 len_bytes, __le32 *data) -{ - unsigned long flags; - bool success = false; - - if (iwl_trans_grab_nic_access(trans, &flags)) { - success = true; - _iwl_read_prph_block(trans, start, len_bytes, data); - iwl_trans_release_nic_access(trans, &flags); - } - - return success; -} - -static void iwl_dump_prph(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data, - const struct iwl_prph_range *iwl_prph_dump_addr, - u32 range_len) -{ - struct iwl_fw_error_dump_prph *prph; - unsigned long flags; - u32 i; - - if (!iwl_trans_grab_nic_access(trans, &flags)) - return; - - for (i = 0; i < range_len; i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); - (*data)->len = cpu_to_le32(sizeof(*prph) + - num_bytes_in_chunk); - prph = (void *)(*data)->data; - prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); - - _iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start, - /* our range is inclusive, hence + 4 */ - iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4, - (void *)prph->data); - - *data = iwl_fw_error_next_data(*data); - } - - iwl_trans_release_nic_access(trans, &flags); -} - -/* - * alloc_sgtable - allocates scallerlist table in the given size, - * fills it with pages and returns it - * @size: the size (in bytes) of the table -*/ -static struct scatterlist *alloc_sgtable(int size) -{ - int alloc_size, nents, i; - struct page *new_page; - struct scatterlist *iter; - struct scatterlist *table; - - nents = DIV_ROUND_UP(size, PAGE_SIZE); - table = kcalloc(nents, sizeof(*table), GFP_KERNEL); - if (!table) - return NULL; - sg_init_table(table, nents); - iter = table; - for_each_sg(table, iter, sg_nents(table), i) { - new_page = alloc_page(GFP_KERNEL); - if (!new_page) { - /* release all previous allocated pages in the table */ - iter = table; - for_each_sg(table, iter, sg_nents(table), i) { - new_page = sg_page(iter); - if (new_page) - __free_page(new_page); - } - return NULL; - } - alloc_size = min_t(int, size, PAGE_SIZE); - size -= PAGE_SIZE; - sg_set_page(iter, new_page, alloc_size, 0); - } - return table; -} - -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) -{ - struct iwl_fw_error_dump_file *dump_file; - struct iwl_fw_error_dump_data *dump_data; - struct iwl_fw_error_dump_info *dump_info; - struct iwl_fw_error_dump_mem *dump_mem; - struct iwl_fw_error_dump_trigger_desc *dump_trig; - struct iwl_mvm_dump_ptrs *fw_error_dump; - struct scatterlist *sg_dump_data; - u32 sram_len, sram_ofs; - const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem = mvm->fw->dbg_mem_tlv; - u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0; - u32 smem_len = mvm->fw->n_dbg_mem_tlv ? 0 : mvm->cfg->smem_len; - u32 sram2_len = mvm->fw->n_dbg_mem_tlv ? 0 : mvm->cfg->dccm2_len; - bool monitor_dump_only = false; - int i; - - if (!IWL_MVM_COLLECT_FW_ERR_DUMP && - !mvm->trans->dbg_dest_tlv) - return; - - lockdep_assert_held(&mvm->mutex); - - /* there's no point in fw dump if the bus is dead */ - if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { - IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); - goto out; - } - - if (mvm->fw_dump_trig && - mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) - monitor_dump_only = true; - - fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); - if (!fw_error_dump) - goto out; - - /* SRAM - include stack CCM if driver knows the values for it */ - if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { - const struct fw_img *img; - - img = &mvm->fw->img[mvm->fwrt.cur_fw_img]; - sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - } else { - sram_ofs = mvm->cfg->dccm_offset; - sram_len = mvm->cfg->dccm_len; - } - - /* reading RXF/TXF sizes */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { - struct iwl_fwrt_shared_mem_cfg *mem_cfg = &mvm->fwrt.smem_cfg; - - fifo_data_len = 0; - - /* Count RXF2 size */ - if (mem_cfg->rxfifo2_size) { - /* Add header info */ - fifo_data_len += mem_cfg->rxfifo2_size + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - - /* Count RXF1 sizes */ - for (i = 0; i < mem_cfg->num_lmacs; i++) { - if (!mem_cfg->lmac[i].rxfifo1_size) - continue; - - /* Add header info */ - fifo_data_len += mem_cfg->lmac[i].rxfifo1_size + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - - /* Count TXF sizes */ - for (i = 0; i < mem_cfg->num_lmacs; i++) { - int j; - - for (j = 0; j < mem_cfg->num_txfifo_entries; j++) { - if (!mem_cfg->lmac[i].txfifo_size[j]) - continue; - - /* Add header info */ - fifo_data_len += - mem_cfg->lmac[i].txfifo_size[j] + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - } - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { - for (i = 0; - i < ARRAY_SIZE(mem_cfg->internal_txfifo_size); - i++) { - if (!mem_cfg->internal_txfifo_size[i]) - continue; - - /* Add header info */ - fifo_data_len += - mem_cfg->internal_txfifo_size[i] + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - } - - /* Make room for PRPH registers */ - if (!mvm->trans->cfg->gen2) { - for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr_comm); - i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = - iwl_prph_dump_addr_comm[i].end - - iwl_prph_dump_addr_comm[i].start + 4; - - prph_len += sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_prph) + - num_bytes_in_chunk; - } - } - - if (!mvm->trans->cfg->gen2 && mvm->cfg->mq_rx_supported) { - for (i = 0; i < - ARRAY_SIZE(iwl_prph_dump_addr_9000); i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = - iwl_prph_dump_addr_9000[i].end - - iwl_prph_dump_addr_9000[i].start + 4; - - prph_len += sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_prph) + - num_bytes_in_chunk; - } - } - - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) - radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ; - } - - file_len = sizeof(*dump_file) + - sizeof(*dump_data) * 2 + - fifo_data_len + - prph_len + - radio_len + - sizeof(*dump_info); - - /* Make room for the SMEM, if it exists */ - if (smem_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; - - /* Make room for the secondary SRAM, if it exists */ - if (sram2_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; - - /* Make room for MEM segments */ - for (i = 0; i < mvm->fw->n_dbg_mem_tlv; i++) { - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + - le32_to_cpu(fw_dbg_mem[i].len); - } - - /* Make room for fw's virtual image pages, if it exists */ - if (!mvm->trans->cfg->gen2 && - mvm->fw->img[mvm->fwrt.cur_fw_img].paging_mem_size && - mvm->fwrt.fw_paging_db[0].fw_paging_block) - file_len += mvm->fwrt.num_of_paging_blk * - (sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_paging) + - PAGING_BLOCK_SIZE); - - /* If we only want a monitor dump, reset the file length */ - if (monitor_dump_only) { - file_len = sizeof(*dump_file) + sizeof(*dump_data) + - sizeof(*dump_info); - } - - if (mvm->fw_dump_desc) - file_len += sizeof(*dump_data) + sizeof(*dump_trig) + - mvm->fw_dump_desc->len; - - if (!mvm->fw->n_dbg_mem_tlv) - file_len += sram_len + sizeof(*dump_mem); - - dump_file = vzalloc(file_len); - if (!dump_file) { - kfree(fw_error_dump); - goto out; - } - - fw_error_dump->op_mode_ptr = dump_file; - - dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); - dump_data = (void *)dump_file->data; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_info)); - dump_info = (void *)dump_data->data; - dump_info->device_family = - mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); - dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); - memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, - sizeof(dump_info->fw_human_readable)); - strncpy(dump_info->dev_human_readable, mvm->cfg->name, - sizeof(dump_info->dev_human_readable)); - strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, - sizeof(dump_info->bus_human_readable)); - - dump_data = iwl_fw_error_next_data(dump_data); - /* We only dump the FIFOs if the FW is in error state */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { - iwl_mvm_dump_fifos(mvm, &dump_data); - if (radio_len) - iwl_mvm_read_radio_reg(mvm, &dump_data); - } - - if (mvm->fw_dump_desc) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_trig) + - mvm->fw_dump_desc->len); - dump_trig = (void *)dump_data->data; - memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, - sizeof(*dump_trig) + mvm->fw_dump_desc->len); - - dump_data = iwl_fw_error_next_data(dump_data); - } - - /* In case we only want monitor dump, skip to dump trasport data */ - if (monitor_dump_only) - goto dump_trans_data; - - if (!mvm->fw->n_dbg_mem_tlv) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(sram_ofs); - iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, - sram_len); - dump_data = iwl_fw_error_next_data(dump_data); - } - - for (i = 0; i < mvm->fw->n_dbg_mem_tlv; i++) { - u32 len = le32_to_cpu(fw_dbg_mem[i].len); - u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs); - bool success; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = fw_dbg_mem[i].data_type; - dump_mem->offset = cpu_to_le32(ofs); - - switch (dump_mem->type & cpu_to_le32(FW_DBG_MEM_TYPE_MASK)) { - case cpu_to_le32(FW_DBG_MEM_TYPE_REGULAR): - iwl_trans_read_mem_bytes(mvm->trans, ofs, - dump_mem->data, - len); - success = true; - break; - case cpu_to_le32(FW_DBG_MEM_TYPE_PRPH): - success = iwl_read_prph_block(mvm->trans, ofs, len, - (void *)dump_mem->data); - break; - default: - /* - * shouldn't get here, we ignored this kind - * of TLV earlier during the TLV parsing?! - */ - WARN_ON(1); - success = false; - } - - if (success) - dump_data = iwl_fw_error_next_data(dump_data); - } - - if (smem_len) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); - dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); - iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, - dump_mem->data, smem_len); - dump_data = iwl_fw_error_next_data(dump_data); - } - - if (sram2_len) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); - iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, - dump_mem->data, sram2_len); - dump_data = iwl_fw_error_next_data(dump_data); - } - - /* Dump fw's virtual image */ - if (!mvm->trans->cfg->gen2 && - mvm->fw->img[mvm->fwrt.cur_fw_img].paging_mem_size && - mvm->fwrt.fw_paging_db[0].fw_paging_block) { - for (i = 1; i < mvm->fwrt.num_of_paging_blk + 1; i++) { - struct iwl_fw_error_dump_paging *paging; - struct page *pages = - mvm->fwrt.fw_paging_db[i].fw_paging_block; - dma_addr_t addr = mvm->fwrt.fw_paging_db[i].fw_paging_phys; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); - dump_data->len = cpu_to_le32(sizeof(*paging) + - PAGING_BLOCK_SIZE); - paging = (void *)dump_data->data; - paging->index = cpu_to_le32(i); - dma_sync_single_for_cpu(mvm->trans->dev, addr, - PAGING_BLOCK_SIZE, - DMA_BIDIRECTIONAL); - memcpy(paging->data, page_address(pages), - PAGING_BLOCK_SIZE); - dump_data = iwl_fw_error_next_data(dump_data); - } - } - - if (prph_len) { - iwl_dump_prph(mvm->trans, &dump_data, - iwl_prph_dump_addr_comm, - ARRAY_SIZE(iwl_prph_dump_addr_comm)); - - if (mvm->cfg->mq_rx_supported) - iwl_dump_prph(mvm->trans, &dump_data, - iwl_prph_dump_addr_9000, - ARRAY_SIZE(iwl_prph_dump_addr_9000)); - } - -dump_trans_data: - fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, - mvm->fw_dump_trig); - fw_error_dump->op_mode_len = file_len; - if (fw_error_dump->trans_ptr) - file_len += fw_error_dump->trans_ptr->len; - dump_file->file_len = cpu_to_le32(file_len); - - sg_dump_data = alloc_sgtable(file_len); - if (sg_dump_data) { - sg_pcopy_from_buffer(sg_dump_data, - sg_nents(sg_dump_data), - fw_error_dump->op_mode_ptr, - fw_error_dump->op_mode_len, 0); - if (fw_error_dump->trans_ptr) - sg_pcopy_from_buffer(sg_dump_data, - sg_nents(sg_dump_data), - fw_error_dump->trans_ptr->data, - fw_error_dump->trans_ptr->len, - fw_error_dump->op_mode_len); - dev_coredumpsg(mvm->trans->dev, sg_dump_data, file_len, - GFP_KERNEL); - } - vfree(fw_error_dump->op_mode_ptr); - vfree(fw_error_dump->trans_ptr); - kfree(fw_error_dump); - -out: - iwl_mvm_free_fw_dump_desc(mvm); - mvm->fw_dump_trig = NULL; - clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); -} - -const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { - .trig_desc = { - .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), - }, -}; - -int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - const struct iwl_mvm_dump_desc *desc, - const struct iwl_fw_dbg_trigger_tlv *trigger) -{ - unsigned int delay = 0; - - if (trigger) - delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); - - if (WARN(mvm->trans->state == IWL_TRANS_NO_FW, - "Can't collect dbg data when FW isn't alive\n")) - return -EIO; - - if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) - return -EBUSY; - - if (WARN_ON(mvm->fw_dump_desc)) - iwl_mvm_free_fw_dump_desc(mvm); - - IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", - le32_to_cpu(desc->trig_desc.type)); - - mvm->fw_dump_desc = desc; - mvm->fw_dump_trig = trigger; - - schedule_delayed_work(&mvm->fw_dump_wk, delay); - - return 0; -} - -int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, - const char *str, size_t len, - const struct iwl_fw_dbg_trigger_tlv *trigger) -{ - struct iwl_mvm_dump_desc *desc; - - desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); - if (!desc) - return -ENOMEM; - - desc->len = len; - desc->trig_desc.type = cpu_to_le32(trig); - memcpy(desc->trig_desc.data, str, len); - - return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger); -} - -int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trigger, - const char *fmt, ...) -{ - u16 occurrences = le16_to_cpu(trigger->occurrences); - int ret, len = 0; - char buf[64]; - - if (!occurrences) - return 0; - - if (fmt) { - va_list ap; - - buf[sizeof(buf) - 1] = '\0'; - - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - /* check for truncation */ - if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) - buf[sizeof(buf) - 1] = '\0'; - - len = strlen(buf) + 1; - } - - ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len, - trigger); - - if (ret) - return ret; - - trigger->occurrences = cpu_to_le16(occurrences - 1); - return 0; -} - -int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) -{ - u8 *ptr; - int ret; - int i; - - if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv), - "Invalid configuration %d\n", conf_id)) - return -EINVAL; - - /* EARLY START - firmware's configuration is hard coded */ - if ((!mvm->fw->dbg_conf_tlv[conf_id] || - !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && - conf_id == FW_DBG_START_FROM_ALIVE) - return 0; - - if (!mvm->fw->dbg_conf_tlv[conf_id]) - return -EINVAL; - - if (mvm->fw_dbg_conf != FW_DBG_INVALID) - IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n", - mvm->fw_dbg_conf); - - /* Send all HCMDs for configuring the FW debug */ - ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd; - for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { - struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; - - ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0, - le16_to_cpu(cmd->len), cmd->data); - if (ret) - return ret; - - ptr += sizeof(*cmd); - ptr += le16_to_cpu(cmd->len); - } - - mvm->fw_dbg_conf = conf_id; - - return 0; -} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h deleted file mode 100644 index 4a5287a0c617..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h +++ /dev/null @@ -1,175 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __mvm_fw_dbg_h__ -#define __mvm_fw_dbg_h__ -#include "fw/file.h" -#include "fw/error-dump.h" -#include "mvm.h" - -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); -void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); -int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - const struct iwl_mvm_dump_desc *desc, - const struct iwl_fw_dbg_trigger_tlv *trigger); -int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, - const char *str, size_t len, - const struct iwl_fw_dbg_trigger_tlv *trigger); -int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trigger, - const char *fmt, ...) __printf(3, 4); -int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id); - -#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ - void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \ - unlikely(__dbg_trigger); \ -}) - -static inline struct iwl_fw_dbg_trigger_tlv* -_iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id) -{ - return fw->dbg_trigger_tlv[id]; -} - -#define iwl_fw_dbg_get_trigger(fw, id) ({ \ - BUILD_BUG_ON(!__builtin_constant_p(id)); \ - BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ - _iwl_fw_dbg_get_trigger((fw), (id)); \ -}) - -static inline bool -iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, - struct ieee80211_vif *vif) -{ - u32 trig_vif = le32_to_cpu(trig->vif_type); - - return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || - ieee80211_vif_type_p2p(vif) == trig_vif; -} - -static inline bool -iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trig) -{ - return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && - (mvm->fw_dbg_conf == FW_DBG_INVALID || - (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); -} - -static inline bool -iwl_fw_dbg_no_trig_window(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trig) -{ - unsigned long wind_jiff = - msecs_to_jiffies(le16_to_cpu(trig->trig_dis_ms)); - u32 id = le32_to_cpu(trig->id); - - /* If this is the first event checked, jump to update start ts */ - if (mvm->fw_dbg_non_collect_ts_start[id] && - (time_after(mvm->fw_dbg_non_collect_ts_start[id] + wind_jiff, - jiffies))) - return true; - - mvm->fw_dbg_non_collect_ts_start[id] = jiffies; - return false; -} - -static inline bool -iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_fw_dbg_trigger_tlv *trig) -{ - if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) - return false; - - if (iwl_fw_dbg_no_trig_window(mvm, trig)) { - IWL_WARN(mvm, "Trigger %d occurred while no-collect window.\n", - trig->id); - return false; - } - - return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); -} - -static inline void -_iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - if (!trigger) - return; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); -} - -#define iwl_fw_dbg_trigger_simple_stop(mvm, vif, trig) \ - _iwl_fw_dbg_trigger_simple_stop((mvm), (vif), \ - iwl_fw_dbg_get_trigger((mvm)->fw,\ - (trig))) - -#endif /* __mvm_fw_dbg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 08108620b977..e6f6de2500db 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -78,7 +78,7 @@ #include "iwl-eeprom-parse.h" #include "mvm.h" -#include "fw-dbg.h" +#include "fw/dbg.h" #include "iwl-phy-db.h" #define MVM_UCODE_ALIVE_TIMEOUT HZ @@ -1091,11 +1091,11 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); - mvm->fw_dbg_conf = FW_DBG_INVALID; + mvm->fwrt.dump.conf = FW_DBG_INVALID; /* if we have a destination, assume EARLY START */ if (mvm->fw->dbg_dest_tlv) - mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE; - iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE); + mvm->fwrt.dump.conf = FW_DBG_START_FROM_ALIVE; + iwl_fw_start_dbg_conf(&mvm->fwrt, FW_DBG_START_FROM_ALIVE); ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index dc631b23e189..d130bdd76368 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -72,7 +72,6 @@ #include "fw-api.h" #include "mvm.h" #include "time-event.h" -#include "fw-dbg.h" const u8 iwl_mvm_ac_to_tx_fifo[] = { IWL_MVM_TX_FIFO_VO, @@ -1559,12 +1558,14 @@ static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, /* TODO: implement start trigger */ - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(vif), + trigger)) return; if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx || rx_missed_bcon >= stop_trig_missed_bcon) - iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trigger, NULL); } void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index bcde1ba0f1c8..ab6c6bf79565 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -87,7 +87,6 @@ #include "fw/error-dump.h" #include "iwl-prph.h" #include "iwl-nvm-parse.h" -#include "fw-dbg.h" static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { @@ -845,11 +844,11 @@ static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) return true; } -#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \ - do { \ - if (!(le16_to_cpu(_tid_bm) & BIT(_tid))) \ - break; \ - iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt); \ +#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \ + do { \ + if (!(le16_to_cpu(_tid_bm) & BIT(_tid))) \ + break; \ + iwl_fw_dbg_collect_trig(&(_mvm)->fwrt, _trig, _fmt); \ } while (0) static void @@ -866,7 +865,8 @@ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); ba_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(vif), trig)) return; switch (action) { @@ -1029,8 +1029,8 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) * on D3->D0 transition */ if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) { - mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert; - iwl_mvm_fw_error_dump(mvm); + mvm->fwrt.dump.desc = &iwl_dump_desc_assert; + iwl_fw_error_dump(&mvm->fwrt); } /* cleanup all stale references (scan, roc), but keep the @@ -1072,7 +1072,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) mvm->vif_count = 0; mvm->rx_ba_sessions = 0; - mvm->fw_dbg_conf = FW_DBG_INVALID; + mvm->fwrt.dump.conf = FW_DBG_INVALID; /* keep statistics ticking */ iwl_mvm_accu_radio_stats(mvm); @@ -1249,16 +1249,16 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) * Lock and clear the firmware running bit here already, so that * new commands coming in elsewhere, e.g. from debugfs, will not * be able to proceed. This is important here because one of those - * debugfs files causes the fw_dump_wk to be triggered, and if we + * debugfs files causes the firmware dump to be triggered, and if we * don't stop debugfs accesses before canceling that it could be * retriggered after we flush it but before we've cleared the bit. */ clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); - cancel_delayed_work_sync(&mvm->fw_dump_wk); + iwl_fw_cancel_dump(&mvm->fwrt); cancel_delayed_work_sync(&mvm->cs_tx_unblock_dwork); cancel_delayed_work_sync(&mvm->scan_timeout_dwork); - iwl_mvm_free_fw_dump_desc(mvm); + iwl_fw_free_dump_desc(&mvm->fwrt); mutex_lock(&mvm->mutex); __iwl_mvm_mac_stop(mvm); @@ -2566,7 +2566,8 @@ iwl_mvm_tdls_check_trigger(struct iwl_mvm *mvm, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TDLS); tdls_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(vif), trig)) return; if (!(tdls_trig->action_bitmap & BIT(action))) @@ -2576,9 +2577,9 @@ iwl_mvm_tdls_check_trigger(struct iwl_mvm *mvm, memcmp(tdls_trig->peer, peer_addr, ETH_ALEN) != 0) return; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "TDLS event occurred, peer %pM, action %d", - peer_addr, action); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, + "TDLS event occurred, peer %pM, action %d", + peer_addr, action); } static void iwl_mvm_purge_deferred_tx_frames(struct iwl_mvm *mvm, @@ -3876,7 +3877,9 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n", chsw->chandef.center_freq1); - iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH); + iwl_fw_dbg_trigger_simple_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(vif), + FW_DBG_TRIGGER_CHANNEL_SWITCH); switch (vif->type) { case NL80211_IFTYPE_AP: @@ -4151,11 +4154,11 @@ static void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm, struct ieee80211_vif *vif, const struct ieee80211_event *event) { -#define CHECK_MLME_TRIGGER(_cnt, _fmt...) \ - do { \ - if ((trig_mlme->_cnt) && --(trig_mlme->_cnt)) \ - break; \ - iwl_mvm_fw_dbg_collect_trig(mvm, trig, _fmt); \ +#define CHECK_MLME_TRIGGER(_cnt, _fmt...) \ + do { \ + if ((trig_mlme->_cnt) && --(trig_mlme->_cnt)) \ + break; \ + iwl_fw_dbg_collect_trig(&(mvm)->fwrt, trig, _fmt); \ } while (0) struct iwl_fw_dbg_trigger_tlv *trig; @@ -4166,7 +4169,8 @@ static void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); trig_mlme = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(vif), trig)) return; if (event->u.mlme.data == ASSOC_EVENT) { @@ -4207,16 +4211,17 @@ static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); ba_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(vif), trig)) return; if (!(le16_to_cpu(ba_trig->rx_bar) & BIT(event->u.ba.tid))) return; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "BAR received from %pM, tid %d, ssn %d", - event->u.ba.sta->addr, event->u.ba.tid, - event->u.ba.ssn); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, + "BAR received from %pM, tid %d, ssn %d", + event->u.ba.sta->addr, event->u.ba.tid, + event->u.ba.ssn); } static void @@ -4232,15 +4237,16 @@ iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); ba_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(vif), trig)) return; if (!(le16_to_cpu(ba_trig->frame_timeout) & BIT(event->u.ba.tid))) return; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "Frame from %pM timed out, tid %d", - event->u.ba.sta->addr, event->u.ba.tid); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, + "Frame from %pM timed out, tid %d", + event->u.ba.sta->addr, event->u.ba.tid); } static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 988f4c331d15..6fa2c44e6edd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -88,6 +88,7 @@ #include "constants.h" #include "tof.h" #include "fw/runtime.h" +#include "fw/dbg.h" #define IWL_MVM_MAX_ADDRESSES 5 /* RSSI offset for WkP */ @@ -138,34 +139,6 @@ struct iwl_mvm_mod_params { }; extern struct iwl_mvm_mod_params iwlmvm_mod_params; -/** - * struct iwl_mvm_dump_ptrs - set of pointers needed for the fw-error-dump - * - * @op_mode_ptr: pointer to the buffer coming from the mvm op_mode - * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the - * transport's data. - * @trans_len: length of the valid data in trans_ptr - * @op_mode_len: length of the valid data in op_mode_ptr - */ -struct iwl_mvm_dump_ptrs { - struct iwl_trans_dump_data *trans_ptr; - void *op_mode_ptr; - u32 op_mode_len; -}; - -/** - * struct iwl_mvm_dump_desc - describes the dump - * @len: length of trig_desc->data - * @trig_desc: the description of the dump - */ -struct iwl_mvm_dump_desc { - size_t len; - /* must be last */ - struct iwl_fw_error_dump_trigger_desc trig_desc; -}; - -extern const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert; - struct iwl_mvm_phy_ctxt { u16 id; u16 color; @@ -831,9 +804,6 @@ struct iwl_mvm { /* max number of simultaneous scans the FW supports */ unsigned int max_scans; - /* ts of the beginning of a non-collect fw dbg data period */ - unsigned long fw_dbg_non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1]; - /* UMAC scan tracking */ u32 scan_uid_status[IWL_MVM_MAX_UMAC_SCANS]; @@ -909,10 +879,6 @@ struct iwl_mvm { /* -1 for always, 0 for never, >0 for that many times */ s8 fw_restart; - u8 fw_dbg_conf; - struct delayed_work fw_dump_wk; - const struct iwl_mvm_dump_desc *fw_dump_desc; - const struct iwl_fw_dbg_trigger_tlv *fw_dump_trig; #ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; @@ -1077,7 +1043,6 @@ struct iwl_mvm { * @IWL_MVM_STATUS_IN_D0I3: NIC is in D0i3 * @IWL_MVM_STATUS_ROC_AUX_RUNNING: AUX remain-on-channel is running * @IWL_MVM_STATUS_D3_RECONFIG: D3 reconfiguration is being done - * @IWL_MVM_STATUS_DUMPING_FW_LOG: FW log is being dumped * @IWL_MVM_STATUS_FIRMWARE_RUNNING: firmware is running */ enum iwl_mvm_status { @@ -1088,7 +1053,6 @@ enum iwl_mvm_status { IWL_MVM_STATUS_IN_D0I3, IWL_MVM_STATUS_ROC_AUX_RUNNING, IWL_MVM_STATUS_D3_RECONFIG, - IWL_MVM_STATUS_DUMPING_FW_LOG, IWL_MVM_STATUS_FIRMWARE_RUNNING, }; @@ -1780,7 +1744,7 @@ static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm) { iwl_free_fw_paging(&mvm->fwrt); clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); - mvm->fw_dbg_conf = FW_DBG_INVALID; + iwl_fw_dump_conf_clear(&mvm->fwrt); iwl_trans_stop_device(mvm->trans); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 60f0c9975538..3c4d82045c1c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -84,7 +84,6 @@ #include "rs.h" #include "fw-api-scan.h" #include "time-event.h" -#include "fw-dbg.h" #include "fw-api.h" #include "fw-api-scan.h" @@ -510,8 +509,6 @@ static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg) return 0; } -static void iwl_mvm_fw_error_dump_wk(struct work_struct *work); - static void iwl_mvm_tx_unblock_dwork(struct work_struct *work) { struct iwl_mvm *mvm = @@ -535,6 +532,34 @@ unlock: mutex_unlock(&mvm->mutex); } +static int iwl_mvm_fwrt_dump_start(void *ctx) +{ + struct iwl_mvm *mvm = ctx; + int ret; + + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT); + if (ret) + return ret; + + mutex_lock(&mvm->mutex); + + return 0; +} + +static void iwl_mvm_fwrt_dump_end(void *ctx) +{ + struct iwl_mvm *mvm = ctx; + + mutex_unlock(&mvm->mutex); + + iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT); +} + +static const struct iwl_fw_runtime_ops iwl_mvm_fwrt_ops = { + .dump_start = iwl_mvm_fwrt_dump_start, + .dump_end = iwl_mvm_fwrt_dump_end, +}; + static struct iwl_op_mode * iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) @@ -580,7 +605,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->fw = fw; mvm->hw = hw; - iwl_fw_runtime_init(&mvm->fwrt, trans, fw); + iwl_fw_runtime_init(&mvm->fwrt, trans, fw, &iwl_mvm_fwrt_ops, mvm); mvm->init_status = 0; @@ -639,7 +664,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); - INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk); INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work); INIT_DELAYED_WORK(&mvm->scan_timeout_dwork, iwl_mvm_scan_timeout_wk); INIT_WORK(&mvm->add_stream_wk, iwl_mvm_add_new_dqa_stream_wk); @@ -802,7 +826,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_mvm_leds_exit(mvm); iwl_mvm_thermal_exit(mvm); out_free: - flush_delayed_work(&mvm->fw_dump_wk); + iwl_fw_flush_dump(&mvm->fwrt); if (iwlmvm_mod_params.init_dbg) return op_mode; @@ -922,7 +946,7 @@ static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF); cmds_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, NULL, trig)) return; for (i = 0; i < ARRAY_SIZE(cmds_trig->cmds); i++) { @@ -934,9 +958,9 @@ static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, cmds_trig->cmds[i].group_id != pkt->hdr.group_id) continue; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "CMD 0x%02x.%02x received", - pkt->hdr.group_id, pkt->hdr.cmd); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, + "CMD 0x%02x.%02x received", + pkt->hdr.group_id, pkt->hdr.cmd); break; } } @@ -1162,57 +1186,6 @@ static void iwl_mvm_reprobe_wk(struct work_struct *wk) module_put(THIS_MODULE); } -static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) -{ - struct iwl_mvm *mvm = - container_of(work, struct iwl_mvm, fw_dump_wk.work); - - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT)) - return; - - mutex_lock(&mvm->mutex); - - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { - /* stop recording */ - iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); - - iwl_mvm_fw_error_dump(mvm); - - /* start recording again if the firmware is not crashed */ - if (!test_bit(STATUS_FW_ERROR, &mvm->trans->status) && - mvm->fw->dbg_dest_tlv) { - iwl_clear_bits_prph(mvm->trans, - MON_BUFF_SAMPLE_CTL, 0x100); - iwl_clear_bits_prph(mvm->trans, - MON_BUFF_SAMPLE_CTL, 0x1); - iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x1); - } - } else { - u32 in_sample = iwl_read_prph(mvm->trans, DBGC_IN_SAMPLE); - u32 out_ctrl = iwl_read_prph(mvm->trans, DBGC_OUT_CTRL); - - /* stop recording */ - iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0); - udelay(100); - iwl_write_prph(mvm->trans, DBGC_OUT_CTRL, 0); - /* wait before we collect the data till the DBGC stop */ - udelay(500); - - iwl_mvm_fw_error_dump(mvm); - - /* start recording again if the firmware is not crashed */ - if (!test_bit(STATUS_FW_ERROR, &mvm->trans->status) && - mvm->fw->dbg_dest_tlv) { - iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, in_sample); - iwl_write_prph(mvm->trans, DBGC_OUT_CTRL, out_ctrl); - } - } - - mutex_unlock(&mvm->mutex); - - iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT); -} - void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) { iwl_abort_notification_waits(&mvm->notif_wait); @@ -1236,8 +1209,8 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) * can't recover this since we're already half suspended. */ if (!mvm->fw_restart && fw_error) { - iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert, - NULL); + iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert, + NULL); } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { struct iwl_mvm_reprobe *reprobe; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 622d543abb70..184c749766f2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -67,7 +67,6 @@ #include "iwl-trans.h" #include "mvm.h" #include "fw-api.h" -#include "fw-dbg.h" /* * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler @@ -397,10 +396,12 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, rssi = le32_to_cpu(rssi_trig->rssi); trig_check = - iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, + iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(mvmsta->vif), trig); if (trig_check && rx_status->signal < rssi) - iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, + NULL); } if (ieee80211_is_data(hdr->frame_control)) @@ -624,7 +625,7 @@ iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_STATS); trig_stats = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, NULL, trig)) return; trig_offset = le32_to_cpu(trig_stats->stop_offset); @@ -636,7 +637,7 @@ iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold) return; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, NULL); } void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index f3e608196369..13733252c1fb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -63,7 +63,6 @@ #include "iwl-trans.h" #include "mvm.h" #include "fw-api.h" -#include "fw-dbg.h" static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb, int queue, struct ieee80211_sta *sta) @@ -906,10 +905,12 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, rssi = le32_to_cpu(rssi_trig->rssi); trig_check = - iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, + iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(mvmsta->vif), trig); if (trig_check && rx_status->signal < rssi) - iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, + NULL); } if (ieee80211_is_data(hdr->frame_control)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 5a682722adce..fcf2d1e4ff4e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -73,7 +73,6 @@ #include "mvm.h" #include "iwl-io.h" #include "iwl-prph.h" -#include "fw-dbg.h" /* * For the high priority TE use a time event type that has similar priority to @@ -248,7 +247,9 @@ static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT); te_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(te_data->vif), + trig)) return; for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) { @@ -263,11 +264,11 @@ static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, !(trig_status_bitmap & BIT(le32_to_cpu(notif->status)))) continue; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "Time event %d Action 0x%x received status: %d", - te_data->id, - le32_to_cpu(notif->action), - le32_to_cpu(notif->status)); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, + "Time event %d Action 0x%x received status: %d", + te_data->id, + le32_to_cpu(notif->action), + le32_to_cpu(notif->status)); break; } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index c7ca6bd3129c..f263a1902e27 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -74,7 +74,6 @@ #include "iwl-eeprom-parse.h" #include "mvm.h" #include "sta.h" -#include "fw-dbg.h" static void iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, @@ -89,15 +88,15 @@ iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); ba_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, NULL, trig)) return; if (!(le16_to_cpu(ba_trig->tx_bar) & BIT(tid))) return; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "BAR sent to %pM, tid %d, ssn %d", - addr, tid, ssn); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, + "BAR sent to %pM, tid %d, ssn %d", + addr, tid, ssn); } #define OPT_HDR(type, skb, off) \ @@ -1296,7 +1295,7 @@ static void iwl_mvm_tx_status_check_trigger(struct iwl_mvm *mvm, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TX_STATUS); status_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, NULL, trig)) return; for (i = 0; i < ARRAY_SIZE(status_trig->statuses); i++) { @@ -1307,9 +1306,9 @@ static void iwl_mvm_tx_status_check_trigger(struct iwl_mvm *mvm, if (status_trig->statuses[i].status != (status & TX_STATUS_MSK)) continue; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "Tx status %d was received", - status & TX_STATUS_MSK); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, + "Tx status %d was received", + status & TX_STATUS_MSK); break; } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 4e80c8fa4741..3ccd16f26b91 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -70,7 +70,6 @@ #include "iwl-io.h" #include "iwl-prph.h" #include "iwl-csr.h" -#include "fw-dbg.h" #include "mvm.h" #include "fw-api-rs.h" @@ -1190,14 +1189,15 @@ void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); trig_mlme = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, + ieee80211_vif_to_wdev(vif), trig)) goto out; if (trig_mlme->stop_connection_loss && --trig_mlme->stop_connection_loss) goto out; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg); + iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, "%s", errmsg); out: ieee80211_connection_loss(vif); -- cgit v1.2.3 From d172a5eff629127c048272e5d44e8b9f9cd30eac Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 2 Jun 2017 15:15:53 +0200 Subject: iwlwifi: reorganize firmware API Apart from DVM, all firmware uses the same base API, and there's code outside iwlmvm that needs to interact with it. Reflect this in the source better and reorganize the firmware API to a new fw/api/ directory. While at it, split the already pretty large fw-api.h file into a number of smaller files, going from almost 3k lines in there to a maximum number of lines less than 1k. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api.h | 359 --- drivers/net/wireless/intel/iwlwifi/fw/api/alive.h | 190 ++ .../net/wireless/intel/iwlwifi/fw/api/binding.h | 144 ++ drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h | 195 ++ drivers/net/wireless/intel/iwlwifi/fw/api/coex.h | 252 ++ .../net/wireless/intel/iwlwifi/fw/api/commands.h | 657 +++++ drivers/net/wireless/intel/iwlwifi/fw/api/config.h | 192 ++ .../net/wireless/intel/iwlwifi/fw/api/context.h | 94 + drivers/net/wireless/intel/iwlwifi/fw/api/d3.h | 466 ++++ .../net/wireless/intel/iwlwifi/fw/api/datapath.h | 127 + drivers/net/wireless/intel/iwlwifi/fw/api/debug.h | 345 +++ drivers/net/wireless/intel/iwlwifi/fw/api/filter.h | 183 ++ .../net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 152 ++ drivers/net/wireless/intel/iwlwifi/fw/api/mac.h | 409 +++ .../net/wireless/intel/iwlwifi/fw/api/nvm-reg.h | 378 +++ .../net/wireless/intel/iwlwifi/fw/api/offload.h | 101 + drivers/net/wireless/intel/iwlwifi/fw/api/paging.h | 108 + .../net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h | 164 ++ drivers/net/wireless/intel/iwlwifi/fw/api/phy.h | 258 ++ drivers/net/wireless/intel/iwlwifi/fw/api/power.h | 526 ++++ drivers/net/wireless/intel/iwlwifi/fw/api/rs.h | 408 +++ drivers/net/wireless/intel/iwlwifi/fw/api/rx.h | 589 +++++ drivers/net/wireless/intel/iwlwifi/fw/api/scan.h | 787 ++++++ drivers/net/wireless/intel/iwlwifi/fw/api/sf.h | 138 + drivers/net/wireless/intel/iwlwifi/fw/api/sta.h | 573 +++++ drivers/net/wireless/intel/iwlwifi/fw/api/stats.h | 474 ++++ drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h | 208 ++ .../net/wireless/intel/iwlwifi/fw/api/time-event.h | 386 +++ drivers/net/wireless/intel/iwlwifi/fw/api/tof.h | 393 +++ drivers/net/wireless/intel/iwlwifi/fw/api/tx.h | 912 +++++++ drivers/net/wireless/intel/iwlwifi/fw/api/txq.h | 156 ++ drivers/net/wireless/intel/iwlwifi/fw/paging.c | 5 +- drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 3 +- drivers/net/wireless/intel/iwlwifi/fw/smem.c | 6 +- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 3 +- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 2 +- .../net/wireless/intel/iwlwifi/mvm/debugfs-vif.c | 2 +- .../net/wireless/intel/iwlwifi/mvm/fw-api-coex.h | 257 -- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h | 471 ---- .../net/wireless/intel/iwlwifi/mvm/fw-api-mac.h | 396 --- .../net/wireless/intel/iwlwifi/mvm/fw-api-power.h | 531 ---- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h | 413 --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 574 ----- .../net/wireless/intel/iwlwifi/mvm/fw-api-scan.h | 792 ------ .../net/wireless/intel/iwlwifi/mvm/fw-api-sta.h | 578 ----- .../net/wireless/intel/iwlwifi/mvm/fw-api-stats.h | 479 ---- .../net/wireless/intel/iwlwifi/mvm/fw-api-tof.h | 398 --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h | 917 ------- drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 2714 +------------------- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 +- drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/power.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tof.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tof.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 5 +- 58 files changed, 10011 insertions(+), 8877 deletions(-) delete mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/alive.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/binding.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/coex.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/commands.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/config.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/context.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/d3.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/debug.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/filter.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/mac.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/offload.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/paging.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/phy.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/power.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/rs.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/rx.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/scan.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/sf.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/sta.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/stats.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/tof.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/tx.h create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/txq.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api.h b/drivers/net/wireless/intel/iwlwifi/fw/api.h deleted file mode 100644 index afd333e57790..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/fw/api.h +++ /dev/null @@ -1,359 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __iwl_fw_api_h__ -#define __iwl_fw_api_h__ - -/** - * DOC: Host command section - * - * A host command is a command issued by the upper layer to the fw. There are - * several versions of fw that have several APIs. The transport layer is - * completely agnostic to these differences. - * The transport does provide helper functionality (i.e. SYNC / ASYNC mode), - */ -#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) -#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) -#define SEQ_TO_INDEX(s) ((s) & 0xff) -#define INDEX_TO_SEQ(i) ((i) & 0xff) -#define SEQ_RX_FRAME cpu_to_le16(0x8000) - -/* - * those functions retrieve specific information from - * the id field in the iwl_host_cmd struct which contains - * the command id, the group id and the version of the command - * and vice versa -*/ -static inline u8 iwl_cmd_opcode(u32 cmdid) -{ - return cmdid & 0xFF; -} - -static inline u8 iwl_cmd_groupid(u32 cmdid) -{ - return ((cmdid & 0xFF00) >> 8); -} - -static inline u8 iwl_cmd_version(u32 cmdid) -{ - return ((cmdid & 0xFF0000) >> 16); -} - -static inline u32 iwl_cmd_id(u8 opcode, u8 groupid, u8 version) -{ - return opcode + (groupid << 8) + (version << 16); -} - -/* make u16 wide id out of u8 group and opcode */ -#define WIDE_ID(grp, opcode) (((grp) << 8) | (opcode)) -#define DEF_ID(opcode) ((1 << 8) | (opcode)) - -/* due to the conversion, this group is special; new groups - * should be defined in the appropriate fw-api header files - */ -#define IWL_ALWAYS_LONG_GROUP 1 - -/** - * struct iwl_cmd_header - * - * This header format appears in the beginning of each command sent from the - * driver, and each response/notification received from uCode. - */ -struct iwl_cmd_header { - u8 cmd; /* Command ID: REPLY_RXON, etc. */ - u8 group_id; - /* - * The driver sets up the sequence number to values of its choosing. - * uCode does not use this value, but passes it back to the driver - * when sending the response to each driver-originated command, so - * the driver can match the response to the command. Since the values - * don't get used by uCode, the driver may set up an arbitrary format. - * - * There is one exception: uCode sets bit 15 when it originates - * the response/notification, i.e. when the response/notification - * is not a direct response to a command sent by the driver. For - * example, uCode issues REPLY_RX when it sends a received frame - * to the driver; it is not a direct response to any driver command. - * - * The Linux driver uses the following format: - * - * 0:7 tfd index - position within TX queue - * 8:12 TX queue id - * 13:14 reserved - * 15 unsolicited RX or uCode-originated notification - */ - __le16 sequence; -} __packed; - -/** - * struct iwl_cmd_header_wide - * - * This header format appears in the beginning of each command sent from the - * driver, and each response/notification received from uCode. - * this is the wide version that contains more information about the command - * like length, version and command type - */ -struct iwl_cmd_header_wide { - u8 cmd; - u8 group_id; - __le16 sequence; - __le16 length; - u8 reserved; - u8 version; -} __packed; - -/** - * iwl_tx_queue_cfg_actions - TXQ config options - * @TX_QUEUE_CFG_ENABLE_QUEUE: enable a queue - * @TX_QUEUE_CFG_TFD_SHORT_FORMAT: use short TFD format - */ -enum iwl_tx_queue_cfg_actions { - TX_QUEUE_CFG_ENABLE_QUEUE = BIT(0), - TX_QUEUE_CFG_TFD_SHORT_FORMAT = BIT(1), -}; - -/** - * struct iwl_tx_queue_cfg_cmd - txq hw scheduler config command - * @sta_id: station id - * @tid: tid of the queue - * @flags: see &enum iwl_tx_queue_cfg_actions - * @cb_size: size of TFD cyclic buffer. Value is exponent - 3. - * Minimum value 0 (8 TFDs), maximum value 5 (256 TFDs) - * @byte_cnt_addr: address of byte count table - * @tfdq_addr: address of TFD circular buffer - */ -struct iwl_tx_queue_cfg_cmd { - u8 sta_id; - u8 tid; - __le16 flags; - __le32 cb_size; - __le64 byte_cnt_addr; - __le64 tfdq_addr; -} __packed; /* TX_QUEUE_CFG_CMD_API_S_VER_2 */ - -/** - * struct iwl_tx_queue_cfg_rsp - response to txq hw scheduler config - * @queue_number: queue number assigned to this RA -TID - * @flags: set on failure - * @write_pointer: initial value for write pointer - */ -struct iwl_tx_queue_cfg_rsp { - __le16 queue_number; - __le16 flags; - __le16 write_pointer; - __le16 reserved; -} __packed; /* TX_QUEUE_CFG_RSP_API_S_VER_2 */ - -/** - * struct iwl_calib_res_notif_phy_db - Receive phy db chunk after calibrations - * @type: type of the result - mostly ignored - * @length: length of the data - * @data: data, length in @length - */ -struct iwl_calib_res_notif_phy_db { - __le16 type; - __le16 length; - u8 data[]; -} __packed; - -/** - * struct iwl_phy_db_cmd - configure operational ucode - * @type: type of the data - * @length: length of the data - * @data: data, length in @length - */ -struct iwl_phy_db_cmd { - __le16 type; - __le16 length; - u8 data[]; -} __packed; - -#define NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */ - -/** - * struct iwl_fw_paging_cmd - paging layout - * - * Send to FW the paging layout in the driver. - * - * @flags: various flags for the command - * @block_size: the block size in powers of 2 - * @block_num: number of blocks specified in the command. - * @device_phy_addr: virtual addresses from device side - */ -struct iwl_fw_paging_cmd { - __le32 flags; - __le32 block_size; - __le32 block_num; - __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS]; -} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */ - -/** - * enum iwl_fw_item_id - FW item IDs - * - * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload - * download - */ -enum iwl_fw_item_id { - IWL_FW_ITEM_ID_PAGING = 3, -}; - -/** - * struct iwl_fw_get_item_cmd - get an item from the fw - * @item_id: ID of item to obtain, see &enum iwl_fw_item_id - */ -struct iwl_fw_get_item_cmd { - __le32 item_id; -} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */ - -struct iwl_fw_get_item_resp { - __le32 item_id; - __le32 item_byte_cnt; - __le32 item_val; -} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */ - -#define TX_FIFO_MAX_NUM_9000 8 -#define TX_FIFO_MAX_NUM 15 -#define RX_FIFO_MAX_NUM 2 -#define TX_FIFO_INTERNAL_MAX_NUM 6 - -/** - * struct iwl_shared_mem_cfg_v2 - Shared memory configuration information - * - * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not - * accessible) - * @shared_mem_size: shared memory size - * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to - * 0x0 as accessible only via DBGM RDAT) - * @sample_buff_size: internal sample buff size - * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre - * 8000 HW set to 0x0 as not accessible) - * @txfifo_size: size of TXF0 ... TXF7 - * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0 - * @page_buff_addr: used by UMAC and performance debug (page miss analysis), - * when paging is not supported this should be 0 - * @page_buff_size: size of %page_buff_addr - * @rxfifo_addr: Start address of rxFifo - * @internal_txfifo_addr: start address of internalFifo - * @internal_txfifo_size: internal fifos' size - * - * NOTE: on firmware that don't have IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG - * set, the last 3 members don't exist. - */ -struct iwl_shared_mem_cfg_v2 { - __le32 shared_mem_addr; - __le32 shared_mem_size; - __le32 sample_buff_addr; - __le32 sample_buff_size; - __le32 txfifo_addr; - __le32 txfifo_size[TX_FIFO_MAX_NUM_9000]; - __le32 rxfifo_size[RX_FIFO_MAX_NUM]; - __le32 page_buff_addr; - __le32 page_buff_size; - __le32 rxfifo_addr; - __le32 internal_txfifo_addr; - __le32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; -} __packed; /* SHARED_MEM_ALLOC_API_S_VER_2 */ - -/** - * struct iwl_shared_mem_lmac_cfg - LMAC shared memory configuration - * - * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB) - * @txfifo_size: size of TX FIFOs - * @rxfifo1_addr: RXF1 addr - * @rxfifo1_size: RXF1 size - */ -struct iwl_shared_mem_lmac_cfg { - __le32 txfifo_addr; - __le32 txfifo_size[TX_FIFO_MAX_NUM]; - __le32 rxfifo1_addr; - __le32 rxfifo1_size; - -} __packed; /* SHARED_MEM_ALLOC_LMAC_API_S_VER_1 */ - -/** - * struct iwl_shared_mem_cfg - Shared memory configuration information - * - * @shared_mem_addr: shared memory address - * @shared_mem_size: shared memory size - * @sample_buff_addr: internal sample (mon/adc) buff addr - * @sample_buff_size: internal sample buff size - * @rxfifo2_addr: start addr of RXF2 - * @rxfifo2_size: size of RXF2 - * @page_buff_addr: used by UMAC and performance debug (page miss analysis), - * when paging is not supported this should be 0 - * @page_buff_size: size of %page_buff_addr - * @lmac_num: number of LMACs (1 or 2) - * @lmac_smem: per - LMAC smem data - */ -struct iwl_shared_mem_cfg { - __le32 shared_mem_addr; - __le32 shared_mem_size; - __le32 sample_buff_addr; - __le32 sample_buff_size; - __le32 rxfifo2_addr; - __le32 rxfifo2_size; - __le32 page_buff_addr; - __le32 page_buff_size; - __le32 lmac_num; - struct iwl_shared_mem_lmac_cfg lmac_smem[2]; -} __packed; /* SHARED_MEM_ALLOC_API_S_VER_3 */ - -#endif /* __iwl_fw_api_h__*/ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h new file mode 100644 index 000000000000..6af6a9b32b69 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h @@ -0,0 +1,190 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_alive_h__ +#define __iwl_fw_api_alive_h__ + +/* alive response is_valid values */ +#define ALIVE_RESP_UCODE_OK BIT(0) +#define ALIVE_RESP_RFKILL BIT(1) + +/* alive response ver_type values */ +enum { + FW_TYPE_HW = 0, + FW_TYPE_PROT = 1, + FW_TYPE_AP = 2, + FW_TYPE_WOWLAN = 3, + FW_TYPE_TIMING = 4, + FW_TYPE_WIPAN = 5 +}; + +/* alive response ver_subtype values */ +enum { + FW_SUBTYPE_FULL_FEATURE = 0, + FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */ + FW_SUBTYPE_REDUCED = 2, + FW_SUBTYPE_ALIVE_ONLY = 3, + FW_SUBTYPE_WOWLAN = 4, + FW_SUBTYPE_AP_SUBTYPE = 5, + FW_SUBTYPE_WIPAN = 6, + FW_SUBTYPE_INITIALIZE = 9 +}; + +#define IWL_ALIVE_STATUS_ERR 0xDEAD +#define IWL_ALIVE_STATUS_OK 0xCAFE + +#define IWL_ALIVE_FLG_RFKILL BIT(0) + +struct iwl_lmac_alive { + __le32 ucode_minor; + __le32 ucode_major; + u8 ver_subtype; + u8 ver_type; + u8 mac; + u8 opt; + __le32 timestamp; + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 log_event_table_ptr; /* SRAM address for LMAC event log */ + __le32 cpu_register_ptr; + __le32 dbgm_config_ptr; + __le32 alive_counter_ptr; + __le32 scd_base_ptr; /* SRAM address for SCD */ + __le32 st_fwrd_addr; /* pointer to Store and forward */ + __le32 st_fwrd_size; +} __packed; /* UCODE_ALIVE_NTFY_API_S_VER_3 */ + +struct iwl_umac_alive { + __le32 umac_minor; /* UMAC version: minor */ + __le32 umac_major; /* UMAC version: major */ + __le32 error_info_addr; /* SRAM address for UMAC error log */ + __le32 dbg_print_buff_addr; +} __packed; /* UMAC_ALIVE_DATA_API_S_VER_2 */ + +struct mvm_alive_resp_v3 { + __le16 status; + __le16 flags; + struct iwl_lmac_alive lmac_data; + struct iwl_umac_alive umac_data; +} __packed; /* ALIVE_RES_API_S_VER_3 */ + +struct mvm_alive_resp { + __le16 status; + __le16 flags; + struct iwl_lmac_alive lmac_data[2]; + struct iwl_umac_alive umac_data; +} __packed; /* ALIVE_RES_API_S_VER_4 */ + +/** + * enum iwl_extended_cfg_flag - commands driver may send before + * finishing init flow + * @IWL_INIT_DEBUG_CFG: driver is going to send debug config command + * @IWL_INIT_NVM: driver is going to send NVM_ACCESS commands + * @IWL_INIT_PHY: driver is going to send PHY_DB commands + */ +enum iwl_extended_cfg_flags { + IWL_INIT_DEBUG_CFG, + IWL_INIT_NVM, + IWL_INIT_PHY, +}; + +/** + * struct iwl_extended_cfg_cmd - mark what commands ucode should wait for + * before finishing init flows + * @init_flags: values from iwl_extended_cfg_flags + */ +struct iwl_init_extended_cfg_cmd { + __le32 init_flags; +} __packed; /* INIT_EXTENDED_CFG_CMD_API_S_VER_1 */ + +/** + * struct iwl_radio_version_notif - information on the radio version + * ( RADIO_VERSION_NOTIFICATION = 0x68 ) + * @radio_flavor: radio flavor + * @radio_step: radio version step + * @radio_dash: radio version dash + */ +struct iwl_radio_version_notif { + __le32 radio_flavor; + __le32 radio_step; + __le32 radio_dash; +} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */ + +enum iwl_card_state_flags { + CARD_ENABLED = 0x00, + HW_CARD_DISABLED = 0x01, + SW_CARD_DISABLED = 0x02, + CT_KILL_CARD_DISABLED = 0x04, + HALT_CARD_DISABLED = 0x08, + CARD_DISABLED_MSK = 0x0f, + CARD_IS_RX_ON = 0x10, +}; + +/** + * struct iwl_radio_version_notif - information on the card state + * ( CARD_STATE_NOTIFICATION = 0xa1 ) + * @flags: &enum iwl_card_state_flags + */ +struct iwl_card_state_notif { + __le32 flags; +} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ + +#endif /* __iwl_fw_api_alive_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/binding.h b/drivers/net/wireless/intel/iwlwifi/fw/api/binding.h new file mode 100644 index 000000000000..d2717fafdf5b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/binding.h @@ -0,0 +1,144 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_binding_h__ +#define __iwl_fw_api_binding_h__ + +#define MAX_MACS_IN_BINDING (3) +#define MAX_BINDINGS (4) + +/** + * struct iwl_binding_cmd_v1 - configuring bindings + * ( BINDING_CONTEXT_CMD = 0x2b ) + * @id_and_color: ID and color of the relevant Binding, + * &enum iwl_ctxt_id_and_color + * @action: action to perform, one of FW_CTXT_ACTION_* + * @macs: array of MAC id and colors which belong to the binding, + * &enum iwl_ctxt_id_and_color + * @phy: PHY id and color which belongs to the binding, + * &enum iwl_ctxt_id_and_color + */ +struct iwl_binding_cmd_v1 { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + /* BINDING_DATA_API_S_VER_1 */ + __le32 macs[MAX_MACS_IN_BINDING]; + __le32 phy; +} __packed; /* BINDING_CMD_API_S_VER_1 */ + +/** + * struct iwl_binding_cmd - configuring bindings + * ( BINDING_CONTEXT_CMD = 0x2b ) + * @id_and_color: ID and color of the relevant Binding, + * &enum iwl_ctxt_id_and_color + * @action: action to perform, one of FW_CTXT_ACTION_* + * @macs: array of MAC id and colors which belong to the binding + * &enum iwl_ctxt_id_and_color + * @phy: PHY id and color which belongs to the binding + * &enum iwl_ctxt_id_and_color + * @lmac_id: the lmac id the binding belongs to + */ +struct iwl_binding_cmd { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + /* BINDING_DATA_API_S_VER_1 */ + __le32 macs[MAX_MACS_IN_BINDING]; + __le32 phy; + __le32 lmac_id; +} __packed; /* BINDING_CMD_API_S_VER_2 */ + +#define IWL_BINDING_CMD_SIZE_V1 sizeof(struct iwl_binding_cmd_v1) +#define IWL_LMAC_24G_INDEX 0 +#define IWL_LMAC_5G_INDEX 1 + +/* The maximal number of fragments in the FW's schedule session */ +#define IWL_MVM_MAX_QUOTA 128 + +/** + * struct iwl_time_quota_data - configuration of time quota per binding + * @id_and_color: ID and color of the relevant Binding, + * &enum iwl_ctxt_id_and_color + * @quota: absolute time quota in TU. The scheduler will try to divide the + * remainig quota (after Time Events) according to this quota. + * @max_duration: max uninterrupted context duration in TU + */ +struct iwl_time_quota_data { + __le32 id_and_color; + __le32 quota; + __le32 max_duration; +} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */ + +/** + * struct iwl_time_quota_cmd - configuration of time quota between bindings + * ( TIME_QUOTA_CMD = 0x2c ) + * @quotas: allocations per binding + * Note: on non-CDB the fourth one is the auxilary mac and is + * essentially zero. + * On CDB the fourth one is a regular binding. + */ +struct iwl_time_quota_cmd { + struct iwl_time_quota_data quotas[MAX_BINDINGS]; +} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */ + +#endif /* __iwl_fw_api_binding_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h b/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h new file mode 100644 index 000000000000..fd97cccaedb8 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h @@ -0,0 +1,195 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_fw_api_cmdhdr_h__ +#define __iwl_fw_api_cmdhdr_h__ + +/** + * DOC: Host command section + * + * A host command is a command issued by the upper layer to the fw. There are + * several versions of fw that have several APIs. The transport layer is + * completely agnostic to these differences. + * The transport does provide helper functionality (i.e. SYNC / ASYNC mode), + */ +#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) +#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) +#define SEQ_TO_INDEX(s) ((s) & 0xff) +#define INDEX_TO_SEQ(i) ((i) & 0xff) +#define SEQ_RX_FRAME cpu_to_le16(0x8000) + +/* + * those functions retrieve specific information from + * the id field in the iwl_host_cmd struct which contains + * the command id, the group id and the version of the command + * and vice versa +*/ +static inline u8 iwl_cmd_opcode(u32 cmdid) +{ + return cmdid & 0xFF; +} + +static inline u8 iwl_cmd_groupid(u32 cmdid) +{ + return ((cmdid & 0xFF00) >> 8); +} + +static inline u8 iwl_cmd_version(u32 cmdid) +{ + return ((cmdid & 0xFF0000) >> 16); +} + +static inline u32 iwl_cmd_id(u8 opcode, u8 groupid, u8 version) +{ + return opcode + (groupid << 8) + (version << 16); +} + +/* make u16 wide id out of u8 group and opcode */ +#define WIDE_ID(grp, opcode) (((grp) << 8) | (opcode)) +#define DEF_ID(opcode) ((1 << 8) | (opcode)) + +/* due to the conversion, this group is special; new groups + * should be defined in the appropriate fw-api header files + */ +#define IWL_ALWAYS_LONG_GROUP 1 + +/** + * struct iwl_cmd_header + * + * This header format appears in the beginning of each command sent from the + * driver, and each response/notification received from uCode. + */ +struct iwl_cmd_header { + u8 cmd; /* Command ID: REPLY_RXON, etc. */ + u8 group_id; + /* + * The driver sets up the sequence number to values of its choosing. + * uCode does not use this value, but passes it back to the driver + * when sending the response to each driver-originated command, so + * the driver can match the response to the command. Since the values + * don't get used by uCode, the driver may set up an arbitrary format. + * + * There is one exception: uCode sets bit 15 when it originates + * the response/notification, i.e. when the response/notification + * is not a direct response to a command sent by the driver. For + * example, uCode issues REPLY_RX when it sends a received frame + * to the driver; it is not a direct response to any driver command. + * + * The Linux driver uses the following format: + * + * 0:7 tfd index - position within TX queue + * 8:12 TX queue id + * 13:14 reserved + * 15 unsolicited RX or uCode-originated notification + */ + __le16 sequence; +} __packed; + +/** + * struct iwl_cmd_header_wide + * + * This header format appears in the beginning of each command sent from the + * driver, and each response/notification received from uCode. + * this is the wide version that contains more information about the command + * like length, version and command type + */ +struct iwl_cmd_header_wide { + u8 cmd; + u8 group_id; + __le16 sequence; + __le16 length; + u8 reserved; + u8 version; +} __packed; + +/** + * struct iwl_calib_res_notif_phy_db - Receive phy db chunk after calibrations + * @type: type of the result - mostly ignored + * @length: length of the data + * @data: data, length in @length + */ +struct iwl_calib_res_notif_phy_db { + __le16 type; + __le16 length; + u8 data[]; +} __packed; + +/** + * struct iwl_phy_db_cmd - configure operational ucode + * @type: type of the data + * @length: length of the data + * @data: data, length in @length + */ +struct iwl_phy_db_cmd { + __le16 type; + __le16 length; + u8 data[]; +} __packed; + +/** + * struct iwl_cmd_response - generic response struct for most commands + * @status: status of the command asked, changes for each one + */ +struct iwl_cmd_response { + __le32 status; +}; + +#endif /* __iwl_fw_api_cmdhdr_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h new file mode 100644 index 000000000000..583f4189f55e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h @@ -0,0 +1,252 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_fw_api_coex_h__ +#define __iwl_fw_api_coex_h__ + +#include +#include + +#define BITS(nb) (BIT(nb) - 1) + +enum iwl_bt_coex_lut_type { + BT_COEX_TIGHT_LUT = 0, + BT_COEX_LOOSE_LUT, + BT_COEX_TX_DIS_LUT, + + BT_COEX_MAX_LUT, + BT_COEX_INVALID_LUT = 0xff, +}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */ + +#define BT_COEX_CORUN_LUT_SIZE (32) +#define BT_REDUCED_TX_POWER_BIT BIT(7) + +enum iwl_bt_coex_mode { + BT_COEX_DISABLE = 0x0, + BT_COEX_NW = 0x1, + BT_COEX_BT = 0x2, + BT_COEX_WIFI = 0x3, +}; /* BT_COEX_MODES_E */ + +enum iwl_bt_coex_enabled_modules { + BT_COEX_MPLUT_ENABLED = BIT(0), + BT_COEX_MPLUT_BOOST_ENABLED = BIT(1), + BT_COEX_SYNC2SCO_ENABLED = BIT(2), + BT_COEX_CORUN_ENABLED = BIT(3), + BT_COEX_HIGH_BAND_RET = BIT(4), +}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */ + +/** + * struct iwl_bt_coex_cmd - bt coex configuration command + * @mode: &enum iwl_bt_coex_mode + * @enabled_modules: &enum iwl_bt_coex_enabled_modules + * + * The structure is used for the BT_COEX command. + */ +struct iwl_bt_coex_cmd { + __le32 mode; + __le32 enabled_modules; +} __packed; /* BT_COEX_CMD_API_S_VER_6 */ + +/** + * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut + * @corun_lut20: co-running 20 MHz LUT configuration + * @corun_lut40: co-running 40 MHz LUT configuration + * + * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command. + */ +struct iwl_bt_coex_corun_lut_update_cmd { + __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE]; + __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE]; +} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */ + +/** + * struct iwl_bt_coex_reduced_txp_update_cmd + * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the + * bits are the sta_id (value) + */ +struct iwl_bt_coex_reduced_txp_update_cmd { + __le32 reduced_txp; +} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */ + +/** + * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command + * @bt_primary_ci: primary channel inhibition bitmap + * @primary_ch_phy_id: primary channel PHY ID + * @bt_secondary_ci: secondary channel inhibition bitmap + * @secondary_ch_phy_id: secondary channel PHY ID + * + * Used for BT_COEX_CI command + */ +struct iwl_bt_coex_ci_cmd { + __le64 bt_primary_ci; + __le32 primary_ch_phy_id; + + __le64 bt_secondary_ci; + __le32 secondary_ch_phy_id; +} __packed; /* BT_CI_MSG_API_S_VER_2 */ + +#define BT_MBOX(n_dw, _msg, _pos, _nbits) \ + BT_MBOX##n_dw##_##_msg##_POS = (_pos), \ + BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS + +enum iwl_bt_mxbox_dw0 { + BT_MBOX(0, LE_SLAVE_LAT, 0, 3), + BT_MBOX(0, LE_PROF1, 3, 1), + BT_MBOX(0, LE_PROF2, 4, 1), + BT_MBOX(0, LE_PROF_OTHER, 5, 1), + BT_MBOX(0, CHL_SEQ_N, 8, 4), + BT_MBOX(0, INBAND_S, 13, 1), + BT_MBOX(0, LE_MIN_RSSI, 16, 4), + BT_MBOX(0, LE_SCAN, 20, 1), + BT_MBOX(0, LE_ADV, 21, 1), + BT_MBOX(0, LE_MAX_TX_POWER, 24, 4), + BT_MBOX(0, OPEN_CON_1, 28, 2), +}; + +enum iwl_bt_mxbox_dw1 { + BT_MBOX(1, BR_MAX_TX_POWER, 0, 4), + BT_MBOX(1, IP_SR, 4, 1), + BT_MBOX(1, LE_MSTR, 5, 1), + BT_MBOX(1, AGGR_TRFC_LD, 8, 6), + BT_MBOX(1, MSG_TYPE, 16, 3), + BT_MBOX(1, SSN, 19, 2), +}; + +enum iwl_bt_mxbox_dw2 { + BT_MBOX(2, SNIFF_ACT, 0, 3), + BT_MBOX(2, PAG, 3, 1), + BT_MBOX(2, INQUIRY, 4, 1), + BT_MBOX(2, CONN, 5, 1), + BT_MBOX(2, SNIFF_INTERVAL, 8, 5), + BT_MBOX(2, DISC, 13, 1), + BT_MBOX(2, SCO_TX_ACT, 16, 2), + BT_MBOX(2, SCO_RX_ACT, 18, 2), + BT_MBOX(2, ESCO_RE_TX, 20, 2), + BT_MBOX(2, SCO_DURATION, 24, 6), +}; + +enum iwl_bt_mxbox_dw3 { + BT_MBOX(3, SCO_STATE, 0, 1), + BT_MBOX(3, SNIFF_STATE, 1, 1), + BT_MBOX(3, A2DP_STATE, 2, 1), + BT_MBOX(3, ACL_STATE, 3, 1), + BT_MBOX(3, MSTR_STATE, 4, 1), + BT_MBOX(3, OBX_STATE, 5, 1), + BT_MBOX(3, OPEN_CON_2, 8, 2), + BT_MBOX(3, TRAFFIC_LOAD, 10, 2), + BT_MBOX(3, CHL_SEQN_LSB, 12, 1), + BT_MBOX(3, INBAND_P, 13, 1), + BT_MBOX(3, MSG_TYPE_2, 16, 3), + BT_MBOX(3, SSN_2, 19, 2), + BT_MBOX(3, UPDATE_REQUEST, 21, 1), +}; + +#define BT_MBOX_MSG(_notif, _num, _field) \ + ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ + >> BT_MBOX##_num##_##_field##_POS) + +enum iwl_bt_activity_grading { + BT_OFF = 0, + BT_ON_NO_CONNECTION = 1, + BT_LOW_TRAFFIC = 2, + BT_HIGH_TRAFFIC = 3, + + BT_MAX_AG, +}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */ + +enum iwl_bt_ci_compliance { + BT_CI_COMPLIANCE_NONE = 0, + BT_CI_COMPLIANCE_PRIMARY = 1, + BT_CI_COMPLIANCE_SECONDARY = 2, + BT_CI_COMPLIANCE_BOTH = 3, +}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */ + +#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \ + (_ttc_rrc_status & BIT(_phy_id)) + +#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \ + ((_ttc_rrc_status >> 4) & BIT(_phy_id)) + +/** + * struct iwl_bt_coex_profile_notif - notification about BT coex + * @mbox_msg: message from BT to WiFi + * @msg_idx: the index of the message + * @bt_ci_compliance: enum %iwl_bt_ci_compliance + * @primary_ch_lut: LUT used for primary channel &enum iwl_bt_coex_lut_type + * @secondary_ch_lut: LUT used for secondary channel &enum iwl_bt_coex_lut_type + * @bt_activity_grading: the activity of BT &enum iwl_bt_activity_grading + * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY + * @reserved: reserved + */ +struct iwl_bt_coex_profile_notif { + __le32 mbox_msg[4]; + __le32 msg_idx; + __le32 bt_ci_compliance; + + __le32 primary_ch_lut; + __le32 secondary_ch_lut; + __le32 bt_activity_grading; + u8 ttc_rrc_status; + u8 reserved[3]; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ + +#endif /* __iwl_fw_api_coex_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h new file mode 100644 index 000000000000..34fceb26447d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -0,0 +1,657 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_commands_h__ +#define __iwl_fw_api_commands_h__ + +/** + * enum iwl_mvm_command_groups - command groups for the firmware + * @LEGACY_GROUP: legacy group, uses command IDs from &enum iwl_legacy_cmds + * @LONG_GROUP: legacy group with long header, also uses command IDs + * from &enum iwl_legacy_cmds + * @SYSTEM_GROUP: system group, uses command IDs from + * &enum iwl_system_subcmd_ids + * @MAC_CONF_GROUP: MAC configuration group, uses command IDs from + * &enum iwl_mac_conf_subcmd_ids + * @PHY_OPS_GROUP: PHY operations group, uses command IDs from + * &enum iwl_phy_ops_subcmd_ids + * @DATA_PATH_GROUP: data path group, uses command IDs from + * &enum iwl_data_path_subcmd_ids + * @NAN_GROUP: NAN group, uses command IDs from &enum iwl_nan_subcmd_ids + * @TOF_GROUP: TOF group, uses command IDs from &enum iwl_tof_subcmd_ids + * @PROT_OFFLOAD_GROUP: protocol offload group, uses command IDs from + * &enum iwl_prot_offload_subcmd_ids + * @REGULATORY_AND_NVM_GROUP: regulatory/NVM group, uses command IDs from + * &enum iwl_regulatory_and_nvm_subcmd_ids + * @DEBUG_GROUP: Debug group, uses command IDs from &enum iwl_debug_cmds + */ +enum iwl_mvm_command_groups { + LEGACY_GROUP = 0x0, + LONG_GROUP = 0x1, + SYSTEM_GROUP = 0x2, + MAC_CONF_GROUP = 0x3, + PHY_OPS_GROUP = 0x4, + DATA_PATH_GROUP = 0x5, + NAN_GROUP = 0x7, + TOF_GROUP = 0x8, + PROT_OFFLOAD_GROUP = 0xb, + REGULATORY_AND_NVM_GROUP = 0xc, + DEBUG_GROUP = 0xf, +}; + +/** + * enum iwl_legacy_cmds - legacy group command IDs + */ +enum iwl_legacy_cmds { + /** + * @MVM_ALIVE: + * Alive data from the firmware, as described in + * &struct mvm_alive_resp_v3 or &struct mvm_alive_resp. + */ + MVM_ALIVE = 0x1, + + /** + * @REPLY_ERROR: Cause an error in the firmware, for testing purposes. + */ + REPLY_ERROR = 0x2, + + /** + * @ECHO_CMD: Send data to the device to have it returned immediately. + */ + ECHO_CMD = 0x3, + + /** + * @INIT_COMPLETE_NOTIF: Notification that initialization is complete. + */ + INIT_COMPLETE_NOTIF = 0x4, + + /** + * @PHY_CONTEXT_CMD: + * Add/modify/remove a PHY context, using &struct iwl_phy_context_cmd. + */ + PHY_CONTEXT_CMD = 0x8, + + /** + * @DBG_CFG: Debug configuration command. + */ + DBG_CFG = 0x9, + + /** + * @ANTENNA_COUPLING_NOTIFICATION: + * Antenna coupling data, &struct iwl_mvm_antenna_coupling_notif + */ + ANTENNA_COUPLING_NOTIFICATION = 0xa, + + /** + * @SCAN_ITERATION_COMPLETE_UMAC: + * Firmware indicates a scan iteration completed, using + * &struct iwl_umac_scan_iter_complete_notif. + */ + SCAN_ITERATION_COMPLETE_UMAC = 0xb5, + + /** + * @SCAN_CFG_CMD: + * uses &struct iwl_scan_config_v1 or &struct iwl_scan_config + */ + SCAN_CFG_CMD = 0xc, + + /** + * @SCAN_REQ_UMAC: uses &struct iwl_scan_req_umac + */ + SCAN_REQ_UMAC = 0xd, + + /** + * @SCAN_ABORT_UMAC: uses &struct iwl_umac_scan_abort + */ + SCAN_ABORT_UMAC = 0xe, + + /** + * @SCAN_COMPLETE_UMAC: uses &struct iwl_umac_scan_complete + */ + SCAN_COMPLETE_UMAC = 0xf, + + /** + * @BA_WINDOW_STATUS_NOTIFICATION_ID: + * uses &struct iwl_ba_window_status_notif + */ + BA_WINDOW_STATUS_NOTIFICATION_ID = 0x13, + + /** + * @ADD_STA_KEY: + * &struct iwl_mvm_add_sta_key_cmd_v1 or + * &struct iwl_mvm_add_sta_key_cmd. + */ + ADD_STA_KEY = 0x17, + + /** + * @ADD_STA: + * &struct iwl_mvm_add_sta_cmd or &struct iwl_mvm_add_sta_cmd_v7. + */ + ADD_STA = 0x18, + + /** + * @REMOVE_STA: &struct iwl_mvm_rm_sta_cmd + */ + REMOVE_STA = 0x19, + + /** + * @FW_GET_ITEM_CMD: uses &struct iwl_fw_get_item_cmd + */ + FW_GET_ITEM_CMD = 0x1a, + + /** + * @TX_CMD: uses &struct iwl_tx_cmd or &struct iwl_tx_cmd_gen2, + * response in &struct iwl_mvm_tx_resp or + * &struct iwl_mvm_tx_resp_v3 + */ + TX_CMD = 0x1c, + + /** + * @TXPATH_FLUSH: &struct iwl_tx_path_flush_cmd + */ + TXPATH_FLUSH = 0x1e, + + /** + * @MGMT_MCAST_KEY: + * &struct iwl_mvm_mgmt_mcast_key_cmd or + * &struct iwl_mvm_mgmt_mcast_key_cmd_v1 + */ + MGMT_MCAST_KEY = 0x1f, + + /* scheduler config */ + /** + * @SCD_QUEUE_CFG: &struct iwl_scd_txq_cfg_cmd for older hardware, + * &struct iwl_tx_queue_cfg_cmd with &struct iwl_tx_queue_cfg_rsp + * for newer (A000) hardware. + */ + SCD_QUEUE_CFG = 0x1d, + + /** + * @WEP_KEY: uses &struct iwl_mvm_wep_key_cmd + */ + WEP_KEY = 0x20, + + /** + * @SHARED_MEM_CFG: + * retrieve shared memory configuration - response in + * &struct iwl_shared_mem_cfg + */ + SHARED_MEM_CFG = 0x25, + + /** + * @TDLS_CHANNEL_SWITCH_CMD: uses &struct iwl_tdls_channel_switch_cmd + */ + TDLS_CHANNEL_SWITCH_CMD = 0x27, + + /** + * @TDLS_CHANNEL_SWITCH_NOTIFICATION: + * uses &struct iwl_tdls_channel_switch_notif + */ + TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa, + + /** + * @TDLS_CONFIG_CMD: + * &struct iwl_tdls_config_cmd, response in &struct iwl_tdls_config_res + */ + TDLS_CONFIG_CMD = 0xa7, + + /** + * @MAC_CONTEXT_CMD: &struct iwl_mac_ctx_cmd + */ + MAC_CONTEXT_CMD = 0x28, + + /** + * @TIME_EVENT_CMD: + * &struct iwl_time_event_cmd, response in &struct iwl_time_event_resp + */ + TIME_EVENT_CMD = 0x29, /* both CMD and response */ + + /** + * @TIME_EVENT_NOTIFICATION: &struct iwl_time_event_notif + */ + TIME_EVENT_NOTIFICATION = 0x2a, + + /** + * @BINDING_CONTEXT_CMD: + * &struct iwl_binding_cmd or &struct iwl_binding_cmd_v1 + */ + BINDING_CONTEXT_CMD = 0x2b, + + /** + * @TIME_QUOTA_CMD: &struct iwl_time_quota_cmd + */ + TIME_QUOTA_CMD = 0x2c, + + /** + * @NON_QOS_TX_COUNTER_CMD: + * command is &struct iwl_nonqos_seq_query_cmd + */ + NON_QOS_TX_COUNTER_CMD = 0x2d, + + /** + * @LQ_CMD: using &struct iwl_lq_cmd + */ + LQ_CMD = 0x4e, + + /** + * @FW_PAGING_BLOCK_CMD: + * &struct iwl_fw_paging_cmd + */ + FW_PAGING_BLOCK_CMD = 0x4f, + + /** + * @SCAN_OFFLOAD_REQUEST_CMD: uses &struct iwl_scan_req_lmac + */ + SCAN_OFFLOAD_REQUEST_CMD = 0x51, + + /** + * @SCAN_OFFLOAD_ABORT_CMD: abort the scan - no further contents + */ + SCAN_OFFLOAD_ABORT_CMD = 0x52, + + /** + * @HOT_SPOT_CMD: uses &struct iwl_hs20_roc_req + */ + HOT_SPOT_CMD = 0x53, + + /** + * @SCAN_OFFLOAD_COMPLETE: + * notification, &struct iwl_periodic_scan_complete + */ + SCAN_OFFLOAD_COMPLETE = 0x6D, + + /** + * @SCAN_OFFLOAD_UPDATE_PROFILES_CMD: + * update scan offload (scheduled scan) profiles/blacklist/etc. + */ + SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, + + /** + * @MATCH_FOUND_NOTIFICATION: scan match found + */ + MATCH_FOUND_NOTIFICATION = 0xd9, + + /** + * @SCAN_ITERATION_COMPLETE: + * uses &struct iwl_lmac_scan_complete_notif + */ + SCAN_ITERATION_COMPLETE = 0xe7, + + /* Phy */ + /** + * @PHY_CONFIGURATION_CMD: &struct iwl_phy_cfg_cmd + */ + PHY_CONFIGURATION_CMD = 0x6a, + + /** + * @CALIB_RES_NOTIF_PHY_DB: &struct iwl_calib_res_notif_phy_db + */ + CALIB_RES_NOTIF_PHY_DB = 0x6b, + + /** + * @PHY_DB_CMD: &struct iwl_phy_db_cmd + */ + PHY_DB_CMD = 0x6c, + + /** + * @TOF_CMD: &struct iwl_tof_config_cmd + */ + TOF_CMD = 0x10, + + /** + * @TOF_NOTIFICATION: &struct iwl_tof_gen_resp_cmd + */ + TOF_NOTIFICATION = 0x11, + + /** + * @POWER_TABLE_CMD: &struct iwl_device_power_cmd + */ + POWER_TABLE_CMD = 0x77, + + /** + * @PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION: + * &struct iwl_uapsd_misbehaving_ap_notif + */ + PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78, + + /** + * @LTR_CONFIG: &struct iwl_ltr_config_cmd + */ + LTR_CONFIG = 0xee, + + /** + * @REPLY_THERMAL_MNG_BACKOFF: + * Thermal throttling command + */ + REPLY_THERMAL_MNG_BACKOFF = 0x7e, + + /** + * @DC2DC_CONFIG_CMD: + * Set/Get DC2DC frequency tune + * Command is &struct iwl_dc2dc_config_cmd, + * response is &struct iwl_dc2dc_config_resp + */ + DC2DC_CONFIG_CMD = 0x83, + + /** + * @NVM_ACCESS_CMD: using &struct iwl_nvm_access_cmd + */ + NVM_ACCESS_CMD = 0x88, + + /** + * @BEACON_NOTIFICATION: &struct iwl_extended_beacon_notif + */ + BEACON_NOTIFICATION = 0x90, + + /** + * @BEACON_TEMPLATE_CMD: + * Uses one of &struct iwl_mac_beacon_cmd_v6, + * &struct iwl_mac_beacon_cmd_v7 or &struct iwl_mac_beacon_cmd + * depending on the device version. + */ + BEACON_TEMPLATE_CMD = 0x91, + /** + * @TX_ANT_CONFIGURATION_CMD: &struct iwl_tx_ant_cfg_cmd + */ + TX_ANT_CONFIGURATION_CMD = 0x98, + + /** + * @STATISTICS_CMD: &struct iwl_statistics_cmd + */ + STATISTICS_CMD = 0x9c, + + /** + * @STATISTICS_NOTIFICATION: + * one of &struct iwl_notif_statistics_v10, + * &struct iwl_notif_statistics_v11, + * &struct iwl_notif_statistics_cdb + */ + STATISTICS_NOTIFICATION = 0x9d, + + /** + * @EOSP_NOTIFICATION: + * Notify that a service period ended, + * &struct iwl_mvm_eosp_notification + */ + EOSP_NOTIFICATION = 0x9e, + + /** + * @REDUCE_TX_POWER_CMD: + * &struct iwl_dev_tx_power_cmd_v3 or &struct iwl_dev_tx_power_cmd + */ + REDUCE_TX_POWER_CMD = 0x9f, + + /** + * @CARD_STATE_NOTIFICATION: + * Card state (RF/CT kill) notification, + * uses &struct iwl_card_state_notif + */ + CARD_STATE_NOTIFICATION = 0xa1, + + /** + * @MISSED_BEACONS_NOTIFICATION: &struct iwl_missed_beacons_notif + */ + MISSED_BEACONS_NOTIFICATION = 0xa2, + + /** + * @MAC_PM_POWER_TABLE: using &struct iwl_mac_power_cmd + */ + MAC_PM_POWER_TABLE = 0xa9, + + /** + * @MFUART_LOAD_NOTIFICATION: &struct iwl_mfuart_load_notif + */ + MFUART_LOAD_NOTIFICATION = 0xb1, + + /** + * @RSS_CONFIG_CMD: &struct iwl_rss_config_cmd + */ + RSS_CONFIG_CMD = 0xb3, + + /** + * @REPLY_RX_PHY_CMD: &struct iwl_rx_phy_info + */ + REPLY_RX_PHY_CMD = 0xc0, + + /** + * @REPLY_RX_MPDU_CMD: + * &struct iwl_rx_mpdu_res_start or &struct iwl_rx_mpdu_desc + */ + REPLY_RX_MPDU_CMD = 0xc1, + + /** + * @FRAME_RELEASE: + * Frame release (reorder helper) notification, uses + * &struct iwl_frame_release + */ + FRAME_RELEASE = 0xc3, + + /** + * @BA_NOTIF: + * BlockAck notification, uses &struct iwl_mvm_compressed_ba_notif + * or &struct iwl_mvm_ba_notif depending on the HW + */ + BA_NOTIF = 0xc5, + + /* Location Aware Regulatory */ + /** + * @MCC_UPDATE_CMD: using &struct iwl_mcc_update_cmd + */ + MCC_UPDATE_CMD = 0xc8, + + /** + * @MCC_CHUB_UPDATE_CMD: using &struct iwl_mcc_chub_notif + */ + MCC_CHUB_UPDATE_CMD = 0xc9, + + /** + * @MARKER_CMD: trace marker command, uses &struct iwl_mvm_marker + */ + MARKER_CMD = 0xcb, + + /** + * @BT_PROFILE_NOTIFICATION: &struct iwl_bt_coex_profile_notif + */ + BT_PROFILE_NOTIFICATION = 0xce, + + /** + * @BT_CONFIG: &struct iwl_bt_coex_cmd + */ + BT_CONFIG = 0x9b, + + /** + * @BT_COEX_UPDATE_CORUN_LUT: + * &struct iwl_bt_coex_corun_lut_update_cmd + */ + BT_COEX_UPDATE_CORUN_LUT = 0x5b, + + /** + * @BT_COEX_UPDATE_REDUCED_TXP: + * &struct iwl_bt_coex_reduced_txp_update_cmd + */ + BT_COEX_UPDATE_REDUCED_TXP = 0x5c, + + /** + * @BT_COEX_CI: &struct iwl_bt_coex_ci_cmd + */ + BT_COEX_CI = 0x5d, + + /** + * @REPLY_SF_CFG_CMD: &struct iwl_sf_cfg_cmd + */ + REPLY_SF_CFG_CMD = 0xd1, + /** + * @REPLY_BEACON_FILTERING_CMD: &struct iwl_beacon_filter_cmd + */ + REPLY_BEACON_FILTERING_CMD = 0xd2, + + /** + * @DTS_MEASUREMENT_NOTIFICATION: + * &struct iwl_dts_measurement_notif_v1 or + * &struct iwl_dts_measurement_notif_v2 + */ + DTS_MEASUREMENT_NOTIFICATION = 0xdd, + + /** + * @LDBG_CONFIG_CMD: configure continuous trace recording + */ + LDBG_CONFIG_CMD = 0xf6, + + /** + * @DEBUG_LOG_MSG: Debugging log data from firmware + */ + DEBUG_LOG_MSG = 0xf7, + + /** + * @BCAST_FILTER_CMD: &struct iwl_bcast_filter_cmd + */ + BCAST_FILTER_CMD = 0xcf, + + /** + * @MCAST_FILTER_CMD: &struct iwl_mcast_filter_cmd + */ + MCAST_FILTER_CMD = 0xd0, + + /** + * @D3_CONFIG_CMD: &struct iwl_d3_manager_config + */ + D3_CONFIG_CMD = 0xd3, + + /** + * @PROT_OFFLOAD_CONFIG_CMD: Depending on firmware, uses one of + * &struct iwl_proto_offload_cmd_v1, &struct iwl_proto_offload_cmd_v2, + * &struct iwl_proto_offload_cmd_v3_small, + * &struct iwl_proto_offload_cmd_v3_large + */ + PROT_OFFLOAD_CONFIG_CMD = 0xd4, + + /** + * @OFFLOADS_QUERY_CMD: + * No data in command, response in &struct iwl_wowlan_status + */ + OFFLOADS_QUERY_CMD = 0xd5, + + /** + * @REMOTE_WAKE_CONFIG_CMD: &struct iwl_wowlan_remote_wake_config + */ + REMOTE_WAKE_CONFIG_CMD = 0xd6, + + /** + * @D0I3_END_CMD: End D0i3/D3 state, no command data + */ + D0I3_END_CMD = 0xed, + + /** + * @WOWLAN_PATTERNS: &struct iwl_wowlan_patterns_cmd + */ + WOWLAN_PATTERNS = 0xe0, + + /** + * @WOWLAN_CONFIGURATION: &struct iwl_wowlan_config_cmd + */ + WOWLAN_CONFIGURATION = 0xe1, + + /** + * @WOWLAN_TSC_RSC_PARAM: &struct iwl_wowlan_rsc_tsc_params_cmd + */ + WOWLAN_TSC_RSC_PARAM = 0xe2, + + /** + * @WOWLAN_TKIP_PARAM: &struct iwl_wowlan_tkip_params_cmd + */ + WOWLAN_TKIP_PARAM = 0xe3, + + /** + * @WOWLAN_KEK_KCK_MATERIAL: &struct iwl_wowlan_kek_kck_material_cmd + */ + WOWLAN_KEK_KCK_MATERIAL = 0xe4, + + /** + * @WOWLAN_GET_STATUSES: response in &struct iwl_wowlan_status + */ + WOWLAN_GET_STATUSES = 0xe5, + + /** + * @SCAN_OFFLOAD_PROFILES_QUERY_CMD: + * No command data, response is &struct iwl_scan_offload_profiles_query + */ + SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56, +}; + +/** + * enum iwl_system_subcmd_ids - system group command IDs + */ +enum iwl_system_subcmd_ids { + /** + * @SHARED_MEM_CFG_CMD: + * response in &struct iwl_shared_mem_cfg or + * &struct iwl_shared_mem_cfg_v2 + */ + SHARED_MEM_CFG_CMD = 0x0, + + /** + * @INIT_EXTENDED_CFG_CMD: &struct iwl_init_extended_cfg_cmd + */ + INIT_EXTENDED_CFG_CMD = 0x03, +}; + +#endif /* __iwl_fw_api_commands_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/config.h b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h new file mode 100644 index 000000000000..ee1bd45b7021 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h @@ -0,0 +1,192 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_config_h__ +#define __iwl_fw_api_config_h__ + +/* + * struct iwl_dqa_enable_cmd + * @cmd_queue: the TXQ number of the command queue + */ +struct iwl_dqa_enable_cmd { + __le32 cmd_queue; +} __packed; /* DQA_CONTROL_CMD_API_S_VER_1 */ + +/* + * struct iwl_tx_ant_cfg_cmd + * @valid: valid antenna configuration + */ +struct iwl_tx_ant_cfg_cmd { + __le32 valid; +} __packed; + +/** + * struct iwl_calib_ctrl - Calibration control struct. + * Sent as part of the phy configuration command. + * @flow_trigger: bitmap for which calibrations to perform according to + * flow triggers, using &enum iwl_calib_cfg + * @event_trigger: bitmap for which calibrations to perform according to + * event triggers, using &enum iwl_calib_cfg + */ +struct iwl_calib_ctrl { + __le32 flow_trigger; + __le32 event_trigger; +} __packed; + +/* This enum defines the bitmap of various calibrations to enable in both + * init ucode and runtime ucode through CALIBRATION_CFG_CMD. + */ +enum iwl_calib_cfg { + IWL_CALIB_CFG_XTAL_IDX = BIT(0), + IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(1), + IWL_CALIB_CFG_VOLTAGE_READ_IDX = BIT(2), + IWL_CALIB_CFG_PAPD_IDX = BIT(3), + IWL_CALIB_CFG_TX_PWR_IDX = BIT(4), + IWL_CALIB_CFG_DC_IDX = BIT(5), + IWL_CALIB_CFG_BB_FILTER_IDX = BIT(6), + IWL_CALIB_CFG_LO_LEAKAGE_IDX = BIT(7), + IWL_CALIB_CFG_TX_IQ_IDX = BIT(8), + IWL_CALIB_CFG_TX_IQ_SKEW_IDX = BIT(9), + IWL_CALIB_CFG_RX_IQ_IDX = BIT(10), + IWL_CALIB_CFG_RX_IQ_SKEW_IDX = BIT(11), + IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(12), + IWL_CALIB_CFG_CHAIN_NOISE_IDX = BIT(13), + IWL_CALIB_CFG_DISCONNECTED_ANT_IDX = BIT(14), + IWL_CALIB_CFG_ANT_COUPLING_IDX = BIT(15), + IWL_CALIB_CFG_DAC_IDX = BIT(16), + IWL_CALIB_CFG_ABS_IDX = BIT(17), + IWL_CALIB_CFG_AGC_IDX = BIT(18), +}; + +/** + * struct iwl_phy_cfg_cmd - Phy configuration command + * @phy_cfg: PHY configuration value, uses &enum iwl_fw_phy_cfg + * @calib_control: calibration control data + */ +struct iwl_phy_cfg_cmd { + __le32 phy_cfg; + struct iwl_calib_ctrl calib_control; +} __packed; + +#define PHY_CFG_RADIO_TYPE (BIT(0) | BIT(1)) +#define PHY_CFG_RADIO_STEP (BIT(2) | BIT(3)) +#define PHY_CFG_RADIO_DASH (BIT(4) | BIT(5)) +#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7)) +#define PHY_CFG_TX_CHAIN_A BIT(8) +#define PHY_CFG_TX_CHAIN_B BIT(9) +#define PHY_CFG_TX_CHAIN_C BIT(10) +#define PHY_CFG_RX_CHAIN_A BIT(12) +#define PHY_CFG_RX_CHAIN_B BIT(13) +#define PHY_CFG_RX_CHAIN_C BIT(14) + +/* + * enum iwl_dc2dc_config_id - flag ids + * + * Ids of dc2dc configuration flags + */ +enum iwl_dc2dc_config_id { + DCDC_LOW_POWER_MODE_MSK_SET = 0x1, /* not used */ + DCDC_FREQ_TUNE_SET = 0x2, +}; /* MARKER_ID_API_E_VER_1 */ + +/** + * struct iwl_dc2dc_config_cmd - configure dc2dc values + * + * (DC2DC_CONFIG_CMD = 0x83) + * + * Set/Get & configure dc2dc values. + * The command always returns the current dc2dc values. + * + * @flags: set/get dc2dc + * @enable_low_power_mode: not used. + * @dc2dc_freq_tune0: frequency divider - digital domain + * @dc2dc_freq_tune1: frequency divider - analog domain + */ +struct iwl_dc2dc_config_cmd { + __le32 flags; + __le32 enable_low_power_mode; /* not used */ + __le32 dc2dc_freq_tune0; + __le32 dc2dc_freq_tune1; +} __packed; /* DC2DC_CONFIG_CMD_API_S_VER_1 */ + +/** + * struct iwl_dc2dc_config_resp - response for iwl_dc2dc_config_cmd + * + * Current dc2dc values returned by the FW. + * + * @dc2dc_freq_tune0: frequency divider - digital domain + * @dc2dc_freq_tune1: frequency divider - analog domain + */ +struct iwl_dc2dc_config_resp { + __le32 dc2dc_freq_tune0; + __le32 dc2dc_freq_tune1; +} __packed; /* DC2DC_CONFIG_RESP_API_S_VER_1 */ + +/** + * struct iwl_mvm_antenna_coupling_notif - antenna coupling notification + * @isolation: antenna isolation value + */ +struct iwl_mvm_antenna_coupling_notif { + __le32 isolation; +} __packed; + +#endif /* __iwl_fw_api_config_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/context.h b/drivers/net/wireless/intel/iwlwifi/fw/api/context.h new file mode 100644 index 000000000000..2f0d7c498b3e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/context.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_context_h__ +#define __iwl_fw_api_context_h__ + +/** + * enum iwl_ctxt_id_and_color - ID and color fields in context dword + * @FW_CTXT_ID_POS: position of the ID + * @FW_CTXT_ID_MSK: mask of the ID + * @FW_CTXT_COLOR_POS: position of the color + * @FW_CTXT_COLOR_MSK: mask of the color + * @FW_CTXT_INVALID: value used to indicate unused/invalid + */ +enum iwl_ctxt_id_and_color { + FW_CTXT_ID_POS = 0, + FW_CTXT_ID_MSK = 0xff << FW_CTXT_ID_POS, + FW_CTXT_COLOR_POS = 8, + FW_CTXT_COLOR_MSK = 0xff << FW_CTXT_COLOR_POS, + FW_CTXT_INVALID = 0xffffffff, +}; + +#define FW_CMD_ID_AND_COLOR(_id, _color) (((_id) << FW_CTXT_ID_POS) |\ + ((_color) << FW_CTXT_COLOR_POS)) + +/* Possible actions on PHYs, MACs and Bindings */ +enum iwl_ctxt_action { + FW_CTXT_ACTION_STUB = 0, + FW_CTXT_ACTION_ADD, + FW_CTXT_ACTION_MODIFY, + FW_CTXT_ACTION_REMOVE, + FW_CTXT_ACTION_NUM +}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */ + +#endif /* __iwl_fw_api_context_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h new file mode 100644 index 000000000000..57f4bc242023 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -0,0 +1,466 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_fw_api_d3_h__ +#define __iwl_fw_api_d3_h__ + +/** + * enum iwl_d3_wakeup_flags - D3 manager wakeup flags + * @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert + */ +enum iwl_d3_wakeup_flags { + IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0), +}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */ + +/** + * struct iwl_d3_manager_config - D3 manager configuration command + * @min_sleep_time: minimum sleep time (in usec) + * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags + * @wakeup_host_timer: force wakeup after this many seconds + * + * The structure is used for the D3_CONFIG_CMD command. + */ +struct iwl_d3_manager_config { + __le32 min_sleep_time; + __le32 wakeup_flags; + __le32 wakeup_host_timer; +} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */ + + +/* TODO: OFFLOADS_QUERY_API_S_VER_1 */ + +/** + * enum iwl_d3_proto_offloads - enabled protocol offloads + * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled + * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled + * @IWL_D3_PROTO_IPV4_VALID: IPv4 data is valid + * @IWL_D3_PROTO_IPV6_VALID: IPv6 data is valid + */ +enum iwl_proto_offloads { + IWL_D3_PROTO_OFFLOAD_ARP = BIT(0), + IWL_D3_PROTO_OFFLOAD_NS = BIT(1), + IWL_D3_PROTO_IPV4_VALID = BIT(2), + IWL_D3_PROTO_IPV6_VALID = BIT(3), +}; + +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12 + +#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4 +#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2 + +/** + * struct iwl_proto_offload_cmd_common - ARP/NS offload common part + * @enabled: enable flags + * @remote_ipv4_addr: remote address to answer to (or zero if all) + * @host_ipv4_addr: our IPv4 address to respond to queries for + * @arp_mac_addr: our MAC address for ARP responses + * @reserved: unused + */ +struct iwl_proto_offload_cmd_common { + __le32 enabled; + __be32 remote_ipv4_addr; + __be32 host_ipv4_addr; + u8 arp_mac_addr[ETH_ALEN]; + __le16 reserved; +} __packed; + +/** + * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @remote_ipv6_addr: remote address to answer to (or zero if all) + * @solicited_node_ipv6_addr: broken -- solicited node address exists + * for each target address + * @target_ipv6_addr: our target addresses + * @ndp_mac_addr: neighbor solicitation response MAC address + * @reserved2: reserved + */ +struct iwl_proto_offload_cmd_v1 { + struct iwl_proto_offload_cmd_common common; + u8 remote_ipv6_addr[16]; + u8 solicited_node_ipv6_addr[16]; + u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16]; + u8 ndp_mac_addr[ETH_ALEN]; + __le16 reserved2; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */ + +/** + * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @remote_ipv6_addr: remote address to answer to (or zero if all) + * @solicited_node_ipv6_addr: broken -- solicited node address exists + * for each target address + * @target_ipv6_addr: our target addresses + * @ndp_mac_addr: neighbor solicitation response MAC address + * @num_valid_ipv6_addrs: number of valid IPv6 addresses + * @reserved2: reserved + */ +struct iwl_proto_offload_cmd_v2 { + struct iwl_proto_offload_cmd_common common; + u8 remote_ipv6_addr[16]; + u8 solicited_node_ipv6_addr[16]; + u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16]; + u8 ndp_mac_addr[ETH_ALEN]; + u8 num_valid_ipv6_addrs; + u8 reserved2[3]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ + +struct iwl_ns_config { + struct in6_addr source_ipv6_addr; + struct in6_addr dest_ipv6_addr; + u8 target_mac_addr[ETH_ALEN]; + __le16 reserved; +} __packed; /* NS_OFFLOAD_CONFIG */ + +struct iwl_targ_addr { + struct in6_addr addr; + __le32 config_num; +} __packed; /* TARGET_IPV6_ADDRESS */ + +/** + * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @num_valid_ipv6_addrs: number of valid IPv6 addresses + * @targ_addrs: target IPv6 addresses + * @ns_config: NS offload configurations + */ +struct iwl_proto_offload_cmd_v3_small { + struct iwl_proto_offload_cmd_common common; + __le32 num_valid_ipv6_addrs; + struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S]; + struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ + +/** + * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @num_valid_ipv6_addrs: number of valid IPv6 addresses + * @targ_addrs: target IPv6 addresses + * @ns_config: NS offload configurations + */ +struct iwl_proto_offload_cmd_v3_large { + struct iwl_proto_offload_cmd_common common; + __le32 num_valid_ipv6_addrs; + struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L]; + struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ + +/* + * WOWLAN_PATTERNS + */ +#define IWL_WOWLAN_MIN_PATTERN_LEN 16 +#define IWL_WOWLAN_MAX_PATTERN_LEN 128 + +struct iwl_wowlan_pattern { + u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; + u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN]; + u8 mask_size; + u8 pattern_size; + __le16 reserved; +} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */ + +#define IWL_WOWLAN_MAX_PATTERNS 20 + +struct iwl_wowlan_patterns_cmd { + __le32 n_patterns; + struct iwl_wowlan_pattern patterns[]; +} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */ + +enum iwl_wowlan_wakeup_filters { + IWL_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0), + IWL_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1), + IWL_WOWLAN_WAKEUP_BEACON_MISS = BIT(2), + IWL_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3), + IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4), + IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5), + IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6), + IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(7), + IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8), + IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9), + IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10), + IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11), + IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12), + IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13), + IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14), + IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15), + IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16), +}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */ + +enum iwl_wowlan_flags { + IS_11W_ASSOC = BIT(0), + ENABLE_L3_FILTERING = BIT(1), + ENABLE_NBNS_FILTERING = BIT(2), + ENABLE_DHCP_FILTERING = BIT(3), + ENABLE_STORE_BEACON = BIT(4), +}; + +/** + * struct iwl_wowlan_config_cmd - WoWLAN configuration + * @wakeup_filter: filter from &enum iwl_wowlan_wakeup_filters + * @non_qos_seq: non-QoS sequence counter to use next + * @qos_seq: QoS sequence counters to use next + * @wowlan_ba_teardown_tids: bitmap of BA sessions to tear down + * @is_11n_connection: indicates HT connection + * @offloading_tid: TID reserved for firmware use + * @flags: extra flags, see &enum iwl_wowlan_flags + * @reserved: reserved + */ +struct iwl_wowlan_config_cmd { + __le32 wakeup_filter; + __le16 non_qos_seq; + __le16 qos_seq[8]; + u8 wowlan_ba_teardown_tids; + u8 is_11n_connection; + u8 offloading_tid; + u8 flags; + u8 reserved[2]; +} __packed; /* WOWLAN_CONFIG_API_S_VER_4 */ + +/* + * WOWLAN_TSC_RSC_PARAMS + */ +#define IWL_NUM_RSC 16 + +struct tkip_sc { + __le16 iv16; + __le16 pad; + __le32 iv32; +} __packed; /* TKIP_SC_API_U_VER_1 */ + +struct iwl_tkip_rsc_tsc { + struct tkip_sc unicast_rsc[IWL_NUM_RSC]; + struct tkip_sc multicast_rsc[IWL_NUM_RSC]; + struct tkip_sc tsc; +} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */ + +struct aes_sc { + __le64 pn; +} __packed; /* TKIP_AES_SC_API_U_VER_1 */ + +struct iwl_aes_rsc_tsc { + struct aes_sc unicast_rsc[IWL_NUM_RSC]; + struct aes_sc multicast_rsc[IWL_NUM_RSC]; + struct aes_sc tsc; +} __packed; /* AES_TSC_RSC_API_S_VER_1 */ + +union iwl_all_tsc_rsc { + struct iwl_tkip_rsc_tsc tkip; + struct iwl_aes_rsc_tsc aes; +}; /* ALL_TSC_RSC_API_S_VER_2 */ + +struct iwl_wowlan_rsc_tsc_params_cmd { + union iwl_all_tsc_rsc all_tsc_rsc; +} __packed; /* ALL_TSC_RSC_API_S_VER_2 */ + +#define IWL_MIC_KEY_SIZE 8 +struct iwl_mic_keys { + u8 tx[IWL_MIC_KEY_SIZE]; + u8 rx_unicast[IWL_MIC_KEY_SIZE]; + u8 rx_mcast[IWL_MIC_KEY_SIZE]; +} __packed; /* MIC_KEYS_API_S_VER_1 */ + +#define IWL_P1K_SIZE 5 +struct iwl_p1k_cache { + __le16 p1k[IWL_P1K_SIZE]; +} __packed; + +#define IWL_NUM_RX_P1K_CACHE 2 + +struct iwl_wowlan_tkip_params_cmd { + struct iwl_mic_keys mic_keys; + struct iwl_p1k_cache tx; + struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE]; + struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE]; +} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */ + +#define IWL_KCK_MAX_SIZE 32 +#define IWL_KEK_MAX_SIZE 32 + +struct iwl_wowlan_kek_kck_material_cmd { + u8 kck[IWL_KCK_MAX_SIZE]; + u8 kek[IWL_KEK_MAX_SIZE]; + __le16 kck_len; + __le16 kek_len; + __le64 replay_ctr; +} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */ + +#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87 + +enum iwl_wowlan_rekey_status { + IWL_WOWLAN_REKEY_POST_REKEY = 0, + IWL_WOWLAN_REKEY_WHILE_REKEY = 1, +}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */ + +enum iwl_wowlan_wakeup_reason { + IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS = 0, + IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET = BIT(0), + IWL_WOWLAN_WAKEUP_BY_PATTERN = BIT(1), + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON = BIT(2), + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH = BIT(3), + IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE = BIT(4), + IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED = BIT(5), + IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR = BIT(6), + IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST = BIT(7), + IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8), + IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9), + IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10), + IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL = BIT(11), + IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), + IWL_WOWLAN_WAKEUP_BY_IOAC_MAGIC_PACKET = BIT(13), + IWL_WOWLAN_WAKEUP_BY_D3_WAKEUP_HOST_TIMER = BIT(14), + IWL_WOWLAN_WAKEUP_BY_RXFRAME_FILTERED_IN = BIT(15), + IWL_WOWLAN_WAKEUP_BY_BEACON_FILTERED_IN = BIT(16), + +}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ + +struct iwl_wowlan_gtk_status { + u8 key_index; + u8 reserved[3]; + u8 decrypt_key[16]; + u8 tkip_mic_key[8]; + struct iwl_wowlan_rsc_tsc_params_cmd rsc; +} __packed; /* WOWLAN_GTK_MATERIAL_VER_1 */ + +/** + * struct iwl_wowlan_status - WoWLAN status + * @gtk: GTK data + * @replay_ctr: GTK rekey replay counter + * @pattern_number: number of the matched pattern + * @non_qos_seq_ctr: non-QoS sequence counter to use next + * @qos_seq_ctr: QoS sequence counters to use next + * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason + * @num_of_gtk_rekeys: number of GTK rekeys + * @transmitted_ndps: number of transmitted neighbor discovery packets + * @received_beacons: number of received beacons + * @wake_packet_length: wakeup packet length + * @wake_packet_bufsize: wakeup packet buffer size + * @wake_packet: wakeup packet + */ +struct iwl_wowlan_status { + struct iwl_wowlan_gtk_status gtk; + __le64 replay_ctr; + __le16 pattern_number; + __le16 non_qos_seq_ctr; + __le16 qos_seq_ctr[8]; + __le32 wakeup_reasons; + __le32 num_of_gtk_rekeys; + __le32 transmitted_ndps; + __le32 received_beacons; + __le32 wake_packet_length; + __le32 wake_packet_bufsize; + u8 wake_packet[]; /* can be truncated from _length to _bufsize */ +} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */ + +#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64 +#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128 +#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048 + +struct iwl_tcp_packet_info { + __le16 tcp_pseudo_header_checksum; + __le16 tcp_payload_length; +} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */ + +struct iwl_tcp_packet { + struct iwl_tcp_packet_info info; + u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; + u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN]; +} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ + +struct iwl_remote_wake_packet { + struct iwl_tcp_packet_info info; + u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; + u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN]; +} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ + +struct iwl_wowlan_remote_wake_config { + __le32 connection_max_time; /* unused */ + /* TCP_PROTOCOL_CONFIG_API_S_VER_1 */ + u8 max_syn_retries; + u8 max_data_retries; + u8 tcp_syn_ack_timeout; + u8 tcp_ack_timeout; + + struct iwl_tcp_packet syn_tx; + struct iwl_tcp_packet synack_rx; + struct iwl_tcp_packet keepalive_ack_rx; + struct iwl_tcp_packet fin_tx; + + struct iwl_remote_wake_packet keepalive_tx; + struct iwl_remote_wake_packet wake_rx; + + /* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */ + u8 sequence_number_offset; + u8 sequence_number_length; + u8 token_offset; + u8 token_length; + /* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */ + __le32 initial_sequence_number; + __le16 keepalive_interval; + __le16 num_tokens; + u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS]; +} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */ + +/* TODO: NetDetect API */ + +#endif /* __iwl_fw_api_d3_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h new file mode 100644 index 000000000000..aa76dcc148bd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -0,0 +1,127 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_datapath_h__ +#define __iwl_fw_api_datapath_h__ + +/** + * enum iwl_data_path_subcmd_ids - data path group commands + */ +enum iwl_data_path_subcmd_ids { + /** + * @DQA_ENABLE_CMD: &struct iwl_dqa_enable_cmd + */ + DQA_ENABLE_CMD = 0x0, + + /** + * @UPDATE_MU_GROUPS_CMD: &struct iwl_mu_group_mgmt_cmd + */ + UPDATE_MU_GROUPS_CMD = 0x1, + + /** + * @TRIGGER_RX_QUEUES_NOTIF_CMD: &struct iwl_rxq_sync_cmd + */ + TRIGGER_RX_QUEUES_NOTIF_CMD = 0x2, + + /** + * @STA_PM_NOTIF: &struct iwl_mvm_pm_state_notification + */ + STA_PM_NOTIF = 0xFD, + + /** + * @MU_GROUP_MGMT_NOTIF: &struct iwl_mu_group_mgmt_notif + */ + MU_GROUP_MGMT_NOTIF = 0xFE, + + /** + * @RX_QUEUES_NOTIFICATION: &struct iwl_rxq_sync_notification + */ + RX_QUEUES_NOTIFICATION = 0xFF, +}; + +/** + * struct iwl_mu_group_mgmt_cmd - VHT MU-MIMO group configuration + * + * @reserved: reserved + * @membership_status: a bitmap of MU groups + * @user_position:the position of station in a group. If the station is in the + * group then bits (group * 2) is the position -1 + */ +struct iwl_mu_group_mgmt_cmd { + __le32 reserved; + __le32 membership_status[2]; + __le32 user_position[4]; +} __packed; /* MU_GROUP_ID_MNG_TABLE_API_S_VER_1 */ + +/** + * struct iwl_mu_group_mgmt_notif - VHT MU-MIMO group id notification + * + * @membership_status: a bitmap of MU groups + * @user_position: the position of station in a group. If the station is in the + * group then bits (group * 2) is the position -1 + */ +struct iwl_mu_group_mgmt_notif { + __le32 membership_status[2]; + __le32 user_position[4]; +} __packed; /* MU_GROUP_MNG_NTFY_API_S_VER_1 */ + +#endif /* __iwl_fw_api_datapath_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h new file mode 100644 index 000000000000..9f88b61536bc --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -0,0 +1,345 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_fw_api_debug_h__ +#define __iwl_fw_api_debug_h__ + +/** + * enum iwl_debug_cmds - debug commands + */ +enum iwl_debug_cmds { + /** + * @LMAC_RD_WR: + * LMAC memory read/write, using &struct iwl_dbg_mem_access_cmd and + * &struct iwl_dbg_mem_access_rsp + */ + LMAC_RD_WR = 0x0, + /** + * @UMAC_RD_WR: + * UMAC memory read/write, using &struct iwl_dbg_mem_access_cmd and + * &struct iwl_dbg_mem_access_rsp + */ + UMAC_RD_WR = 0x1, + /** + * @MFU_ASSERT_DUMP_NTF: + * &struct iwl_mfu_assert_dump_notif + */ + MFU_ASSERT_DUMP_NTF = 0xFE, +}; + +/* Error response/notification */ +enum { + FW_ERR_UNKNOWN_CMD = 0x0, + FW_ERR_INVALID_CMD_PARAM = 0x1, + FW_ERR_SERVICE = 0x2, + FW_ERR_ARC_MEMORY = 0x3, + FW_ERR_ARC_CODE = 0x4, + FW_ERR_WATCH_DOG = 0x5, + FW_ERR_WEP_GRP_KEY_INDX = 0x10, + FW_ERR_WEP_KEY_SIZE = 0x11, + FW_ERR_OBSOLETE_FUNC = 0x12, + FW_ERR_UNEXPECTED = 0xFE, + FW_ERR_FATAL = 0xFF +}; + +/** + * struct iwl_error_resp - FW error indication + * ( REPLY_ERROR = 0x2 ) + * @error_type: one of FW_ERR_* + * @cmd_id: the command ID for which the error occurred + * @reserved1: reserved + * @bad_cmd_seq_num: sequence number of the erroneous command + * @error_service: which service created the error, applicable only if + * error_type = 2, otherwise 0 + * @timestamp: TSF in usecs. + */ +struct iwl_error_resp { + __le32 error_type; + u8 cmd_id; + u8 reserved1; + __le16 bad_cmd_seq_num; + __le32 error_service; + __le64 timestamp; +} __packed; + +#define TX_FIFO_MAX_NUM_9000 8 +#define TX_FIFO_MAX_NUM 15 +#define RX_FIFO_MAX_NUM 2 +#define TX_FIFO_INTERNAL_MAX_NUM 6 + +/** + * struct iwl_shared_mem_cfg_v2 - Shared memory configuration information + * + * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not + * accessible) + * @shared_mem_size: shared memory size + * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to + * 0x0 as accessible only via DBGM RDAT) + * @sample_buff_size: internal sample buff size + * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre + * 8000 HW set to 0x0 as not accessible) + * @txfifo_size: size of TXF0 ... TXF7 + * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0 + * @page_buff_addr: used by UMAC and performance debug (page miss analysis), + * when paging is not supported this should be 0 + * @page_buff_size: size of %page_buff_addr + * @rxfifo_addr: Start address of rxFifo + * @internal_txfifo_addr: start address of internalFifo + * @internal_txfifo_size: internal fifos' size + * + * NOTE: on firmware that don't have IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG + * set, the last 3 members don't exist. + */ +struct iwl_shared_mem_cfg_v2 { + __le32 shared_mem_addr; + __le32 shared_mem_size; + __le32 sample_buff_addr; + __le32 sample_buff_size; + __le32 txfifo_addr; + __le32 txfifo_size[TX_FIFO_MAX_NUM_9000]; + __le32 rxfifo_size[RX_FIFO_MAX_NUM]; + __le32 page_buff_addr; + __le32 page_buff_size; + __le32 rxfifo_addr; + __le32 internal_txfifo_addr; + __le32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; +} __packed; /* SHARED_MEM_ALLOC_API_S_VER_2 */ + +/** + * struct iwl_shared_mem_lmac_cfg - LMAC shared memory configuration + * + * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB) + * @txfifo_size: size of TX FIFOs + * @rxfifo1_addr: RXF1 addr + * @rxfifo1_size: RXF1 size + */ +struct iwl_shared_mem_lmac_cfg { + __le32 txfifo_addr; + __le32 txfifo_size[TX_FIFO_MAX_NUM]; + __le32 rxfifo1_addr; + __le32 rxfifo1_size; + +} __packed; /* SHARED_MEM_ALLOC_LMAC_API_S_VER_1 */ + +/** + * struct iwl_shared_mem_cfg - Shared memory configuration information + * + * @shared_mem_addr: shared memory address + * @shared_mem_size: shared memory size + * @sample_buff_addr: internal sample (mon/adc) buff addr + * @sample_buff_size: internal sample buff size + * @rxfifo2_addr: start addr of RXF2 + * @rxfifo2_size: size of RXF2 + * @page_buff_addr: used by UMAC and performance debug (page miss analysis), + * when paging is not supported this should be 0 + * @page_buff_size: size of %page_buff_addr + * @lmac_num: number of LMACs (1 or 2) + * @lmac_smem: per - LMAC smem data + */ +struct iwl_shared_mem_cfg { + __le32 shared_mem_addr; + __le32 shared_mem_size; + __le32 sample_buff_addr; + __le32 sample_buff_size; + __le32 rxfifo2_addr; + __le32 rxfifo2_size; + __le32 page_buff_addr; + __le32 page_buff_size; + __le32 lmac_num; + struct iwl_shared_mem_lmac_cfg lmac_smem[2]; +} __packed; /* SHARED_MEM_ALLOC_API_S_VER_3 */ + +/** + * struct iwl_mfuart_load_notif - mfuart image version & status + * ( MFUART_LOAD_NOTIFICATION = 0xb1 ) + * @installed_ver: installed image version + * @external_ver: external image version + * @status: MFUART loading status + * @duration: MFUART loading time + * @image_size: MFUART image size in bytes +*/ +struct iwl_mfuart_load_notif { + __le32 installed_ver; + __le32 external_ver; + __le32 status; + __le32 duration; + /* image size valid only in v2 of the command */ + __le32 image_size; +} __packed; /* MFU_LOADER_NTFY_API_S_VER_2 */ + +/** + * struct iwl_mfu_assert_dump_notif - mfuart dump logs + * ( MFU_ASSERT_DUMP_NTF = 0xfe ) + * @assert_id: mfuart assert id that cause the notif + * @curr_reset_num: number of asserts since uptime + * @index_num: current chunk id + * @parts_num: total number of chunks + * @data_size: number of data bytes sent + * @data: data buffer + */ +struct iwl_mfu_assert_dump_notif { + __le32 assert_id; + __le32 curr_reset_num; + __le16 index_num; + __le16 parts_num; + __le32 data_size; + __le32 data[0]; +} __packed; /* MFU_DUMP_ASSERT_API_S_VER_1 */ + +/** + * enum iwl_mvm_marker_id - marker ids + * + * The ids for different type of markers to insert into the usniffer logs + * + * @MARKER_ID_TX_FRAME_LATENCY: TX latency marker + */ +enum iwl_mvm_marker_id { + MARKER_ID_TX_FRAME_LATENCY = 1, +}; /* MARKER_ID_API_E_VER_1 */ + +/** + * struct iwl_mvm_marker - mark info into the usniffer logs + * + * (MARKER_CMD = 0xcb) + * + * Mark the UTC time stamp into the usniffer logs together with additional + * metadata, so the usniffer output can be parsed. + * In the command response the ucode will return the GP2 time. + * + * @dw_len: The amount of dwords following this byte including this byte. + * @marker_id: A unique marker id (iwl_mvm_marker_id). + * @reserved: reserved. + * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC + * @metadata: additional meta data that will be written to the unsiffer log + */ +struct iwl_mvm_marker { + u8 dw_len; + u8 marker_id; + __le16 reserved; + __le64 timestamp; + __le32 metadata[0]; +} __packed; /* MARKER_API_S_VER_1 */ + +/* Operation types for the debug mem access */ +enum { + DEBUG_MEM_OP_READ = 0, + DEBUG_MEM_OP_WRITE = 1, + DEBUG_MEM_OP_WRITE_BYTES = 2, +}; + +#define DEBUG_MEM_MAX_SIZE_DWORDS 32 + +/** + * struct iwl_dbg_mem_access_cmd - Request the device to read/write memory + * @op: DEBUG_MEM_OP_* + * @addr: address to read/write from/to + * @len: in dwords, to read/write + * @data: for write opeations, contains the source buffer + */ +struct iwl_dbg_mem_access_cmd { + __le32 op; + __le32 addr; + __le32 len; + __le32 data[]; +} __packed; /* DEBUG_(U|L)MAC_RD_WR_CMD_API_S_VER_1 */ + +/* Status responses for the debug mem access */ +enum { + DEBUG_MEM_STATUS_SUCCESS = 0x0, + DEBUG_MEM_STATUS_FAILED = 0x1, + DEBUG_MEM_STATUS_LOCKED = 0x2, + DEBUG_MEM_STATUS_HIDDEN = 0x3, + DEBUG_MEM_STATUS_LENGTH = 0x4, +}; + +/** + * struct iwl_dbg_mem_access_rsp - Response to debug mem commands + * @status: DEBUG_MEM_STATUS_* + * @len: read dwords (0 for write operations) + * @data: contains the read DWs + */ +struct iwl_dbg_mem_access_rsp { + __le32 status; + __le32 len; + __le32 data[]; +} __packed; /* DEBUG_(U|L)MAC_RD_WR_RSP_API_S_VER_1 */ + +#define CONT_REC_COMMAND_SIZE 80 +#define ENABLE_CONT_RECORDING 0x15 +#define DISABLE_CONT_RECORDING 0x16 + +/* + * struct iwl_continuous_record_mode - recording mode + */ +struct iwl_continuous_record_mode { + __le16 enable_recording; +} __packed; + +/* + * struct iwl_continuous_record_cmd - enable/disable continuous recording + */ +struct iwl_continuous_record_cmd { + struct iwl_continuous_record_mode record_mode; + u8 pad[CONT_REC_COMMAND_SIZE - + sizeof(struct iwl_continuous_record_mode)]; +} __packed; + +#endif /* __iwl_fw_api_debug_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/filter.h b/drivers/net/wireless/intel/iwlwifi/fw/api/filter.h new file mode 100644 index 000000000000..befc3b126041 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/filter.h @@ -0,0 +1,183 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_filter_h__ +#define __iwl_fw_api_filter_h__ + +#include "fw/api/mac.h" + +#define MAX_PORT_ID_NUM 2 +#define MAX_MCAST_FILTERING_ADDRESSES 256 + +/** + * struct iwl_mcast_filter_cmd - configure multicast filter. + * @filter_own: Set 1 to filter out multicast packets sent by station itself + * @port_id: Multicast MAC addresses array specifier. This is a strange way + * to identify network interface adopted in host-device IF. + * It is used by FW as index in array of addresses. This array has + * MAX_PORT_ID_NUM members. + * @count: Number of MAC addresses in the array + * @pass_all: Set 1 to pass all multicast packets. + * @bssid: current association BSSID. + * @reserved: reserved + * @addr_list: Place holder for array of MAC addresses. + * IMPORTANT: add padding if necessary to ensure DWORD alignment. + */ +struct iwl_mcast_filter_cmd { + u8 filter_own; + u8 port_id; + u8 count; + u8 pass_all; + u8 bssid[6]; + u8 reserved[2]; + u8 addr_list[0]; +} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */ + +#define MAX_BCAST_FILTERS 8 +#define MAX_BCAST_FILTER_ATTRS 2 + +/** + * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet + * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start. + * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e. + * start of ip payload). + */ +enum iwl_mvm_bcast_filter_attr_offset { + BCAST_FILTER_OFFSET_PAYLOAD_START = 0, + BCAST_FILTER_OFFSET_IP_END = 1, +}; + +/** + * struct iwl_fw_bcast_filter_attr - broadcast filter attribute + * @offset_type: &enum iwl_mvm_bcast_filter_attr_offset. + * @offset: starting offset of this pattern. + * @reserved1: reserved + * @val: value to match - big endian (MSB is the first + * byte to match from offset pos). + * @mask: mask to match (big endian). + */ +struct iwl_fw_bcast_filter_attr { + u8 offset_type; + u8 offset; + __le16 reserved1; + __be32 val; + __be32 mask; +} __packed; /* BCAST_FILTER_ATT_S_VER_1 */ + +/** + * enum iwl_mvm_bcast_filter_frame_type - filter frame type + * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames. + * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames + */ +enum iwl_mvm_bcast_filter_frame_type { + BCAST_FILTER_FRAME_TYPE_ALL = 0, + BCAST_FILTER_FRAME_TYPE_IPV4 = 1, +}; + +/** + * struct iwl_fw_bcast_filter - broadcast filter + * @discard: discard frame (1) or let it pass (0). + * @frame_type: &enum iwl_mvm_bcast_filter_frame_type. + * @reserved1: reserved + * @num_attrs: number of valid attributes in this filter. + * @attrs: attributes of this filter. a filter is considered matched + * only when all its attributes are matched (i.e. AND relationship) + */ +struct iwl_fw_bcast_filter { + u8 discard; + u8 frame_type; + u8 num_attrs; + u8 reserved1; + struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS]; +} __packed; /* BCAST_FILTER_S_VER_1 */ + +/** + * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration. + * @default_discard: default action for this mac (discard (1) / pass (0)). + * @reserved1: reserved + * @attached_filters: bitmap of relevant filters for this mac. + */ +struct iwl_fw_bcast_mac { + u8 default_discard; + u8 reserved1; + __le16 attached_filters; +} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */ + +/** + * struct iwl_bcast_filter_cmd - broadcast filtering configuration + * @disable: enable (0) / disable (1) + * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS) + * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER) + * @reserved1: reserved + * @filters: broadcast filters + * @macs: broadcast filtering configuration per-mac + */ +struct iwl_bcast_filter_cmd { + u8 disable; + u8 max_bcast_filters; + u8 max_macs; + u8 reserved1; + struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS]; + struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER]; +} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */ + +#endif /* __iwl_fw_api_filter_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h new file mode 100644 index 000000000000..39c89e85fd2f --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -0,0 +1,152 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_mac_cfg_h__ +#define __iwl_fw_api_mac_cfg_h__ + +/** + * enum iwl_mac_conf_subcmd_ids - mac configuration command IDs + */ +enum iwl_mac_conf_subcmd_ids { + /** + * @LINK_QUALITY_MEASUREMENT_CMD: &struct iwl_link_qual_msrmnt_cmd + */ + LINK_QUALITY_MEASUREMENT_CMD = 0x1, + + /** + * @LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF: + * &struct iwl_link_qual_msrmnt_notif + */ + LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF = 0xFE, + + /** + * @CHANNEL_SWITCH_NOA_NOTIF: &struct iwl_channel_switch_noa_notif + */ + CHANNEL_SWITCH_NOA_NOTIF = 0xFF, +}; + +#define LQM_NUMBER_OF_STATIONS_IN_REPORT 16 + +enum iwl_lqm_cmd_operatrions { + LQM_CMD_OPERATION_START_MEASUREMENT = 0x01, + LQM_CMD_OPERATION_STOP_MEASUREMENT = 0x02, +}; + +enum iwl_lqm_status { + LQM_STATUS_SUCCESS = 0, + LQM_STATUS_TIMEOUT = 1, + LQM_STATUS_ABORT = 2, +}; + +/** + * struct iwl_link_qual_msrmnt_cmd - Link Quality Measurement command + * @cmd_operation: command operation to be performed (start or stop) + * as defined above. + * @mac_id: MAC ID the measurement applies to. + * @measurement_time: time of the total measurement to be performed, in uSec. + * @timeout: maximum time allowed until a response is sent, in uSec. + */ +struct iwl_link_qual_msrmnt_cmd { + __le32 cmd_operation; + __le32 mac_id; + __le32 measurement_time; + __le32 timeout; +} __packed /* LQM_CMD_API_S_VER_1 */; + +/** + * struct iwl_link_qual_msrmnt_notif - Link Quality Measurement notification + * + * @frequent_stations_air_time: an array containing the total air time + * (in uSec) used by the most frequently transmitting stations. + * @number_of_stations: the number of uniqe stations included in the array + * (a number between 0 to 16) + * @total_air_time_other_stations: the total air time (uSec) used by all the + * stations which are not included in the above report. + * @time_in_measurement_window: the total time in uSec in which a measurement + * took place. + * @tx_frame_dropped: the number of TX frames dropped due to retry limit during + * measurement + * @mac_id: MAC ID the measurement applies to. + * @status: return status. may be one of the LQM_STATUS_* defined above. + * @reserved: reserved. + */ +struct iwl_link_qual_msrmnt_notif { + __le32 frequent_stations_air_time[LQM_NUMBER_OF_STATIONS_IN_REPORT]; + __le32 number_of_stations; + __le32 total_air_time_other_stations; + __le32 time_in_measurement_window; + __le32 tx_frame_dropped; + __le32 mac_id; + __le32 status; + u8 reserved[12]; +} __packed; /* LQM_MEASUREMENT_COMPLETE_NTF_API_S_VER1 */ + +/** + * struct iwl_channel_switch_noa_notif - Channel switch NOA notification + * + * @id_and_color: ID and color of the MAC + */ +struct iwl_channel_switch_noa_notif { + __le32 id_and_color; +} __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */ + +#endif /* __iwl_fw_api_mac_cfg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h new file mode 100644 index 000000000000..f2e31e040a7b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h @@ -0,0 +1,409 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_fw_api_mac_h__ +#define __iwl_fw_api_mac_h__ + +/* + * The first MAC indices (starting from 0) are available to the driver, + * AUX indices follows - 1 for non-CDB, 2 for CDB. + */ +#define MAC_INDEX_AUX 4 +#define MAC_INDEX_MIN_DRIVER 0 +#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX +#define NUM_MAC_INDEX (NUM_MAC_INDEX_DRIVER + 1) +#define NUM_MAC_INDEX_CDB (NUM_MAC_INDEX_DRIVER + 2) + +#define IWL_MVM_STATION_COUNT 16 +#define IWL_MVM_INVALID_STA 0xFF + +enum iwl_ac { + AC_BK, + AC_BE, + AC_VI, + AC_VO, + AC_NUM, +}; + +/** + * enum iwl_mac_protection_flags - MAC context flags + * @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames, + * this will require CCK RTS/CTS2self. + * RTS/CTS will protect full burst time. + * @MAC_PROT_FLG_HT_PROT: enable HT protection + * @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions + * @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self + */ +enum iwl_mac_protection_flags { + MAC_PROT_FLG_TGG_PROTECT = BIT(3), + MAC_PROT_FLG_HT_PROT = BIT(23), + MAC_PROT_FLG_FAT_PROT = BIT(24), + MAC_PROT_FLG_SELF_CTS_EN = BIT(30), +}; + +#define MAC_FLG_SHORT_SLOT BIT(4) +#define MAC_FLG_SHORT_PREAMBLE BIT(5) + +/** + * enum iwl_mac_types - Supported MAC types + * @FW_MAC_TYPE_FIRST: lowest supported MAC type + * @FW_MAC_TYPE_AUX: Auxiliary MAC (internal) + * @FW_MAC_TYPE_LISTENER: monitor MAC type (?) + * @FW_MAC_TYPE_PIBSS: Pseudo-IBSS + * @FW_MAC_TYPE_IBSS: IBSS + * @FW_MAC_TYPE_BSS_STA: BSS (managed) station + * @FW_MAC_TYPE_P2P_DEVICE: P2P Device + * @FW_MAC_TYPE_P2P_STA: P2P client + * @FW_MAC_TYPE_GO: P2P GO + * @FW_MAC_TYPE_TEST: ? + * @FW_MAC_TYPE_MAX: highest support MAC type + */ +enum iwl_mac_types { + FW_MAC_TYPE_FIRST = 1, + FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST, + FW_MAC_TYPE_LISTENER, + FW_MAC_TYPE_PIBSS, + FW_MAC_TYPE_IBSS, + FW_MAC_TYPE_BSS_STA, + FW_MAC_TYPE_P2P_DEVICE, + FW_MAC_TYPE_P2P_STA, + FW_MAC_TYPE_GO, + FW_MAC_TYPE_TEST, + FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST +}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */ + +/** + * enum iwl_tsf_id - TSF hw timer ID + * @TSF_ID_A: use TSF A + * @TSF_ID_B: use TSF B + * @TSF_ID_C: use TSF C + * @TSF_ID_D: use TSF D + * @NUM_TSF_IDS: number of TSF timers available + */ +enum iwl_tsf_id { + TSF_ID_A = 0, + TSF_ID_B = 1, + TSF_ID_C = 2, + TSF_ID_D = 3, + NUM_TSF_IDS = 4, +}; /* TSF_ID_API_E_VER_1 */ + +/** + * struct iwl_mac_data_ap - configuration data for AP MAC context + * @beacon_time: beacon transmit time in system time + * @beacon_tsf: beacon transmit time in TSF + * @bi: beacon interval in TU + * @bi_reciprocal: 2^32 / bi + * @dtim_interval: dtim transmit time in TU + * @dtim_reciprocal: 2^32 / dtim_interval + * @mcast_qid: queue ID for multicast traffic. + * NOTE: obsolete from VER2 and on + * @beacon_template: beacon template ID + */ +struct iwl_mac_data_ap { + __le32 beacon_time; + __le64 beacon_tsf; + __le32 bi; + __le32 bi_reciprocal; + __le32 dtim_interval; + __le32 dtim_reciprocal; + __le32 mcast_qid; + __le32 beacon_template; +} __packed; /* AP_MAC_DATA_API_S_VER_2 */ + +/** + * struct iwl_mac_data_ibss - configuration data for IBSS MAC context + * @beacon_time: beacon transmit time in system time + * @beacon_tsf: beacon transmit time in TSF + * @bi: beacon interval in TU + * @bi_reciprocal: 2^32 / bi + * @beacon_template: beacon template ID + */ +struct iwl_mac_data_ibss { + __le32 beacon_time; + __le64 beacon_tsf; + __le32 bi; + __le32 bi_reciprocal; + __le32 beacon_template; +} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_data_sta - configuration data for station MAC context + * @is_assoc: 1 for associated state, 0 otherwise + * @dtim_time: DTIM arrival time in system time + * @dtim_tsf: DTIM arrival time in TSF + * @bi: beacon interval in TU, applicable only when associated + * @bi_reciprocal: 2^32 / bi , applicable only when associated + * @dtim_interval: DTIM interval in TU, applicable only when associated + * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated + * @listen_interval: in beacon intervals, applicable only when associated + * @assoc_id: unique ID assigned by the AP during association + * @assoc_beacon_arrive_time: TSF of first beacon after association + */ +struct iwl_mac_data_sta { + __le32 is_assoc; + __le32 dtim_time; + __le64 dtim_tsf; + __le32 bi; + __le32 bi_reciprocal; + __le32 dtim_interval; + __le32 dtim_reciprocal; + __le32 listen_interval; + __le32 assoc_id; + __le32 assoc_beacon_arrive_time; +} __packed; /* STA_MAC_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_data_go - configuration data for P2P GO MAC context + * @ap: iwl_mac_data_ap struct with most config data + * @ctwin: client traffic window in TU (period after TBTT when GO is present). + * 0 indicates that there is no CT window. + * @opp_ps_enabled: indicate that opportunistic PS allowed + */ +struct iwl_mac_data_go { + struct iwl_mac_data_ap ap; + __le32 ctwin; + __le32 opp_ps_enabled; +} __packed; /* GO_MAC_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context + * @sta: iwl_mac_data_sta struct with most config data + * @ctwin: client traffic window in TU (period after TBTT when GO is present). + * 0 indicates that there is no CT window. + */ +struct iwl_mac_data_p2p_sta { + struct iwl_mac_data_sta sta; + __le32 ctwin; +} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_data_pibss - Pseudo IBSS config data + * @stats_interval: interval in TU between statistics notifications to host. + */ +struct iwl_mac_data_pibss { + __le32 stats_interval; +} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */ + +/* + * struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC + * context. + * @is_disc_extended: if set to true, P2P Device discoverability is enabled on + * other channels as well. This should be to true only in case that the + * device is discoverable and there is an active GO. Note that setting this + * field when not needed, will increase the number of interrupts and have + * effect on the platform power, as this setting opens the Rx filters on + * all macs. + */ +struct iwl_mac_data_p2p_dev { + __le32 is_disc_extended; +} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */ + +/** + * enum iwl_mac_filter_flags - MAC context filter flags + * @MAC_FILTER_IN_PROMISC: accept all data frames + * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and + * control frames to the host + * @MAC_FILTER_ACCEPT_GRP: accept multicast frames + * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames + * @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames + * @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host + * (in station mode when associated) + * @MAC_FILTER_OUT_BCAST: filter out all broadcast frames + * @MAC_FILTER_IN_CRC32: extract FCS and append it to frames + * @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host + */ +enum iwl_mac_filter_flags { + MAC_FILTER_IN_PROMISC = BIT(0), + MAC_FILTER_IN_CONTROL_AND_MGMT = BIT(1), + MAC_FILTER_ACCEPT_GRP = BIT(2), + MAC_FILTER_DIS_DECRYPT = BIT(3), + MAC_FILTER_DIS_GRP_DECRYPT = BIT(4), + MAC_FILTER_IN_BEACON = BIT(6), + MAC_FILTER_OUT_BCAST = BIT(8), + MAC_FILTER_IN_CRC32 = BIT(11), + MAC_FILTER_IN_PROBE_REQUEST = BIT(12), +}; + +/** + * enum iwl_mac_qos_flags - QoS flags + * @MAC_QOS_FLG_UPDATE_EDCA: ? + * @MAC_QOS_FLG_TGN: HT is enabled + * @MAC_QOS_FLG_TXOP_TYPE: ? + * + */ +enum iwl_mac_qos_flags { + MAC_QOS_FLG_UPDATE_EDCA = BIT(0), + MAC_QOS_FLG_TGN = BIT(1), + MAC_QOS_FLG_TXOP_TYPE = BIT(4), +}; + +/** + * struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD + * @cw_min: Contention window, start value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x0f. + * @cw_max: Contention window, max value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x3f. + * @aifsn: Number of slots in Arbitration Interframe Space (before + * performing random backoff timing prior to Tx). Device default 1. + * @fifos_mask: FIFOs used by this MAC for this AC + * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. + * + * One instance of this config struct for each of 4 EDCA access categories + * in struct iwl_qosparam_cmd. + * + * Device will automatically increase contention window by (2*CW) + 1 for each + * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW + * value, to cap the CW value. + */ +struct iwl_ac_qos { + __le16 cw_min; + __le16 cw_max; + u8 aifsn; + u8 fifos_mask; + __le16 edca_txop; +} __packed; /* AC_QOS_API_S_VER_2 */ + +/** + * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts + * ( MAC_CONTEXT_CMD = 0x28 ) + * @id_and_color: ID and color of the MAC + * @action: action to perform, one of FW_CTXT_ACTION_* + * @mac_type: one of &enum iwl_mac_types + * @tsf_id: TSF HW timer, one of &enum iwl_tsf_id + * @node_addr: MAC address + * @reserved_for_node_addr: reserved + * @bssid_addr: BSSID + * @reserved_for_bssid_addr: reserved + * @cck_rates: basic rates available for CCK + * @ofdm_rates: basic rates available for OFDM + * @protection_flags: combination of &enum iwl_mac_protection_flags + * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise + * @short_slot: 0x10 for enabling short slots, 0 otherwise + * @filter_flags: combination of &enum iwl_mac_filter_flags + * @qos_flags: from &enum iwl_mac_qos_flags + * @ac: one iwl_mac_qos configuration for each AC + */ +struct iwl_mac_ctx_cmd { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + /* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */ + __le32 mac_type; + __le32 tsf_id; + u8 node_addr[6]; + __le16 reserved_for_node_addr; + u8 bssid_addr[6]; + __le16 reserved_for_bssid_addr; + __le32 cck_rates; + __le32 ofdm_rates; + __le32 protection_flags; + __le32 cck_short_preamble; + __le32 short_slot; + __le32 filter_flags; + /* MAC_QOS_PARAM_API_S_VER_1 */ + __le32 qos_flags; + struct iwl_ac_qos ac[AC_NUM+1]; + /* MAC_CONTEXT_COMMON_DATA_API_S */ + union { + struct iwl_mac_data_ap ap; + struct iwl_mac_data_go go; + struct iwl_mac_data_sta sta; + struct iwl_mac_data_p2p_sta p2p_sta; + struct iwl_mac_data_p2p_dev p2p_dev; + struct iwl_mac_data_pibss pibss; + struct iwl_mac_data_ibss ibss; + }; +} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */ + +static inline u32 iwl_mvm_reciprocal(u32 v) +{ + if (!v) + return 0; + return 0xFFFFFFFF / v; +} + +#define IWL_NONQOS_SEQ_GET 0x1 +#define IWL_NONQOS_SEQ_SET 0x2 +struct iwl_nonqos_seq_query_cmd { + __le32 get_set_flag; + __le32 mac_id_n_color; + __le16 value; + __le16 reserved; +} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */ + +/** + * struct iwl_missed_beacons_notif - information on missed beacons + * ( MISSED_BEACONS_NOTIFICATION = 0xa2 ) + * @mac_id: interface ID + * @consec_missed_beacons_since_last_rx: number of consecutive missed + * beacons since last RX. + * @consec_missed_beacons: number of consecutive missed beacons + * @num_expected_beacons: number of expected beacons + * @num_recvd_beacons: number of received beacons + */ +struct iwl_missed_beacons_notif { + __le32 mac_id; + __le32 consec_missed_beacons_since_last_rx; + __le32 consec_missed_beacons; + __le32 num_expected_beacons; + __le32 num_recvd_beacons; +} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */ + +#endif /* __iwl_fw_api_mac_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h new file mode 100644 index 000000000000..d4c01f3dce32 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -0,0 +1,378 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_nvm_reg_h__ +#define __iwl_fw_api_nvm_reg_h__ + +/** + * enum iwl_regulatory_and_nvm_subcmd_ids - regulatory/NVM commands + */ +enum iwl_regulatory_and_nvm_subcmd_ids { + /** + * @NVM_ACCESS_COMPLETE: &struct iwl_nvm_access_complete_cmd + */ + NVM_ACCESS_COMPLETE = 0x0, + + /** + * @NVM_GET_INFO: + * Command is &struct iwl_nvm_get_info, + * response is &struct iwl_nvm_get_info_rsp + */ + NVM_GET_INFO = 0x2, +}; + +/** + * enum iwl_nvm_access_op - NVM access opcode + * @IWL_NVM_READ: read NVM + * @IWL_NVM_WRITE: write NVM + */ +enum iwl_nvm_access_op { + IWL_NVM_READ = 0, + IWL_NVM_WRITE = 1, +}; + +/** + * enum iwl_nvm_access_target - target of the NVM_ACCESS_CMD + * @NVM_ACCESS_TARGET_CACHE: access the cache + * @NVM_ACCESS_TARGET_OTP: access the OTP + * @NVM_ACCESS_TARGET_EEPROM: access the EEPROM + */ +enum iwl_nvm_access_target { + NVM_ACCESS_TARGET_CACHE = 0, + NVM_ACCESS_TARGET_OTP = 1, + NVM_ACCESS_TARGET_EEPROM = 2, +}; + +/** + * enum iwl_nvm_section_type - section types for NVM_ACCESS_CMD + * @NVM_SECTION_TYPE_SW: software section + * @NVM_SECTION_TYPE_REGULATORY: regulatory section + * @NVM_SECTION_TYPE_CALIBRATION: calibration section + * @NVM_SECTION_TYPE_PRODUCTION: production section + * @NVM_SECTION_TYPE_MAC_OVERRIDE: MAC override section + * @NVM_SECTION_TYPE_PHY_SKU: PHY SKU section + * @NVM_MAX_NUM_SECTIONS: number of sections + */ +enum iwl_nvm_section_type { + NVM_SECTION_TYPE_SW = 1, + NVM_SECTION_TYPE_REGULATORY = 3, + NVM_SECTION_TYPE_CALIBRATION = 4, + NVM_SECTION_TYPE_PRODUCTION = 5, + NVM_SECTION_TYPE_MAC_OVERRIDE = 11, + NVM_SECTION_TYPE_PHY_SKU = 12, + NVM_MAX_NUM_SECTIONS = 13, +}; + +/** + * struct iwl_nvm_access_cmd - Request the device to send an NVM section + * @op_code: &enum iwl_nvm_access_op + * @target: &enum iwl_nvm_access_target + * @type: &enum iwl_nvm_section_type + * @offset: offset in bytes into the section + * @length: in bytes, to read/write + * @data: if write operation, the data to write. On read its empty + */ +struct iwl_nvm_access_cmd { + u8 op_code; + u8 target; + __le16 type; + __le16 offset; + __le16 length; + u8 data[]; +} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */ + +/** + * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD + * @offset: offset in bytes into the section + * @length: in bytes, either how much was written or read + * @type: NVM_SECTION_TYPE_* + * @status: 0 for success, fail otherwise + * @data: if read operation, the data returned. Empty on write. + */ +struct iwl_nvm_access_resp { + __le16 offset; + __le16 length; + __le16 type; + __le16 status; + u8 data[]; +} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */ + +/* + * struct iwl_nvm_get_info - request to get NVM data + */ +struct iwl_nvm_get_info { + __le32 reserved; +} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_S_VER_1 */ + +/** + * struct iwl_nvm_get_info_general - general NVM data + * @flags: 1 - empty, 0 - valid + * @nvm_version: nvm version + * @board_type: board type + * @reserved: reserved + */ +struct iwl_nvm_get_info_general { + __le32 flags; + __le16 nvm_version; + u8 board_type; + u8 reserved; +} __packed; /* GRP_REGULATORY_NVM_GET_INFO_GENERAL_S_VER_1 */ + +/** + * struct iwl_nvm_get_info_sku - mac information + * @enable_24g: band 2.4G enabled + * @enable_5g: band 5G enabled + * @enable_11n: 11n enabled + * @enable_11ac: 11ac enabled + * @mimo_disable: MIMO enabled + * @ext_crypto: Extended crypto enabled + */ +struct iwl_nvm_get_info_sku { + __le32 enable_24g; + __le32 enable_5g; + __le32 enable_11n; + __le32 enable_11ac; + __le32 mimo_disable; + __le32 ext_crypto; +} __packed; /* GRP_REGULATORY_NVM_GET_INFO_MAC_SKU_SECTION_S_VER_1 */ + +/** + * struct iwl_nvm_get_info_phy - phy information + * @tx_chains: BIT 0 chain A, BIT 1 chain B + * @rx_chains: BIT 0 chain A, BIT 1 chain B + */ +struct iwl_nvm_get_info_phy { + __le32 tx_chains; + __le32 rx_chains; +} __packed; /* GRP_REGULATORY_NVM_GET_INFO_PHY_SKU_SECTION_S_VER_1 */ + +#define IWL_NUM_CHANNELS (51) + +/** + * struct iwl_nvm_get_info_regulatory - regulatory information + * @lar_enabled: is LAR enabled + * @channel_profile: regulatory data of this channel + * @reserved: reserved + */ +struct iwl_nvm_get_info_regulatory { + __le32 lar_enabled; + __le16 channel_profile[IWL_NUM_CHANNELS]; + __le16 reserved; +} __packed; /* GRP_REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_1 */ + +/** + * struct iwl_nvm_get_info_rsp - response to get NVM data + * @general: general NVM data + * @mac_sku: data relating to MAC sku + * @phy_sku: data relating to PHY sku + * @regulatory: regulatory data + */ +struct iwl_nvm_get_info_rsp { + struct iwl_nvm_get_info_general general; + struct iwl_nvm_get_info_sku mac_sku; + struct iwl_nvm_get_info_phy phy_sku; + struct iwl_nvm_get_info_regulatory regulatory; +} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_RSP_S_VER_1 */ + +/** + * struct iwl_nvm_access_complete_cmd - NVM_ACCESS commands are completed + * @reserved: reserved + */ +struct iwl_nvm_access_complete_cmd { + __le32 reserved; +} __packed; /* NVM_ACCESS_COMPLETE_CMD_API_S_VER_1 */ + +/** + * struct iwl_mcc_update_cmd_v1 - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwl_mcc_source + * @reserved: reserved for alignment + */ +struct iwl_mcc_update_cmd_v1 { + __le16 mcc; + u8 source_id; + u8 reserved; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_1 */ + +/** + * struct iwl_mcc_update_cmd - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwl_mcc_source + * @reserved: reserved for alignment + * @key: integrity key for MCC API OEM testing + * @reserved2: reserved + */ +struct iwl_mcc_update_cmd { + __le16 mcc; + u8 source_id; + u8 reserved; + __le32 key; + u8 reserved2[20]; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_2 */ + +/** + * struct iwl_mcc_update_resp_v1 - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwl_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwl_mcc_source + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwl_mcc_update_resp_v1 { + __le32 status; + __le16 mcc; + u8 cap; + u8 source_id; + __le32 n_channels; + __le32 channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */ + +/** + * struct iwl_mcc_update_resp - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwl_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwl_mcc_source + * @time: time elapsed from the MCC test start (in 30 seconds TU) + * @reserved: reserved. + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwl_mcc_update_resp { + __le32 status; + __le16 mcc; + u8 cap; + u8 source_id; + __le16 time; + __le16 reserved; + __le32 n_channels; + __le32 channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */ + +/** + * struct iwl_mcc_chub_notif - chub notifies of mcc change + * (MCC_CHUB_UPDATE_CMD = 0xc9) + * The Chub (Communication Hub, CommsHUB) is a HW component that connects to + * the cellular and connectivity cores that gets updates of the mcc, and + * notifies the ucode directly of any mcc change. + * The ucode requests the driver to request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: identity of the change originator, see iwl_mcc_source + * @reserved1: reserved for alignment + */ +struct iwl_mcc_chub_notif { + __le16 mcc; + u8 source_id; + u8 reserved1; +} __packed; /* LAR_MCC_NOTIFY_S */ + +enum iwl_mcc_update_status { + MCC_RESP_NEW_CHAN_PROFILE, + MCC_RESP_SAME_CHAN_PROFILE, + MCC_RESP_INVALID, + MCC_RESP_NVM_DISABLED, + MCC_RESP_ILLEGAL, + MCC_RESP_LOW_PRIORITY, + MCC_RESP_TEST_MODE_ACTIVE, + MCC_RESP_TEST_MODE_NOT_ACTIVE, + MCC_RESP_TEST_MODE_DENIAL_OF_SERVICE, +}; + +enum iwl_mcc_source { + MCC_SOURCE_OLD_FW = 0, + MCC_SOURCE_ME = 1, + MCC_SOURCE_BIOS = 2, + MCC_SOURCE_3G_LTE_HOST = 3, + MCC_SOURCE_3G_LTE_DEVICE = 4, + MCC_SOURCE_WIFI = 5, + MCC_SOURCE_RESERVED = 6, + MCC_SOURCE_DEFAULT = 7, + MCC_SOURCE_UNINITIALIZED = 8, + MCC_SOURCE_MCC_API = 9, + MCC_SOURCE_GET_CURRENT = 0x10, + MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11, +}; + +#endif /* __iwl_fw_api_nvm_reg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/offload.h b/drivers/net/wireless/intel/iwlwifi/fw/api/offload.h new file mode 100644 index 000000000000..53cab993068f --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/offload.h @@ -0,0 +1,101 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_offload_h__ +#define __iwl_fw_api_offload_h__ + +/** + * enum iwl_prot_offload_subcmd_ids - protocol offload commands + */ +enum iwl_prot_offload_subcmd_ids { + /** + * @STORED_BEACON_NTF: &struct iwl_stored_beacon_notif + */ + STORED_BEACON_NTF = 0xFF, +}; + +#define MAX_STORED_BEACON_SIZE 600 + +/** + * struct iwl_stored_beacon_notif - Stored beacon notification + * + * @system_time: system time on air rise + * @tsf: TSF on air rise + * @beacon_timestamp: beacon on air rise + * @band: band, matches &RX_RES_PHY_FLAGS_BAND_24 definition + * @channel: channel this beacon was received on + * @rates: rate in ucode internal format + * @byte_count: frame's byte count + * @data: beacon data, length in @byte_count + */ +struct iwl_stored_beacon_notif { + __le32 system_time; + __le64 tsf; + __le32 beacon_timestamp; + __le16 band; + __le16 channel; + __le32 rates; + __le32 byte_count; + u8 data[MAX_STORED_BEACON_SIZE]; +} __packed; /* WOWLAN_STROED_BEACON_INFO_S_VER_2 */ + +#endif /* __iwl_fw_api_offload_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/paging.h b/drivers/net/wireless/intel/iwlwifi/fw/api/paging.h new file mode 100644 index 000000000000..e76f9cd4473d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/paging.h @@ -0,0 +1,108 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_fw_api_paging_h__ +#define __iwl_fw_api_paging_h__ + +#define NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */ + +/** + * struct iwl_fw_paging_cmd - paging layout + * + * Send to FW the paging layout in the driver. + * + * @flags: various flags for the command + * @block_size: the block size in powers of 2 + * @block_num: number of blocks specified in the command. + * @device_phy_addr: virtual addresses from device side + */ +struct iwl_fw_paging_cmd { + __le32 flags; + __le32 block_size; + __le32 block_num; + __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS]; +} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */ + +/** + * enum iwl_fw_item_id - FW item IDs + * + * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload + * download + */ +enum iwl_fw_item_id { + IWL_FW_ITEM_ID_PAGING = 3, +}; + +/** + * struct iwl_fw_get_item_cmd - get an item from the fw + * @item_id: ID of item to obtain, see &enum iwl_fw_item_id + */ +struct iwl_fw_get_item_cmd { + __le32 item_id; +} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */ + +struct iwl_fw_get_item_resp { + __le32 item_id; + __le32 item_byte_cnt; + __le32 item_val; +} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */ + +#endif /* __iwl_fw_api_paging_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h new file mode 100644 index 000000000000..45f61c6af14e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h @@ -0,0 +1,164 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_phy_ctxt_h__ +#define __iwl_fw_api_phy_ctxt_h__ + +/* Supported bands */ +#define PHY_BAND_5 (0) +#define PHY_BAND_24 (1) + +/* Supported channel width, vary if there is VHT support */ +#define PHY_VHT_CHANNEL_MODE20 (0x0) +#define PHY_VHT_CHANNEL_MODE40 (0x1) +#define PHY_VHT_CHANNEL_MODE80 (0x2) +#define PHY_VHT_CHANNEL_MODE160 (0x3) + +/* + * Control channel position: + * For legacy set bit means upper channel, otherwise lower. + * For VHT - bit-2 marks if the control is lower/upper relative to center-freq + * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0. + * center_freq + * | + * 40Mhz |_______|_______| + * 80Mhz |_______|_______|_______|_______| + * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______| + * code 011 010 001 000 | 100 101 110 111 + */ +#define PHY_VHT_CTRL_POS_1_BELOW (0x0) +#define PHY_VHT_CTRL_POS_2_BELOW (0x1) +#define PHY_VHT_CTRL_POS_3_BELOW (0x2) +#define PHY_VHT_CTRL_POS_4_BELOW (0x3) +#define PHY_VHT_CTRL_POS_1_ABOVE (0x4) +#define PHY_VHT_CTRL_POS_2_ABOVE (0x5) +#define PHY_VHT_CTRL_POS_3_ABOVE (0x6) +#define PHY_VHT_CTRL_POS_4_ABOVE (0x7) + +/* + * @band: PHY_BAND_* + * @channel: channel number + * @width: PHY_[VHT|LEGACY]_CHANNEL_* + * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_* + */ +struct iwl_fw_channel_info { + u8 band; + u8 channel; + u8 width; + u8 ctrl_pos; +} __packed; + +#define PHY_RX_CHAIN_DRIVER_FORCE_POS (0) +#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \ + (0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS) +#define PHY_RX_CHAIN_VALID_POS (1) +#define PHY_RX_CHAIN_VALID_MSK \ + (0x7 << PHY_RX_CHAIN_VALID_POS) +#define PHY_RX_CHAIN_FORCE_SEL_POS (4) +#define PHY_RX_CHAIN_FORCE_SEL_MSK \ + (0x7 << PHY_RX_CHAIN_FORCE_SEL_POS) +#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS (7) +#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \ + (0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS) +#define PHY_RX_CHAIN_CNT_POS (10) +#define PHY_RX_CHAIN_CNT_MSK \ + (0x3 << PHY_RX_CHAIN_CNT_POS) +#define PHY_RX_CHAIN_MIMO_CNT_POS (12) +#define PHY_RX_CHAIN_MIMO_CNT_MSK \ + (0x3 << PHY_RX_CHAIN_MIMO_CNT_POS) +#define PHY_RX_CHAIN_MIMO_FORCE_POS (14) +#define PHY_RX_CHAIN_MIMO_FORCE_MSK \ + (0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS) + +/* TODO: fix the value, make it depend on firmware at runtime? */ +#define NUM_PHY_CTX 3 + +/* TODO: complete missing documentation */ +/** + * struct iwl_phy_context_cmd - config of the PHY context + * ( PHY_CONTEXT_CMD = 0x8 ) + * @id_and_color: ID and color of the relevant Binding + * @action: action to perform, one of FW_CTXT_ACTION_* + * @apply_time: 0 means immediate apply and context switch. + * other value means apply new params after X usecs + * @tx_param_color: ??? + * @ci: channel info + * @txchain_info: ??? + * @rxchain_info: ??? + * @acquisition_data: ??? + * @dsp_cfg_flags: set to 0 + */ +struct iwl_phy_context_cmd { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + /* PHY_CONTEXT_DATA_API_S_VER_1 */ + __le32 apply_time; + __le32 tx_param_color; + struct iwl_fw_channel_info ci; + __le32 txchain_info; + __le32 rxchain_info; + __le32 acquisition_data; + __le32 dsp_cfg_flags; +} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */ + +#endif /* __iwl_fw_api_phy_ctxt_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h new file mode 100644 index 000000000000..9cc59e00bd95 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h @@ -0,0 +1,258 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_phy_h__ +#define __iwl_fw_api_phy_h__ + +/** + * enum iwl_phy_ops_subcmd_ids - PHY group commands + */ +enum iwl_phy_ops_subcmd_ids { + /** + * @CMD_DTS_MEASUREMENT_TRIGGER_WIDE: + * Uses either &struct iwl_dts_measurement_cmd or + * &struct iwl_ext_dts_measurement_cmd + */ + CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, + + /** + * @CTDP_CONFIG_CMD: &struct iwl_mvm_ctdp_cmd + */ + CTDP_CONFIG_CMD = 0x03, + + /** + * @TEMP_REPORTING_THRESHOLDS_CMD: &struct temp_report_ths_cmd + */ + TEMP_REPORTING_THRESHOLDS_CMD = 0x04, + + /** + * @GEO_TX_POWER_LIMIT: &struct iwl_geo_tx_power_profiles_cmd + */ + GEO_TX_POWER_LIMIT = 0x05, + + /** + * @CT_KILL_NOTIFICATION: &struct ct_kill_notif + */ + CT_KILL_NOTIFICATION = 0xFE, + + /** + * @DTS_MEASUREMENT_NOTIF_WIDE: + * &struct iwl_dts_measurement_notif_v1 or + * &struct iwl_dts_measurement_notif_v2 + */ + DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, +}; + +/* DTS measurements */ + +enum iwl_dts_measurement_flags { + DTS_TRIGGER_CMD_FLAGS_TEMP = BIT(0), + DTS_TRIGGER_CMD_FLAGS_VOLT = BIT(1), +}; + +/** + * struct iwl_dts_measurement_cmd - request DTS temp and/or voltage measurements + * + * @flags: indicates which measurements we want as specified in + * &enum iwl_dts_measurement_flags + */ +struct iwl_dts_measurement_cmd { + __le32 flags; +} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_CMD_S */ + +/** +* enum iwl_dts_control_measurement_mode - DTS measurement type +* @DTS_AUTOMATIC: Automatic mode (full SW control). Provide temperature read +* back (latest value. Not waiting for new value). Use automatic +* SW DTS configuration. +* @DTS_REQUEST_READ: Request DTS read. Configure DTS with manual settings, +* trigger DTS reading and provide read back temperature read +* when available. +* @DTS_OVER_WRITE: over-write the DTS temperatures in the SW until next read +* @DTS_DIRECT_WITHOUT_MEASURE: DTS returns its latest temperature result, +* without measurement trigger. +*/ +enum iwl_dts_control_measurement_mode { + DTS_AUTOMATIC = 0, + DTS_REQUEST_READ = 1, + DTS_OVER_WRITE = 2, + DTS_DIRECT_WITHOUT_MEASURE = 3, +}; + +/** +* enum iwl_dts_used - DTS to use or used for measurement in the DTS request +* @DTS_USE_TOP: Top +* @DTS_USE_CHAIN_A: chain A +* @DTS_USE_CHAIN_B: chain B +* @DTS_USE_CHAIN_C: chain C +* @XTAL_TEMPERATURE: read temperature from xtal +*/ +enum iwl_dts_used { + DTS_USE_TOP = 0, + DTS_USE_CHAIN_A = 1, + DTS_USE_CHAIN_B = 2, + DTS_USE_CHAIN_C = 3, + XTAL_TEMPERATURE = 4, +}; + +/** +* enum iwl_dts_bit_mode - bit-mode to use in DTS request read mode +* @DTS_BIT6_MODE: bit 6 mode +* @DTS_BIT8_MODE: bit 8 mode +*/ +enum iwl_dts_bit_mode { + DTS_BIT6_MODE = 0, + DTS_BIT8_MODE = 1, +}; + +/** + * struct iwl_ext_dts_measurement_cmd - request extended DTS temp measurements + * @control_mode: see &enum iwl_dts_control_measurement_mode + * @temperature: used when over write DTS mode is selected + * @sensor: set temperature sensor to use. See &enum iwl_dts_used + * @avg_factor: average factor to DTS in request DTS read mode + * @bit_mode: value defines the DTS bit mode to use. See &enum iwl_dts_bit_mode + * @step_duration: step duration for the DTS + */ +struct iwl_ext_dts_measurement_cmd { + __le32 control_mode; + __le32 temperature; + __le32 sensor; + __le32 avg_factor; + __le32 bit_mode; + __le32 step_duration; +} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */ + +/** + * struct iwl_dts_measurement_notif_v1 - measurements notification + * + * @temp: the measured temperature + * @voltage: the measured voltage + */ +struct iwl_dts_measurement_notif_v1 { + __le32 temp; + __le32 voltage; +} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_1*/ + +/** + * struct iwl_dts_measurement_notif_v2 - measurements notification + * + * @temp: the measured temperature + * @voltage: the measured voltage + * @threshold_idx: the trip index that was crossed + */ +struct iwl_dts_measurement_notif_v2 { + __le32 temp; + __le32 voltage; + __le32 threshold_idx; +} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_2 */ + +/** + * struct ct_kill_notif - CT-kill entry notification + * + * @temperature: the current temperature in celsius + * @reserved: reserved + */ +struct ct_kill_notif { + __le16 temperature; + __le16 reserved; +} __packed; /* GRP_PHY_CT_KILL_NTF */ + +/** +* enum ctdp_cmd_operation - CTDP command operations +* @CTDP_CMD_OPERATION_START: update the current budget +* @CTDP_CMD_OPERATION_STOP: stop ctdp +* @CTDP_CMD_OPERATION_REPORT: get the average budget +*/ +enum iwl_mvm_ctdp_cmd_operation { + CTDP_CMD_OPERATION_START = 0x1, + CTDP_CMD_OPERATION_STOP = 0x2, + CTDP_CMD_OPERATION_REPORT = 0x4, +};/* CTDP_CMD_OPERATION_TYPE_E */ + +/** + * struct iwl_mvm_ctdp_cmd - track and manage the FW power consumption budget + * + * @operation: see &enum iwl_mvm_ctdp_cmd_operation + * @budget: the budget in milliwatt + * @window_size: defined in API but not used + */ +struct iwl_mvm_ctdp_cmd { + __le32 operation; + __le32 budget; + __le32 window_size; +} __packed; + +#define IWL_MAX_DTS_TRIPS 8 + +/** + * struct temp_report_ths_cmd - set temperature thresholds + * + * @num_temps: number of temperature thresholds passed + * @thresholds: array with the thresholds to be configured + */ +struct temp_report_ths_cmd { + __le32 num_temps; + __le16 thresholds[IWL_MAX_DTS_TRIPS]; +} __packed; /* GRP_PHY_TEMP_REPORTING_THRESHOLDS_CMD */ + +#endif /* __iwl_fw_api_phy_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h new file mode 100644 index 000000000000..a06afb5605d2 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -0,0 +1,526 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_power_h__ +#define __iwl_fw_api_power_h__ + +/* Power Management Commands, Responses, Notifications */ + +/** + * enum iwl_ltr_config_flags - masks for LTR config command flags + * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status + * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow + * memory access + * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR + * reg change + * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from + * D0 to D3 + * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register + * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register + * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD + * @LTR_CFG_FLAG_UPDATE_VALUES: update config values and short + * idle timeout + */ +enum iwl_ltr_config_flags { + LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0), + LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1), + LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2), + LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3), + LTR_CFG_FLAG_SW_SET_SHORT = BIT(4), + LTR_CFG_FLAG_SW_SET_LONG = BIT(5), + LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6), + LTR_CFG_FLAG_UPDATE_VALUES = BIT(7), +}; + +/** + * struct iwl_ltr_config_cmd_v1 - configures the LTR + * @flags: See &enum iwl_ltr_config_flags + * @static_long: static LTR Long register value. + * @static_short: static LTR Short register value. + */ +struct iwl_ltr_config_cmd_v1 { + __le32 flags; + __le32 static_long; + __le32 static_short; +} __packed; /* LTR_CAPABLE_API_S_VER_1 */ + +#define LTR_VALID_STATES_NUM 4 + +/** + * struct iwl_ltr_config_cmd - configures the LTR + * @flags: See &enum iwl_ltr_config_flags + * @static_long: static LTR Long register value. + * @static_short: static LTR Short register value. + * @ltr_cfg_values: LTR parameters table values (in usec) in folowing order: + * TX, RX, Short Idle, Long Idle. Used only if %LTR_CFG_FLAG_UPDATE_VALUES + * is set. + * @ltr_short_idle_timeout: LTR Short Idle timeout (in usec). Used only if + * %LTR_CFG_FLAG_UPDATE_VALUES is set. + */ +struct iwl_ltr_config_cmd { + __le32 flags; + __le32 static_long; + __le32 static_short; + __le32 ltr_cfg_values[LTR_VALID_STATES_NUM]; + __le32 ltr_short_idle_timeout; +} __packed; /* LTR_CAPABLE_API_S_VER_2 */ + +/* Radio LP RX Energy Threshold measured in dBm */ +#define POWER_LPRX_RSSI_THRESHOLD 75 +#define POWER_LPRX_RSSI_THRESHOLD_MAX 94 +#define POWER_LPRX_RSSI_THRESHOLD_MIN 30 + +/** + * enum iwl_power_flags - masks for power table command flags + * @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off + * receiver and transmitter. '0' - does not allow. + * @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management, + * '1' Driver enables PM (use rest of parameters) + * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM, + * '1' PM could sleep over DTIM till listen Interval. + * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all + * access categories are both delivery and trigger enabled. + * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and + * PBW Snoozing enabled + * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask + * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. + * @POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving + * detection enablement +*/ +enum iwl_power_flags { + POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), + POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1), + POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2), + POWER_FLAGS_SNOOZE_ENA_MSK = BIT(5), + POWER_FLAGS_BT_SCO_ENA = BIT(8), + POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), + POWER_FLAGS_LPRX_ENA_MSK = BIT(11), + POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12), +}; + +#define IWL_POWER_VEC_SIZE 5 + +/** + * struct iwl_powertable_cmd - legacy power command. Beside old API support this + * is used also with a new power API for device wide power settings. + * POWER_TABLE_CMD = 0x77 (command, has simple generic response) + * + * @flags: Power table command flags from POWER_FLAGS_* + * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. + * Minimum allowed:- 3 * DTIM. Keep alive period must be + * set regardless of power scheme or current power state. + * FW use this value also when PM is disabled. + * @debug_flags: debug flags + * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to + * PSM transition - legacy PM + * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to + * PSM transition - legacy PM + * @sleep_interval: not in use + * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag + * is set. For example, if it is required to skip over + * one DTIM, this value need to be set to 2 (DTIM periods). + * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. + * Default: 80dbm + */ +struct iwl_powertable_cmd { + /* PM_POWER_TABLE_CMD_API_S_VER_6 */ + __le16 flags; + u8 keep_alive_seconds; + u8 debug_flags; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 sleep_interval[IWL_POWER_VEC_SIZE]; + __le32 skip_dtim_periods; + __le32 lprx_rssi_threshold; +} __packed; + +/** + * enum iwl_device_power_flags - masks for device power command flags + * @DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK: + * '1' Allow to save power by turning off + * receiver and transmitter. '0' - does not allow. +*/ +enum iwl_device_power_flags { + DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), +}; + +/** + * struct iwl_device_power_cmd - device wide power command. + * DEVICE_POWER_CMD = 0x77 (command, has simple generic response) + * + * @flags: Power table command flags from &enum iwl_device_power_flags + * @reserved: reserved (padding) + */ +struct iwl_device_power_cmd { + /* PM_POWER_TABLE_CMD_API_S_VER_6 */ + __le16 flags; + __le16 reserved; +} __packed; + +/** + * struct iwl_mac_power_cmd - New power command containing uAPSD support + * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) + * @id_and_color: MAC contex identifier, &enum iwl_ctxt_id_and_color + * @flags: Power table command flags from POWER_FLAGS_* + * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. + * Minimum allowed:- 3 * DTIM. Keep alive period must be + * set regardless of power scheme or current power state. + * FW use this value also when PM is disabled. + * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to + * PSM transition - legacy PM + * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to + * PSM transition - legacy PM + * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag + * is set. For example, if it is required to skip over + * one DTIM, this value need to be set to 2 (DTIM periods). + * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to + * PSM transition - uAPSD + * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to + * PSM transition - uAPSD + * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. + * Default: 80dbm + * @snooze_interval: Maximum time between attempts to retrieve buffered data + * from the AP [msec] + * @snooze_window: A window of time in which PBW snoozing insures that all + * packets received. It is also the minimum time from last + * received unicast RX packet, before client stops snoozing + * for data. [msec] + * @snooze_step: TBD + * @qndp_tid: TID client shall use for uAPSD QNDP triggers + * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for + * each corresponding AC. + * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values. + * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct + * values. + * @heavy_tx_thld_packets: TX threshold measured in number of packets + * @heavy_rx_thld_packets: RX threshold measured in number of packets + * @heavy_tx_thld_percentage: TX threshold measured in load's percentage + * @heavy_rx_thld_percentage: RX threshold measured in load's percentage + * @limited_ps_threshold: (unused) + * @reserved: reserved (padding) + */ +struct iwl_mac_power_cmd { + /* CONTEXT_DESC_API_T_VER_1 */ + __le32 id_and_color; + + /* CLIENT_PM_POWER_TABLE_S_VER_1 */ + __le16 flags; + __le16 keep_alive_seconds; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 rx_data_timeout_uapsd; + __le32 tx_data_timeout_uapsd; + u8 lprx_rssi_threshold; + u8 skip_dtim_periods; + __le16 snooze_interval; + __le16 snooze_window; + u8 snooze_step; + u8 qndp_tid; + u8 uapsd_ac_flags; + u8 uapsd_max_sp; + u8 heavy_tx_thld_packets; + u8 heavy_rx_thld_packets; + u8 heavy_tx_thld_percentage; + u8 heavy_rx_thld_percentage; + u8 limited_ps_threshold; + u8 reserved; +} __packed; + +/* + * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when + * associated AP is identified as improperly implementing uAPSD protocol. + * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78 + * @sta_id: index of station in uCode's station table - associated AP ID in + * this context. + */ +struct iwl_uapsd_misbehaving_ap_notif { + __le32 sta_id; + u8 mac_id; + u8 reserved[3]; +} __packed; + +/** + * struct iwl_reduce_tx_power_cmd - TX power reduction command + * REDUCE_TX_POWER_CMD = 0x9f + * @flags: (reserved for future implementation) + * @mac_context_id: id of the mac ctx for which we are reducing TX power. + * @pwr_restriction: TX power restriction in dBms. + */ +struct iwl_reduce_tx_power_cmd { + u8 flags; + u8 mac_context_id; + __le16 pwr_restriction; +} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */ + +enum iwl_dev_tx_power_cmd_mode { + IWL_TX_POWER_MODE_SET_MAC = 0, + IWL_TX_POWER_MODE_SET_DEVICE = 1, + IWL_TX_POWER_MODE_SET_CHAINS = 2, + IWL_TX_POWER_MODE_SET_ACK = 3, +}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_4 */; + +#define IWL_NUM_CHAIN_LIMITS 2 +#define IWL_NUM_SUB_BANDS 5 + +/** + * struct iwl_dev_tx_power_cmd - TX power reduction command + * @set_mode: see &enum iwl_dev_tx_power_cmd_mode + * @mac_context_id: id of the mac ctx for which we are reducing TX power. + * @pwr_restriction: TX power restriction in 1/8 dBms. + * @dev_24: device TX power restriction in 1/8 dBms + * @dev_52_low: device TX power restriction upper band - low + * @dev_52_high: device TX power restriction upper band - high + * @per_chain_restriction: per chain restrictions + */ +struct iwl_dev_tx_power_cmd_v3 { + __le32 set_mode; + __le32 mac_context_id; + __le16 pwr_restriction; + __le16 dev_24; + __le16 dev_52_low; + __le16 dev_52_high; + __le16 per_chain_restriction[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS]; +} __packed; /* TX_REDUCED_POWER_API_S_VER_3 */ + +#define IWL_DEV_MAX_TX_POWER 0x7FFF + +/** + * struct iwl_dev_tx_power_cmd - TX power reduction command + * @v3: version 3 of the command, embedded here for easier software handling + * @enable_ack_reduction: enable or disable close range ack TX power + * reduction. + * @reserved: reserved (padding) + */ +struct iwl_dev_tx_power_cmd { + /* v4 is just an extension of v3 - keep this here */ + struct iwl_dev_tx_power_cmd_v3 v3; + u8 enable_ack_reduction; + u8 reserved[3]; +} __packed; /* TX_REDUCED_POWER_API_S_VER_4 */ + +#define IWL_NUM_GEO_PROFILES 3 +#define IWL_GEO_PER_CHAIN_SIZE 3 + +/** + * enum iwl_geo_per_chain_offset_operation - type of operation + * @IWL_PER_CHAIN_OFFSET_SET_TABLES: send the tables from the host to the FW. + * @IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE: retrieve the last configured table. + */ +enum iwl_geo_per_chain_offset_operation { + IWL_PER_CHAIN_OFFSET_SET_TABLES, + IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE, +}; /* GEO_TX_POWER_LIMIT FLAGS TYPE */ + +/** + * struct iwl_per_chain_offset - embedded struct for GEO_TX_POWER_LIMIT. + * @max_tx_power: maximum allowed tx power. + * @chain_a: tx power offset for chain a. + * @chain_b: tx power offset for chain b. + */ +struct iwl_per_chain_offset { + __le16 max_tx_power; + u8 chain_a; + u8 chain_b; +} __packed; /* PER_CHAIN_LIMIT_OFFSET_PER_CHAIN_S_VER_1 */ + +struct iwl_per_chain_offset_group { + struct iwl_per_chain_offset lb; + struct iwl_per_chain_offset hb; +} __packed; /* PER_CHAIN_LIMIT_OFFSET_GROUP_S_VER_1 */ + +/** + * struct iwl_geo_tx_power_profile_cmd - struct for GEO_TX_POWER_LIMIT cmd. + * @ops: operations, value from &enum iwl_geo_per_chain_offset_operation + * @table: offset profile per band. + */ +struct iwl_geo_tx_power_profiles_cmd { + __le32 ops; + struct iwl_per_chain_offset_group table[IWL_NUM_GEO_PROFILES]; +} __packed; /* GEO_TX_POWER_LIMIT */ + +/** + * struct iwl_geo_tx_power_profiles_resp - response to GEO_TX_POWER_LIMIT cmd + * @profile_idx: current geo profile in use + */ +struct iwl_geo_tx_power_profiles_resp { + __le32 profile_idx; +} __packed; /* GEO_TX_POWER_LIMIT_RESP */ + +/** + * struct iwl_beacon_filter_cmd + * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) + * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon + * to driver if delta in Energy values calculated for this and last + * passed beacon is greater than this threshold. Zero value means that + * the Energy change is ignored for beacon filtering, and beacon will + * not be forced to be sent to driver regardless of this delta. Typical + * energy delta 5dB. + * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state. + * Send beacon to driver if delta in Energy values calculated for this + * and last passed beacon is greater than this threshold. Zero value + * means that the Energy change is ignored for beacon filtering while in + * Roaming state, typical energy delta 1dB. + * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values + * calculated for current beacon is less than the threshold, use + * Roaming Energy Delta Threshold, otherwise use normal Energy Delta + * Threshold. Typical energy threshold is -72dBm. + * @bf_temp_threshold: This threshold determines the type of temperature + * filtering (Slow or Fast) that is selected (Units are in Celsuis): + * If the current temperature is above this threshold - Fast filter + * will be used, If the current temperature is below this threshold - + * Slow filter will be used. + * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values + * calculated for this and the last passed beacon is greater than this + * threshold. Zero value means that the temperature change is ignored for + * beacon filtering; beacons will not be forced to be sent to driver + * regardless of whether its temerature has been changed. + * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values + * calculated for this and the last passed beacon is greater than this + * threshold. Zero value means that the temperature change is ignored for + * beacon filtering; beacons will not be forced to be sent to driver + * regardless of whether its temerature has been changed. + * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. + * @bf_debug_flag: beacon filtering debug configuration + * @bf_escape_timer: Send beacons to to driver if no beacons were passed + * for a specific period of time. Units: Beacons. + * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed + * for a longer period of time then this escape-timeout. Units: Beacons. + * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. + */ +struct iwl_beacon_filter_cmd { + __le32 bf_energy_delta; + __le32 bf_roaming_energy_delta; + __le32 bf_roaming_state; + __le32 bf_temp_threshold; + __le32 bf_temp_fast_filter; + __le32 bf_temp_slow_filter; + __le32 bf_enable_beacon_filter; + __le32 bf_debug_flag; + __le32 bf_escape_timer; + __le32 ba_escape_timer; + __le32 ba_enable_beacon_abort; +} __packed; + +/* Beacon filtering and beacon abort */ +#define IWL_BF_ENERGY_DELTA_DEFAULT 5 +#define IWL_BF_ENERGY_DELTA_D0I3 20 +#define IWL_BF_ENERGY_DELTA_MAX 255 +#define IWL_BF_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 +#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20 +#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255 +#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_STATE_DEFAULT 72 +#define IWL_BF_ROAMING_STATE_D0I3 72 +#define IWL_BF_ROAMING_STATE_MAX 255 +#define IWL_BF_ROAMING_STATE_MIN 0 + +#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112 +#define IWL_BF_TEMP_THRESHOLD_D0I3 112 +#define IWL_BF_TEMP_THRESHOLD_MAX 255 +#define IWL_BF_TEMP_THRESHOLD_MIN 0 + +#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1 +#define IWL_BF_TEMP_FAST_FILTER_D0I3 1 +#define IWL_BF_TEMP_FAST_FILTER_MAX 255 +#define IWL_BF_TEMP_FAST_FILTER_MIN 0 + +#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5 +#define IWL_BF_TEMP_SLOW_FILTER_D0I3 20 +#define IWL_BF_TEMP_SLOW_FILTER_MAX 255 +#define IWL_BF_TEMP_SLOW_FILTER_MIN 0 + +#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 + +#define IWL_BF_DEBUG_FLAG_DEFAULT 0 +#define IWL_BF_DEBUG_FLAG_D0I3 0 + +#define IWL_BF_ESCAPE_TIMER_DEFAULT 0 +#define IWL_BF_ESCAPE_TIMER_D0I3 0 +#define IWL_BF_ESCAPE_TIMER_MAX 1024 +#define IWL_BF_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ESCAPE_TIMER_DEFAULT 6 +#define IWL_BA_ESCAPE_TIMER_D0I3 6 +#define IWL_BA_ESCAPE_TIMER_D3 9 +#define IWL_BA_ESCAPE_TIMER_MAX 1024 +#define IWL_BA_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 + +#define IWL_BF_CMD_CONFIG(mode) \ + .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \ + .bf_roaming_energy_delta = \ + cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \ + .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \ + .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \ + .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \ + .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \ + .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \ + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \ + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode) + +#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT) +#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3) +#endif /* __iwl_fw_api_power_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h new file mode 100644 index 000000000000..a13fd8a1be62 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -0,0 +1,408 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_fw_api_rs_h__ +#define __iwl_fw_api_rs_h__ + +#include "mac.h" + +/* + * These serve as indexes into + * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT]; + * TODO: avoid overlap between legacy and HT rates + */ +enum { + IWL_RATE_1M_INDEX = 0, + IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, + IWL_RATE_2M_INDEX, + IWL_RATE_5M_INDEX, + IWL_RATE_11M_INDEX, + IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, + IWL_RATE_6M_INDEX, + IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, + IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX, + IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX, + IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX, + IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, + IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX, + IWL_RATE_18M_INDEX, + IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, + IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX, + IWL_RATE_36M_INDEX, + IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, + IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX, + IWL_RATE_54M_INDEX, + IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX, + IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX, + IWL_RATE_60M_INDEX, + IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX, + IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX, + IWL_RATE_MCS_8_INDEX, + IWL_RATE_MCS_9_INDEX, + IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX, + IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1, + IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1, +}; + +#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX) + +/* fw API values for legacy bit rates, both OFDM and CCK */ +enum { + IWL_RATE_6M_PLCP = 13, + IWL_RATE_9M_PLCP = 15, + IWL_RATE_12M_PLCP = 5, + IWL_RATE_18M_PLCP = 7, + IWL_RATE_24M_PLCP = 9, + IWL_RATE_36M_PLCP = 11, + IWL_RATE_48M_PLCP = 1, + IWL_RATE_54M_PLCP = 3, + IWL_RATE_1M_PLCP = 10, + IWL_RATE_2M_PLCP = 20, + IWL_RATE_5M_PLCP = 55, + IWL_RATE_11M_PLCP = 110, + IWL_RATE_INVM_PLCP = -1, +}; + +/* + * rate_n_flags bit fields + * + * The 32-bit value has different layouts in the low 8 bites depending on the + * format. There are three formats, HT, VHT and legacy (11abg, with subformats + * for CCK and OFDM). + * + * High-throughput (HT) rate format + * bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM) + * Very High-throughput (VHT) rate format + * bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM) + * Legacy OFDM rate format for bits 7:0 + * bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM) + * Legacy CCK rate format for bits 7:0: + * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK) + */ + +/* Bit 8: (1) HT format, (0) legacy or VHT format */ +#define RATE_MCS_HT_POS 8 +#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS) + +/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ +#define RATE_MCS_CCK_POS 9 +#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS) + +/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */ +#define RATE_MCS_VHT_POS 26 +#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS) + + +/* + * High-throughput (HT) rate format for bits 7:0 + * + * 2-0: MCS rate base + * 0) 6 Mbps + * 1) 12 Mbps + * 2) 18 Mbps + * 3) 24 Mbps + * 4) 36 Mbps + * 5) 48 Mbps + * 6) 54 Mbps + * 7) 60 Mbps + * 4-3: 0) Single stream (SISO) + * 1) Dual stream (MIMO) + * 2) Triple stream (MIMO) + * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data + * (bits 7-6 are zero) + * + * Together the low 5 bits work out to the MCS index because we don't + * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two + * streams and 16-23 have three streams. We could also support MCS 32 + * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.) + */ +#define RATE_HT_MCS_RATE_CODE_MSK 0x7 +#define RATE_HT_MCS_NSS_POS 3 +#define RATE_HT_MCS_NSS_MSK (3 << RATE_HT_MCS_NSS_POS) + +/* Bit 10: (1) Use Green Field preamble */ +#define RATE_HT_MCS_GF_POS 10 +#define RATE_HT_MCS_GF_MSK (1 << RATE_HT_MCS_GF_POS) + +#define RATE_HT_MCS_INDEX_MSK 0x3f + +/* + * Very High-throughput (VHT) rate format for bits 7:0 + * + * 3-0: VHT MCS (0-9) + * 5-4: number of streams - 1: + * 0) Single stream (SISO) + * 1) Dual stream (MIMO) + * 2) Triple stream (MIMO) + */ + +/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */ +#define RATE_VHT_MCS_RATE_CODE_MSK 0xf +#define RATE_VHT_MCS_NSS_POS 4 +#define RATE_VHT_MCS_NSS_MSK (3 << RATE_VHT_MCS_NSS_POS) + +/* + * Legacy OFDM rate format for bits 7:0 + * + * 3-0: 0xD) 6 Mbps + * 0xF) 9 Mbps + * 0x5) 12 Mbps + * 0x7) 18 Mbps + * 0x9) 24 Mbps + * 0xB) 36 Mbps + * 0x1) 48 Mbps + * 0x3) 54 Mbps + * (bits 7-4 are 0) + * + * Legacy CCK rate format for bits 7:0: + * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK): + * + * 6-0: 10) 1 Mbps + * 20) 2 Mbps + * 55) 5.5 Mbps + * 110) 11 Mbps + * (bit 7 is 0) + */ +#define RATE_LEGACY_RATE_MSK 0xff + +/* Bit 10 - OFDM HE */ +#define RATE_MCS_OFDM_HE_POS 10 +#define RATE_MCS_OFDM_HE_MSK BIT(RATE_MCS_OFDM_HE_POS) + +/* + * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz + * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT + */ +#define RATE_MCS_CHAN_WIDTH_POS 11 +#define RATE_MCS_CHAN_WIDTH_MSK (3 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_20 (0 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_40 (1 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_80 (2 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_160 (3 << RATE_MCS_CHAN_WIDTH_POS) + +/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ +#define RATE_MCS_SGI_POS 13 +#define RATE_MCS_SGI_MSK (1 << RATE_MCS_SGI_POS) + +/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */ +#define RATE_MCS_ANT_POS 14 +#define RATE_MCS_ANT_A_MSK (1 << RATE_MCS_ANT_POS) +#define RATE_MCS_ANT_B_MSK (2 << RATE_MCS_ANT_POS) +#define RATE_MCS_ANT_C_MSK (4 << RATE_MCS_ANT_POS) +#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | \ + RATE_MCS_ANT_B_MSK) +#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \ + RATE_MCS_ANT_C_MSK) +#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK +#define RATE_MCS_ANT_NUM 3 + +/* Bit 17: (0) SS, (1) SS*2 */ +#define RATE_MCS_STBC_POS 17 +#define RATE_MCS_STBC_MSK BIT(RATE_MCS_STBC_POS) + +/* Bit 18: OFDM-HE dual carrier mode */ +#define RATE_HE_DUAL_CARRIER_MODE 18 +#define RATE_HE_DUAL_CARRIER_MODE_MSK BIT(RATE_HE_DUAL_CARRIER_MODE) + +/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */ +#define RATE_MCS_BF_POS 19 +#define RATE_MCS_BF_MSK (1 << RATE_MCS_BF_POS) + +/* + * Bit 20-21: HE guard interval and LTF type. + * (0) 1xLTF+1.6us, (1) 2xLTF+0.8us, + * (2) 2xLTF+1.6us, (3) 4xLTF+3.2us + */ +#define RATE_MCS_HE_GI_LTF_POS 20 +#define RATE_MCS_HE_GI_LTF_MSK (3 << RATE_MCS_HE_GI_LTF_POS) + +/* Bit 22-23: HE type. (0) SU, (1) SU_EXT, (2) MU, (3) trigger based */ +#define RATE_MCS_HE_TYPE_POS 22 +#define RATE_MCS_HE_TYPE_MSK (3 << RATE_MCS_HE_TYPE_POS) + +/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */ +#define RATE_MCS_DUP_POS 24 +#define RATE_MCS_DUP_MSK (3 << RATE_MCS_DUP_POS) + +/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */ +#define RATE_MCS_LDPC_POS 27 +#define RATE_MCS_LDPC_MSK (1 << RATE_MCS_LDPC_POS) + + +/* Link Quality definitions */ + +/* # entries in rate scale table to support Tx retries */ +#define LQ_MAX_RETRY_NUM 16 + +/* Link quality command flags bit fields */ + +/* Bit 0: (0) Don't use RTS (1) Use RTS */ +#define LQ_FLAG_USE_RTS_POS 0 +#define LQ_FLAG_USE_RTS_MSK (1 << LQ_FLAG_USE_RTS_POS) + +/* Bit 1-3: LQ command color. Used to match responses to LQ commands */ +#define LQ_FLAG_COLOR_POS 1 +#define LQ_FLAG_COLOR_MSK (7 << LQ_FLAG_COLOR_POS) +#define LQ_FLAG_COLOR_GET(_f) (((_f) & LQ_FLAG_COLOR_MSK) >>\ + LQ_FLAG_COLOR_POS) +#define LQ_FLAGS_COLOR_INC(_c) ((((_c) + 1) << LQ_FLAG_COLOR_POS) &\ + LQ_FLAG_COLOR_MSK) +#define LQ_FLAG_COLOR_SET(_f, _c) ((_c) | ((_f) & ~LQ_FLAG_COLOR_MSK)) + +/* Bit 4-5: Tx RTS BW Signalling + * (0) No RTS BW signalling + * (1) Static BW signalling + * (2) Dynamic BW signalling + */ +#define LQ_FLAG_RTS_BW_SIG_POS 4 +#define LQ_FLAG_RTS_BW_SIG_NONE (0 << LQ_FLAG_RTS_BW_SIG_POS) +#define LQ_FLAG_RTS_BW_SIG_STATIC (1 << LQ_FLAG_RTS_BW_SIG_POS) +#define LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << LQ_FLAG_RTS_BW_SIG_POS) + +/* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection + * Dyanmic BW selection allows Tx with narrower BW then requested in rates + */ +#define LQ_FLAG_DYNAMIC_BW_POS 6 +#define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS) + +/* Single Stream Tx Parameters (lq_cmd->ss_params) + * Flags to control a smart FW decision about whether BFER/STBC/SISO will be + * used for single stream Tx. + */ + +/* Bit 0-1: Max STBC streams allowed. Can be 0-3. + * (0) - No STBC allowed + * (1) - 2x1 STBC allowed (HT/VHT) + * (2) - 4x2 STBC allowed (HT/VHT) + * (3) - 3x2 STBC allowed (HT only) + * All our chips are at most 2 antennas so only (1) is valid for now. + */ +#define LQ_SS_STBC_ALLOWED_POS 0 +#define LQ_SS_STBC_ALLOWED_MSK (3 << LQ_SS_STBC_ALLOWED_MSK) + +/* 2x1 STBC is allowed */ +#define LQ_SS_STBC_1SS_ALLOWED (1 << LQ_SS_STBC_ALLOWED_POS) + +/* Bit 2: Beamformer (VHT only) is allowed */ +#define LQ_SS_BFER_ALLOWED_POS 2 +#define LQ_SS_BFER_ALLOWED (1 << LQ_SS_BFER_ALLOWED_POS) + +/* Bit 3: Force BFER or STBC for testing + * If this is set: + * If BFER is allowed then force the ucode to choose BFER else + * If STBC is allowed then force the ucode to choose STBC over SISO + */ +#define LQ_SS_FORCE_POS 3 +#define LQ_SS_FORCE (1 << LQ_SS_FORCE_POS) + +/* Bit 31: ss_params field is valid. Used for FW backward compatibility + * with other drivers which don't support the ss_params API yet + */ +#define LQ_SS_PARAMS_VALID_POS 31 +#define LQ_SS_PARAMS_VALID (1 << LQ_SS_PARAMS_VALID_POS) + +/** + * struct iwl_lq_cmd - link quality command + * @sta_id: station to update + * @reduced_tpc: reduced transmit power control value + * @control: not used + * @flags: combination of LQ_FLAG_* + * @mimo_delim: the first SISO index in rs_table, which separates MIMO + * and SISO rates + * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD). + * Should be ANT_[ABC] + * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC] + * @initial_rate_index: first index from rs_table per AC category + * @agg_time_limit: aggregation max time threshold in usec/100, meaning + * value of 100 is one usec. Range is 100 to 8000 + * @agg_disable_start_th: try-count threshold for starting aggregation. + * If a frame has higher try-count, it should not be selected for + * starting an aggregation sequence. + * @agg_frame_cnt_limit: max frame count in an aggregation. + * 0: no limit + * 1: no aggregation (one frame per aggregation) + * 2 - 0x3f: maximal number of frames (up to 3f == 63) + * @reserved2: reserved + * @rs_table: array of rates for each TX try, each is rate_n_flags, + * meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP + * @ss_params: single stream features. declare whether STBC or BFER are allowed. + */ +struct iwl_lq_cmd { + u8 sta_id; + u8 reduced_tpc; + __le16 control; + /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */ + u8 flags; + u8 mimo_delim; + u8 single_stream_ant_msk; + u8 dual_stream_ant_msk; + u8 initial_rate_index[AC_NUM]; + /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */ + __le16 agg_time_limit; + u8 agg_disable_start_th; + u8 agg_frame_cnt_limit; + __le32 reserved2; + __le32 rs_table[LQ_MAX_RETRY_NUM]; + __le32 ss_params; +}; /* LINK_QUALITY_CMD_API_S_VER_1 */ + +#endif /* __iwl_fw_api_rs_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h new file mode 100644 index 000000000000..e7565f37ece9 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -0,0 +1,589 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_rx_h__ +#define __iwl_fw_api_rx_h__ + +/* API for pre-9000 hardware */ + +#define IWL_RX_INFO_PHY_CNT 8 +#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1 +#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff +#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00 +#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000 +#define IWL_RX_INFO_ENERGY_ANT_A_POS 0 +#define IWL_RX_INFO_ENERGY_ANT_B_POS 8 +#define IWL_RX_INFO_ENERGY_ANT_C_POS 16 + +enum iwl_mac_context_info { + MAC_CONTEXT_INFO_NONE, + MAC_CONTEXT_INFO_GSCAN, +}; + +/** + * struct iwl_rx_phy_info - phy info + * (REPLY_RX_PHY_CMD = 0xc0) + * @non_cfg_phy_cnt: non configurable DSP phy data byte count + * @cfg_phy_cnt: configurable DSP phy data byte count + * @stat_id: configurable DSP phy data set ID + * @reserved1: reserved + * @system_timestamp: GP2 at on air rise + * @timestamp: TSF at on air rise + * @beacon_time_stamp: beacon at on-air rise + * @phy_flags: general phy flags: band, modulation, ... + * @channel: channel number + * @non_cfg_phy: for various implementations of non_cfg_phy + * @rate_n_flags: RATE_MCS_* + * @byte_count: frame's byte-count + * @frame_time: frame's time on the air, based on byte count and frame rate + * calculation + * @mac_active_msk: what MACs were active when the frame was received + * @mac_context_info: additional info on the context in which the frame was + * received as defined in &enum iwl_mac_context_info + * + * Before each Rx, the device sends this data. It contains PHY information + * about the reception of the packet. + */ +struct iwl_rx_phy_info { + u8 non_cfg_phy_cnt; + u8 cfg_phy_cnt; + u8 stat_id; + u8 reserved1; + __le32 system_timestamp; + __le64 timestamp; + __le32 beacon_time_stamp; + __le16 phy_flags; + __le16 channel; + __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT]; + __le32 rate_n_flags; + __le32 byte_count; + u8 mac_active_msk; + u8 mac_context_info; + __le16 frame_time; +} __packed; + +/* + * TCP offload Rx assist info + * + * bits 0:3 - reserved + * bits 4:7 - MIC CRC length + * bits 8:12 - MAC header length + * bit 13 - Padding indication + * bit 14 - A-AMSDU indication + * bit 15 - Offload enabled + */ +enum iwl_csum_rx_assist_info { + CSUM_RXA_RESERVED_MASK = 0x000f, + CSUM_RXA_MICSIZE_MASK = 0x00f0, + CSUM_RXA_HEADERLEN_MASK = 0x1f00, + CSUM_RXA_PADD = BIT(13), + CSUM_RXA_AMSDU = BIT(14), + CSUM_RXA_ENA = BIT(15) +}; + +/** + * struct iwl_rx_mpdu_res_start - phy info + * @byte_count: byte count of the frame + * @assist: see &enum iwl_csum_rx_assist_info + */ +struct iwl_rx_mpdu_res_start { + __le16 byte_count; + __le16 assist; +} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */ + +/** + * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags + * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band + * @RX_RES_PHY_FLAGS_MOD_CCK: modulation is CCK + * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short + * @RX_RES_PHY_FLAGS_NARROW_BAND: narrow band (<20 MHz) receive + * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received + * @RX_RES_PHY_FLAGS_ANTENNA_POS: antenna bit position + * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU + * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame + * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble + * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame + */ +enum iwl_rx_phy_flags { + RX_RES_PHY_FLAGS_BAND_24 = BIT(0), + RX_RES_PHY_FLAGS_MOD_CCK = BIT(1), + RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2), + RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3), + RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4), + RX_RES_PHY_FLAGS_ANTENNA_POS = 4, + RX_RES_PHY_FLAGS_AGG = BIT(7), + RX_RES_PHY_FLAGS_OFDM_HT = BIT(8), + RX_RES_PHY_FLAGS_OFDM_GF = BIT(9), + RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10), +}; + +/** + * enum iwl_mvm_rx_status - written by fw for each Rx packet + * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine + * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow + * @RX_MPDU_RES_STATUS_SRC_STA_FOUND: station was found + * @RX_MPDU_RES_STATUS_KEY_VALID: key was valid + * @RX_MPDU_RES_STATUS_KEY_PARAM_OK: key parameters were usable + * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed + * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked + * in the driver. + * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine + * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or + * alg = CCM only. Checks replay attack for 11w frames. Relevant only if + * %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set. + * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted + * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP + * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM + * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP + * @RX_MPDU_RES_STATUS_SEC_EXT_ENC: this frame is encrypted using extension + * algorithm + * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC + * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted + * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm + * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted + * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP: extended IV (set with TKIP) + * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT: key ID comparison done + * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame + * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw + * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors + * @RX_MPDU_RES_STATUS_STA_ID_MSK: station ID mask + * @RX_MDPU_RES_STATUS_STA_ID_SHIFT: station ID bit shift + * @RX_MPDU_RES_STATUS_FILTERING_MSK: filter status + * @RX_MPDU_RES_STATUS2_FILTERING_MSK: filter status 2 + */ +enum iwl_mvm_rx_status { + RX_MPDU_RES_STATUS_CRC_OK = BIT(0), + RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1), + RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2), + RX_MPDU_RES_STATUS_KEY_VALID = BIT(3), + RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4), + RX_MPDU_RES_STATUS_ICV_OK = BIT(5), + RX_MPDU_RES_STATUS_MIC_OK = BIT(6), + RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), + RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7), + RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8), + RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), + RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), + RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), + RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), + RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), + RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), + RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), + RX_MPDU_RES_STATUS_DEC_DONE = BIT(11), + RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13), + RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14), + RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15), + RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16), + RX_MPDU_RES_STATUS_CSUM_OK = BIT(17), + RX_MDPU_RES_STATUS_STA_ID_SHIFT = 24, + RX_MPDU_RES_STATUS_STA_ID_MSK = 0x1f << RX_MDPU_RES_STATUS_STA_ID_SHIFT, + RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000), + RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), +}; + +/* 9000 series API */ +enum iwl_rx_mpdu_mac_flags1 { + IWL_RX_MDPU_MFLG1_ADDRTYPE_MASK = 0x03, + IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_MASK = 0xf0, + /* shift should be 4, but the length is measured in 2-byte + * words, so shifting only by 3 gives a byte result + */ + IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_SHIFT = 3, +}; + +enum iwl_rx_mpdu_mac_flags2 { + /* in 2-byte words */ + IWL_RX_MPDU_MFLG2_HDR_LEN_MASK = 0x1f, + IWL_RX_MPDU_MFLG2_PAD = 0x20, + IWL_RX_MPDU_MFLG2_AMSDU = 0x40, +}; + +enum iwl_rx_mpdu_amsdu_info { + IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK = 0x7f, + IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x80, +}; + +enum iwl_rx_l3_proto_values { + IWL_RX_L3_TYPE_NONE, + IWL_RX_L3_TYPE_IPV4, + IWL_RX_L3_TYPE_IPV4_FRAG, + IWL_RX_L3_TYPE_IPV6_FRAG, + IWL_RX_L3_TYPE_IPV6, + IWL_RX_L3_TYPE_IPV6_IN_IPV4, + IWL_RX_L3_TYPE_ARP, + IWL_RX_L3_TYPE_EAPOL, +}; + +#define IWL_RX_L3_PROTO_POS 4 + +enum iwl_rx_l3l4_flags { + IWL_RX_L3L4_IP_HDR_CSUM_OK = BIT(0), + IWL_RX_L3L4_TCP_UDP_CSUM_OK = BIT(1), + IWL_RX_L3L4_TCP_FIN_SYN_RST_PSH = BIT(2), + IWL_RX_L3L4_TCP_ACK = BIT(3), + IWL_RX_L3L4_L3_PROTO_MASK = 0xf << IWL_RX_L3_PROTO_POS, + IWL_RX_L3L4_L4_PROTO_MASK = 0xf << 8, + IWL_RX_L3L4_RSS_HASH_MASK = 0xf << 12, +}; + +enum iwl_rx_mpdu_status { + IWL_RX_MPDU_STATUS_CRC_OK = BIT(0), + IWL_RX_MPDU_STATUS_OVERRUN_OK = BIT(1), + IWL_RX_MPDU_STATUS_SRC_STA_FOUND = BIT(2), + IWL_RX_MPDU_STATUS_KEY_VALID = BIT(3), + IWL_RX_MPDU_STATUS_KEY_PARAM_OK = BIT(4), + IWL_RX_MPDU_STATUS_ICV_OK = BIT(5), + IWL_RX_MPDU_STATUS_MIC_OK = BIT(6), + IWL_RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), + IWL_RX_MPDU_STATUS_SEC_MASK = 0x7 << 8, + IWL_RX_MPDU_STATUS_SEC_NONE = 0x0 << 8, + IWL_RX_MPDU_STATUS_SEC_WEP = 0x1 << 8, + IWL_RX_MPDU_STATUS_SEC_CCM = 0x2 << 8, + IWL_RX_MPDU_STATUS_SEC_TKIP = 0x3 << 8, + IWL_RX_MPDU_STATUS_SEC_EXT_ENC = 0x4 << 8, + IWL_RX_MPDU_STATUS_SEC_GCM = 0x5 << 8, + IWL_RX_MPDU_STATUS_DECRYPTED = BIT(11), + IWL_RX_MPDU_STATUS_WEP_MATCH = BIT(12), + IWL_RX_MPDU_STATUS_EXT_IV_MATCH = BIT(13), + IWL_RX_MPDU_STATUS_KEY_ID_MATCH = BIT(14), + IWL_RX_MPDU_STATUS_ROBUST_MNG_FRAME = BIT(15), +}; + +enum iwl_rx_mpdu_hash_filter { + IWL_RX_MPDU_HF_A1_HASH_MASK = 0x3f, + IWL_RX_MPDU_HF_FILTER_STATUS_MASK = 0xc0, +}; + +enum iwl_rx_mpdu_sta_id_flags { + IWL_RX_MPDU_SIF_STA_ID_MASK = 0x1f, + IWL_RX_MPDU_SIF_RRF_ABORT = 0x20, + IWL_RX_MPDU_SIF_FILTER_STATUS_MASK = 0xc0, +}; + +#define IWL_RX_REORDER_DATA_INVALID_BAID 0x7f + +enum iwl_rx_mpdu_reorder_data { + IWL_RX_MPDU_REORDER_NSSN_MASK = 0x00000fff, + IWL_RX_MPDU_REORDER_SN_MASK = 0x00fff000, + IWL_RX_MPDU_REORDER_SN_SHIFT = 12, + IWL_RX_MPDU_REORDER_BAID_MASK = 0x7f000000, + IWL_RX_MPDU_REORDER_BAID_SHIFT = 24, + IWL_RX_MPDU_REORDER_BA_OLD_SN = 0x80000000, +}; + +enum iwl_rx_mpdu_phy_info { + IWL_RX_MPDU_PHY_AMPDU = BIT(5), + IWL_RX_MPDU_PHY_AMPDU_TOGGLE = BIT(6), + IWL_RX_MPDU_PHY_SHORT_PREAMBLE = BIT(7), + IWL_RX_MPDU_PHY_TSF_OVERLOAD = BIT(8), +}; + +enum iwl_rx_mpdu_mac_info { + IWL_RX_MPDU_PHY_MAC_INDEX_MASK = 0x0f, + IWL_RX_MPDU_PHY_PHY_INDEX_MASK = 0xf0, +}; + +/** + * struct iwl_rx_mpdu_desc - RX MPDU descriptor + */ +struct iwl_rx_mpdu_desc { + /* DW2 */ + /** + * @mpdu_len: MPDU length + */ + __le16 mpdu_len; + /** + * @mac_flags1: &enum iwl_rx_mpdu_mac_flags1 + */ + u8 mac_flags1; + /** + * @mac_flags2: &enum iwl_rx_mpdu_mac_flags2 + */ + u8 mac_flags2; + /* DW3 */ + /** + * @amsdu_info: &enum iwl_rx_mpdu_amsdu_info + */ + u8 amsdu_info; + /** + * @phy_info: &enum iwl_rx_mpdu_phy_info + */ + __le16 phy_info; + /** + * @mac_phy_idx: MAC/PHY index + */ + u8 mac_phy_idx; + /* DW4 - carries csum data only when rpa_en == 1 */ + /** + * @raw_csum: raw checksum (alledgedly unreliable) + */ + __le16 raw_csum; + /** + * @l3l4_flags: &enum iwl_rx_l3l4_flags + */ + __le16 l3l4_flags; + /* DW5 */ + /** + * @status: &enum iwl_rx_mpdu_status + */ + __le16 status; + /** + * @hash_filter: hash filter value + */ + u8 hash_filter; + /** + * @sta_id_flags: &enum iwl_rx_mpdu_sta_id_flags + */ + u8 sta_id_flags; + /* DW6 */ + /** + * @reorder_data: &enum iwl_rx_mpdu_reorder_data + */ + __le32 reorder_data; + /* DW7 - carries rss_hash only when rpa_en == 1 */ + /** + * @rss_hash: RSS hash value + */ + __le32 rss_hash; + /* DW8 - carries filter_match only when rpa_en == 1 */ + /** + * @filter_match: filter match value + */ + __le32 filter_match; + /* DW9 */ + /** + * @rate_n_flags: RX rate/flags encoding + */ + __le32 rate_n_flags; + /* DW10 */ + /** + * @energy_a: energy chain A + */ + u8 energy_a; + /** + * @energy_b: energy chain B + */ + u8 energy_b; + /** + * @channel: channel number + */ + u8 channel; + /** + * @mac_context: MAC context mask + */ + u8 mac_context; + /* DW11 */ + /** + * @gp2_on_air_rise: GP2 timer value on air rise (INA) + */ + __le32 gp2_on_air_rise; + /* DW12 & DW13 */ + /** + * @tsf_on_air_rise: + * TSF value on air rise (INA), only valid if + * %IWL_RX_MPDU_PHY_TSF_OVERLOAD isn't set + */ + __le64 tsf_on_air_rise; +} __packed; + +struct iwl_frame_release { + u8 baid; + u8 reserved; + __le16 nssn; +}; + +enum iwl_rss_hash_func_en { + IWL_RSS_HASH_TYPE_IPV4_TCP, + IWL_RSS_HASH_TYPE_IPV4_UDP, + IWL_RSS_HASH_TYPE_IPV4_PAYLOAD, + IWL_RSS_HASH_TYPE_IPV6_TCP, + IWL_RSS_HASH_TYPE_IPV6_UDP, + IWL_RSS_HASH_TYPE_IPV6_PAYLOAD, +}; + +#define IWL_RSS_HASH_KEY_CNT 10 +#define IWL_RSS_INDIRECTION_TABLE_SIZE 128 +#define IWL_RSS_ENABLE 1 + +/** + * struct iwl_rss_config_cmd - RSS (Receive Side Scaling) configuration + * + * @flags: 1 - enable, 0 - disable + * @hash_mask: Type of RSS to use. Values are from %iwl_rss_hash_func_en + * @reserved: reserved + * @secret_key: 320 bit input of random key configuration from driver + * @indirection_table: indirection table + */ +struct iwl_rss_config_cmd { + __le32 flags; + u8 hash_mask; + u8 reserved[3]; + __le32 secret_key[IWL_RSS_HASH_KEY_CNT]; + u8 indirection_table[IWL_RSS_INDIRECTION_TABLE_SIZE]; +} __packed; /* RSS_CONFIG_CMD_API_S_VER_1 */ + +#define IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE 128 +#define IWL_MULTI_QUEUE_SYNC_SENDER_POS 0 +#define IWL_MULTI_QUEUE_SYNC_SENDER_MSK 0xf + +/** + * struct iwl_rxq_sync_cmd - RXQ notification trigger + * + * @flags: flags of the notification. bit 0:3 are the sender queue + * @rxq_mask: rx queues to send the notification on + * @count: number of bytes in payload, should be DWORD aligned + * @payload: data to send to rx queues + */ +struct iwl_rxq_sync_cmd { + __le32 flags; + __le32 rxq_mask; + __le32 count; + u8 payload[]; +} __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */ + +/** + * struct iwl_rxq_sync_notification - Notification triggered by RXQ + * sync command + * + * @count: number of bytes in payload + * @payload: data to send to rx queues + */ +struct iwl_rxq_sync_notification { + __le32 count; + u8 payload[]; +} __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */ + +/** + * enum iwl_mvm_rxq_notif_type - Internal message identifier + * + * @IWL_MVM_RXQ_EMPTY: empty sync notification + * @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA + */ +enum iwl_mvm_rxq_notif_type { + IWL_MVM_RXQ_EMPTY, + IWL_MVM_RXQ_NOTIF_DEL_BA, +}; + +/** + * struct iwl_mvm_internal_rxq_notif - Internal representation of the data sent + * in &iwl_rxq_sync_cmd. Should be DWORD aligned. + * FW is agnostic to the payload, so there are no endianity requirements. + * + * @type: value from &iwl_mvm_rxq_notif_type + * @sync: ctrl path is waiting for all notifications to be received + * @cookie: internal cookie to identify old notifications + * @data: payload + */ +struct iwl_mvm_internal_rxq_notif { + u16 type; + u16 sync; + u32 cookie; + u8 data[]; +} __packed; + +/** + * enum iwl_mvm_pm_event - type of station PM event + * @IWL_MVM_PM_EVENT_AWAKE: station woke up + * @IWL_MVM_PM_EVENT_ASLEEP: station went to sleep + * @IWL_MVM_PM_EVENT_UAPSD: station sent uAPSD trigger + * @IWL_MVM_PM_EVENT_PS_POLL: station sent PS-Poll + */ +enum iwl_mvm_pm_event { + IWL_MVM_PM_EVENT_AWAKE, + IWL_MVM_PM_EVENT_ASLEEP, + IWL_MVM_PM_EVENT_UAPSD, + IWL_MVM_PM_EVENT_PS_POLL, +}; /* PEER_PM_NTFY_API_E_VER_1 */ + +/** + * struct iwl_mvm_pm_state_notification - station PM state notification + * @sta_id: station ID of the station changing state + * @type: the new powersave state, see &enum iwl_mvm_pm_event + */ +struct iwl_mvm_pm_state_notification { + u8 sta_id; + u8 type; + /* private: */ + __le16 reserved; +} __packed; /* PEER_PM_NTFY_API_S_VER_1 */ + +#define BA_WINDOW_STREAMS_MAX 16 +#define BA_WINDOW_STATUS_TID_MSK 0x000F +#define BA_WINDOW_STATUS_STA_ID_POS 4 +#define BA_WINDOW_STATUS_STA_ID_MSK 0x01F0 +#define BA_WINDOW_STATUS_VALID_MSK BIT(9) + +/** + * struct iwl_ba_window_status_notif - reordering window's status notification + * @bitmap: bitmap of received frames [start_seq_num + 0]..[start_seq_num + 63] + * @ra_tid: bit 3:0 - TID, bit 8:4 - STA_ID, bit 9 - valid + * @start_seq_num: the start sequence number of the bitmap + * @mpdu_rx_count: the number of received MPDUs since entering D0i3 + */ +struct iwl_ba_window_status_notif { + __le64 bitmap[BA_WINDOW_STREAMS_MAX]; + __le16 ra_tid[BA_WINDOW_STREAMS_MAX]; + __le32 start_seq_num[BA_WINDOW_STREAMS_MAX]; + __le16 mpdu_rx_count[BA_WINDOW_STREAMS_MAX]; +} __packed; /* BA_WINDOW_STATUS_NTFY_API_S_VER_1 */ + +#endif /* __iwl_fw_api_rx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h new file mode 100644 index 000000000000..5a40092febfb --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -0,0 +1,787 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_scan_h__ +#define __iwl_fw_api_scan_h__ + +/* Scan Commands, Responses, Notifications */ + +/* Max number of IEs for direct SSID scans in a command */ +#define PROBE_OPTION_MAX 20 + +/** + * struct iwl_ssid_ie - directed scan network information element + * + * Up to 20 of these may appear in REPLY_SCAN_CMD, + * selected by "type" bit field in struct iwl_scan_channel; + * each channel may select different ssids from among the 20 entries. + * SSID IEs get transmitted in reverse order of entry. + * + * @id: element ID + * @len: element length + * @ssid: element (SSID) data + */ +struct iwl_ssid_ie { + u8 id; + u8 len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; +} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */ + +/* scan offload */ +#define IWL_SCAN_MAX_BLACKLIST_LEN 64 +#define IWL_SCAN_SHORT_BLACKLIST_LEN 16 +#define IWL_SCAN_MAX_PROFILES 11 +#define SCAN_OFFLOAD_PROBE_REQ_SIZE 512 + +/* Default watchdog (in MS) for scheduled scan iteration */ +#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000) + +#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) +#define CAN_ABORT_STATUS 1 + +#define IWL_FULL_SCAN_MULTIPLIER 5 +#define IWL_FAST_SCHED_SCAN_ITERATIONS 3 +#define IWL_MAX_SCHED_SCAN_PLANS 2 + +enum scan_framework_client { + SCAN_CLIENT_SCHED_SCAN = BIT(0), + SCAN_CLIENT_NETDETECT = BIT(1), + SCAN_CLIENT_ASSET_TRACKING = BIT(2), +}; + +/** + * struct iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S + * @ssid: MAC address to filter out + * @reported_rssi: AP rssi reported to the host + * @client_bitmap: clients ignore this entry - enum scan_framework_client + */ +struct iwl_scan_offload_blacklist { + u8 ssid[ETH_ALEN]; + u8 reported_rssi; + u8 client_bitmap; +} __packed; + +enum iwl_scan_offload_network_type { + IWL_NETWORK_TYPE_BSS = 1, + IWL_NETWORK_TYPE_IBSS = 2, + IWL_NETWORK_TYPE_ANY = 3, +}; + +enum iwl_scan_offload_band_selection { + IWL_SCAN_OFFLOAD_SELECT_2_4 = 0x4, + IWL_SCAN_OFFLOAD_SELECT_5_2 = 0x8, + IWL_SCAN_OFFLOAD_SELECT_ANY = 0xc, +}; + +/** + * struct iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S + * @ssid_index: index to ssid list in fixed part + * @unicast_cipher: encryption algorithm to match - bitmap + * @auth_alg: authentication algorithm to match - bitmap + * @network_type: enum iwl_scan_offload_network_type + * @band_selection: enum iwl_scan_offload_band_selection + * @client_bitmap: clients waiting for match - enum scan_framework_client + * @reserved: reserved + */ +struct iwl_scan_offload_profile { + u8 ssid_index; + u8 unicast_cipher; + u8 auth_alg; + u8 network_type; + u8 band_selection; + u8 client_bitmap; + u8 reserved[2]; +} __packed; + +/** + * struct iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1 + * @profiles: profiles to search for match + * @blacklist_len: length of blacklist + * @num_profiles: num of profiles in the list + * @match_notify: clients waiting for match found notification + * @pass_match: clients waiting for the results + * @active_clients: active clients bitmap - enum scan_framework_client + * @any_beacon_notify: clients waiting for match notification without match + * @reserved: reserved + */ +struct iwl_scan_offload_profile_cfg { + struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES]; + u8 blacklist_len; + u8 num_profiles; + u8 match_notify; + u8 pass_match; + u8 active_clients; + u8 any_beacon_notify; + u8 reserved[2]; +} __packed; + +/** + * struct iwl_scan_schedule_lmac - schedule of scan offload + * @delay: delay between iterations, in seconds. + * @iterations: num of scan iterations + * @full_scan_mul: number of partial scans before each full scan + */ +struct iwl_scan_schedule_lmac { + __le16 delay; + u8 iterations; + u8 full_scan_mul; +} __packed; /* SCAN_SCHEDULE_API_S */ + +enum iwl_scan_offload_complete_status { + IWL_SCAN_OFFLOAD_COMPLETED = 1, + IWL_SCAN_OFFLOAD_ABORTED = 2, +}; + +enum iwl_scan_ebs_status { + IWL_SCAN_EBS_SUCCESS, + IWL_SCAN_EBS_FAILED, + IWL_SCAN_EBS_CHAN_NOT_FOUND, + IWL_SCAN_EBS_INACTIVE, +}; + +/** + * struct iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S + * @tx_flags: combination of TX_CMD_FLG_* + * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is + * cleared. Combination of RATE_MCS_* + * @sta_id: index of destination station in FW station table + * @reserved: for alignment and future use + */ +struct iwl_scan_req_tx_cmd { + __le32 tx_flags; + __le32 rate_n_flags; + u8 sta_id; + u8 reserved[3]; +} __packed; + +enum iwl_scan_channel_flags_lmac { + IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27), + IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28), +}; + +/** + * struct iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2 + * @flags: bits 1-20: directed scan to i'th ssid + * other bits &enum iwl_scan_channel_flags_lmac + * @channel_num: channel number 1-13 etc + * @iter_count: scan iteration on this channel + * @iter_interval: interval in seconds between iterations on one channel + */ +struct iwl_scan_channel_cfg_lmac { + __le32 flags; + __le16 channel_num; + __le16 iter_count; + __le32 iter_interval; +} __packed; + +/* + * struct iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1 + * @offset: offset in the data block + * @len: length of the segment + */ +struct iwl_scan_probe_segment { + __le16 offset; + __le16 len; +} __packed; + +/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2 + * @mac_header: first (and common) part of the probe + * @band_data: band specific data + * @common_data: last (and common) part of the probe + * @buf: raw data block + */ +struct iwl_scan_probe_req { + struct iwl_scan_probe_segment mac_header; + struct iwl_scan_probe_segment band_data[2]; + struct iwl_scan_probe_segment common_data; + u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE]; +} __packed; + +enum iwl_scan_channel_flags { + IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0), + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1), + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2), +}; + +/* struct iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S + * @flags: enum iwl_scan_channel_flags + * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is + * involved. + * 1 - EBS is disabled. + * 2 - every second scan will be full scan(and so on). + */ +struct iwl_scan_channel_opt { + __le16 flags; + __le16 non_ebs_ratio; +} __packed; + +/** + * enum iwl_mvm_lmac_scan_flags - LMAC scan flags + * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses + * without filtering. + * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels + * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan + * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification + * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS: multiple SSID matching + * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented + * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report + * and DS parameter set IEs into probe requests. + * @IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL: use extended dwell time on channels + * 1, 6 and 11. + * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches + */ +enum iwl_mvm_lmac_scan_flags { + IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0), + IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1), + IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2), + IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3), + IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4), + IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5), + IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6), + IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL = BIT(7), + IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9), +}; + +enum iwl_scan_priority { + IWL_SCAN_PRIORITY_LOW, + IWL_SCAN_PRIORITY_MEDIUM, + IWL_SCAN_PRIORITY_HIGH, +}; + +enum iwl_scan_priority_ext { + IWL_SCAN_PRIORITY_EXT_0_LOWEST, + IWL_SCAN_PRIORITY_EXT_1, + IWL_SCAN_PRIORITY_EXT_2, + IWL_SCAN_PRIORITY_EXT_3, + IWL_SCAN_PRIORITY_EXT_4, + IWL_SCAN_PRIORITY_EXT_5, + IWL_SCAN_PRIORITY_EXT_6, + IWL_SCAN_PRIORITY_EXT_7_HIGHEST, +}; + +/** + * struct iwl_scan_req_lmac - SCAN_REQUEST_CMD_API_S_VER_1 + * @reserved1: for alignment and future use + * @n_channels: num of channels to scan + * @active_dwell: dwell time for active channels + * @passive_dwell: dwell time for passive channels + * @fragmented_dwell: dwell time for fragmented passive scan + * @extended_dwell: dwell time for channels 1, 6 and 11 (in certain cases) + * @reserved2: for alignment and future use + * @rx_chain_select: PHY_RX_CHAIN_* flags + * @scan_flags: &enum iwl_mvm_lmac_scan_flags + * @max_out_time: max time (in TU) to be out of associated channel + * @suspend_time: pause scan this long (TUs) when returning to service channel + * @flags: RXON flags + * @filter_flags: RXON filter + * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz + * @direct_scan: list of SSIDs for directed active scan + * @scan_prio: enum iwl_scan_priority + * @iter_num: number of scan iterations + * @delay: delay in seconds before first iteration + * @schedule: two scheduling plans. The first one is finite, the second one can + * be infinite. + * @channel_opt: channel optimization options, for full and partial scan + * @data: channel configuration and probe request packet. + */ +struct iwl_scan_req_lmac { + /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */ + __le32 reserved1; + u8 n_channels; + u8 active_dwell; + u8 passive_dwell; + u8 fragmented_dwell; + u8 extended_dwell; + u8 reserved2; + __le16 rx_chain_select; + __le32 scan_flags; + __le32 max_out_time; + __le32 suspend_time; + /* RX_ON_FLAGS_API_S_VER_1 */ + __le32 flags; + __le32 filter_flags; + struct iwl_scan_req_tx_cmd tx_cmd[2]; + struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; + __le32 scan_prio; + /* SCAN_REQ_PERIODIC_PARAMS_API_S */ + __le32 iter_num; + __le32 delay; + struct iwl_scan_schedule_lmac schedule[IWL_MAX_SCHED_SCAN_PLANS]; + struct iwl_scan_channel_opt channel_opt[2]; + u8 data[]; +} __packed; + +/** + * struct iwl_scan_results_notif - scan results for one channel - + * SCAN_RESULT_NTF_API_S_VER_3 + * @channel: which channel the results are from + * @band: 0 for 5.2 GHz, 1 for 2.4 GHz + * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request + * @num_probe_not_sent: # of request that weren't sent due to not enough time + * @duration: duration spent in channel, in usecs + */ +struct iwl_scan_results_notif { + u8 channel; + u8 band; + u8 probe_status; + u8 num_probe_not_sent; + __le32 duration; +} __packed; + +/** + * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels) + * SCAN_COMPLETE_NTF_API_S_VER_3 + * @scanned_channels: number of channels scanned (and number of valid results) + * @status: one of SCAN_COMP_STATUS_* + * @bt_status: BT on/off status + * @last_channel: last channel that was scanned + * @tsf_low: TSF timer (lower half) in usecs + * @tsf_high: TSF timer (higher half) in usecs + * @results: an array of scan results, only "scanned_channels" of them are valid + */ +struct iwl_lmac_scan_complete_notif { + u8 scanned_channels; + u8 status; + u8 bt_status; + u8 last_channel; + __le32 tsf_low; + __le32 tsf_high; + struct iwl_scan_results_notif results[]; +} __packed; + +/** + * struct iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2 + * @last_schedule_line: last schedule line executed (fast or regular) + * @last_schedule_iteration: last scan iteration executed before scan abort + * @status: &enum iwl_scan_offload_complete_status + * @ebs_status: EBS success status &enum iwl_scan_ebs_status + * @time_after_last_iter: time in seconds elapsed after last iteration + * @reserved: reserved + */ +struct iwl_periodic_scan_complete { + u8 last_schedule_line; + u8 last_schedule_iteration; + u8 status; + u8 ebs_status; + __le32 time_after_last_iter; + __le32 reserved; +} __packed; + +/* UMAC Scan API */ + +/* The maximum of either of these cannot exceed 8, because we use an + * 8-bit mask (see IWL_MVM_SCAN_MASK in mvm.h). + */ +#define IWL_MVM_MAX_UMAC_SCANS 8 +#define IWL_MVM_MAX_LMAC_SCANS 1 + +enum scan_config_flags { + SCAN_CONFIG_FLAG_ACTIVATE = BIT(0), + SCAN_CONFIG_FLAG_DEACTIVATE = BIT(1), + SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = BIT(2), + SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = BIT(3), + SCAN_CONFIG_FLAG_SET_TX_CHAINS = BIT(8), + SCAN_CONFIG_FLAG_SET_RX_CHAINS = BIT(9), + SCAN_CONFIG_FLAG_SET_AUX_STA_ID = BIT(10), + SCAN_CONFIG_FLAG_SET_ALL_TIMES = BIT(11), + SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = BIT(12), + SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = BIT(13), + SCAN_CONFIG_FLAG_SET_LEGACY_RATES = BIT(14), + SCAN_CONFIG_FLAG_SET_MAC_ADDR = BIT(15), + SCAN_CONFIG_FLAG_SET_FRAGMENTED = BIT(16), + SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = BIT(17), + SCAN_CONFIG_FLAG_SET_CAM_MODE = BIT(18), + SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = BIT(19), + SCAN_CONFIG_FLAG_SET_PROMISC_MODE = BIT(20), + SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = BIT(21), + SCAN_CONFIG_FLAG_SET_LMAC2_FRAGMENTED = BIT(22), + SCAN_CONFIG_FLAG_CLEAR_LMAC2_FRAGMENTED = BIT(23), + + /* Bits 26-31 are for num of channels in channel_array */ +#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26) +}; + +enum scan_config_rates { + /* OFDM basic rates */ + SCAN_CONFIG_RATE_6M = BIT(0), + SCAN_CONFIG_RATE_9M = BIT(1), + SCAN_CONFIG_RATE_12M = BIT(2), + SCAN_CONFIG_RATE_18M = BIT(3), + SCAN_CONFIG_RATE_24M = BIT(4), + SCAN_CONFIG_RATE_36M = BIT(5), + SCAN_CONFIG_RATE_48M = BIT(6), + SCAN_CONFIG_RATE_54M = BIT(7), + /* CCK basic rates */ + SCAN_CONFIG_RATE_1M = BIT(8), + SCAN_CONFIG_RATE_2M = BIT(9), + SCAN_CONFIG_RATE_5M = BIT(10), + SCAN_CONFIG_RATE_11M = BIT(11), + + /* Bits 16-27 are for supported rates */ +#define SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16) +}; + +enum iwl_channel_flags { + IWL_CHANNEL_FLAG_EBS = BIT(0), + IWL_CHANNEL_FLAG_ACCURATE_EBS = BIT(1), + IWL_CHANNEL_FLAG_EBS_ADD = BIT(2), + IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = BIT(3), +}; + +/** + * struct iwl_scan_dwell + * @active: default dwell time for active scan + * @passive: default dwell time for passive scan + * @fragmented: default dwell time for fragmented scan + * @extended: default dwell time for channels 1, 6 and 11 + */ +struct iwl_scan_dwell { + u8 active; + u8 passive; + u8 fragmented; + u8 extended; +} __packed; + +/** + * struct iwl_scan_config + * @flags: enum scan_config_flags + * @tx_chains: valid_tx antenna - ANT_* definitions + * @rx_chains: valid_rx antenna - ANT_* definitions + * @legacy_rates: default legacy rates - enum scan_config_rates + * @out_of_channel_time: default max out of serving channel time + * @suspend_time: default max suspend time + * @dwell: dwells for the scan + * @mac_addr: default mac address to be used in probes + * @bcast_sta_id: the index of the station in the fw + * @channel_flags: default channel flags - enum iwl_channel_flags + * scan_config_channel_flag + * @channel_array: default supported channels + */ +struct iwl_scan_config_v1 { + __le32 flags; + __le32 tx_chains; + __le32 rx_chains; + __le32 legacy_rates; + __le32 out_of_channel_time; + __le32 suspend_time; + struct iwl_scan_dwell dwell; + u8 mac_addr[ETH_ALEN]; + u8 bcast_sta_id; + u8 channel_flags; + u8 channel_array[]; +} __packed; /* SCAN_CONFIG_DB_CMD_API_S */ + +#define SCAN_TWO_LMACS 2 + +struct iwl_scan_config { + __le32 flags; + __le32 tx_chains; + __le32 rx_chains; + __le32 legacy_rates; + __le32 out_of_channel_time[SCAN_TWO_LMACS]; + __le32 suspend_time[SCAN_TWO_LMACS]; + struct iwl_scan_dwell dwell; + u8 mac_addr[ETH_ALEN]; + u8 bcast_sta_id; + u8 channel_flags; + u8 channel_array[]; +} __packed; /* SCAN_CONFIG_DB_CMD_API_S_3 */ + +/** + * enum iwl_umac_scan_flags - UMAC scan flags + * @IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request + * can be preempted by other scan requests with higher priority. + * The low priority scan will be resumed when the higher proirity scan is + * completed. + * @IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver + * when scan starts. + */ +enum iwl_umac_scan_flags { + IWL_UMAC_SCAN_FLAG_PREEMPTIVE = BIT(0), + IWL_UMAC_SCAN_FLAG_START_NOTIF = BIT(1), +}; + +enum iwl_umac_scan_uid_offsets { + IWL_UMAC_SCAN_UID_TYPE_OFFSET = 0, + IWL_UMAC_SCAN_UID_SEQ_OFFSET = 8, +}; + +enum iwl_umac_scan_general_flags { + IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC = BIT(0), + IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT = BIT(1), + IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL = BIT(2), + IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE = BIT(3), + IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = BIT(4), + IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = BIT(5), + IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6), + IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7), + IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8), + IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9), + IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = BIT(10), + IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED = BIT(11), +}; + +/** + * struct iwl_scan_channel_cfg_umac + * @flags: bitmap - 0-19: directed scan to i'th ssid. + * @channel_num: channel number 1-13 etc. + * @iter_count: repetition count for the channel. + * @iter_interval: interval between two scan iterations on one channel. + */ +struct iwl_scan_channel_cfg_umac { + __le32 flags; + u8 channel_num; + u8 iter_count; + __le16 iter_interval; +} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */ + +/** + * struct iwl_scan_umac_schedule + * @interval: interval in seconds between scan iterations + * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop + * @reserved: for alignment and future use + */ +struct iwl_scan_umac_schedule { + __le16 interval; + u8 iter_count; + u8 reserved; +} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */ + +/** + * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command + * parameters following channels configuration array. + * @schedule: two scheduling plans. + * @delay: delay in TUs before starting the first scan iteration + * @reserved: for future use and alignment + * @preq: probe request with IEs blocks + * @direct_scan: list of SSIDs for directed active scan + */ +struct iwl_scan_req_umac_tail { + /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */ + struct iwl_scan_umac_schedule schedule[IWL_MAX_SCHED_SCAN_PLANS]; + __le16 delay; + __le16 reserved; + /* SCAN_PROBE_PARAMS_API_S_VER_1 */ + struct iwl_scan_probe_req preq; + struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; +} __packed; + +/** + * struct iwl_scan_req_umac + * @flags: &enum iwl_umac_scan_flags + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @ooc_priority: out of channel priority - &enum iwl_scan_priority + * @general_flags: &enum iwl_umac_scan_general_flags + * @reserved2: for future use and alignment + * @scan_start_mac_id: report the scan start TSF time according to this mac TSF + * @extended_dwell: dwell time for channels 1, 6 and 11 + * @active_dwell: dwell time for active scan + * @passive_dwell: dwell time for passive scan + * @fragmented_dwell: dwell time for fragmented passive scan + * @max_out_time: max out of serving channel time, per LMAC - for CDB there + * are 2 LMACs + * @suspend_time: max suspend time, per LMAC - for CDB there are 2 LMACs + * @scan_priority: scan internal prioritization &enum iwl_scan_priority + * @channel_flags: &enum iwl_scan_channel_flags + * @n_channels: num of channels in scan request + * @reserved: for future use and alignment + * @data: &struct iwl_scan_channel_cfg_umac and + * &struct iwl_scan_req_umac_tail + */ +struct iwl_scan_req_umac { + __le32 flags; + __le32 uid; + __le32 ooc_priority; + /* SCAN_GENERAL_PARAMS_API_S_VER_4 */ + __le16 general_flags; + u8 reserved2; + u8 scan_start_mac_id; + u8 extended_dwell; + u8 active_dwell; + u8 passive_dwell; + u8 fragmented_dwell; + union { + struct { + __le32 max_out_time; + __le32 suspend_time; + __le32 scan_priority; + /* SCAN_CHANNEL_PARAMS_API_S_VER_4 */ + u8 channel_flags; + u8 n_channels; + __le16 reserved; + u8 data[]; + } v1; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ + struct { + __le32 max_out_time[SCAN_TWO_LMACS]; + __le32 suspend_time[SCAN_TWO_LMACS]; + __le32 scan_priority; + /* SCAN_CHANNEL_PARAMS_API_S_VER_4 */ + u8 channel_flags; + u8 n_channels; + __le16 reserved; + u8 data[]; + } v6; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_6 */ + }; +} __packed; + +#define IWL_SCAN_REQ_UMAC_SIZE sizeof(struct iwl_scan_req_umac) +#define IWL_SCAN_REQ_UMAC_SIZE_V1 (sizeof(struct iwl_scan_req_umac) - \ + 2 * sizeof(__le32)) + +/** + * struct iwl_umac_scan_abort + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @flags: reserved + */ +struct iwl_umac_scan_abort { + __le32 uid; + __le32 flags; +} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */ + +/** + * struct iwl_umac_scan_complete + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @last_schedule: last scheduling line + * @last_iter: last scan iteration number + * @status: &enum iwl_scan_offload_complete_status + * @ebs_status: &enum iwl_scan_ebs_status + * @time_from_last_iter: time elapsed from last iteration + * @reserved: for future use + */ +struct iwl_umac_scan_complete { + __le32 uid; + u8 last_schedule; + u8 last_iter; + u8 status; + u8 ebs_status; + __le32 time_from_last_iter; + __le32 reserved; +} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */ + +#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5 +/** + * struct iwl_scan_offload_profile_match - match information + * @bssid: matched bssid + * @reserved: reserved + * @channel: channel where the match occurred + * @energy: energy + * @matching_feature: feature matches + * @matching_channels: bitmap of channels that matched, referencing + * the channels passed in tue scan offload request + */ +struct iwl_scan_offload_profile_match { + u8 bssid[ETH_ALEN]; + __le16 reserved; + u8 channel; + u8 energy; + u8 matching_feature; + u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN]; +} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */ + +/** + * struct iwl_scan_offload_profiles_query - match results query response + * @matched_profiles: bitmap of matched profiles, referencing the + * matches passed in the scan offload request + * @last_scan_age: age of the last offloaded scan + * @n_scans_done: number of offloaded scans done + * @gp2_d0u: GP2 when D0U occurred + * @gp2_invoked: GP2 when scan offload was invoked + * @resume_while_scanning: not used + * @self_recovery: obsolete + * @reserved: reserved + * @matches: array of match information, one for each match + */ +struct iwl_scan_offload_profiles_query { + __le32 matched_profiles; + __le32 last_scan_age; + __le32 n_scans_done; + __le32 gp2_d0u; + __le32 gp2_invoked; + u8 resume_while_scanning; + u8 self_recovery; + __le16 reserved; + struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; +} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */ + +/** + * struct iwl_umac_scan_iter_complete_notif - notifies end of scanning iteration + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @scanned_channels: number of channels scanned and number of valid elements in + * results array + * @status: one of SCAN_COMP_STATUS_* + * @bt_status: BT on/off status + * @last_channel: last channel that was scanned + * @start_tsf: TSF timer in usecs of the scan start time for the mac specified + * in &struct iwl_scan_req_umac. + * @results: array of scan results, length in @scanned_channels + */ +struct iwl_umac_scan_iter_complete_notif { + __le32 uid; + u8 scanned_channels; + u8 status; + u8 bt_status; + u8 last_channel; + __le64 start_tsf; + struct iwl_scan_results_notif results[]; +} __packed; /* SCAN_ITER_COMPLETE_NTF_UMAC_API_S_VER_2 */ + +#endif /* __iwl_fw_api_scan_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sf.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sf.h new file mode 100644 index 000000000000..e517b55f1bc6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sf.h @@ -0,0 +1,138 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_sf_h__ +#define __iwl_fw_api_sf_h__ + +/* Smart Fifo state */ +enum iwl_sf_state { + SF_LONG_DELAY_ON = 0, /* should never be called by driver */ + SF_FULL_ON, + SF_UNINIT, + SF_INIT_OFF, + SF_HW_NUM_STATES +}; + +/* Smart Fifo possible scenario */ +enum iwl_sf_scenario { + SF_SCENARIO_SINGLE_UNICAST, + SF_SCENARIO_AGG_UNICAST, + SF_SCENARIO_MULTICAST, + SF_SCENARIO_BA_RESP, + SF_SCENARIO_TX_RESP, + SF_NUM_SCENARIO +}; + +#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */ +#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */ + +/* smart FIFO default values */ +#define SF_W_MARK_SISO 6144 +#define SF_W_MARK_MIMO2 8192 +#define SF_W_MARK_MIMO3 6144 +#define SF_W_MARK_LEGACY 4096 +#define SF_W_MARK_SCAN 4096 + +/* SF Scenarios timers for default configuration (aligned to 32 uSec) */ +#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */ +#define SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */ + +/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */ +#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ +#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ +#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */ +#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */ +#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */ +#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */ +#define SF_BA_IDLE_TIMER 320 /* 300 uSec */ +#define SF_BA_AGING_TIMER 2016 /* 2 mSec */ +#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */ +#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */ + +#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */ + +#define SF_CFG_DUMMY_NOTIF_OFF BIT(16) + +/** + * struct iwl_sf_cfg_cmd - Smart Fifo configuration command. + * @state: smart fifo state, types listed in &enum iwl_sf_state. + * @watermark: Minimum allowed available free space in RXF for transient state. + * @long_delay_timeouts: aging and idle timer values for each scenario + * in long delay state. + * @full_on_timeouts: timer values for each scenario in full on state. + */ +struct iwl_sf_cfg_cmd { + __le32 state; + __le32 watermark[SF_TRANSIENT_STATES_NUMBER]; + __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; + __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; +} __packed; /* SF_CFG_API_S_VER_2 */ + +#endif /* __iwl_fw_api_sf_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h new file mode 100644 index 000000000000..af369eba3795 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h @@ -0,0 +1,573 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_fw_api_sta_h__ +#define __iwl_fw_api_sta_h__ + +/** + * enum iwl_sta_flags - flags for the ADD_STA host command + * @STA_FLG_REDUCED_TX_PWR_CTRL: reduced TX power (control frames) + * @STA_FLG_REDUCED_TX_PWR_DATA: reduced TX power (data frames) + * @STA_FLG_DISABLE_TX: set if TX should be disabled + * @STA_FLG_PS: set if STA is in Power Save + * @STA_FLG_INVALID: set if STA is invalid + * @STA_FLG_DLP_EN: Direct Link Protocol is enabled + * @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs + * @STA_FLG_DRAIN_FLOW: drain flow + * @STA_FLG_PAN: STA is for PAN interface + * @STA_FLG_CLASS_AUTH: station is authenticated + * @STA_FLG_CLASS_ASSOC: station is associated + * @STA_FLG_RTS_MIMO_PROT: station requires RTS MIMO protection (dynamic SMPS) + * @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU (mask) + * @STA_FLG_MAX_AGG_SIZE_SHIFT: maximal size for A-MPDU (bit shift) + * @STA_FLG_MAX_AGG_SIZE_8K: maximal size for A-MPDU (8k supported) + * @STA_FLG_MAX_AGG_SIZE_16K: maximal size for A-MPDU (16k supported) + * @STA_FLG_MAX_AGG_SIZE_32K: maximal size for A-MPDU (32k supported) + * @STA_FLG_MAX_AGG_SIZE_64K: maximal size for A-MPDU (64k supported) + * @STA_FLG_MAX_AGG_SIZE_128K: maximal size for A-MPDU (128k supported) + * @STA_FLG_MAX_AGG_SIZE_256K: maximal size for A-MPDU (256k supported) + * @STA_FLG_MAX_AGG_SIZE_512K: maximal size for A-MPDU (512k supported) + * @STA_FLG_MAX_AGG_SIZE_1024K: maximal size for A-MPDU (1024k supported) + * @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation + * @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is + * initialised by driver and can be updated by fw upon reception of + * action frames that can change the channel width. When cleared the fw + * will send all the frames in 20MHz even when FAT channel is requested. + * @STA_FLG_FAT_EN_20MHZ: no wide channels are supported, only 20 MHz + * @STA_FLG_FAT_EN_40MHZ: wide channels up to 40 MHz supported + * @STA_FLG_FAT_EN_80MHZ: wide channels up to 80 MHz supported + * @STA_FLG_FAT_EN_160MHZ: wide channels up to 160 MHz supported + * @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the + * driver and can be updated by fw upon reception of action frames. + * @STA_FLG_MIMO_EN_SISO: no support for MIMO + * @STA_FLG_MIMO_EN_MIMO2: 2 streams supported + * @STA_FLG_MIMO_EN_MIMO3: 3 streams supported + * @STA_FLG_MFP_EN: Management Frame Protection + * @STA_FLG_AGG_MPDU_DENS_MSK: A-MPDU density (mask) + * @STA_FLG_AGG_MPDU_DENS_SHIFT: A-MPDU density (bit shift) + * @STA_FLG_AGG_MPDU_DENS_2US: A-MPDU density (2 usec gap) + * @STA_FLG_AGG_MPDU_DENS_4US: A-MPDU density (4 usec gap) + * @STA_FLG_AGG_MPDU_DENS_8US: A-MPDU density (8 usec gap) + * @STA_FLG_AGG_MPDU_DENS_16US: A-MPDU density (16 usec gap) + */ +enum iwl_sta_flags { + STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3), + STA_FLG_REDUCED_TX_PWR_DATA = BIT(6), + + STA_FLG_DISABLE_TX = BIT(4), + + STA_FLG_PS = BIT(8), + STA_FLG_DRAIN_FLOW = BIT(12), + STA_FLG_PAN = BIT(13), + STA_FLG_CLASS_AUTH = BIT(14), + STA_FLG_CLASS_ASSOC = BIT(15), + STA_FLG_RTS_MIMO_PROT = BIT(17), + + STA_FLG_MAX_AGG_SIZE_SHIFT = 19, + STA_FLG_MAX_AGG_SIZE_8K = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_16K = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_32K = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_64K = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_128K = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_256K = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_512K = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_1024K = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_MSK = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), + + STA_FLG_AGG_MPDU_DENS_SHIFT = 23, + STA_FLG_AGG_MPDU_DENS_2US = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT), + STA_FLG_AGG_MPDU_DENS_4US = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT), + STA_FLG_AGG_MPDU_DENS_8US = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT), + STA_FLG_AGG_MPDU_DENS_16US = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), + STA_FLG_AGG_MPDU_DENS_MSK = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), + + STA_FLG_FAT_EN_20MHZ = (0 << 26), + STA_FLG_FAT_EN_40MHZ = (1 << 26), + STA_FLG_FAT_EN_80MHZ = (2 << 26), + STA_FLG_FAT_EN_160MHZ = (3 << 26), + STA_FLG_FAT_EN_MSK = (3 << 26), + + STA_FLG_MIMO_EN_SISO = (0 << 28), + STA_FLG_MIMO_EN_MIMO2 = (1 << 28), + STA_FLG_MIMO_EN_MIMO3 = (2 << 28), + STA_FLG_MIMO_EN_MSK = (3 << 28), +}; + +/** + * enum iwl_sta_key_flag - key flags for the ADD_STA host command + * @STA_KEY_FLG_NO_ENC: no encryption + * @STA_KEY_FLG_WEP: WEP encryption algorithm + * @STA_KEY_FLG_CCM: CCMP encryption algorithm + * @STA_KEY_FLG_TKIP: TKIP encryption algorithm + * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support) + * @STA_KEY_FLG_GCMP: GCMP encryption algorithm + * @STA_KEY_FLG_CMAC: CMAC encryption algorithm + * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm + * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value + * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from + * station info array (1 - n 1X mode) + * @STA_KEY_FLG_KEYID_MSK: the index of the key + * @STA_KEY_FLG_KEYID_POS: key index bit position + * @STA_KEY_NOT_VALID: key is invalid + * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key + * @STA_KEY_FLG_KEY_32BYTES: for non-wep key set for 32 bytes key + * @STA_KEY_MULTICAST: set for multical key + * @STA_KEY_MFP: key is used for Management Frame Protection + */ +enum iwl_sta_key_flag { + STA_KEY_FLG_NO_ENC = (0 << 0), + STA_KEY_FLG_WEP = (1 << 0), + STA_KEY_FLG_CCM = (2 << 0), + STA_KEY_FLG_TKIP = (3 << 0), + STA_KEY_FLG_EXT = (4 << 0), + STA_KEY_FLG_GCMP = (5 << 0), + STA_KEY_FLG_CMAC = (6 << 0), + STA_KEY_FLG_ENC_UNKNOWN = (7 << 0), + STA_KEY_FLG_EN_MSK = (7 << 0), + + STA_KEY_FLG_WEP_KEY_MAP = BIT(3), + STA_KEY_FLG_KEYID_POS = 8, + STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS), + STA_KEY_NOT_VALID = BIT(11), + STA_KEY_FLG_WEP_13BYTES = BIT(12), + STA_KEY_FLG_KEY_32BYTES = BIT(12), + STA_KEY_MULTICAST = BIT(14), + STA_KEY_MFP = BIT(15), +}; + +/** + * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed + * @STA_MODIFY_QUEUE_REMOVAL: this command removes a queue + * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx + * @STA_MODIFY_UAPSD_ACS: this command modifies %uapsd_acs + * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid + * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid + * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count + * @STA_MODIFY_PROT_TH: modify RTS threshold + * @STA_MODIFY_QUEUES: modify the queues used by this station + */ +enum iwl_sta_modify_flag { + STA_MODIFY_QUEUE_REMOVAL = BIT(0), + STA_MODIFY_TID_DISABLE_TX = BIT(1), + STA_MODIFY_UAPSD_ACS = BIT(2), + STA_MODIFY_ADD_BA_TID = BIT(3), + STA_MODIFY_REMOVE_BA_TID = BIT(4), + STA_MODIFY_SLEEPING_STA_TX_COUNT = BIT(5), + STA_MODIFY_PROT_TH = BIT(6), + STA_MODIFY_QUEUES = BIT(7), +}; + +/** + * enum iwl_sta_mode - station command mode + * @STA_MODE_ADD: add new station + * @STA_MODE_MODIFY: modify the station + */ +enum iwl_sta_mode { + STA_MODE_ADD = 0, + STA_MODE_MODIFY = 1, +}; + +/** + * enum iwl_sta_sleep_flag - type of sleep of the station + * @STA_SLEEP_STATE_AWAKE: station is awake + * @STA_SLEEP_STATE_PS_POLL: station is PS-polling + * @STA_SLEEP_STATE_UAPSD: station uses U-APSD + * @STA_SLEEP_STATE_MOREDATA: set more-data bit on + * (last) released frame + */ +enum iwl_sta_sleep_flag { + STA_SLEEP_STATE_AWAKE = 0, + STA_SLEEP_STATE_PS_POLL = BIT(0), + STA_SLEEP_STATE_UAPSD = BIT(1), + STA_SLEEP_STATE_MOREDATA = BIT(2), +}; + +#define STA_KEY_MAX_NUM (16) +#define STA_KEY_IDX_INVALID (0xff) +#define STA_KEY_MAX_DATA_KEY_NUM (4) +#define IWL_MAX_GLOBAL_KEYS (4) +#define STA_KEY_LEN_WEP40 (5) +#define STA_KEY_LEN_WEP104 (13) + +/** + * struct iwl_mvm_keyinfo - key information + * @key_flags: type &enum iwl_sta_key_flag + * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection + * @reserved1: reserved + * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx + * @key_offset: key offset in the fw's key table + * @reserved2: reserved + * @key: 16-byte unicast decryption key + * @tx_secur_seq_cnt: initial RSC / PN needed for replay check + * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only + * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only + */ +struct iwl_mvm_keyinfo { + __le16 key_flags; + u8 tkip_rx_tsc_byte2; + u8 reserved1; + __le16 tkip_rx_ttak[5]; + u8 key_offset; + u8 reserved2; + u8 key[16]; + __le64 tx_secur_seq_cnt; + __le64 hw_tkip_mic_rx_key; + __le64 hw_tkip_mic_tx_key; +} __packed; + +#define IWL_ADD_STA_STATUS_MASK 0xFF +#define IWL_ADD_STA_BAID_VALID_MASK 0x8000 +#define IWL_ADD_STA_BAID_MASK 0x7F00 +#define IWL_ADD_STA_BAID_SHIFT 8 + +/** + * struct iwl_mvm_add_sta_cmd_v7 - Add/modify a station in the fw's sta table. + * ( REPLY_ADD_STA = 0x18 ) + * @add_modify: see &enum iwl_sta_mode + * @awake_acs: ACs to transmit data on while station is sleeping (for U-APSD) + * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable + * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. + * @mac_id_n_color: the Mac context this station belongs to, + * see &enum iwl_ctxt_id_and_color + * @addr: station's MAC address + * @reserved2: reserved + * @sta_id: index of station in uCode's station table + * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave + * alone. 1 - modify, 0 - don't change. + * @reserved3: reserved + * @station_flags: look at &enum iwl_sta_flags + * @station_flags_msk: what of %station_flags have changed, + * also &enum iwl_sta_flags + * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) + * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set + * add_immediate_ba_ssn. + * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx) + * Set %STA_MODIFY_REMOVE_BA_TID to use this field + * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with + * add_immediate_ba_tid. + * @sleep_tx_count: number of packets to transmit to station even though it is + * asleep. Used to synchronise PS-poll and u-APSD responses while ucode + * keeps track of STA sleep state. + * @sleep_state_flags: Look at &enum iwl_sta_sleep_flag. + * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP + * mac-addr. + * @beamform_flags: beam forming controls + * @tfd_queue_msk: tfd queues used by this station + * + * The device contains an internal table of per-station information, with info + * on security keys, aggregation parameters, and Tx rates for initial Tx + * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD). + * + * ADD_STA sets up the table entry for one station, either creating a new + * entry, or modifying a pre-existing one. + */ +struct iwl_mvm_add_sta_cmd_v7 { + u8 add_modify; + u8 awake_acs; + __le16 tid_disable_tx; + __le32 mac_id_n_color; + u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ + __le16 reserved2; + u8 sta_id; + u8 modify_mask; + __le16 reserved3; + __le32 station_flags; + __le32 station_flags_msk; + u8 add_immediate_ba_tid; + u8 remove_immediate_ba_tid; + __le16 add_immediate_ba_ssn; + __le16 sleep_tx_count; + __le16 sleep_state_flags; + __le16 assoc_id; + __le16 beamform_flags; + __le32 tfd_queue_msk; +} __packed; /* ADD_STA_CMD_API_S_VER_7 */ + +/** + * enum iwl_sta_type - FW station types + * ( REPLY_ADD_STA = 0x18 ) + * @IWL_STA_LINK: Link station - normal RX and TX traffic. + * @IWL_STA_GENERAL_PURPOSE: General purpose. In AP mode used for beacons + * and probe responses. + * @IWL_STA_MULTICAST: multicast traffic, + * @IWL_STA_TDLS_LINK: TDLS link station + * @IWL_STA_AUX_ACTIVITY: auxilary station (scan, ROC and so on). + */ +enum iwl_sta_type { + IWL_STA_LINK, + IWL_STA_GENERAL_PURPOSE, + IWL_STA_MULTICAST, + IWL_STA_TDLS_LINK, + IWL_STA_AUX_ACTIVITY, +}; + +/** + * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table. + * ( REPLY_ADD_STA = 0x18 ) + * @add_modify: see &enum iwl_sta_mode + * @awake_acs: ACs to transmit data on while station is sleeping (for U-APSD) + * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable + * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. + * @mac_id_n_color: the Mac context this station belongs to, + * see &enum iwl_ctxt_id_and_color + * @addr: station's MAC address + * @reserved2: reserved + * @sta_id: index of station in uCode's station table + * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave + * alone. 1 - modify, 0 - don't change. + * @reserved3: reserved + * @station_flags: look at &enum iwl_sta_flags + * @station_flags_msk: what of %station_flags have changed, + * also &enum iwl_sta_flags + * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) + * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set + * add_immediate_ba_ssn. + * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx) + * Set %STA_MODIFY_REMOVE_BA_TID to use this field + * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with + * add_immediate_ba_tid. + * @sleep_tx_count: number of packets to transmit to station even though it is + * asleep. Used to synchronise PS-poll and u-APSD responses while ucode + * keeps track of STA sleep state. + * @station_type: type of this station. See &enum iwl_sta_type. + * @sleep_state_flags: Look at &enum iwl_sta_sleep_flag. + * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP + * mac-addr. + * @beamform_flags: beam forming controls + * @tfd_queue_msk: tfd queues used by this station. + * Obselete for new TX API (9 and above). + * @rx_ba_window: aggregation window size + * @sp_length: the size of the SP as it appears in the WME IE + * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver + * enabled ACs. + * + * The device contains an internal table of per-station information, with info + * on security keys, aggregation parameters, and Tx rates for initial Tx + * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD). + * + * ADD_STA sets up the table entry for one station, either creating a new + * entry, or modifying a pre-existing one. + */ +struct iwl_mvm_add_sta_cmd { + u8 add_modify; + u8 awake_acs; + __le16 tid_disable_tx; + __le32 mac_id_n_color; + u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ + __le16 reserved2; + u8 sta_id; + u8 modify_mask; + __le16 reserved3; + __le32 station_flags; + __le32 station_flags_msk; + u8 add_immediate_ba_tid; + u8 remove_immediate_ba_tid; + __le16 add_immediate_ba_ssn; + __le16 sleep_tx_count; + u8 sleep_state_flags; + u8 station_type; + __le16 assoc_id; + __le16 beamform_flags; + __le32 tfd_queue_msk; + __le16 rx_ba_window; + u8 sp_length; + u8 uapsd_acs; +} __packed; /* ADD_STA_CMD_API_S_VER_10 */ + +/** + * struct iwl_mvm_add_sta_key_common - add/modify sta key common part + * ( REPLY_ADD_STA_KEY = 0x17 ) + * @sta_id: index of station in uCode's station table + * @key_offset: key offset in key storage + * @key_flags: type &enum iwl_sta_key_flag + * @key: key material data + * @rx_secur_seq_cnt: RX security sequence counter for the key + */ +struct iwl_mvm_add_sta_key_common { + u8 sta_id; + u8 key_offset; + __le16 key_flags; + u8 key[32]; + u8 rx_secur_seq_cnt[16]; +} __packed; + +/** + * struct iwl_mvm_add_sta_key_cmd_v1 - add/modify sta key + * @common: see &struct iwl_mvm_add_sta_key_common + * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection + * @reserved: reserved + * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx + */ +struct iwl_mvm_add_sta_key_cmd_v1 { + struct iwl_mvm_add_sta_key_common common; + u8 tkip_rx_tsc_byte2; + u8 reserved; + __le16 tkip_rx_ttak[5]; +} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */ + +/** + * struct iwl_mvm_add_sta_key_cmd - add/modify sta key + * @common: see &struct iwl_mvm_add_sta_key_common + * @rx_mic_key: TKIP RX unicast or multicast key + * @tx_mic_key: TKIP TX key + * @transmit_seq_cnt: TSC, transmit packet number + */ +struct iwl_mvm_add_sta_key_cmd { + struct iwl_mvm_add_sta_key_common common; + __le64 rx_mic_key; + __le64 tx_mic_key; + __le64 transmit_seq_cnt; +} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_2 */ + +/** + * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command + * @ADD_STA_SUCCESS: operation was executed successfully + * @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table + * @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session + * @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that + * doesn't exist. + */ +enum iwl_mvm_add_sta_rsp_status { + ADD_STA_SUCCESS = 0x1, + ADD_STA_STATIONS_OVERLOAD = 0x2, + ADD_STA_IMMEDIATE_BA_FAILURE = 0x4, + ADD_STA_MODIFY_NON_EXISTING_STA = 0x8, +}; + +/** + * struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table + * ( REMOVE_STA = 0x19 ) + * @sta_id: the station id of the station to be removed + * @reserved: reserved + */ +struct iwl_mvm_rm_sta_cmd { + u8 sta_id; + u8 reserved[3]; +} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */ + +/** + * struct iwl_mvm_mgmt_mcast_key_cmd_v1 + * ( MGMT_MCAST_KEY = 0x1f ) + * @ctrl_flags: &enum iwl_sta_key_flag + * @igtk: IGTK key material + * @k1: unused + * @k2: unused + * @sta_id: station ID that support IGTK + * @key_id: key ID + * @receive_seq_cnt: initial RSC/PN needed for replay check + */ +struct iwl_mvm_mgmt_mcast_key_cmd_v1 { + __le32 ctrl_flags; + u8 igtk[16]; + u8 k1[16]; + u8 k2[16]; + __le32 key_id; + __le32 sta_id; + __le64 receive_seq_cnt; +} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */ + +/** + * struct iwl_mvm_mgmt_mcast_key_cmd + * ( MGMT_MCAST_KEY = 0x1f ) + * @ctrl_flags: &enum iwl_sta_key_flag + * @igtk: IGTK master key + * @sta_id: station ID that support IGTK + * @key_id: key ID + * @receive_seq_cnt: initial RSC/PN needed for replay check + */ +struct iwl_mvm_mgmt_mcast_key_cmd { + __le32 ctrl_flags; + u8 igtk[32]; + __le32 key_id; + __le32 sta_id; + __le64 receive_seq_cnt; +} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_2 */ + +struct iwl_mvm_wep_key { + u8 key_index; + u8 key_offset; + __le16 reserved1; + u8 key_size; + u8 reserved2[3]; + u8 key[16]; +} __packed; + +struct iwl_mvm_wep_key_cmd { + __le32 mac_id_n_color; + u8 num_keys; + u8 decryption_type; + u8 flags; + u8 reserved; + struct iwl_mvm_wep_key wep_key[0]; +} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ + +/** + * struct iwl_mvm_eosp_notification - EOSP notification from firmware + * @remain_frame_count: # of frames remaining, non-zero if SP was cut + * short by GO absence + * @sta_id: station ID + */ +struct iwl_mvm_eosp_notification { + __le32 remain_frame_count; + __le32 sta_id; +} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */ + +#endif /* __iwl_fw_api_sta_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h new file mode 100644 index 000000000000..53cb622aa9ab --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h @@ -0,0 +1,474 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_stats_h__ +#define __iwl_fw_api_stats_h__ +#include "mac.h" + +struct mvm_statistics_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + u8 reserved[12]; +} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ + +struct mvm_statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 rssi_ant; + __le32 reserved2; +} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ + +/** + * struct mvm_statistics_rx_non_phy + * @bogus_cts: CTS received when not expecting CTS + * @bogus_ack: ACK received when not expecting ACK + * @non_channel_beacons: beacons with our bss id but not on our serving channel + * @channel_beacons: beacons with our bss id and in our serving channel + * @num_missed_bcon: number of missed beacons + * @adc_rx_saturation_time: count in 0.8us units the time the ADC was in + * saturation + * @ina_detection_search_time: total time (in 0.8us) searched for INA + * @beacon_silence_rssi_a: RSSI silence after beacon frame + * @beacon_silence_rssi_b: RSSI silence after beacon frame + * @beacon_silence_rssi_c: RSSI silence after beacon frame + * @interference_data_flag: flag for interference data availability. 1 when data + * is available. + * @channel_load: counts RX Enable time in uSec + * @beacon_rssi_a: beacon RSSI on anntena A + * @beacon_rssi_b: beacon RSSI on antenna B + * @beacon_rssi_c: beacon RSSI on antenna C + * @beacon_energy_a: beacon energy on antenna A + * @beacon_energy_b: beacon energy on antenna B + * @beacon_energy_c: beacon energy on antenna C + * @num_bt_kills: number of BT "kills" (frame TX aborts) + * @mac_id: mac ID + */ +struct mvm_statistics_rx_non_phy { + __le32 bogus_cts; + __le32 bogus_ack; + __le32 non_channel_beacons; + __le32 channel_beacons; + __le32 num_missed_bcon; + __le32 adc_rx_saturation_time; + __le32 ina_detection_search_time; + __le32 beacon_silence_rssi_a; + __le32 beacon_silence_rssi_b; + __le32 beacon_silence_rssi_c; + __le32 interference_data_flag; + __le32 channel_load; + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; + __le32 num_bt_kills; + __le32 mac_id; +} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_4 */ + +struct mvm_statistics_rx_non_phy_v3 { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time;/* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; + __le32 num_bt_kills; + __le32 mac_id; + __le32 directed_data_mpdu; +} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ + +struct mvm_statistics_rx_phy { + __le32 unresponded_rts; + __le32 rxe_frame_lmt_overrun; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 reserved; +} __packed; /* STATISTICS_RX_PHY_API_S_VER_3 */ + +struct mvm_statistics_rx_phy_v2 { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_lmt_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved; +} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ + +struct mvm_statistics_rx_ht_phy_v1 { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ + +struct mvm_statistics_rx_ht_phy { + __le32 mh_format_err; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_2 */ + +struct mvm_statistics_tx_non_phy_v3 { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; +} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_3 */ + +struct mvm_statistics_tx_non_phy { + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; +} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_4 */ + +#define MAX_CHAINS 3 + +struct mvm_statistics_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; + __s8 txpower[MAX_CHAINS]; + __s8 reserved; + __le32 reserved2; +} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ + +struct mvm_statistics_tx_channel_width { + __le32 ext_cca_narrow_ch20[1]; + __le32 ext_cca_narrow_ch40[2]; + __le32 ext_cca_narrow_ch80[3]; + __le32 ext_cca_narrow_ch160[4]; + __le32 last_tx_ch_width_indx; + __le32 rx_detected_per_ch_width[4]; + __le32 success_per_ch_width[4]; + __le32 fail_per_ch_width[4]; +}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ + +struct mvm_statistics_tx_v4 { + struct mvm_statistics_tx_non_phy_v3 general; + struct mvm_statistics_tx_non_phy_agg agg; + struct mvm_statistics_tx_channel_width channel_width; +} __packed; /* STATISTICS_TX_API_S_VER_4 */ + +struct mvm_statistics_tx { + struct mvm_statistics_tx_non_phy general; + struct mvm_statistics_tx_non_phy_agg agg; + struct mvm_statistics_tx_channel_width channel_width; +} __packed; /* STATISTICS_TX_API_S_VER_5 */ + + +struct mvm_statistics_bt_activity { + __le32 hi_priority_tx_req_cnt; + __le32 hi_priority_tx_denied_cnt; + __le32 lo_priority_tx_req_cnt; + __le32 lo_priority_tx_denied_cnt; + __le32 hi_priority_rx_req_cnt; + __le32 hi_priority_rx_denied_cnt; + __le32 lo_priority_rx_req_cnt; + __le32 lo_priority_rx_denied_cnt; +} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ + +struct mvm_statistics_general_common_v19 { + __le32 radio_temperature; + __le32 radio_voltage; + struct mvm_statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct mvm_statistics_div slow_div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; + __le32 beacon_filtered; + __le32 missed_beacons; + u8 beacon_filter_average_energy; + u8 beacon_filter_reason; + u8 beacon_filter_current_energy; + u8 beacon_filter_reserved; + __le32 beacon_filter_delta_time; + struct mvm_statistics_bt_activity bt_activity; + __le64 rx_time; + __le64 on_time_rf; + __le64 on_time_scan; + __le64 tx_time; +} __packed; + +struct mvm_statistics_general_common { + __le32 radio_temperature; + struct mvm_statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct mvm_statistics_div slow_div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; + __le32 beacon_filtered; + __le32 missed_beacons; + u8 beacon_filter_average_energy; + u8 beacon_filter_reason; + u8 beacon_filter_current_energy; + u8 beacon_filter_reserved; + __le32 beacon_filter_delta_time; + struct mvm_statistics_bt_activity bt_activity; + __le64 rx_time; + __le64 on_time_rf; + __le64 on_time_scan; + __le64 tx_time; +} __packed; /* STATISTICS_GENERAL_API_S_VER_10 */ + +struct mvm_statistics_general_v8 { + struct mvm_statistics_general_common_v19 common; + __le32 beacon_counter[NUM_MAC_INDEX]; + u8 beacon_average_energy[NUM_MAC_INDEX]; + u8 reserved[4 - (NUM_MAC_INDEX % 4)]; +} __packed; /* STATISTICS_GENERAL_API_S_VER_8 */ + +struct mvm_statistics_general_cdb_v9 { + struct mvm_statistics_general_common_v19 common; + __le32 beacon_counter[NUM_MAC_INDEX_CDB]; + u8 beacon_average_energy[NUM_MAC_INDEX_CDB]; + u8 reserved[4 - (NUM_MAC_INDEX_CDB % 4)]; +} __packed; /* STATISTICS_GENERAL_API_S_VER_9 */ + +struct mvm_statistics_general_cdb { + struct mvm_statistics_general_common common; + __le32 beacon_counter[MAC_INDEX_AUX]; + u8 beacon_average_energy[MAC_INDEX_AUX]; + u8 reserved[8 - MAC_INDEX_AUX]; +} __packed; /* STATISTICS_GENERAL_API_S_VER_10 */ + +/** + * struct mvm_statistics_load - RX statistics for multi-queue devices + * @air_time: accumulated air time, per mac + * @byte_count: accumulated byte count, per mac + * @pkt_count: accumulated packet count, per mac + * @avg_energy: average RSSI, per station + */ +struct mvm_statistics_load { + __le32 air_time[MAC_INDEX_AUX]; + __le32 byte_count[MAC_INDEX_AUX]; + __le32 pkt_count[MAC_INDEX_AUX]; + u8 avg_energy[IWL_MVM_STATION_COUNT]; +} __packed; /* STATISTICS_RX_MAC_STATION_S_VER_3 */ + +struct mvm_statistics_load_v1 { + __le32 air_time[NUM_MAC_INDEX]; + __le32 byte_count[NUM_MAC_INDEX]; + __le32 pkt_count[NUM_MAC_INDEX]; + u8 avg_energy[IWL_MVM_STATION_COUNT]; +} __packed; /* STATISTICS_RX_MAC_STATION_S_VER_1 */ + +struct mvm_statistics_rx { + struct mvm_statistics_rx_phy ofdm; + struct mvm_statistics_rx_phy cck; + struct mvm_statistics_rx_non_phy general; + struct mvm_statistics_rx_ht_phy ofdm_ht; +} __packed; /* STATISTICS_RX_API_S_VER_4 */ + +struct mvm_statistics_rx_v3 { + struct mvm_statistics_rx_phy_v2 ofdm; + struct mvm_statistics_rx_phy_v2 cck; + struct mvm_statistics_rx_non_phy_v3 general; + struct mvm_statistics_rx_ht_phy_v1 ofdm_ht; +} __packed; /* STATISTICS_RX_API_S_VER_3 */ + +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * STATISTICS_CMD (0x9c), below. + */ + +struct iwl_notif_statistics_v10 { + __le32 flag; + struct mvm_statistics_rx_v3 rx; + struct mvm_statistics_tx_v4 tx; + struct mvm_statistics_general_v8 general; +} __packed; /* STATISTICS_NTFY_API_S_VER_10 */ + +struct iwl_notif_statistics_v11 { + __le32 flag; + struct mvm_statistics_rx_v3 rx; + struct mvm_statistics_tx_v4 tx; + struct mvm_statistics_general_v8 general; + struct mvm_statistics_load_v1 load_stats; +} __packed; /* STATISTICS_NTFY_API_S_VER_11 */ + +struct iwl_notif_statistics_cdb { + __le32 flag; + struct mvm_statistics_rx rx; + struct mvm_statistics_tx tx; + struct mvm_statistics_general_cdb general; + struct mvm_statistics_load load_stats; +} __packed; /* STATISTICS_NTFY_API_S_VER_13 */ + +/** + * enum iwl_statistics_notif_flags - flags used in statistics notification + * @IWL_STATISTICS_REPLY_FLG_CLEAR: statistics were cleared after this report + */ +enum iwl_statistics_notif_flags { + IWL_STATISTICS_REPLY_FLG_CLEAR = 0x1, +}; + +/** + * enum iwl_statistics_cmd_flags - flags used in statistics command + * @IWL_STATISTICS_FLG_CLEAR: request to clear statistics after the report + * that's sent after this command + * @IWL_STATISTICS_FLG_DISABLE_NOTIF: disable unilateral statistics + * notifications + */ +enum iwl_statistics_cmd_flags { + IWL_STATISTICS_FLG_CLEAR = 0x1, + IWL_STATISTICS_FLG_DISABLE_NOTIF = 0x2, +}; + +/** + * struct iwl_statistics_cmd - statistics config command + * @flags: flags from &enum iwl_statistics_cmd_flags + */ +struct iwl_statistics_cmd { + __le32 flags; +} __packed; /* STATISTICS_CMD_API_S_VER_1 */ + +#endif /* __iwl_fw_api_stats_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h new file mode 100644 index 000000000000..7c6c2462d0e8 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h @@ -0,0 +1,208 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_tdls_h__ +#define __iwl_fw_api_tdls_h__ + +#include "fw/api/tx.h" +#include "fw/api/phy-ctxt.h" + +#define IWL_MVM_TDLS_STA_COUNT 4 + +/* Type of TDLS request */ +enum iwl_tdls_channel_switch_type { + TDLS_SEND_CHAN_SW_REQ = 0, + TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH, + TDLS_MOVE_CH, +}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */ + +/** + * struct iwl_tdls_channel_switch_timing - Switch timing in TDLS channel-switch + * @frame_timestamp: GP2 timestamp of channel-switch request/response packet + * received from peer + * @max_offchan_duration: What amount of microseconds out of a DTIM is given + * to the TDLS off-channel communication. For instance if the DTIM is + * 200TU and the TDLS peer is to be given 25% of the time, the value + * given will be 50TU, or 50 * 1024 if translated into microseconds. + * @switch_time: switch time the peer sent in its channel switch timing IE + * @switch_timeout: switch timeout the peer sent in its channel switch timing IE + */ +struct iwl_tdls_channel_switch_timing { + __le32 frame_timestamp; /* GP2 time of peer packet Rx */ + __le32 max_offchan_duration; /* given in micro-seconds */ + __le32 switch_time; /* given in micro-seconds */ + __le32 switch_timeout; /* given in micro-seconds */ +} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */ + +#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200 + +/** + * struct iwl_tdls_channel_switch_frame - TDLS channel switch frame template + * + * A template representing a TDLS channel-switch request or response frame + * + * @switch_time_offset: offset to the channel switch timing IE in the template + * @tx_cmd: Tx parameters for the frame + * @data: frame data + */ +struct iwl_tdls_channel_switch_frame { + __le32 switch_time_offset; + struct iwl_tx_cmd tx_cmd; + u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE]; +} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */ + +/** + * struct iwl_tdls_channel_switch_cmd - TDLS channel switch command + * + * The command is sent to initiate a channel switch and also in response to + * incoming TDLS channel-switch request/response packets from remote peers. + * + * @switch_type: see &enum iwl_tdls_channel_switch_type + * @peer_sta_id: station id of TDLS peer + * @ci: channel we switch to + * @timing: timing related data for command + * @frame: channel-switch request/response template, depending to switch_type + */ +struct iwl_tdls_channel_switch_cmd { + u8 switch_type; + __le32 peer_sta_id; + struct iwl_fw_channel_info ci; + struct iwl_tdls_channel_switch_timing timing; + struct iwl_tdls_channel_switch_frame frame; +} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */ + +/** + * struct iwl_tdls_channel_switch_notif - TDLS channel switch start notification + * + * @status: non-zero on success + * @offchannel_duration: duration given in microseconds + * @sta_id: peer currently performing the channel-switch with + */ +struct iwl_tdls_channel_switch_notif { + __le32 status; + __le32 offchannel_duration; + __le32 sta_id; +} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */ + +/** + * struct iwl_tdls_sta_info - TDLS station info + * + * @sta_id: station id of the TDLS peer + * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx + * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer + * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise + */ +struct iwl_tdls_sta_info { + u8 sta_id; + u8 tx_to_peer_tid; + __le16 tx_to_peer_ssn; + __le32 is_initiator; +} __packed; /* TDLS_STA_INFO_VER_1 */ + +/** + * struct iwl_tdls_config_cmd - TDLS basic config command + * + * @id_and_color: MAC id and color being configured + * @tdls_peer_count: amount of currently connected TDLS peers + * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx + * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP + * @sta_info: per-station info. Only the first tdls_peer_count entries are set + * @pti_req_data_offset: offset of network-level data for the PTI template + * @pti_req_tx_cmd: Tx parameters for PTI request template + * @pti_req_template: PTI request template data + */ +struct iwl_tdls_config_cmd { + __le32 id_and_color; /* mac id and color */ + u8 tdls_peer_count; + u8 tx_to_ap_tid; + __le16 tx_to_ap_ssn; + struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT]; + + __le32 pti_req_data_offset; + struct iwl_tx_cmd pti_req_tx_cmd; + u8 pti_req_template[0]; +} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */ + +/** + * struct iwl_tdls_config_sta_info_res - TDLS per-station config information + * + * @sta_id: station id of the TDLS peer + * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to + * the peer + */ +struct iwl_tdls_config_sta_info_res { + __le16 sta_id; + __le16 tx_to_peer_last_seq; +} __packed; /* TDLS_STA_INFO_RSP_VER_1 */ + +/** + * struct iwl_tdls_config_res - TDLS config information from FW + * + * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP + * @sta_info: per-station TDLS config information + */ +struct iwl_tdls_config_res { + __le32 tx_to_ap_last_seq; + struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT]; +} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */ + +#endif /* __iwl_fw_api_tdls_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h new file mode 100644 index 000000000000..3721a3ed358b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h @@ -0,0 +1,386 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_time_event_h__ +#define __iwl_fw_api_time_event_h__ + +#include "fw/api/phy-ctxt.h" + +/* Time Event types, according to MAC type */ +enum iwl_time_event_type { + /* BSS Station Events */ + TE_BSS_STA_AGGRESSIVE_ASSOC, + TE_BSS_STA_ASSOC, + TE_BSS_EAP_DHCP_PROT, + TE_BSS_QUIET_PERIOD, + + /* P2P Device Events */ + TE_P2P_DEVICE_DISCOVERABLE, + TE_P2P_DEVICE_LISTEN, + TE_P2P_DEVICE_ACTION_SCAN, + TE_P2P_DEVICE_FULL_SCAN, + + /* P2P Client Events */ + TE_P2P_CLIENT_AGGRESSIVE_ASSOC, + TE_P2P_CLIENT_ASSOC, + TE_P2P_CLIENT_QUIET_PERIOD, + + /* P2P GO Events */ + TE_P2P_GO_ASSOC_PROT, + TE_P2P_GO_REPETITIVET_NOA, + TE_P2P_GO_CT_WINDOW, + + /* WiDi Sync Events */ + TE_WIDI_TX_SYNC, + + /* Channel Switch NoA */ + TE_CHANNEL_SWITCH_PERIOD, + + TE_MAX +}; /* MAC_EVENT_TYPE_API_E_VER_1 */ + +/* Time event - defines for command API v1 */ + +/* + * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed. + * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only + * the first fragment is scheduled. + * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only + * the first 2 fragments are scheduled. + * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any + * number of fragments are valid. + * + * Other than the constant defined above, specifying a fragmentation value 'x' + * means that the event can be fragmented but only the first 'x' will be + * scheduled. + */ +enum { + TE_V1_FRAG_NONE = 0, + TE_V1_FRAG_SINGLE = 1, + TE_V1_FRAG_DUAL = 2, + TE_V1_FRAG_ENDLESS = 0xffffffff +}; + +/* If a Time Event can be fragmented, this is the max number of fragments */ +#define TE_V1_FRAG_MAX_MSK 0x0fffffff +/* Repeat the time event endlessly (until removed) */ +#define TE_V1_REPEAT_ENDLESS 0xffffffff +/* If a Time Event has bounded repetitions, this is the maximal value */ +#define TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff + +/* Time Event dependencies: none, on another TE, or in a specific time */ +enum { + TE_V1_INDEPENDENT = 0, + TE_V1_DEP_OTHER = BIT(0), + TE_V1_DEP_TSF = BIT(1), + TE_V1_EVENT_SOCIOPATHIC = BIT(2), +}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */ + +/* + * @TE_V1_NOTIF_NONE: no notifications + * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start + * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end + * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use + * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use. + * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start + * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end + * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use. + * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use. + * + * Supported Time event notifications configuration. + * A notification (both event and fragment) includes a status indicating weather + * the FW was able to schedule the event or not. For fragment start/end + * notification the status is always success. There is no start/end fragment + * notification for monolithic events. + */ +enum { + TE_V1_NOTIF_NONE = 0, + TE_V1_NOTIF_HOST_EVENT_START = BIT(0), + TE_V1_NOTIF_HOST_EVENT_END = BIT(1), + TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2), + TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3), + TE_V1_NOTIF_HOST_FRAG_START = BIT(4), + TE_V1_NOTIF_HOST_FRAG_END = BIT(5), + TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6), + TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7), +}; /* MAC_EVENT_ACTION_API_E_VER_2 */ + +/* Time event - defines for command API */ + +/* + * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed. + * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only + * the first fragment is scheduled. + * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only + * the first 2 fragments are scheduled. + * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any + * number of fragments are valid. + * + * Other than the constant defined above, specifying a fragmentation value 'x' + * means that the event can be fragmented but only the first 'x' will be + * scheduled. + */ +enum { + TE_V2_FRAG_NONE = 0, + TE_V2_FRAG_SINGLE = 1, + TE_V2_FRAG_DUAL = 2, + TE_V2_FRAG_MAX = 0xfe, + TE_V2_FRAG_ENDLESS = 0xff +}; + +/* Repeat the time event endlessly (until removed) */ +#define TE_V2_REPEAT_ENDLESS 0xff +/* If a Time Event has bounded repetitions, this is the maximal value */ +#define TE_V2_REPEAT_MAX 0xfe + +#define TE_V2_PLACEMENT_POS 12 +#define TE_V2_ABSENCE_POS 15 + +/** + * enum iwl_time_event_policy - Time event policy values + * A notification (both event and fragment) includes a status indicating weather + * the FW was able to schedule the event or not. For fragment start/end + * notification the status is always success. There is no start/end fragment + * notification for monolithic events. + * + * @TE_V2_DEFAULT_POLICY: independent, social, present, unoticable + * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start + * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end + * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use + * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use. + * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start + * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end + * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use. + * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use. + * @T2_V2_START_IMMEDIATELY: start time event immediately + * @TE_V2_DEP_OTHER: depends on another time event + * @TE_V2_DEP_TSF: depends on a specific time + * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC + * @TE_V2_ABSENCE: are we present or absent during the Time Event. + */ +enum iwl_time_event_policy { + TE_V2_DEFAULT_POLICY = 0x0, + + /* notifications (event start/stop, fragment start/stop) */ + TE_V2_NOTIF_HOST_EVENT_START = BIT(0), + TE_V2_NOTIF_HOST_EVENT_END = BIT(1), + TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2), + TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3), + + TE_V2_NOTIF_HOST_FRAG_START = BIT(4), + TE_V2_NOTIF_HOST_FRAG_END = BIT(5), + TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6), + TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7), + T2_V2_START_IMMEDIATELY = BIT(11), + + /* placement characteristics */ + TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS), + TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1), + TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2), + + /* are we present or absent during the Time Event. */ + TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS), +}; + +/** + * struct iwl_time_event_cmd - configuring Time Events + * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also + * with version 1. determined by IWL_UCODE_TLV_FLAGS) + * ( TIME_EVENT_CMD = 0x29 ) + * @id_and_color: ID and color of the relevant MAC, + * &enum iwl_ctxt_id_and_color + * @action: action to perform, one of &enum iwl_ctxt_action + * @id: this field has two meanings, depending on the action: + * If the action is ADD, then it means the type of event to add. + * For all other actions it is the unique event ID assigned when the + * event was added by the FW. + * @apply_time: When to start the Time Event (in GP2) + * @max_delay: maximum delay to event's start (apply time), in TU + * @depends_on: the unique ID of the event we depend on (if any) + * @interval: interval between repetitions, in TU + * @duration: duration of event in TU + * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS + * @max_frags: maximal number of fragments the Time Event can be divided to + * @policy: defines whether uCode shall notify the host or other uCode modules + * on event and/or fragment start and/or end + * using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF + * TE_EVENT_SOCIOPATHIC + * using TE_ABSENCE and using TE_NOTIF_*, + * &enum iwl_time_event_policy + */ +struct iwl_time_event_cmd { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + __le32 id; + /* MAC_TIME_EVENT_DATA_API_S_VER_2 */ + __le32 apply_time; + __le32 max_delay; + __le32 depends_on; + __le32 interval; + __le32 duration; + u8 repeat; + u8 max_frags; + __le16 policy; +} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */ + +/** + * struct iwl_time_event_resp - response structure to iwl_time_event_cmd + * @status: bit 0 indicates success, all others specify errors + * @id: the Time Event type + * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE + * @id_and_color: ID and color of the relevant MAC, + * &enum iwl_ctxt_id_and_color + */ +struct iwl_time_event_resp { + __le32 status; + __le32 id; + __le32 unique_id; + __le32 id_and_color; +} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */ + +/** + * struct iwl_time_event_notif - notifications of time event start/stop + * ( TIME_EVENT_NOTIFICATION = 0x2a ) + * @timestamp: action timestamp in GP2 + * @session_id: session's unique id + * @unique_id: unique id of the Time Event itself + * @id_and_color: ID and color of the relevant MAC + * @action: &enum iwl_time_event_policy + * @status: true if scheduled, false otherwise (not executed) + */ +struct iwl_time_event_notif { + __le32 timestamp; + __le32 session_id; + __le32 unique_id; + __le32 id_and_color; + __le32 action; + __le32 status; +} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */ + +/* + * Aux ROC command + * + * Command requests the firmware to create a time event for a certain duration + * and remain on the given channel. This is done by using the Aux framework in + * the FW. + * The command was first used for Hot Spot issues - but can be used regardless + * to Hot Spot. + * + * ( HOT_SPOT_CMD 0x53 ) + * + * @id_and_color: ID and color of the MAC + * @action: action to perform, one of FW_CTXT_ACTION_* + * @event_unique_id: If the action FW_CTXT_ACTION_REMOVE then the + * event_unique_id should be the id of the time event assigned by ucode. + * Otherwise ignore the event_unique_id. + * @sta_id_and_color: station id and color, resumed during "Remain On Channel" + * activity. + * @channel_info: channel info + * @node_addr: Our MAC Address + * @reserved: reserved for alignment + * @apply_time: GP2 value to start (should always be the current GP2 value) + * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max + * time by which start of the event is allowed to be postponed. + * @duration: event duration in TU To calculate event duration: + * timeEventDuration = min(duration, remainingQuota) + */ +struct iwl_hs20_roc_req { + /* COMMON_INDEX_HDR_API_S_VER_1 hdr */ + __le32 id_and_color; + __le32 action; + __le32 event_unique_id; + __le32 sta_id_and_color; + struct iwl_fw_channel_info channel_info; + u8 node_addr[ETH_ALEN]; + __le16 reserved; + __le32 apply_time; + __le32 apply_time_max_delay; + __le32 duration; +} __packed; /* HOT_SPOT_CMD_API_S_VER_1 */ + +/* + * values for AUX ROC result values + */ +enum iwl_mvm_hot_spot { + HOT_SPOT_RSP_STATUS_OK, + HOT_SPOT_RSP_STATUS_TOO_MANY_EVENTS, + HOT_SPOT_MAX_NUM_OF_SESSIONS, +}; + +/* + * Aux ROC command response + * + * In response to iwl_hs20_roc_req the FW sends this command to notify the + * driver the uid of the timevent. + * + * ( HOT_SPOT_CMD 0x53 ) + * + * @event_unique_id: Unique ID of time event assigned by ucode + * @status: Return status 0 is success, all the rest used for specific errors + */ +struct iwl_hs20_roc_res { + __le32 event_unique_id; + __le32 status; +} __packed; /* HOT_SPOT_RSP_API_S_VER_1 */ + +#endif /* __iwl_fw_api_time_event_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tof.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tof.h new file mode 100644 index 000000000000..7328a1606146 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tof.h @@ -0,0 +1,393 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_fw_api_tof_h__ +#define __iwl_fw_api_tof_h__ + +/* ToF sub-group command IDs */ +enum iwl_mvm_tof_sub_grp_ids { + TOF_RANGE_REQ_CMD = 0x1, + TOF_CONFIG_CMD = 0x2, + TOF_RANGE_ABORT_CMD = 0x3, + TOF_RANGE_REQ_EXT_CMD = 0x4, + TOF_RESPONDER_CONFIG_CMD = 0x5, + TOF_NW_INITIATED_RES_SEND_CMD = 0x6, + TOF_NEIGHBOR_REPORT_REQ_CMD = 0x7, + TOF_NEIGHBOR_REPORT_RSP_NOTIF = 0xFC, + TOF_NW_INITIATED_REQ_RCVD_NOTIF = 0xFD, + TOF_RANGE_RESPONSE_NOTIF = 0xFE, + TOF_MCSI_DEBUG_NOTIF = 0xFB, +}; + +/** + * struct iwl_tof_config_cmd - ToF configuration + * @tof_disabled: 0 enabled, 1 - disabled + * @one_sided_disabled: 0 enabled, 1 - disabled + * @is_debug_mode: 1 debug mode, 0 - otherwise + * @is_buf_required: 1 channel estimation buffer required, 0 - otherwise + */ +struct iwl_tof_config_cmd { + __le32 sub_grp_cmd_id; + u8 tof_disabled; + u8 one_sided_disabled; + u8 is_debug_mode; + u8 is_buf_required; +} __packed; + +/** + * struct iwl_tof_responder_config_cmd - ToF AP mode (for debug) + * @burst_period: future use: (currently hard coded in the LMAC) + * The interval between two sequential bursts. + * @min_delta_ftm: future use: (currently hard coded in the LMAC) + * The minimum delay between two sequential FTM Responses + * in the same burst. + * @burst_duration: future use: (currently hard coded in the LMAC) + * The total time for all FTMs handshake in the same burst. + * Affect the time events duration in the LMAC. + * @num_of_burst_exp: future use: (currently hard coded in the LMAC) + * The number of bursts for the current ToF request. Affect + * the number of events allocations in the current iteration. + * @get_ch_est: for xVT only, NA for driver + * @abort_responder: when set to '1' - Responder will terminate its activity + * (all other fields in the command are ignored) + * @recv_sta_req_params: 1 - Responder will ignore the other Responder's + * params and use the recomended Initiator params. + * 0 - otherwise + * @channel_num: current AP Channel + * @bandwidth: current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz + * @rate: current AP rate + * @ctrl_ch_position: coding of the control channel position relative to + * the center frequency: + * + * 40 MHz + * 0 below center, 1 above center + * + * 80 MHz + * bits [0..1] + * * 0 the near 20MHz to the center, + * * 1 the far 20MHz to the center + * bit[2] + * as above 40MHz + * @ftm_per_burst: FTMs per Burst + * @ftm_resp_ts_avail: '0' - we don't measure over the Initial FTM Response, + * '1' - we measure over the Initial FTM Response + * @asap_mode: ASAP / Non ASAP mode for the current WLS station + * @sta_id: index of the AP STA when in AP mode + * @tsf_timer_offset_msecs: The dictated time offset (mSec) from the AP's TSF + * @toa_offset: Artificial addition [0.1nsec] for the ToA - to be used for debug + * purposes, simulating station movement by adding various values + * to this field + * @bssid: Current AP BSSID + */ +struct iwl_tof_responder_config_cmd { + __le32 sub_grp_cmd_id; + __le16 burst_period; + u8 min_delta_ftm; + u8 burst_duration; + u8 num_of_burst_exp; + u8 get_ch_est; + u8 abort_responder; + u8 recv_sta_req_params; + u8 channel_num; + u8 bandwidth; + u8 rate; + u8 ctrl_ch_position; + u8 ftm_per_burst; + u8 ftm_resp_ts_avail; + u8 asap_mode; + u8 sta_id; + __le16 tsf_timer_offset_msecs; + __le16 toa_offset; + u8 bssid[ETH_ALEN]; +} __packed; + +/** + * struct iwl_tof_range_request_ext_cmd - extended range req for WLS + * @tsf_timer_offset_msec: the recommended time offset (mSec) from the AP's TSF + * @reserved: reserved + * @min_delta_ftm: Minimal time between two consecutive measurements, + * in units of 100us. 0 means no preference by station + * @ftm_format_and_bw20M: FTM Channel Spacing/Format for 20MHz: recommended + * value be sent to the AP + * @ftm_format_and_bw40M: FTM Channel Spacing/Format for 40MHz: recommended + * value to be sent to the AP + * @ftm_format_and_bw80M: FTM Channel Spacing/Format for 80MHz: recommended + * value to be sent to the AP + */ +struct iwl_tof_range_req_ext_cmd { + __le32 sub_grp_cmd_id; + __le16 tsf_timer_offset_msec; + __le16 reserved; + u8 min_delta_ftm; + u8 ftm_format_and_bw20M; + u8 ftm_format_and_bw40M; + u8 ftm_format_and_bw80M; +} __packed; + +#define IWL_MVM_TOF_MAX_APS 21 + +/** + * struct iwl_tof_range_req_ap_entry - AP configuration parameters + * @channel_num: Current AP Channel + * @bandwidth: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz + * @tsf_delta_direction: TSF relatively to the subject AP + * @ctrl_ch_position: Coding of the control channel position relative to the + * center frequency. + * 40MHz 0 below center, 1 above center + * 80MHz bits [0..1]: 0 the near 20MHz to the center, + * 1 the far 20MHz to the center + * bit[2] as above 40MHz + * @bssid: AP's bss id + * @measure_type: Measurement type: 0 - two sided, 1 - One sided + * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of the + * number of measurement iterations (min 2^0 = 1, max 2^14) + * @burst_period: Recommended value to be sent to the AP. Measurement + * periodicity In units of 100ms. ignored if num_of_bursts = 0 + * @samples_per_burst: 2-sided: the number of FTMs pairs in single Burst (1-31) + * 1-sided: how many rts/cts pairs should be used per burst. + * @retries_per_sample: Max number of retries that the LMAC should send + * in case of no replies by the AP. + * @tsf_delta: TSF Delta in units of microseconds. + * The difference between the AP TSF and the device local clock. + * @location_req: Location Request Bit[0] LCI should be sent in the FTMR + * Bit[1] Civic should be sent in the FTMR + * @asap_mode: 0 - non asap mode, 1 - asap mode (not relevant for one sided) + * @enable_dyn_ack: Enable Dynamic ACK BW. + * 0 Initiator interact with regular AP + * 1 Initiator interact with Responder machine: need to send the + * Initiator Acks with HT 40MHz / 80MHz, since the Responder should + * use it for its ch est measurement (this flag will be set when we + * configure the opposite machine to be Responder). + * @rssi: Last received value + * leagal values: -128-0 (0x7f). above 0x0 indicating an invalid value. + */ +struct iwl_tof_range_req_ap_entry { + u8 channel_num; + u8 bandwidth; + u8 tsf_delta_direction; + u8 ctrl_ch_position; + u8 bssid[ETH_ALEN]; + u8 measure_type; + u8 num_of_bursts; + __le16 burst_period; + u8 samples_per_burst; + u8 retries_per_sample; + __le32 tsf_delta; + u8 location_req; + u8 asap_mode; + u8 enable_dyn_ack; + s8 rssi; +} __packed; + +/** + * enum iwl_tof_response_mode + * @IWL_MVM_TOF_RESPOSE_ASAP: report each AP measurement separately as soon as + * possible (not supported for this release) + * @IWL_MVM_TOF_RESPOSE_TIMEOUT: report all AP measurements as a batch upon + * timeout expiration + * @IWL_MVM_TOF_RESPOSE_COMPLETE: report all AP measurements as a batch at the + * earlier of: measurements completion / timeout + * expiration. + */ +enum iwl_tof_response_mode { + IWL_MVM_TOF_RESPOSE_ASAP = 1, + IWL_MVM_TOF_RESPOSE_TIMEOUT, + IWL_MVM_TOF_RESPOSE_COMPLETE, +}; + +/** + * struct iwl_tof_range_req_cmd - start measurement cmd + * @request_id: A Token incremented per request. The same Token will be + * sent back in the range response + * @initiator: 0- NW initiated, 1 - Client Initiated + * @one_sided_los_disable: '0'- run ML-Algo for both ToF/OneSided, + * '1' - run ML-Algo for ToF only + * @req_timeout: Requested timeout of the response in units of 100ms. + * This is equivalent to the session time configured to the + * LMAC in Initiator Request + * @report_policy: Supported partially for this release: For current release - + * the range report will be uploaded as a batch when ready or + * when the session is done (successfully / partially). + * one of iwl_tof_response_mode. + * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @macaddr_random: '0' Use default source MAC address (i.e. p2_p), + * '1' Use MAC Address randomization according to the below + * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. + * Bits set to 1 shall be randomized by the UMAC + * @ap: per-AP request data + */ +struct iwl_tof_range_req_cmd { + __le32 sub_grp_cmd_id; + u8 request_id; + u8 initiator; + u8 one_sided_los_disable; + u8 req_timeout; + u8 report_policy; + u8 los_det_disable; + u8 num_of_ap; + u8 macaddr_random; + u8 macaddr_template[ETH_ALEN]; + u8 macaddr_mask[ETH_ALEN]; + struct iwl_tof_range_req_ap_entry ap[IWL_MVM_TOF_MAX_APS]; +} __packed; + +/** + * struct iwl_tof_gen_resp_cmd - generic ToF response + */ +struct iwl_tof_gen_resp_cmd { + __le32 sub_grp_cmd_id; + u8 data[]; +} __packed; + +/** + * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response) + * @bssid: BSSID of the AP + * @measure_status: current APs measurement status, one of + * &enum iwl_tof_entry_status. + * @measure_bw: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz + * @rtt: The Round Trip Time that took for the last measurement for + * current AP [nSec] + * @rtt_variance: The Variance of the RTT values measured for current AP + * @rtt_spread: The Difference between the maximum and the minimum RTT + * values measured for current AP in the current session [nsec] + * @rssi: RSSI as uploaded in the Channel Estimation notification + * @rssi_spread: The Difference between the maximum and the minimum RSSI values + * measured for current AP in the current session + * @reserved: reserved + * @range: Measured range [cm] + * @range_variance: Measured range variance [cm] + * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was + * uploaded by the LMAC + */ +struct iwl_tof_range_rsp_ap_entry_ntfy { + u8 bssid[ETH_ALEN]; + u8 measure_status; + u8 measure_bw; + __le32 rtt; + __le32 rtt_variance; + __le32 rtt_spread; + s8 rssi; + u8 rssi_spread; + __le16 reserved; + __le32 range; + __le32 range_variance; + __le32 timestamp; +} __packed; + +/** + * struct iwl_tof_range_rsp_ntfy - + * @request_id: A Token ID of the corresponding Range request + * @request_status: status of current measurement session + * @last_in_batch: reprot policy (when not all responses are uploaded at once) + * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @ap: per-AP data + */ +struct iwl_tof_range_rsp_ntfy { + u8 request_id; + u8 request_status; + u8 last_in_batch; + u8 num_of_aps; + struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_MVM_TOF_MAX_APS]; +} __packed; + +#define IWL_MVM_TOF_MCSI_BUF_SIZE (245) +/** + * struct iwl_tof_mcsi_notif - used for debug + * @token: token ID for the current session + * @role: '0' - initiator, '1' - responder + * @reserved: reserved + * @initiator_bssid: initiator machine + * @responder_bssid: responder machine + * @mcsi_buffer: debug data + */ +struct iwl_tof_mcsi_notif { + u8 token; + u8 role; + __le16 reserved; + u8 initiator_bssid[ETH_ALEN]; + u8 responder_bssid[ETH_ALEN]; + u8 mcsi_buffer[IWL_MVM_TOF_MCSI_BUF_SIZE * 4]; +} __packed; + +/** + * struct iwl_tof_neighbor_report_notif + * @bssid: BSSID of the AP which sent the report + * @request_token: same token as the corresponding request + * @status: + * @report_ie_len: the length of the response frame starting from the Element ID + * @data: the IEs + */ +struct iwl_tof_neighbor_report { + u8 bssid[ETH_ALEN]; + u8 request_token; + u8 status; + __le16 report_ie_len; + u8 data[]; +} __packed; + +/** + * struct iwl_tof_range_abort_cmd + * @request_id: corresponds to a range request + * @reserved: reserved + */ +struct iwl_tof_range_abort_cmd { + __le32 sub_grp_cmd_id; + u8 request_id; + u8 reserved[3]; +} __packed; + +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h new file mode 100644 index 000000000000..d20baedead98 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -0,0 +1,912 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_fw_api_tx_h__ +#define __iwl_fw_api_tx_h__ + +/** + * enum iwl_tx_flags - bitmasks for tx_flags in TX command + * @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame + * @TX_CMD_FLG_WRITE_TX_POWER: update current tx power value in the mgmt frame + * @TX_CMD_FLG_ACK: expect ACK from receiving station + * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command. + * Otherwise, use rate_n_flags from the TX command + * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected + * Must set TX_CMD_FLG_ACK with this flag. + * @TX_CMD_FLG_TXOP_PROT: TXOP protection requested + * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence + * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence + * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC) + * @TX_CMD_FLG_BT_PRIO_POS: the position of the BT priority (bit 11 is ignored + * on old firmwares). + * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame + * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control. + * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command + * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU + * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame + * Should be set for beacons and probe responses + * @TX_CMD_FLG_CALIB: activate PA TX power calibrations + * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count + * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header. + * Should be set for 26/30 length MAC headers + * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW + * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation + * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id + * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped + * @TX_CMD_FLG_EXEC_PAPD: execute PAPD + * @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power + * @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk + */ +enum iwl_tx_flags { + TX_CMD_FLG_PROT_REQUIRE = BIT(0), + TX_CMD_FLG_WRITE_TX_POWER = BIT(1), + TX_CMD_FLG_ACK = BIT(3), + TX_CMD_FLG_STA_RATE = BIT(4), + TX_CMD_FLG_BAR = BIT(6), + TX_CMD_FLG_TXOP_PROT = BIT(7), + TX_CMD_FLG_VHT_NDPA = BIT(8), + TX_CMD_FLG_HT_NDPA = BIT(9), + TX_CMD_FLG_CSI_FDBK2HOST = BIT(10), + TX_CMD_FLG_BT_PRIO_POS = 11, + TX_CMD_FLG_BT_DIS = BIT(12), + TX_CMD_FLG_SEQ_CTL = BIT(13), + TX_CMD_FLG_MORE_FRAG = BIT(14), + TX_CMD_FLG_TSF = BIT(16), + TX_CMD_FLG_CALIB = BIT(17), + TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18), + TX_CMD_FLG_MH_PAD = BIT(20), + TX_CMD_FLG_RESP_TO_DRV = BIT(21), + TX_CMD_FLG_TKIP_MIC_DONE = BIT(23), + TX_CMD_FLG_DUR = BIT(25), + TX_CMD_FLG_FW_DROP = BIT(26), + TX_CMD_FLG_EXEC_PAPD = BIT(27), + TX_CMD_FLG_PAPD_TYPE = BIT(28), + TX_CMD_FLG_HCCA_CHUNK = BIT(31) +}; /* TX_FLAGS_BITS_API_S_VER_1 */ + +/** + * enum iwl_tx_cmd_flags - bitmasks for tx_flags in TX command for a000 + * @IWL_TX_FLAGS_CMD_RATE: use rate from the TX command + * @IWL_TX_FLAGS_ENCRYPT_DIS: frame should not be encrypted, even if it belongs + * to a secured STA + * @IWL_TX_FLAGS_HIGH_PRI: high priority frame (like EAPOL) - can affect rate + * selection, retry limits and BT kill + */ +enum iwl_tx_cmd_flags { + IWL_TX_FLAGS_CMD_RATE = BIT(0), + IWL_TX_FLAGS_ENCRYPT_DIS = BIT(1), + IWL_TX_FLAGS_HIGH_PRI = BIT(2), +}; /* TX_FLAGS_BITS_API_S_VER_3 */ + +/** + * enum iwl_tx_pm_timeouts - pm timeout values in TX command + * @PM_FRAME_NONE: no need to suspend sleep mode + * @PM_FRAME_MGMT: fw suspend sleep mode for 100TU + * @PM_FRAME_ASSOC: fw suspend sleep mode for 10sec + */ +enum iwl_tx_pm_timeouts { + PM_FRAME_NONE = 0, + PM_FRAME_MGMT = 2, + PM_FRAME_ASSOC = 3, +}; + +#define TX_CMD_SEC_MSK 0x07 +#define TX_CMD_SEC_WEP_KEY_IDX_POS 6 +#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 + +/** + * enum iwl_tx_cmd_sec_ctrl - bitmasks for security control in TX command + * @TX_CMD_SEC_WEP: WEP encryption algorithm. + * @TX_CMD_SEC_CCM: CCM encryption algorithm. + * @TX_CMD_SEC_TKIP: TKIP encryption algorithm. + * @TX_CMD_SEC_EXT: extended cipher algorithm. + * @TX_CMD_SEC_GCMP: GCMP encryption algorithm. + * @TX_CMD_SEC_KEY128: set for 104 bits WEP key. + * @TX_CMD_SEC_KEY_FROM_TABLE: for a non-WEP key, set if the key should be taken + * from the table instead of from the TX command. + * If the key is taken from the key table its index should be given by the + * first byte of the TX command key field. + */ +enum iwl_tx_cmd_sec_ctrl { + TX_CMD_SEC_WEP = 0x01, + TX_CMD_SEC_CCM = 0x02, + TX_CMD_SEC_TKIP = 0x03, + TX_CMD_SEC_EXT = 0x04, + TX_CMD_SEC_GCMP = 0x05, + TX_CMD_SEC_KEY128 = 0x08, + TX_CMD_SEC_KEY_FROM_TABLE = 0x10, +}; + +/* + * TX command Frame life time in us - to be written in pm_frame_timeout + */ +#define TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF +#define TX_CMD_LIFE_TIME_DEFAULT 2000000 /* 2000 ms*/ +#define TX_CMD_LIFE_TIME_PROBE_RESP 40000 /* 40 ms */ +#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0 + +/* + * TID for non QoS frames - to be written in tid_tspec + */ +#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT + +/* + * Limits on the retransmissions - to be written in {data,rts}_retry_limit + */ +#define IWL_DEFAULT_TX_RETRY 15 +#define IWL_MGMT_DFAULT_RETRY_LIMIT 3 +#define IWL_RTS_DFAULT_RETRY_LIMIT 60 +#define IWL_BAR_DFAULT_RETRY_LIMIT 60 +#define IWL_LOW_RETRY_LIMIT 7 + +/** + * enum iwl_tx_offload_assist_flags_pos - set %iwl_tx_cmd offload_assist values + * @TX_CMD_OFFLD_IP_HDR: offset to start of IP header (in words) + * from mac header end. For normal case it is 4 words for SNAP. + * note: tx_cmd, mac header and pad are not counted in the offset. + * This is used to help the offload in case there is tunneling such as + * IPv6 in IPv4, in such case the ip header offset should point to the + * inner ip header and IPv4 checksum of the external header should be + * calculated by driver. + * @TX_CMD_OFFLD_L4_EN: enable TCP/UDP checksum + * @TX_CMD_OFFLD_L3_EN: enable IP header checksum + * @TX_CMD_OFFLD_MH_SIZE: size of the mac header in words. Includes the IV + * field. Doesn't include the pad. + * @TX_CMD_OFFLD_PAD: mark 2-byte pad was inserted after the mac header for + * alignment + * @TX_CMD_OFFLD_AMSDU: mark TX command is A-MSDU + */ +enum iwl_tx_offload_assist_flags_pos { + TX_CMD_OFFLD_IP_HDR = 0, + TX_CMD_OFFLD_L4_EN = 6, + TX_CMD_OFFLD_L3_EN = 7, + TX_CMD_OFFLD_MH_SIZE = 8, + TX_CMD_OFFLD_PAD = 13, + TX_CMD_OFFLD_AMSDU = 14, +}; + +#define IWL_TX_CMD_OFFLD_MH_MASK 0x1f +#define IWL_TX_CMD_OFFLD_IP_HDR_MASK 0x3f + +/* TODO: complete documentation for try_cnt and btkill_cnt */ +/** + * struct iwl_tx_cmd - TX command struct to FW + * ( TX_CMD = 0x1c ) + * @len: in bytes of the payload, see below for details + * @offload_assist: TX offload configuration + * @tx_flags: combination of TX_CMD_FLG_* + * @scratch: scratch buffer used by the device + * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is + * cleared. Combination of RATE_MCS_* + * @sta_id: index of destination station in FW station table + * @sec_ctl: security control, TX_CMD_SEC_* + * @initial_rate_index: index into the the rate table for initial TX attempt. + * Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames. + * @reserved2: reserved + * @key: security key + * @reserved3: reserved + * @life_time: frame life time (usecs??) + * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt + + * btkill_cnd + reserved), first 32 bits. "0" disables usage. + * @dram_msb_ptr: upper bits of the scratch physical address + * @rts_retry_limit: max attempts for RTS + * @data_retry_limit: max attempts to send the data packet + * @tid_tspec: TID/tspec + * @pm_frame_timeout: PM TX frame timeout + * @reserved4: reserved + * @payload: payload (same as @hdr) + * @hdr: 802.11 header (same as @payload) + * + * The byte count (both len and next_frame_len) includes MAC header + * (24/26/30/32 bytes) + * + 2 bytes pad if 26/30 header size + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * It does not include post-MAC padding, i.e., + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes. + * Range of len: 14-2342 bytes. + * + * After the struct fields the MAC header is placed, plus any padding, + * and then the actial payload. + */ +struct iwl_tx_cmd { + __le16 len; + __le16 offload_assist; + __le32 tx_flags; + struct { + u8 try_cnt; + u8 btkill_cnt; + __le16 reserved; + } scratch; /* DRAM_SCRATCH_API_U_VER_1 */ + __le32 rate_n_flags; + u8 sta_id; + u8 sec_ctl; + u8 initial_rate_index; + u8 reserved2; + u8 key[16]; + __le32 reserved3; + __le32 life_time; + __le32 dram_lsb_ptr; + u8 dram_msb_ptr; + u8 rts_retry_limit; + u8 data_retry_limit; + u8 tid_tspec; + __le16 pm_frame_timeout; + __le16 reserved4; + u8 payload[0]; + struct ieee80211_hdr hdr[0]; +} __packed; /* TX_CMD_API_S_VER_6 */ + +struct iwl_dram_sec_info { + __le32 pn_low; + __le16 pn_high; + __le16 aux_info; +} __packed; /* DRAM_SEC_INFO_API_S_VER_1 */ + +/** + * struct iwl_tx_cmd_gen2 - TX command struct to FW for a000 devices + * ( TX_CMD = 0x1c ) + * @len: in bytes of the payload, see below for details + * @offload_assist: TX offload configuration + * @flags: combination of &enum iwl_tx_cmd_flags + * @dram_info: FW internal DRAM storage + * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is + * cleared. Combination of RATE_MCS_* + * @hdr: 802.11 header + */ +struct iwl_tx_cmd_gen2 { + __le16 len; + __le16 offload_assist; + __le32 flags; + struct iwl_dram_sec_info dram_info; + __le32 rate_n_flags; + struct ieee80211_hdr hdr[0]; +} __packed; /* TX_CMD_API_S_VER_7 */ + +/* + * TX response related data + */ + +/* + * enum iwl_tx_status - status that is returned by the fw after attempts to Tx + * @TX_STATUS_SUCCESS: + * @TX_STATUS_DIRECT_DONE: + * @TX_STATUS_POSTPONE_DELAY: + * @TX_STATUS_POSTPONE_FEW_BYTES: + * @TX_STATUS_POSTPONE_BT_PRIO: + * @TX_STATUS_POSTPONE_QUIET_PERIOD: + * @TX_STATUS_POSTPONE_CALC_TTAK: + * @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: + * @TX_STATUS_FAIL_SHORT_LIMIT: + * @TX_STATUS_FAIL_LONG_LIMIT: + * @TX_STATUS_FAIL_UNDERRUN: + * @TX_STATUS_FAIL_DRAIN_FLOW: + * @TX_STATUS_FAIL_RFKILL_FLUSH: + * @TX_STATUS_FAIL_LIFE_EXPIRE: + * @TX_STATUS_FAIL_DEST_PS: + * @TX_STATUS_FAIL_HOST_ABORTED: + * @TX_STATUS_FAIL_BT_RETRY: + * @TX_STATUS_FAIL_STA_INVALID: + * @TX_TATUS_FAIL_FRAG_DROPPED: + * @TX_STATUS_FAIL_TID_DISABLE: + * @TX_STATUS_FAIL_FIFO_FLUSHED: + * @TX_STATUS_FAIL_SMALL_CF_POLL: + * @TX_STATUS_FAIL_FW_DROP: + * @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and + * STA table + * @TX_FRAME_STATUS_INTERNAL_ABORT: + * @TX_MODE_MSK: + * @TX_MODE_NO_BURST: + * @TX_MODE_IN_BURST_SEQ: + * @TX_MODE_FIRST_IN_BURST: + * @TX_QUEUE_NUM_MSK: + * + * Valid only if frame_count =1 + * TODO: complete documentation + */ +enum iwl_tx_status { + TX_STATUS_MSK = 0x000000ff, + TX_STATUS_SUCCESS = 0x01, + TX_STATUS_DIRECT_DONE = 0x02, + /* postpone TX */ + TX_STATUS_POSTPONE_DELAY = 0x40, + TX_STATUS_POSTPONE_FEW_BYTES = 0x41, + TX_STATUS_POSTPONE_BT_PRIO = 0x42, + TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, + TX_STATUS_POSTPONE_CALC_TTAK = 0x44, + /* abort TX */ + TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, + TX_STATUS_FAIL_SHORT_LIMIT = 0x82, + TX_STATUS_FAIL_LONG_LIMIT = 0x83, + TX_STATUS_FAIL_UNDERRUN = 0x84, + TX_STATUS_FAIL_DRAIN_FLOW = 0x85, + TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, + TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, + TX_STATUS_FAIL_DEST_PS = 0x88, + TX_STATUS_FAIL_HOST_ABORTED = 0x89, + TX_STATUS_FAIL_BT_RETRY = 0x8a, + TX_STATUS_FAIL_STA_INVALID = 0x8b, + TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, + TX_STATUS_FAIL_TID_DISABLE = 0x8d, + TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, + TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f, + TX_STATUS_FAIL_FW_DROP = 0x90, + TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91, + TX_STATUS_INTERNAL_ABORT = 0x92, + TX_MODE_MSK = 0x00000f00, + TX_MODE_NO_BURST = 0x00000000, + TX_MODE_IN_BURST_SEQ = 0x00000100, + TX_MODE_FIRST_IN_BURST = 0x00000200, + TX_QUEUE_NUM_MSK = 0x0001f000, + TX_NARROW_BW_MSK = 0x00060000, + TX_NARROW_BW_1DIV2 = 0x00020000, + TX_NARROW_BW_1DIV4 = 0x00040000, + TX_NARROW_BW_1DIV8 = 0x00060000, +}; + +/* + * enum iwl_tx_agg_status - TX aggregation status + * @AGG_TX_STATE_STATUS_MSK: + * @AGG_TX_STATE_TRANSMITTED: + * @AGG_TX_STATE_UNDERRUN: + * @AGG_TX_STATE_BT_PRIO: + * @AGG_TX_STATE_FEW_BYTES: + * @AGG_TX_STATE_ABORT: + * @AGG_TX_STATE_LAST_SENT_TTL: + * @AGG_TX_STATE_LAST_SENT_TRY_CNT: + * @AGG_TX_STATE_LAST_SENT_BT_KILL: + * @AGG_TX_STATE_SCD_QUERY: + * @AGG_TX_STATE_TEST_BAD_CRC32: + * @AGG_TX_STATE_RESPONSE: + * @AGG_TX_STATE_DUMP_TX: + * @AGG_TX_STATE_DELAY_TX: + * @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries + * occur if tx failed for this frame when it was a member of a previous + * aggregation block). If rate scaling is used, retry count indicates the + * rate table entry used for all frames in the new agg. + *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for + * this frame + * + * TODO: complete documentation + */ +enum iwl_tx_agg_status { + AGG_TX_STATE_STATUS_MSK = 0x00fff, + AGG_TX_STATE_TRANSMITTED = 0x000, + AGG_TX_STATE_UNDERRUN = 0x001, + AGG_TX_STATE_BT_PRIO = 0x002, + AGG_TX_STATE_FEW_BYTES = 0x004, + AGG_TX_STATE_ABORT = 0x008, + AGG_TX_STATE_LAST_SENT_TTL = 0x010, + AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020, + AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040, + AGG_TX_STATE_SCD_QUERY = 0x080, + AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100, + AGG_TX_STATE_RESPONSE = 0x1ff, + AGG_TX_STATE_DUMP_TX = 0x200, + AGG_TX_STATE_DELAY_TX = 0x400, + AGG_TX_STATE_TRY_CNT_POS = 12, + AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS, +}; + +#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL| \ + AGG_TX_STATE_LAST_SENT_TRY_CNT| \ + AGG_TX_STATE_LAST_SENT_BT_KILL) + +/* + * The mask below describes a status where we are absolutely sure that the MPDU + * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've + * written the bytes to the TXE, but we know nothing about what the DSP did. + */ +#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \ + AGG_TX_STATE_ABORT | \ + AGG_TX_STATE_SCD_QUERY) + +/* + * REPLY_TX = 0x1c (response) + * + * This response may be in one of two slightly different formats, indicated + * by the frame_count field: + * + * 1) No aggregation (frame_count == 1). This reports Tx results for a single + * frame. Multiple attempts, at various bit rates, may have been made for + * this frame. + * + * 2) Aggregation (frame_count > 1). This reports Tx results for two or more + * frames that used block-acknowledge. All frames were transmitted at + * same rate. Rate scaling may have been used if first frame in this new + * agg block failed in previous agg block(s). + * + * Note that, for aggregation, ACK (block-ack) status is not delivered + * here; block-ack has not been received by the time the device records + * this status. + * This status relates to reasons the tx might have been blocked or aborted + * within the device, rather than whether it was received successfully by + * the destination station. + */ + +/** + * struct agg_tx_status - per packet TX aggregation status + * @status: See &enum iwl_tx_agg_status + * @sequence: Sequence # for this frame's Tx cmd (not SSN!) + */ +struct agg_tx_status { + __le16 status; + __le16 sequence; +} __packed; + +/* + * definitions for initial rate index field + * bits [3:0] initial rate index + * bits [6:4] rate table color, used for the initial rate + * bit-7 invalid rate indication + */ +#define TX_RES_INIT_RATE_INDEX_MSK 0x0f +#define TX_RES_RATE_TABLE_COLOR_POS 4 +#define TX_RES_RATE_TABLE_COLOR_MSK 0x70 +#define TX_RES_INV_RATE_INDEX_MSK 0x80 +#define TX_RES_RATE_TABLE_COL_GET(_f) (((_f) & TX_RES_RATE_TABLE_COLOR_MSK) >>\ + TX_RES_RATE_TABLE_COLOR_POS) + +#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f) +#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4) + +/** + * struct iwl_mvm_tx_resp_v3 - notifies that fw is TXing a packet + * ( REPLY_TX = 0x1c ) + * @frame_count: 1 no aggregation, >1 aggregation + * @bt_kill_count: num of times blocked by bluetooth (unused for agg) + * @failure_rts: num of failures due to unsuccessful RTS + * @failure_frame: num failures due to no ACK (unused for agg) + * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the + * Tx of all the batch. RATE_MCS_* + * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. + * for agg: RTS + CTS + aggregation tx time + block-ack time. + * in usec. + * @pa_status: tx power info + * @pa_integ_res_a: tx power info + * @pa_integ_res_b: tx power info + * @pa_integ_res_c: tx power info + * @measurement_req_id: tx power info + * @reduced_tpc: transmit power reduction used + * @reserved: reserved + * @tfd_info: TFD information set by the FH + * @seq_ctl: sequence control from the Tx cmd + * @byte_cnt: byte count from the Tx cmd + * @tlc_info: TLC rate info + * @ra_tid: bits [3:0] = ra, bits [7:4] = tid + * @frame_ctrl: frame control + * @status: for non-agg: frame status TX_STATUS_* + * for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields + * follow this one, up to frame_count. Length in @frame_count. + * + * After the array of statuses comes the SSN of the SCD. Look at + * %iwl_mvm_get_scd_ssn for more details. + */ +struct iwl_mvm_tx_resp_v3 { + u8 frame_count; + u8 bt_kill_count; + u8 failure_rts; + u8 failure_frame; + __le32 initial_rate; + __le16 wireless_media_time; + + u8 pa_status; + u8 pa_integ_res_a[3]; + u8 pa_integ_res_b[3]; + u8 pa_integ_res_c[3]; + __le16 measurement_req_id; + u8 reduced_tpc; + u8 reserved; + + __le32 tfd_info; + __le16 seq_ctl; + __le16 byte_cnt; + u8 tlc_info; + u8 ra_tid; + __le16 frame_ctrl; + struct agg_tx_status status[]; +} __packed; /* TX_RSP_API_S_VER_3 */ + +/** + * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet + * ( REPLY_TX = 0x1c ) + * @frame_count: 1 no aggregation, >1 aggregation + * @bt_kill_count: num of times blocked by bluetooth (unused for agg) + * @failure_rts: num of failures due to unsuccessful RTS + * @failure_frame: num failures due to no ACK (unused for agg) + * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the + * Tx of all the batch. RATE_MCS_* + * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. + * for agg: RTS + CTS + aggregation tx time + block-ack time. + * in usec. + * @pa_status: tx power info + * @pa_integ_res_a: tx power info + * @pa_integ_res_b: tx power info + * @pa_integ_res_c: tx power info + * @measurement_req_id: tx power info + * @reduced_tpc: transmit power reduction used + * @reserved: reserved + * @tfd_info: TFD information set by the FH + * @seq_ctl: sequence control from the Tx cmd + * @byte_cnt: byte count from the Tx cmd + * @tlc_info: TLC rate info + * @ra_tid: bits [3:0] = ra, bits [7:4] = tid + * @frame_ctrl: frame control + * @tx_queue: TX queue for this response + * @reserved2: reserved for padding/alignment + * @status: for non-agg: frame status TX_STATUS_* + * For version 6 TX response isn't received for aggregation at all. + * + * After the array of statuses comes the SSN of the SCD. Look at + * %iwl_mvm_get_scd_ssn for more details. + */ +struct iwl_mvm_tx_resp { + u8 frame_count; + u8 bt_kill_count; + u8 failure_rts; + u8 failure_frame; + __le32 initial_rate; + __le16 wireless_media_time; + + u8 pa_status; + u8 pa_integ_res_a[3]; + u8 pa_integ_res_b[3]; + u8 pa_integ_res_c[3]; + __le16 measurement_req_id; + u8 reduced_tpc; + u8 reserved; + + __le32 tfd_info; + __le16 seq_ctl; + __le16 byte_cnt; + u8 tlc_info; + u8 ra_tid; + __le16 frame_ctrl; + __le16 tx_queue; + __le16 reserved2; + struct agg_tx_status status; +} __packed; /* TX_RSP_API_S_VER_6 */ + +/** + * struct iwl_mvm_ba_notif - notifies about reception of BA + * ( BA_NOTIF = 0xc5 ) + * @sta_addr: MAC address + * @reserved: reserved + * @sta_id: Index of recipient (BA-sending) station in fw's station table + * @tid: tid of the session + * @seq_ctl: sequence control field + * @bitmap: the bitmap of the BA notification as seen in the air + * @scd_flow: the tx queue this BA relates to + * @scd_ssn: the index of the last contiguously sent packet + * @txed: number of Txed frames in this batch + * @txed_2_done: number of Acked frames in this batch + * @reduced_txp: power reduced according to TPC. This is the actual value and + * not a copy from the LQ command. Thus, if not the first rate was used + * for Tx-ing then this value will be set to 0 by FW. + * @reserved1: reserved + */ +struct iwl_mvm_ba_notif { + u8 sta_addr[ETH_ALEN]; + __le16 reserved; + + u8 sta_id; + u8 tid; + __le16 seq_ctl; + __le64 bitmap; + __le16 scd_flow; + __le16 scd_ssn; + u8 txed; + u8 txed_2_done; + u8 reduced_txp; + u8 reserved1; +} __packed; + +/** + * struct iwl_mvm_compressed_ba_tfd - progress of a TFD queue + * @q_num: TFD queue number + * @tfd_index: Index of first un-acked frame in the TFD queue + * @scd_queue: For debug only - the physical queue the TFD queue is bound to + * @tid: TID of the queue (0-7) + * @reserved: reserved for alignment + */ +struct iwl_mvm_compressed_ba_tfd { + __le16 q_num; + __le16 tfd_index; + u8 scd_queue; + u8 tid; + u8 reserved[2]; +} __packed; /* COMPRESSED_BA_TFD_API_S_VER_1 */ + +/** + * struct iwl_mvm_compressed_ba_ratid - progress of a RA TID queue + * @q_num: RA TID queue number + * @tid: TID of the queue + * @ssn: BA window current SSN + */ +struct iwl_mvm_compressed_ba_ratid { + u8 q_num; + u8 tid; + __le16 ssn; +} __packed; /* COMPRESSED_BA_RATID_API_S_VER_1 */ + +/* + * enum iwl_mvm_ba_resp_flags - TX aggregation status + * @IWL_MVM_BA_RESP_TX_AGG: generated due to BA + * @IWL_MVM_BA_RESP_TX_BAR: generated due to BA after BAR + * @IWL_MVM_BA_RESP_TX_AGG_FAIL: aggregation didn't receive BA + * @IWL_MVM_BA_RESP_TX_UNDERRUN: aggregation got underrun + * @IWL_MVM_BA_RESP_TX_BT_KILL: aggregation got BT-kill + * @IWL_MVM_BA_RESP_TX_DSP_TIMEOUT: aggregation didn't finish within the + * expected time + */ +enum iwl_mvm_ba_resp_flags { + IWL_MVM_BA_RESP_TX_AGG, + IWL_MVM_BA_RESP_TX_BAR, + IWL_MVM_BA_RESP_TX_AGG_FAIL, + IWL_MVM_BA_RESP_TX_UNDERRUN, + IWL_MVM_BA_RESP_TX_BT_KILL, + IWL_MVM_BA_RESP_TX_DSP_TIMEOUT +}; + +/** + * struct iwl_mvm_compressed_ba_notif - notifies about reception of BA + * ( BA_NOTIF = 0xc5 ) + * @flags: status flag, see the &iwl_mvm_ba_resp_flags + * @sta_id: Index of recipient (BA-sending) station in fw's station table + * @reduced_txp: power reduced according to TPC. This is the actual value and + * not a copy from the LQ command. Thus, if not the first rate was used + * for Tx-ing then this value will be set to 0 by FW. + * @initial_rate: TLC rate info, initial rate index, TLC table color + * @retry_cnt: retry count + * @query_byte_cnt: SCD query byte count + * @query_frame_cnt: SCD query frame count + * @txed: number of frames sent in the aggregation (all-TIDs) + * @done: number of frames that were Acked by the BA (all-TIDs) + * @reserved: reserved (for alignment) + * @wireless_time: Wireless-media time + * @tx_rate: the rate the aggregation was sent at + * @tfd_cnt: number of TFD-Q elements + * @ra_tid_cnt: number of RATID-Q elements + * @tfd: array of TFD queue status updates. See &iwl_mvm_compressed_ba_tfd + * for details. + * @ra_tid: array of RA-TID queue status updates. For debug purposes only. See + * &iwl_mvm_compressed_ba_ratid for more details. + */ +struct iwl_mvm_compressed_ba_notif { + __le32 flags; + u8 sta_id; + u8 reduced_txp; + u8 initial_rate; + u8 retry_cnt; + __le32 query_byte_cnt; + __le16 query_frame_cnt; + __le16 txed; + __le16 done; + __le16 reserved; + __le32 wireless_time; + __le32 tx_rate; + __le16 tfd_cnt; + __le16 ra_tid_cnt; + struct iwl_mvm_compressed_ba_tfd tfd[1]; + struct iwl_mvm_compressed_ba_ratid ra_tid[0]; +} __packed; /* COMPRESSED_BA_RES_API_S_VER_4 */ + +/** + * struct iwl_mac_beacon_cmd_v6 - beacon template command + * @tx: the tx commands associated with the beacon frame + * @template_id: currently equal to the mac context id of the coresponding + * mac. + * @tim_idx: the offset of the tim IE in the beacon + * @tim_size: the length of the tim IE + * @frame: the template of the beacon frame + */ +struct iwl_mac_beacon_cmd_v6 { + struct iwl_tx_cmd tx; + __le32 template_id; + __le32 tim_idx; + __le32 tim_size; + struct ieee80211_hdr frame[0]; +} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_6 */ + +/** + * struct iwl_mac_beacon_cmd_data - data of beacon template with offloaded CSA + * @template_id: currently equal to the mac context id of the coresponding + * mac. + * @tim_idx: the offset of the tim IE in the beacon + * @tim_size: the length of the tim IE + * @ecsa_offset: offset to the ECSA IE if present + * @csa_offset: offset to the CSA IE if present + * @frame: the template of the beacon frame + */ +struct iwl_mac_beacon_cmd_data { + __le32 template_id; + __le32 tim_idx; + __le32 tim_size; + __le32 ecsa_offset; + __le32 csa_offset; + struct ieee80211_hdr frame[0]; +}; + +/** + * struct iwl_mac_beacon_cmd_v7 - beacon template command with offloaded CSA + * @tx: the tx commands associated with the beacon frame + * @data: see &iwl_mac_beacon_cmd_data + */ +struct iwl_mac_beacon_cmd_v7 { + struct iwl_tx_cmd tx; + struct iwl_mac_beacon_cmd_data data; +} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_7 */ + +/** + * struct iwl_mac_beacon_cmd - beacon template command with offloaded CSA + * @byte_cnt: byte count of the beacon frame + * @flags: for future use + * @reserved: reserved + * @data: see &iwl_mac_beacon_cmd_data + */ +struct iwl_mac_beacon_cmd { + __le16 byte_cnt; + __le16 flags; + __le64 reserved; + struct iwl_mac_beacon_cmd_data data; +} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_8 */ + +struct iwl_beacon_notif { + struct iwl_mvm_tx_resp beacon_notify_hdr; + __le64 tsf; + __le32 ibss_mgr_status; +} __packed; + +/** + * struct iwl_extended_beacon_notif - notifies about beacon transmission + * @beacon_notify_hdr: tx response command associated with the beacon + * @tsf: last beacon tsf + * @ibss_mgr_status: whether IBSS is manager + * @gp2: last beacon time in gp2 + */ +struct iwl_extended_beacon_notif { + struct iwl_mvm_tx_resp beacon_notify_hdr; + __le64 tsf; + __le32 ibss_mgr_status; + __le32 gp2; +} __packed; /* BEACON_NTFY_API_S_VER_5 */ + +/** + * enum iwl_dump_control - dump (flush) control flags + * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty + * and the TFD queues are empty. + */ +enum iwl_dump_control { + DUMP_TX_FIFO_FLUSH = BIT(1), +}; + +/** + * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command + * @queues_ctl: bitmap of queues to flush + * @flush_ctl: control flags + * @reserved: reserved + */ +struct iwl_tx_path_flush_cmd_v1 { + __le32 queues_ctl; + __le16 flush_ctl; + __le16 reserved; +} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */ + +/** + * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command + * @sta_id: station ID to flush + * @tid_mask: TID mask to flush + * @reserved: reserved + */ +struct iwl_tx_path_flush_cmd { + __le32 sta_id; + __le16 tid_mask; + __le16 reserved; +} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_2 */ + +/* Available options for the SCD_QUEUE_CFG HCMD */ +enum iwl_scd_cfg_actions { + SCD_CFG_DISABLE_QUEUE = 0x0, + SCD_CFG_ENABLE_QUEUE = 0x1, + SCD_CFG_UPDATE_QUEUE_TID = 0x2, +}; + +/** + * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command + * @token: unused + * @sta_id: station id + * @tid: TID + * @scd_queue: scheduler queue to confiug + * @action: 1 queue enable, 0 queue disable, 2 change txq's tid owner + * Value is one of &enum iwl_scd_cfg_actions options + * @aggregate: 1 aggregated queue, 0 otherwise + * @tx_fifo: &enum iwl_mvm_tx_fifo + * @window: BA window size + * @ssn: SSN for the BA agreement + * @reserved: reserved + */ +struct iwl_scd_txq_cfg_cmd { + u8 token; + u8 sta_id; + u8 tid; + u8 scd_queue; + u8 action; + u8 aggregate; + u8 tx_fifo; + u8 window; + __le16 ssn; + __le16 reserved; +} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */ + +/** + * struct iwl_scd_txq_cfg_rsp + * @token: taken from the command + * @sta_id: station id from the command + * @tid: tid from the command + * @scd_queue: scd_queue from the command + */ +struct iwl_scd_txq_cfg_rsp { + u8 token; + u8 sta_id; + u8 tid; + u8 scd_queue; +} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */ + +#endif /* __iwl_fw_api_tx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h new file mode 100644 index 000000000000..7e7ebe82729b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h @@ -0,0 +1,156 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_fw_api_txq_h__ +#define __iwl_fw_api_txq_h__ + +/* Tx queue numbers for non-DQA mode */ +enum { + IWL_MVM_OFFCHANNEL_QUEUE = 8, + IWL_MVM_CMD_QUEUE = 9, +}; + +/* + * DQA queue numbers + * + * @IWL_MVM_DQA_CMD_QUEUE: a queue reserved for sending HCMDs to the FW + * @IWL_MVM_DQA_AUX_QUEUE: a queue reserved for aux frames + * @IWL_MVM_DQA_P2P_DEVICE_QUEUE: a queue reserved for P2P device frames + * @IWL_MVM_DQA_GCAST_QUEUE: a queue reserved for P2P GO/SoftAP GCAST frames + * @IWL_MVM_DQA_BSS_CLIENT_QUEUE: a queue reserved for BSS activity, to ensure + * that we are never left without the possibility to connect to an AP. + * @IWL_MVM_DQA_MIN_MGMT_QUEUE: first TXQ in pool for MGMT and non-QOS frames. + * Each MGMT queue is mapped to a single STA + * MGMT frames are frames that return true on ieee80211_is_mgmt() + * @IWL_MVM_DQA_MAX_MGMT_QUEUE: last TXQ in pool for MGMT frames + * @IWL_MVM_DQA_AP_PROBE_RESP_QUEUE: a queue reserved for P2P GO/SoftAP probe + * responses + * @IWL_MVM_DQA_MIN_DATA_QUEUE: first TXQ in pool for DATA frames. + * DATA frames are intended for !ieee80211_is_mgmt() frames, but if + * the MGMT TXQ pool is exhausted, mgmt frames can be sent on DATA queues + * as well + * @IWL_MVM_DQA_MAX_DATA_QUEUE: last TXQ in pool for DATA frames + */ +enum iwl_mvm_dqa_txq { + IWL_MVM_DQA_CMD_QUEUE = 0, + IWL_MVM_DQA_AUX_QUEUE = 1, + IWL_MVM_DQA_P2P_DEVICE_QUEUE = 2, + IWL_MVM_DQA_GCAST_QUEUE = 3, + IWL_MVM_DQA_BSS_CLIENT_QUEUE = 4, + IWL_MVM_DQA_MIN_MGMT_QUEUE = 5, + IWL_MVM_DQA_MAX_MGMT_QUEUE = 8, + IWL_MVM_DQA_AP_PROBE_RESP_QUEUE = 9, + IWL_MVM_DQA_MIN_DATA_QUEUE = 10, + IWL_MVM_DQA_MAX_DATA_QUEUE = 31, +}; + +enum iwl_mvm_tx_fifo { + IWL_MVM_TX_FIFO_BK = 0, + IWL_MVM_TX_FIFO_BE, + IWL_MVM_TX_FIFO_VI, + IWL_MVM_TX_FIFO_VO, + IWL_MVM_TX_FIFO_MCAST = 5, + IWL_MVM_TX_FIFO_CMD = 7, +}; + +/** + * iwl_tx_queue_cfg_actions - TXQ config options + * @TX_QUEUE_CFG_ENABLE_QUEUE: enable a queue + * @TX_QUEUE_CFG_TFD_SHORT_FORMAT: use short TFD format + */ +enum iwl_tx_queue_cfg_actions { + TX_QUEUE_CFG_ENABLE_QUEUE = BIT(0), + TX_QUEUE_CFG_TFD_SHORT_FORMAT = BIT(1), +}; + +/** + * struct iwl_tx_queue_cfg_cmd - txq hw scheduler config command + * @sta_id: station id + * @tid: tid of the queue + * @flags: see &enum iwl_tx_queue_cfg_actions + * @cb_size: size of TFD cyclic buffer. Value is exponent - 3. + * Minimum value 0 (8 TFDs), maximum value 5 (256 TFDs) + * @byte_cnt_addr: address of byte count table + * @tfdq_addr: address of TFD circular buffer + */ +struct iwl_tx_queue_cfg_cmd { + u8 sta_id; + u8 tid; + __le16 flags; + __le32 cb_size; + __le64 byte_cnt_addr; + __le64 tfdq_addr; +} __packed; /* TX_QUEUE_CFG_CMD_API_S_VER_2 */ + +/** + * struct iwl_tx_queue_cfg_rsp - response to txq hw scheduler config + * @queue_number: queue number assigned to this RA -TID + * @flags: set on failure + * @write_pointer: initial value for write pointer + */ +struct iwl_tx_queue_cfg_rsp { + __le16 queue_number; + __le16 flags; + __le16 write_pointer; + __le16 reserved; +} __packed; /* TX_QUEUE_CFG_RSP_API_S_VER_2 */ + +#endif /* __iwl_fw_api_txq_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/paging.c b/drivers/net/wireless/intel/iwlwifi/fw/paging.c index 2f4044922be3..1610722b8099 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/paging.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/paging.c @@ -61,10 +61,7 @@ *****************************************************************************/ #include "iwl-drv.h" #include "runtime.h" - -/* FIXME */ -#define FW_PAGING_BLOCK_CMD 0x4f -#define FW_GET_ITEM_CMD 0x1a +#include "fw/api/commands.h" void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 02f1bc985383..75575290a3e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -61,7 +61,8 @@ #include "iwl-config.h" #include "iwl-trans.h" #include "img.h" -#include "api.h" +#include "fw/api/debug.h" +#include "fw/api/paging.h" struct iwl_fw_runtime_ops { int (*dump_start)(void *ctx); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c index 053993bf00f9..065a951cefba 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/smem.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c @@ -61,11 +61,7 @@ *****************************************************************************/ #include "iwl-drv.h" #include "runtime.h" - -/* FIXME */ -#define SHARED_MEM_CFG_CMD 0x00 -#define SYSTEM_GROUP 0x2 -#define SHARED_MEM_CFG 0x25 +#include "fw/api/commands.h" static void iwl_parse_shared_mem_a000(struct iwl_fw_runtime *fwrt, struct iwl_rx_packet *pkt) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index eb6842abb4c7..e90abbfba718 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -76,7 +76,8 @@ #include "iwl-config.h" #include "fw/img.h" #include "iwl-op-mode.h" -#include "fw/api.h" +#include "fw/api/cmdhdr.h" +#include "fw/api/txq.h" /** * DOC: Transport layer - what is it ? diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 34dd5c40ce77..6c5c6510428a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -67,7 +67,7 @@ #include #include -#include "fw-api-coex.h" +#include "fw/api/coex.h" #include "iwl-modparams.h" #include "mvm.h" #include "iwl-debug.h" diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index a7ac281e5cde..71a01df96f8b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -65,7 +65,7 @@ * *****************************************************************************/ #include "mvm.h" -#include "fw-api-tof.h" +#include "fw/api/tof.h" #include "debugfs.h" static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h deleted file mode 100644 index 8cd06aaa1f54..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h +++ /dev/null @@ -1,257 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_bt_coex_h__ -#define __fw_api_bt_coex_h__ - -#include -#include - -#define BITS(nb) (BIT(nb) - 1) - -enum iwl_bt_coex_lut_type { - BT_COEX_TIGHT_LUT = 0, - BT_COEX_LOOSE_LUT, - BT_COEX_TX_DIS_LUT, - - BT_COEX_MAX_LUT, - BT_COEX_INVALID_LUT = 0xff, -}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */ - -#define BT_COEX_CORUN_LUT_SIZE (32) -#define BT_REDUCED_TX_POWER_BIT BIT(7) - -enum iwl_bt_coex_mode { - BT_COEX_DISABLE = 0x0, - BT_COEX_NW = 0x1, - BT_COEX_BT = 0x2, - BT_COEX_WIFI = 0x3, -}; /* BT_COEX_MODES_E */ - -enum iwl_bt_coex_enabled_modules { - BT_COEX_MPLUT_ENABLED = BIT(0), - BT_COEX_MPLUT_BOOST_ENABLED = BIT(1), - BT_COEX_SYNC2SCO_ENABLED = BIT(2), - BT_COEX_CORUN_ENABLED = BIT(3), - BT_COEX_HIGH_BAND_RET = BIT(4), -}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */ - -/** - * struct iwl_bt_coex_cmd - bt coex configuration command - * @mode: &enum iwl_bt_coex_mode - * @enabled_modules: &enum iwl_bt_coex_enabled_modules - * - * The structure is used for the BT_COEX command. - */ -struct iwl_bt_coex_cmd { - __le32 mode; - __le32 enabled_modules; -} __packed; /* BT_COEX_CMD_API_S_VER_6 */ - -/** - * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut - * @corun_lut20: co-running 20 MHz LUT configuration - * @corun_lut40: co-running 40 MHz LUT configuration - * - * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command. - */ -struct iwl_bt_coex_corun_lut_update_cmd { - __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE]; - __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE]; -} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */ - -/** - * struct iwl_bt_coex_reduced_txp_update_cmd - * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the - * bits are the sta_id (value) - */ -struct iwl_bt_coex_reduced_txp_update_cmd { - __le32 reduced_txp; -} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */ - -/** - * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command - * @bt_primary_ci: primary channel inhibition bitmap - * @primary_ch_phy_id: primary channel PHY ID - * @bt_secondary_ci: secondary channel inhibition bitmap - * @secondary_ch_phy_id: secondary channel PHY ID - * - * Used for BT_COEX_CI command - */ -struct iwl_bt_coex_ci_cmd { - __le64 bt_primary_ci; - __le32 primary_ch_phy_id; - - __le64 bt_secondary_ci; - __le32 secondary_ch_phy_id; -} __packed; /* BT_CI_MSG_API_S_VER_2 */ - -#define BT_MBOX(n_dw, _msg, _pos, _nbits) \ - BT_MBOX##n_dw##_##_msg##_POS = (_pos), \ - BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS - -enum iwl_bt_mxbox_dw0 { - BT_MBOX(0, LE_SLAVE_LAT, 0, 3), - BT_MBOX(0, LE_PROF1, 3, 1), - BT_MBOX(0, LE_PROF2, 4, 1), - BT_MBOX(0, LE_PROF_OTHER, 5, 1), - BT_MBOX(0, CHL_SEQ_N, 8, 4), - BT_MBOX(0, INBAND_S, 13, 1), - BT_MBOX(0, LE_MIN_RSSI, 16, 4), - BT_MBOX(0, LE_SCAN, 20, 1), - BT_MBOX(0, LE_ADV, 21, 1), - BT_MBOX(0, LE_MAX_TX_POWER, 24, 4), - BT_MBOX(0, OPEN_CON_1, 28, 2), -}; - -enum iwl_bt_mxbox_dw1 { - BT_MBOX(1, BR_MAX_TX_POWER, 0, 4), - BT_MBOX(1, IP_SR, 4, 1), - BT_MBOX(1, LE_MSTR, 5, 1), - BT_MBOX(1, AGGR_TRFC_LD, 8, 6), - BT_MBOX(1, MSG_TYPE, 16, 3), - BT_MBOX(1, SSN, 19, 2), -}; - -enum iwl_bt_mxbox_dw2 { - BT_MBOX(2, SNIFF_ACT, 0, 3), - BT_MBOX(2, PAG, 3, 1), - BT_MBOX(2, INQUIRY, 4, 1), - BT_MBOX(2, CONN, 5, 1), - BT_MBOX(2, SNIFF_INTERVAL, 8, 5), - BT_MBOX(2, DISC, 13, 1), - BT_MBOX(2, SCO_TX_ACT, 16, 2), - BT_MBOX(2, SCO_RX_ACT, 18, 2), - BT_MBOX(2, ESCO_RE_TX, 20, 2), - BT_MBOX(2, SCO_DURATION, 24, 6), -}; - -enum iwl_bt_mxbox_dw3 { - BT_MBOX(3, SCO_STATE, 0, 1), - BT_MBOX(3, SNIFF_STATE, 1, 1), - BT_MBOX(3, A2DP_STATE, 2, 1), - BT_MBOX(3, ACL_STATE, 3, 1), - BT_MBOX(3, MSTR_STATE, 4, 1), - BT_MBOX(3, OBX_STATE, 5, 1), - BT_MBOX(3, OPEN_CON_2, 8, 2), - BT_MBOX(3, TRAFFIC_LOAD, 10, 2), - BT_MBOX(3, CHL_SEQN_LSB, 12, 1), - BT_MBOX(3, INBAND_P, 13, 1), - BT_MBOX(3, MSG_TYPE_2, 16, 3), - BT_MBOX(3, SSN_2, 19, 2), - BT_MBOX(3, UPDATE_REQUEST, 21, 1), -}; - -#define BT_MBOX_MSG(_notif, _num, _field) \ - ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ - >> BT_MBOX##_num##_##_field##_POS) - -enum iwl_bt_activity_grading { - BT_OFF = 0, - BT_ON_NO_CONNECTION = 1, - BT_LOW_TRAFFIC = 2, - BT_HIGH_TRAFFIC = 3, - - BT_MAX_AG, -}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */ - -enum iwl_bt_ci_compliance { - BT_CI_COMPLIANCE_NONE = 0, - BT_CI_COMPLIANCE_PRIMARY = 1, - BT_CI_COMPLIANCE_SECONDARY = 2, - BT_CI_COMPLIANCE_BOTH = 3, -}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */ - -#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \ - (_ttc_rrc_status & BIT(_phy_id)) - -#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \ - ((_ttc_rrc_status >> 4) & BIT(_phy_id)) - -/** - * struct iwl_bt_coex_profile_notif - notification about BT coex - * @mbox_msg: message from BT to WiFi - * @msg_idx: the index of the message - * @bt_ci_compliance: enum %iwl_bt_ci_compliance - * @primary_ch_lut: LUT used for primary channel &enum iwl_bt_coex_lut_type - * @secondary_ch_lut: LUT used for secondary channel &enum iwl_bt_coex_lut_type - * @bt_activity_grading: the activity of BT &enum iwl_bt_activity_grading - * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY - * @reserved: reserved - */ -struct iwl_bt_coex_profile_notif { - __le32 mbox_msg[4]; - __le32 msg_idx; - __le32 bt_ci_compliance; - - __le32 primary_ch_lut; - __le32 secondary_ch_lut; - __le32 bt_activity_grading; - u8 ttc_rrc_status; - u8 reserved[3]; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ - -#endif /* __fw_api_bt_coex_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h deleted file mode 100644 index d4a4c28b7192..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h +++ /dev/null @@ -1,471 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_d3_h__ -#define __fw_api_d3_h__ - -/** - * enum iwl_d3_wakeup_flags - D3 manager wakeup flags - * @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert - */ -enum iwl_d3_wakeup_flags { - IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0), -}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */ - -/** - * struct iwl_d3_manager_config - D3 manager configuration command - * @min_sleep_time: minimum sleep time (in usec) - * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags - * @wakeup_host_timer: force wakeup after this many seconds - * - * The structure is used for the D3_CONFIG_CMD command. - */ -struct iwl_d3_manager_config { - __le32 min_sleep_time; - __le32 wakeup_flags; - __le32 wakeup_host_timer; -} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */ - - -/* TODO: OFFLOADS_QUERY_API_S_VER_1 */ - -/** - * enum iwl_d3_proto_offloads - enabled protocol offloads - * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled - * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled - * @IWL_D3_PROTO_IPV4_VALID: IPv4 data is valid - * @IWL_D3_PROTO_IPV6_VALID: IPv6 data is valid - */ -enum iwl_proto_offloads { - IWL_D3_PROTO_OFFLOAD_ARP = BIT(0), - IWL_D3_PROTO_OFFLOAD_NS = BIT(1), - IWL_D3_PROTO_IPV4_VALID = BIT(2), - IWL_D3_PROTO_IPV6_VALID = BIT(3), -}; - -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12 - -#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4 -#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2 - -/** - * struct iwl_proto_offload_cmd_common - ARP/NS offload common part - * @enabled: enable flags - * @remote_ipv4_addr: remote address to answer to (or zero if all) - * @host_ipv4_addr: our IPv4 address to respond to queries for - * @arp_mac_addr: our MAC address for ARP responses - * @reserved: unused - */ -struct iwl_proto_offload_cmd_common { - __le32 enabled; - __be32 remote_ipv4_addr; - __be32 host_ipv4_addr; - u8 arp_mac_addr[ETH_ALEN]; - __le16 reserved; -} __packed; - -/** - * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @remote_ipv6_addr: remote address to answer to (or zero if all) - * @solicited_node_ipv6_addr: broken -- solicited node address exists - * for each target address - * @target_ipv6_addr: our target addresses - * @ndp_mac_addr: neighbor solicitation response MAC address - * @reserved2: reserved - */ -struct iwl_proto_offload_cmd_v1 { - struct iwl_proto_offload_cmd_common common; - u8 remote_ipv6_addr[16]; - u8 solicited_node_ipv6_addr[16]; - u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16]; - u8 ndp_mac_addr[ETH_ALEN]; - __le16 reserved2; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */ - -/** - * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @remote_ipv6_addr: remote address to answer to (or zero if all) - * @solicited_node_ipv6_addr: broken -- solicited node address exists - * for each target address - * @target_ipv6_addr: our target addresses - * @ndp_mac_addr: neighbor solicitation response MAC address - * @num_valid_ipv6_addrs: number of valid IPv6 addresses - * @reserved2: reserved - */ -struct iwl_proto_offload_cmd_v2 { - struct iwl_proto_offload_cmd_common common; - u8 remote_ipv6_addr[16]; - u8 solicited_node_ipv6_addr[16]; - u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16]; - u8 ndp_mac_addr[ETH_ALEN]; - u8 num_valid_ipv6_addrs; - u8 reserved2[3]; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ - -struct iwl_ns_config { - struct in6_addr source_ipv6_addr; - struct in6_addr dest_ipv6_addr; - u8 target_mac_addr[ETH_ALEN]; - __le16 reserved; -} __packed; /* NS_OFFLOAD_CONFIG */ - -struct iwl_targ_addr { - struct in6_addr addr; - __le32 config_num; -} __packed; /* TARGET_IPV6_ADDRESS */ - -/** - * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @num_valid_ipv6_addrs: number of valid IPv6 addresses - * @targ_addrs: target IPv6 addresses - * @ns_config: NS offload configurations - */ -struct iwl_proto_offload_cmd_v3_small { - struct iwl_proto_offload_cmd_common common; - __le32 num_valid_ipv6_addrs; - struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S]; - struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S]; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ - -/** - * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @num_valid_ipv6_addrs: number of valid IPv6 addresses - * @targ_addrs: target IPv6 addresses - * @ns_config: NS offload configurations - */ -struct iwl_proto_offload_cmd_v3_large { - struct iwl_proto_offload_cmd_common common; - __le32 num_valid_ipv6_addrs; - struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L]; - struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L]; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ - -/* - * WOWLAN_PATTERNS - */ -#define IWL_WOWLAN_MIN_PATTERN_LEN 16 -#define IWL_WOWLAN_MAX_PATTERN_LEN 128 - -struct iwl_wowlan_pattern { - u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; - u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN]; - u8 mask_size; - u8 pattern_size; - __le16 reserved; -} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */ - -#define IWL_WOWLAN_MAX_PATTERNS 20 - -struct iwl_wowlan_patterns_cmd { - __le32 n_patterns; - struct iwl_wowlan_pattern patterns[]; -} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */ - -enum iwl_wowlan_wakeup_filters { - IWL_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0), - IWL_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1), - IWL_WOWLAN_WAKEUP_BEACON_MISS = BIT(2), - IWL_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3), - IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4), - IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5), - IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6), - IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(7), - IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8), - IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9), - IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10), - IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11), - IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12), - IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13), - IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14), - IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15), - IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16), -}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */ - -enum iwl_wowlan_flags { - IS_11W_ASSOC = BIT(0), - ENABLE_L3_FILTERING = BIT(1), - ENABLE_NBNS_FILTERING = BIT(2), - ENABLE_DHCP_FILTERING = BIT(3), - ENABLE_STORE_BEACON = BIT(4), -}; - -/** - * struct iwl_wowlan_config_cmd - WoWLAN configuration - * @wakeup_filter: filter from &enum iwl_wowlan_wakeup_filters - * @non_qos_seq: non-QoS sequence counter to use next - * @qos_seq: QoS sequence counters to use next - * @wowlan_ba_teardown_tids: bitmap of BA sessions to tear down - * @is_11n_connection: indicates HT connection - * @offloading_tid: TID reserved for firmware use - * @flags: extra flags, see &enum iwl_wowlan_flags - * @reserved: reserved - */ -struct iwl_wowlan_config_cmd { - __le32 wakeup_filter; - __le16 non_qos_seq; - __le16 qos_seq[8]; - u8 wowlan_ba_teardown_tids; - u8 is_11n_connection; - u8 offloading_tid; - u8 flags; - u8 reserved[2]; -} __packed; /* WOWLAN_CONFIG_API_S_VER_4 */ - -/* - * WOWLAN_TSC_RSC_PARAMS - */ -#define IWL_NUM_RSC 16 - -struct tkip_sc { - __le16 iv16; - __le16 pad; - __le32 iv32; -} __packed; /* TKIP_SC_API_U_VER_1 */ - -struct iwl_tkip_rsc_tsc { - struct tkip_sc unicast_rsc[IWL_NUM_RSC]; - struct tkip_sc multicast_rsc[IWL_NUM_RSC]; - struct tkip_sc tsc; -} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */ - -struct aes_sc { - __le64 pn; -} __packed; /* TKIP_AES_SC_API_U_VER_1 */ - -struct iwl_aes_rsc_tsc { - struct aes_sc unicast_rsc[IWL_NUM_RSC]; - struct aes_sc multicast_rsc[IWL_NUM_RSC]; - struct aes_sc tsc; -} __packed; /* AES_TSC_RSC_API_S_VER_1 */ - -union iwl_all_tsc_rsc { - struct iwl_tkip_rsc_tsc tkip; - struct iwl_aes_rsc_tsc aes; -}; /* ALL_TSC_RSC_API_S_VER_2 */ - -struct iwl_wowlan_rsc_tsc_params_cmd { - union iwl_all_tsc_rsc all_tsc_rsc; -} __packed; /* ALL_TSC_RSC_API_S_VER_2 */ - -#define IWL_MIC_KEY_SIZE 8 -struct iwl_mic_keys { - u8 tx[IWL_MIC_KEY_SIZE]; - u8 rx_unicast[IWL_MIC_KEY_SIZE]; - u8 rx_mcast[IWL_MIC_KEY_SIZE]; -} __packed; /* MIC_KEYS_API_S_VER_1 */ - -#define IWL_P1K_SIZE 5 -struct iwl_p1k_cache { - __le16 p1k[IWL_P1K_SIZE]; -} __packed; - -#define IWL_NUM_RX_P1K_CACHE 2 - -struct iwl_wowlan_tkip_params_cmd { - struct iwl_mic_keys mic_keys; - struct iwl_p1k_cache tx; - struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE]; - struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE]; -} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */ - -#define IWL_KCK_MAX_SIZE 32 -#define IWL_KEK_MAX_SIZE 32 - -struct iwl_wowlan_kek_kck_material_cmd { - u8 kck[IWL_KCK_MAX_SIZE]; - u8 kek[IWL_KEK_MAX_SIZE]; - __le16 kck_len; - __le16 kek_len; - __le64 replay_ctr; -} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */ - -#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87 - -enum iwl_wowlan_rekey_status { - IWL_WOWLAN_REKEY_POST_REKEY = 0, - IWL_WOWLAN_REKEY_WHILE_REKEY = 1, -}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */ - -enum iwl_wowlan_wakeup_reason { - IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS = 0, - IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET = BIT(0), - IWL_WOWLAN_WAKEUP_BY_PATTERN = BIT(1), - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON = BIT(2), - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH = BIT(3), - IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE = BIT(4), - IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED = BIT(5), - IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR = BIT(6), - IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST = BIT(7), - IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL = BIT(11), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), - IWL_WOWLAN_WAKEUP_BY_IOAC_MAGIC_PACKET = BIT(13), - IWL_WOWLAN_WAKEUP_BY_D3_WAKEUP_HOST_TIMER = BIT(14), - IWL_WOWLAN_WAKEUP_BY_RXFRAME_FILTERED_IN = BIT(15), - IWL_WOWLAN_WAKEUP_BY_BEACON_FILTERED_IN = BIT(16), - -}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ - -struct iwl_wowlan_gtk_status { - u8 key_index; - u8 reserved[3]; - u8 decrypt_key[16]; - u8 tkip_mic_key[8]; - struct iwl_wowlan_rsc_tsc_params_cmd rsc; -} __packed; /* WOWLAN_GTK_MATERIAL_VER_1 */ - -/** - * struct iwl_wowlan_status - WoWLAN status - * @gtk: GTK data - * @replay_ctr: GTK rekey replay counter - * @pattern_number: number of the matched pattern - * @non_qos_seq_ctr: non-QoS sequence counter to use next - * @qos_seq_ctr: QoS sequence counters to use next - * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason - * @num_of_gtk_rekeys: number of GTK rekeys - * @transmitted_ndps: number of transmitted neighbor discovery packets - * @received_beacons: number of received beacons - * @wake_packet_length: wakeup packet length - * @wake_packet_bufsize: wakeup packet buffer size - * @wake_packet: wakeup packet - */ -struct iwl_wowlan_status { - struct iwl_wowlan_gtk_status gtk; - __le64 replay_ctr; - __le16 pattern_number; - __le16 non_qos_seq_ctr; - __le16 qos_seq_ctr[8]; - __le32 wakeup_reasons; - __le32 num_of_gtk_rekeys; - __le32 transmitted_ndps; - __le32 received_beacons; - __le32 wake_packet_length; - __le32 wake_packet_bufsize; - u8 wake_packet[]; /* can be truncated from _length to _bufsize */ -} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */ - -#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64 -#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128 -#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048 - -struct iwl_tcp_packet_info { - __le16 tcp_pseudo_header_checksum; - __le16 tcp_payload_length; -} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */ - -struct iwl_tcp_packet { - struct iwl_tcp_packet_info info; - u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; - u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN]; -} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ - -struct iwl_remote_wake_packet { - struct iwl_tcp_packet_info info; - u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; - u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN]; -} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ - -struct iwl_wowlan_remote_wake_config { - __le32 connection_max_time; /* unused */ - /* TCP_PROTOCOL_CONFIG_API_S_VER_1 */ - u8 max_syn_retries; - u8 max_data_retries; - u8 tcp_syn_ack_timeout; - u8 tcp_ack_timeout; - - struct iwl_tcp_packet syn_tx; - struct iwl_tcp_packet synack_rx; - struct iwl_tcp_packet keepalive_ack_rx; - struct iwl_tcp_packet fin_tx; - - struct iwl_remote_wake_packet keepalive_tx; - struct iwl_remote_wake_packet wake_rx; - - /* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */ - u8 sequence_number_offset; - u8 sequence_number_length; - u8 token_offset; - u8 token_length; - /* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */ - __le32 initial_sequence_number; - __le16 keepalive_interval; - __le16 num_tokens; - u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS]; -} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */ - -/* TODO: NetDetect API */ - -#endif /* __fw_api_d3_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h deleted file mode 100644 index 0c3350ad2f2f..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h +++ /dev/null @@ -1,396 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_mac_h__ -#define __fw_api_mac_h__ - -/* - * The first MAC indices (starting from 0) are available to the driver, - * AUX indices follows - 1 for non-CDB, 2 for CDB. - */ -#define MAC_INDEX_AUX 4 -#define MAC_INDEX_MIN_DRIVER 0 -#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX -#define NUM_MAC_INDEX (NUM_MAC_INDEX_DRIVER + 1) -#define NUM_MAC_INDEX_CDB (NUM_MAC_INDEX_DRIVER + 2) - -#define IWL_MVM_STATION_COUNT 16 -#define IWL_MVM_INVALID_STA 0xFF - -#define IWL_MVM_TDLS_STA_COUNT 4 - -enum iwl_ac { - AC_BK, - AC_BE, - AC_VI, - AC_VO, - AC_NUM, -}; - -/** - * enum iwl_mac_protection_flags - MAC context flags - * @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames, - * this will require CCK RTS/CTS2self. - * RTS/CTS will protect full burst time. - * @MAC_PROT_FLG_HT_PROT: enable HT protection - * @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions - * @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self - */ -enum iwl_mac_protection_flags { - MAC_PROT_FLG_TGG_PROTECT = BIT(3), - MAC_PROT_FLG_HT_PROT = BIT(23), - MAC_PROT_FLG_FAT_PROT = BIT(24), - MAC_PROT_FLG_SELF_CTS_EN = BIT(30), -}; - -#define MAC_FLG_SHORT_SLOT BIT(4) -#define MAC_FLG_SHORT_PREAMBLE BIT(5) - -/** - * enum iwl_mac_types - Supported MAC types - * @FW_MAC_TYPE_FIRST: lowest supported MAC type - * @FW_MAC_TYPE_AUX: Auxiliary MAC (internal) - * @FW_MAC_TYPE_LISTENER: monitor MAC type (?) - * @FW_MAC_TYPE_PIBSS: Pseudo-IBSS - * @FW_MAC_TYPE_IBSS: IBSS - * @FW_MAC_TYPE_BSS_STA: BSS (managed) station - * @FW_MAC_TYPE_P2P_DEVICE: P2P Device - * @FW_MAC_TYPE_P2P_STA: P2P client - * @FW_MAC_TYPE_GO: P2P GO - * @FW_MAC_TYPE_TEST: ? - * @FW_MAC_TYPE_MAX: highest support MAC type - */ -enum iwl_mac_types { - FW_MAC_TYPE_FIRST = 1, - FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST, - FW_MAC_TYPE_LISTENER, - FW_MAC_TYPE_PIBSS, - FW_MAC_TYPE_IBSS, - FW_MAC_TYPE_BSS_STA, - FW_MAC_TYPE_P2P_DEVICE, - FW_MAC_TYPE_P2P_STA, - FW_MAC_TYPE_GO, - FW_MAC_TYPE_TEST, - FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST -}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */ - -/** - * enum iwl_tsf_id - TSF hw timer ID - * @TSF_ID_A: use TSF A - * @TSF_ID_B: use TSF B - * @TSF_ID_C: use TSF C - * @TSF_ID_D: use TSF D - * @NUM_TSF_IDS: number of TSF timers available - */ -enum iwl_tsf_id { - TSF_ID_A = 0, - TSF_ID_B = 1, - TSF_ID_C = 2, - TSF_ID_D = 3, - NUM_TSF_IDS = 4, -}; /* TSF_ID_API_E_VER_1 */ - -/** - * struct iwl_mac_data_ap - configuration data for AP MAC context - * @beacon_time: beacon transmit time in system time - * @beacon_tsf: beacon transmit time in TSF - * @bi: beacon interval in TU - * @bi_reciprocal: 2^32 / bi - * @dtim_interval: dtim transmit time in TU - * @dtim_reciprocal: 2^32 / dtim_interval - * @mcast_qid: queue ID for multicast traffic. - * NOTE: obsolete from VER2 and on - * @beacon_template: beacon template ID - */ -struct iwl_mac_data_ap { - __le32 beacon_time; - __le64 beacon_tsf; - __le32 bi; - __le32 bi_reciprocal; - __le32 dtim_interval; - __le32 dtim_reciprocal; - __le32 mcast_qid; - __le32 beacon_template; -} __packed; /* AP_MAC_DATA_API_S_VER_2 */ - -/** - * struct iwl_mac_data_ibss - configuration data for IBSS MAC context - * @beacon_time: beacon transmit time in system time - * @beacon_tsf: beacon transmit time in TSF - * @bi: beacon interval in TU - * @bi_reciprocal: 2^32 / bi - * @beacon_template: beacon template ID - */ -struct iwl_mac_data_ibss { - __le32 beacon_time; - __le64 beacon_tsf; - __le32 bi; - __le32 bi_reciprocal; - __le32 beacon_template; -} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_sta - configuration data for station MAC context - * @is_assoc: 1 for associated state, 0 otherwise - * @dtim_time: DTIM arrival time in system time - * @dtim_tsf: DTIM arrival time in TSF - * @bi: beacon interval in TU, applicable only when associated - * @bi_reciprocal: 2^32 / bi , applicable only when associated - * @dtim_interval: DTIM interval in TU, applicable only when associated - * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated - * @listen_interval: in beacon intervals, applicable only when associated - * @assoc_id: unique ID assigned by the AP during association - * @assoc_beacon_arrive_time: TSF of first beacon after association - */ -struct iwl_mac_data_sta { - __le32 is_assoc; - __le32 dtim_time; - __le64 dtim_tsf; - __le32 bi; - __le32 bi_reciprocal; - __le32 dtim_interval; - __le32 dtim_reciprocal; - __le32 listen_interval; - __le32 assoc_id; - __le32 assoc_beacon_arrive_time; -} __packed; /* STA_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_go - configuration data for P2P GO MAC context - * @ap: iwl_mac_data_ap struct with most config data - * @ctwin: client traffic window in TU (period after TBTT when GO is present). - * 0 indicates that there is no CT window. - * @opp_ps_enabled: indicate that opportunistic PS allowed - */ -struct iwl_mac_data_go { - struct iwl_mac_data_ap ap; - __le32 ctwin; - __le32 opp_ps_enabled; -} __packed; /* GO_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context - * @sta: iwl_mac_data_sta struct with most config data - * @ctwin: client traffic window in TU (period after TBTT when GO is present). - * 0 indicates that there is no CT window. - */ -struct iwl_mac_data_p2p_sta { - struct iwl_mac_data_sta sta; - __le32 ctwin; -} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_pibss - Pseudo IBSS config data - * @stats_interval: interval in TU between statistics notifications to host. - */ -struct iwl_mac_data_pibss { - __le32 stats_interval; -} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */ - -/* - * struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC - * context. - * @is_disc_extended: if set to true, P2P Device discoverability is enabled on - * other channels as well. This should be to true only in case that the - * device is discoverable and there is an active GO. Note that setting this - * field when not needed, will increase the number of interrupts and have - * effect on the platform power, as this setting opens the Rx filters on - * all macs. - */ -struct iwl_mac_data_p2p_dev { - __le32 is_disc_extended; -} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */ - -/** - * enum iwl_mac_filter_flags - MAC context filter flags - * @MAC_FILTER_IN_PROMISC: accept all data frames - * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and - * control frames to the host - * @MAC_FILTER_ACCEPT_GRP: accept multicast frames - * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames - * @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames - * @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host - * (in station mode when associated) - * @MAC_FILTER_OUT_BCAST: filter out all broadcast frames - * @MAC_FILTER_IN_CRC32: extract FCS and append it to frames - * @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host - */ -enum iwl_mac_filter_flags { - MAC_FILTER_IN_PROMISC = BIT(0), - MAC_FILTER_IN_CONTROL_AND_MGMT = BIT(1), - MAC_FILTER_ACCEPT_GRP = BIT(2), - MAC_FILTER_DIS_DECRYPT = BIT(3), - MAC_FILTER_DIS_GRP_DECRYPT = BIT(4), - MAC_FILTER_IN_BEACON = BIT(6), - MAC_FILTER_OUT_BCAST = BIT(8), - MAC_FILTER_IN_CRC32 = BIT(11), - MAC_FILTER_IN_PROBE_REQUEST = BIT(12), -}; - -/** - * enum iwl_mac_qos_flags - QoS flags - * @MAC_QOS_FLG_UPDATE_EDCA: ? - * @MAC_QOS_FLG_TGN: HT is enabled - * @MAC_QOS_FLG_TXOP_TYPE: ? - * - */ -enum iwl_mac_qos_flags { - MAC_QOS_FLG_UPDATE_EDCA = BIT(0), - MAC_QOS_FLG_TGN = BIT(1), - MAC_QOS_FLG_TXOP_TYPE = BIT(4), -}; - -/** - * struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD - * @cw_min: Contention window, start value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x0f. - * @cw_max: Contention window, max value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x3f. - * @aifsn: Number of slots in Arbitration Interframe Space (before - * performing random backoff timing prior to Tx). Device default 1. - * @fifos_mask: FIFOs used by this MAC for this AC - * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. - * - * One instance of this config struct for each of 4 EDCA access categories - * in struct iwl_qosparam_cmd. - * - * Device will automatically increase contention window by (2*CW) + 1 for each - * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW - * value, to cap the CW value. - */ -struct iwl_ac_qos { - __le16 cw_min; - __le16 cw_max; - u8 aifsn; - u8 fifos_mask; - __le16 edca_txop; -} __packed; /* AC_QOS_API_S_VER_2 */ - -/** - * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts - * ( MAC_CONTEXT_CMD = 0x28 ) - * @id_and_color: ID and color of the MAC - * @action: action to perform, one of FW_CTXT_ACTION_* - * @mac_type: one of &enum iwl_mac_types - * @tsf_id: TSF HW timer, one of &enum iwl_tsf_id - * @node_addr: MAC address - * @reserved_for_node_addr: reserved - * @bssid_addr: BSSID - * @reserved_for_bssid_addr: reserved - * @cck_rates: basic rates available for CCK - * @ofdm_rates: basic rates available for OFDM - * @protection_flags: combination of &enum iwl_mac_protection_flags - * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise - * @short_slot: 0x10 for enabling short slots, 0 otherwise - * @filter_flags: combination of &enum iwl_mac_filter_flags - * @qos_flags: from &enum iwl_mac_qos_flags - * @ac: one iwl_mac_qos configuration for each AC - */ -struct iwl_mac_ctx_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */ - __le32 mac_type; - __le32 tsf_id; - u8 node_addr[6]; - __le16 reserved_for_node_addr; - u8 bssid_addr[6]; - __le16 reserved_for_bssid_addr; - __le32 cck_rates; - __le32 ofdm_rates; - __le32 protection_flags; - __le32 cck_short_preamble; - __le32 short_slot; - __le32 filter_flags; - /* MAC_QOS_PARAM_API_S_VER_1 */ - __le32 qos_flags; - struct iwl_ac_qos ac[AC_NUM+1]; - /* MAC_CONTEXT_COMMON_DATA_API_S */ - union { - struct iwl_mac_data_ap ap; - struct iwl_mac_data_go go; - struct iwl_mac_data_sta sta; - struct iwl_mac_data_p2p_sta p2p_sta; - struct iwl_mac_data_p2p_dev p2p_dev; - struct iwl_mac_data_pibss pibss; - struct iwl_mac_data_ibss ibss; - }; -} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */ - -static inline u32 iwl_mvm_reciprocal(u32 v) -{ - if (!v) - return 0; - return 0xFFFFFFFF / v; -} - -#define IWL_NONQOS_SEQ_GET 0x1 -#define IWL_NONQOS_SEQ_SET 0x2 -struct iwl_nonqos_seq_query_cmd { - __le32 get_set_flag; - __le32 mac_id_n_color; - __le16 value; - __le16 reserved; -} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */ - -#endif /* __fw_api_mac_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h deleted file mode 100644 index 7da57ef2454e..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h +++ /dev/null @@ -1,531 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __fw_api_power_h__ -#define __fw_api_power_h__ - -/* Power Management Commands, Responses, Notifications */ - -/** - * enum iwl_ltr_config_flags - masks for LTR config command flags - * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status - * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow - * memory access - * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR - * reg change - * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from - * D0 to D3 - * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register - * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register - * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD - * @LTR_CFG_FLAG_UPDATE_VALUES: update config values and short - * idle timeout - */ -enum iwl_ltr_config_flags { - LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0), - LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1), - LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2), - LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3), - LTR_CFG_FLAG_SW_SET_SHORT = BIT(4), - LTR_CFG_FLAG_SW_SET_LONG = BIT(5), - LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6), - LTR_CFG_FLAG_UPDATE_VALUES = BIT(7), -}; - -/** - * struct iwl_ltr_config_cmd_v1 - configures the LTR - * @flags: See &enum iwl_ltr_config_flags - * @static_long: static LTR Long register value. - * @static_short: static LTR Short register value. - */ -struct iwl_ltr_config_cmd_v1 { - __le32 flags; - __le32 static_long; - __le32 static_short; -} __packed; /* LTR_CAPABLE_API_S_VER_1 */ - -#define LTR_VALID_STATES_NUM 4 - -/** - * struct iwl_ltr_config_cmd - configures the LTR - * @flags: See &enum iwl_ltr_config_flags - * @static_long: static LTR Long register value. - * @static_short: static LTR Short register value. - * @ltr_cfg_values: LTR parameters table values (in usec) in folowing order: - * TX, RX, Short Idle, Long Idle. Used only if %LTR_CFG_FLAG_UPDATE_VALUES - * is set. - * @ltr_short_idle_timeout: LTR Short Idle timeout (in usec). Used only if - * %LTR_CFG_FLAG_UPDATE_VALUES is set. - */ -struct iwl_ltr_config_cmd { - __le32 flags; - __le32 static_long; - __le32 static_short; - __le32 ltr_cfg_values[LTR_VALID_STATES_NUM]; - __le32 ltr_short_idle_timeout; -} __packed; /* LTR_CAPABLE_API_S_VER_2 */ - -/* Radio LP RX Energy Threshold measured in dBm */ -#define POWER_LPRX_RSSI_THRESHOLD 75 -#define POWER_LPRX_RSSI_THRESHOLD_MAX 94 -#define POWER_LPRX_RSSI_THRESHOLD_MIN 30 - -/** - * enum iwl_power_flags - masks for power table command flags - * @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off - * receiver and transmitter. '0' - does not allow. - * @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management, - * '1' Driver enables PM (use rest of parameters) - * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM, - * '1' PM could sleep over DTIM till listen Interval. - * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all - * access categories are both delivery and trigger enabled. - * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and - * PBW Snoozing enabled - * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask - * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. - * @POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving - * detection enablement -*/ -enum iwl_power_flags { - POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), - POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1), - POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2), - POWER_FLAGS_SNOOZE_ENA_MSK = BIT(5), - POWER_FLAGS_BT_SCO_ENA = BIT(8), - POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), - POWER_FLAGS_LPRX_ENA_MSK = BIT(11), - POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12), -}; - -#define IWL_POWER_VEC_SIZE 5 - -/** - * struct iwl_powertable_cmd - legacy power command. Beside old API support this - * is used also with a new power API for device wide power settings. - * POWER_TABLE_CMD = 0x77 (command, has simple generic response) - * - * @flags: Power table command flags from POWER_FLAGS_* - * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. - * Minimum allowed:- 3 * DTIM. Keep alive period must be - * set regardless of power scheme or current power state. - * FW use this value also when PM is disabled. - * @debug_flags: debug flags - * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to - * PSM transition - legacy PM - * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to - * PSM transition - legacy PM - * @sleep_interval: not in use - * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag - * is set. For example, if it is required to skip over - * one DTIM, this value need to be set to 2 (DTIM periods). - * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. - * Default: 80dbm - */ -struct iwl_powertable_cmd { - /* PM_POWER_TABLE_CMD_API_S_VER_6 */ - __le16 flags; - u8 keep_alive_seconds; - u8 debug_flags; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 sleep_interval[IWL_POWER_VEC_SIZE]; - __le32 skip_dtim_periods; - __le32 lprx_rssi_threshold; -} __packed; - -/** - * enum iwl_device_power_flags - masks for device power command flags - * @DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK: - * '1' Allow to save power by turning off - * receiver and transmitter. '0' - does not allow. -*/ -enum iwl_device_power_flags { - DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), -}; - -/** - * struct iwl_device_power_cmd - device wide power command. - * DEVICE_POWER_CMD = 0x77 (command, has simple generic response) - * - * @flags: Power table command flags from &enum iwl_device_power_flags - * @reserved: reserved (padding) - */ -struct iwl_device_power_cmd { - /* PM_POWER_TABLE_CMD_API_S_VER_6 */ - __le16 flags; - __le16 reserved; -} __packed; - -/** - * struct iwl_mac_power_cmd - New power command containing uAPSD support - * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) - * @id_and_color: MAC contex identifier, &enum iwl_mvm_id_and_color - * @flags: Power table command flags from POWER_FLAGS_* - * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. - * Minimum allowed:- 3 * DTIM. Keep alive period must be - * set regardless of power scheme or current power state. - * FW use this value also when PM is disabled. - * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to - * PSM transition - legacy PM - * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to - * PSM transition - legacy PM - * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag - * is set. For example, if it is required to skip over - * one DTIM, this value need to be set to 2 (DTIM periods). - * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to - * PSM transition - uAPSD - * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to - * PSM transition - uAPSD - * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. - * Default: 80dbm - * @snooze_interval: Maximum time between attempts to retrieve buffered data - * from the AP [msec] - * @snooze_window: A window of time in which PBW snoozing insures that all - * packets received. It is also the minimum time from last - * received unicast RX packet, before client stops snoozing - * for data. [msec] - * @snooze_step: TBD - * @qndp_tid: TID client shall use for uAPSD QNDP triggers - * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for - * each corresponding AC. - * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values. - * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct - * values. - * @heavy_tx_thld_packets: TX threshold measured in number of packets - * @heavy_rx_thld_packets: RX threshold measured in number of packets - * @heavy_tx_thld_percentage: TX threshold measured in load's percentage - * @heavy_rx_thld_percentage: RX threshold measured in load's percentage - * @limited_ps_threshold: (unused) - * @reserved: reserved (padding) - */ -struct iwl_mac_power_cmd { - /* CONTEXT_DESC_API_T_VER_1 */ - __le32 id_and_color; - - /* CLIENT_PM_POWER_TABLE_S_VER_1 */ - __le16 flags; - __le16 keep_alive_seconds; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 rx_data_timeout_uapsd; - __le32 tx_data_timeout_uapsd; - u8 lprx_rssi_threshold; - u8 skip_dtim_periods; - __le16 snooze_interval; - __le16 snooze_window; - u8 snooze_step; - u8 qndp_tid; - u8 uapsd_ac_flags; - u8 uapsd_max_sp; - u8 heavy_tx_thld_packets; - u8 heavy_rx_thld_packets; - u8 heavy_tx_thld_percentage; - u8 heavy_rx_thld_percentage; - u8 limited_ps_threshold; - u8 reserved; -} __packed; - -/* - * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when - * associated AP is identified as improperly implementing uAPSD protocol. - * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78 - * @sta_id: index of station in uCode's station table - associated AP ID in - * this context. - */ -struct iwl_uapsd_misbehaving_ap_notif { - __le32 sta_id; - u8 mac_id; - u8 reserved[3]; -} __packed; - -/** - * struct iwl_reduce_tx_power_cmd - TX power reduction command - * REDUCE_TX_POWER_CMD = 0x9f - * @flags: (reserved for future implementation) - * @mac_context_id: id of the mac ctx for which we are reducing TX power. - * @pwr_restriction: TX power restriction in dBms. - */ -struct iwl_reduce_tx_power_cmd { - u8 flags; - u8 mac_context_id; - __le16 pwr_restriction; -} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */ - -enum iwl_dev_tx_power_cmd_mode { - IWL_TX_POWER_MODE_SET_MAC = 0, - IWL_TX_POWER_MODE_SET_DEVICE = 1, - IWL_TX_POWER_MODE_SET_CHAINS = 2, - IWL_TX_POWER_MODE_SET_ACK = 3, -}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_4 */; - -#define IWL_NUM_CHAIN_LIMITS 2 -#define IWL_NUM_SUB_BANDS 5 - -/** - * struct iwl_dev_tx_power_cmd - TX power reduction command - * @set_mode: see &enum iwl_dev_tx_power_cmd_mode - * @mac_context_id: id of the mac ctx for which we are reducing TX power. - * @pwr_restriction: TX power restriction in 1/8 dBms. - * @dev_24: device TX power restriction in 1/8 dBms - * @dev_52_low: device TX power restriction upper band - low - * @dev_52_high: device TX power restriction upper band - high - * @per_chain_restriction: per chain restrictions - */ -struct iwl_dev_tx_power_cmd_v3 { - __le32 set_mode; - __le32 mac_context_id; - __le16 pwr_restriction; - __le16 dev_24; - __le16 dev_52_low; - __le16 dev_52_high; - __le16 per_chain_restriction[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS]; -} __packed; /* TX_REDUCED_POWER_API_S_VER_3 */ - -#define IWL_DEV_MAX_TX_POWER 0x7FFF - -/** - * struct iwl_dev_tx_power_cmd - TX power reduction command - * @v3: version 3 of the command, embedded here for easier software handling - * @enable_ack_reduction: enable or disable close range ack TX power - * reduction. - * @reserved: reserved (padding) - */ -struct iwl_dev_tx_power_cmd { - /* v4 is just an extension of v3 - keep this here */ - struct iwl_dev_tx_power_cmd_v3 v3; - u8 enable_ack_reduction; - u8 reserved[3]; -} __packed; /* TX_REDUCED_POWER_API_S_VER_4 */ - -#define IWL_NUM_GEO_PROFILES 3 -#define IWL_GEO_PER_CHAIN_SIZE 3 - -/** - * enum iwl_geo_per_chain_offset_operation - type of operation - * @IWL_PER_CHAIN_OFFSET_SET_TABLES: send the tables from the host to the FW. - * @IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE: retrieve the last configured table. - */ -enum iwl_geo_per_chain_offset_operation { - IWL_PER_CHAIN_OFFSET_SET_TABLES, - IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE, -}; /* GEO_TX_POWER_LIMIT FLAGS TYPE */ - -/** - * struct iwl_per_chain_offset - embedded struct for GEO_TX_POWER_LIMIT. - * @max_tx_power: maximum allowed tx power. - * @chain_a: tx power offset for chain a. - * @chain_b: tx power offset for chain b. - */ -struct iwl_per_chain_offset { - __le16 max_tx_power; - u8 chain_a; - u8 chain_b; -} __packed; /* PER_CHAIN_LIMIT_OFFSET_PER_CHAIN_S_VER_1 */ - -struct iwl_per_chain_offset_group { - struct iwl_per_chain_offset lb; - struct iwl_per_chain_offset hb; -} __packed; /* PER_CHAIN_LIMIT_OFFSET_GROUP_S_VER_1 */ - -/** - * struct iwl_geo_tx_power_profile_cmd - struct for GEO_TX_POWER_LIMIT cmd. - * @ops: operations, value from &enum iwl_geo_per_chain_offset_operation - * @table: offset profile per band. - */ -struct iwl_geo_tx_power_profiles_cmd { - __le32 ops; - struct iwl_per_chain_offset_group table[IWL_NUM_GEO_PROFILES]; -} __packed; /* GEO_TX_POWER_LIMIT */ - -/** - * struct iwl_geo_tx_power_profiles_resp - response to GEO_TX_POWER_LIMIT cmd - * @profile_idx: current geo profile in use - */ -struct iwl_geo_tx_power_profiles_resp { - __le32 profile_idx; -} __packed; /* GEO_TX_POWER_LIMIT_RESP */ - -/** - * struct iwl_beacon_filter_cmd - * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) - * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon - * to driver if delta in Energy values calculated for this and last - * passed beacon is greater than this threshold. Zero value means that - * the Energy change is ignored for beacon filtering, and beacon will - * not be forced to be sent to driver regardless of this delta. Typical - * energy delta 5dB. - * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state. - * Send beacon to driver if delta in Energy values calculated for this - * and last passed beacon is greater than this threshold. Zero value - * means that the Energy change is ignored for beacon filtering while in - * Roaming state, typical energy delta 1dB. - * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values - * calculated for current beacon is less than the threshold, use - * Roaming Energy Delta Threshold, otherwise use normal Energy Delta - * Threshold. Typical energy threshold is -72dBm. - * @bf_temp_threshold: This threshold determines the type of temperature - * filtering (Slow or Fast) that is selected (Units are in Celsuis): - * If the current temperature is above this threshold - Fast filter - * will be used, If the current temperature is below this threshold - - * Slow filter will be used. - * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values - * calculated for this and the last passed beacon is greater than this - * threshold. Zero value means that the temperature change is ignored for - * beacon filtering; beacons will not be forced to be sent to driver - * regardless of whether its temerature has been changed. - * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values - * calculated for this and the last passed beacon is greater than this - * threshold. Zero value means that the temperature change is ignored for - * beacon filtering; beacons will not be forced to be sent to driver - * regardless of whether its temerature has been changed. - * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. - * @bf_debug_flag: beacon filtering debug configuration - * @bf_escape_timer: Send beacons to to driver if no beacons were passed - * for a specific period of time. Units: Beacons. - * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed - * for a longer period of time then this escape-timeout. Units: Beacons. - * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. - */ -struct iwl_beacon_filter_cmd { - __le32 bf_energy_delta; - __le32 bf_roaming_energy_delta; - __le32 bf_roaming_state; - __le32 bf_temp_threshold; - __le32 bf_temp_fast_filter; - __le32 bf_temp_slow_filter; - __le32 bf_enable_beacon_filter; - __le32 bf_debug_flag; - __le32 bf_escape_timer; - __le32 ba_escape_timer; - __le32 ba_enable_beacon_abort; -} __packed; - -/* Beacon filtering and beacon abort */ -#define IWL_BF_ENERGY_DELTA_DEFAULT 5 -#define IWL_BF_ENERGY_DELTA_D0I3 20 -#define IWL_BF_ENERGY_DELTA_MAX 255 -#define IWL_BF_ENERGY_DELTA_MIN 0 - -#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 -#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20 -#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255 -#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0 - -#define IWL_BF_ROAMING_STATE_DEFAULT 72 -#define IWL_BF_ROAMING_STATE_D0I3 72 -#define IWL_BF_ROAMING_STATE_MAX 255 -#define IWL_BF_ROAMING_STATE_MIN 0 - -#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112 -#define IWL_BF_TEMP_THRESHOLD_D0I3 112 -#define IWL_BF_TEMP_THRESHOLD_MAX 255 -#define IWL_BF_TEMP_THRESHOLD_MIN 0 - -#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1 -#define IWL_BF_TEMP_FAST_FILTER_D0I3 1 -#define IWL_BF_TEMP_FAST_FILTER_MAX 255 -#define IWL_BF_TEMP_FAST_FILTER_MIN 0 - -#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5 -#define IWL_BF_TEMP_SLOW_FILTER_D0I3 20 -#define IWL_BF_TEMP_SLOW_FILTER_MAX 255 -#define IWL_BF_TEMP_SLOW_FILTER_MIN 0 - -#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 - -#define IWL_BF_DEBUG_FLAG_DEFAULT 0 -#define IWL_BF_DEBUG_FLAG_D0I3 0 - -#define IWL_BF_ESCAPE_TIMER_DEFAULT 0 -#define IWL_BF_ESCAPE_TIMER_D0I3 0 -#define IWL_BF_ESCAPE_TIMER_MAX 1024 -#define IWL_BF_ESCAPE_TIMER_MIN 0 - -#define IWL_BA_ESCAPE_TIMER_DEFAULT 6 -#define IWL_BA_ESCAPE_TIMER_D0I3 6 -#define IWL_BA_ESCAPE_TIMER_D3 9 -#define IWL_BA_ESCAPE_TIMER_MAX 1024 -#define IWL_BA_ESCAPE_TIMER_MIN 0 - -#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 - -#define IWL_BF_CMD_CONFIG(mode) \ - .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \ - .bf_roaming_energy_delta = \ - cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \ - .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \ - .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \ - .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \ - .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \ - .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \ - .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \ - .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode) - -#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT) -#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3) -#endif diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h deleted file mode 100644 index bdf1228d050b..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h +++ /dev/null @@ -1,413 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_rs_h__ -#define __fw_api_rs_h__ - -#include "fw-api-mac.h" - -/* - * These serve as indexes into - * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT]; - * TODO: avoid overlap between legacy and HT rates - */ -enum { - IWL_RATE_1M_INDEX = 0, - IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, - IWL_RATE_2M_INDEX, - IWL_RATE_5M_INDEX, - IWL_RATE_11M_INDEX, - IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, - IWL_RATE_6M_INDEX, - IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, - IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX, - IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX, - IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX, - IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, - IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX, - IWL_RATE_18M_INDEX, - IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, - IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX, - IWL_RATE_36M_INDEX, - IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, - IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX, - IWL_RATE_54M_INDEX, - IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX, - IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX, - IWL_RATE_60M_INDEX, - IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX, - IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX, - IWL_RATE_MCS_8_INDEX, - IWL_RATE_MCS_9_INDEX, - IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX, - IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1, - IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1, -}; - -#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX) - -/* fw API values for legacy bit rates, both OFDM and CCK */ -enum { - IWL_RATE_6M_PLCP = 13, - IWL_RATE_9M_PLCP = 15, - IWL_RATE_12M_PLCP = 5, - IWL_RATE_18M_PLCP = 7, - IWL_RATE_24M_PLCP = 9, - IWL_RATE_36M_PLCP = 11, - IWL_RATE_48M_PLCP = 1, - IWL_RATE_54M_PLCP = 3, - IWL_RATE_1M_PLCP = 10, - IWL_RATE_2M_PLCP = 20, - IWL_RATE_5M_PLCP = 55, - IWL_RATE_11M_PLCP = 110, - IWL_RATE_INVM_PLCP = -1, -}; - -/* - * rate_n_flags bit fields - * - * The 32-bit value has different layouts in the low 8 bites depending on the - * format. There are three formats, HT, VHT and legacy (11abg, with subformats - * for CCK and OFDM). - * - * High-throughput (HT) rate format - * bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM) - * Very High-throughput (VHT) rate format - * bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM) - * Legacy OFDM rate format for bits 7:0 - * bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM) - * Legacy CCK rate format for bits 7:0: - * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK) - */ - -/* Bit 8: (1) HT format, (0) legacy or VHT format */ -#define RATE_MCS_HT_POS 8 -#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS) - -/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ -#define RATE_MCS_CCK_POS 9 -#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS) - -/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */ -#define RATE_MCS_VHT_POS 26 -#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS) - - -/* - * High-throughput (HT) rate format for bits 7:0 - * - * 2-0: MCS rate base - * 0) 6 Mbps - * 1) 12 Mbps - * 2) 18 Mbps - * 3) 24 Mbps - * 4) 36 Mbps - * 5) 48 Mbps - * 6) 54 Mbps - * 7) 60 Mbps - * 4-3: 0) Single stream (SISO) - * 1) Dual stream (MIMO) - * 2) Triple stream (MIMO) - * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data - * (bits 7-6 are zero) - * - * Together the low 5 bits work out to the MCS index because we don't - * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two - * streams and 16-23 have three streams. We could also support MCS 32 - * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.) - */ -#define RATE_HT_MCS_RATE_CODE_MSK 0x7 -#define RATE_HT_MCS_NSS_POS 3 -#define RATE_HT_MCS_NSS_MSK (3 << RATE_HT_MCS_NSS_POS) - -/* Bit 10: (1) Use Green Field preamble */ -#define RATE_HT_MCS_GF_POS 10 -#define RATE_HT_MCS_GF_MSK (1 << RATE_HT_MCS_GF_POS) - -#define RATE_HT_MCS_INDEX_MSK 0x3f - -/* - * Very High-throughput (VHT) rate format for bits 7:0 - * - * 3-0: VHT MCS (0-9) - * 5-4: number of streams - 1: - * 0) Single stream (SISO) - * 1) Dual stream (MIMO) - * 2) Triple stream (MIMO) - */ - -/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */ -#define RATE_VHT_MCS_RATE_CODE_MSK 0xf -#define RATE_VHT_MCS_NSS_POS 4 -#define RATE_VHT_MCS_NSS_MSK (3 << RATE_VHT_MCS_NSS_POS) - -/* - * Legacy OFDM rate format for bits 7:0 - * - * 3-0: 0xD) 6 Mbps - * 0xF) 9 Mbps - * 0x5) 12 Mbps - * 0x7) 18 Mbps - * 0x9) 24 Mbps - * 0xB) 36 Mbps - * 0x1) 48 Mbps - * 0x3) 54 Mbps - * (bits 7-4 are 0) - * - * Legacy CCK rate format for bits 7:0: - * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK): - * - * 6-0: 10) 1 Mbps - * 20) 2 Mbps - * 55) 5.5 Mbps - * 110) 11 Mbps - * (bit 7 is 0) - */ -#define RATE_LEGACY_RATE_MSK 0xff - -/* Bit 10 - OFDM HE */ -#define RATE_MCS_OFDM_HE_POS 10 -#define RATE_MCS_OFDM_HE_MSK BIT(RATE_MCS_OFDM_HE_POS) - -/* - * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz - * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT - */ -#define RATE_MCS_CHAN_WIDTH_POS 11 -#define RATE_MCS_CHAN_WIDTH_MSK (3 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_20 (0 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_40 (1 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_80 (2 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_160 (3 << RATE_MCS_CHAN_WIDTH_POS) - -/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ -#define RATE_MCS_SGI_POS 13 -#define RATE_MCS_SGI_MSK (1 << RATE_MCS_SGI_POS) - -/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */ -#define RATE_MCS_ANT_POS 14 -#define RATE_MCS_ANT_A_MSK (1 << RATE_MCS_ANT_POS) -#define RATE_MCS_ANT_B_MSK (2 << RATE_MCS_ANT_POS) -#define RATE_MCS_ANT_C_MSK (4 << RATE_MCS_ANT_POS) -#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | \ - RATE_MCS_ANT_B_MSK) -#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \ - RATE_MCS_ANT_C_MSK) -#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK -#define RATE_MCS_ANT_NUM 3 - -/* Bit 17: (0) SS, (1) SS*2 */ -#define RATE_MCS_STBC_POS 17 -#define RATE_MCS_STBC_MSK BIT(RATE_MCS_STBC_POS) - -/* Bit 18: OFDM-HE dual carrier mode */ -#define RATE_HE_DUAL_CARRIER_MODE 18 -#define RATE_HE_DUAL_CARRIER_MODE_MSK BIT(RATE_HE_DUAL_CARRIER_MODE) - -/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */ -#define RATE_MCS_BF_POS 19 -#define RATE_MCS_BF_MSK (1 << RATE_MCS_BF_POS) - -/* - * Bit 20-21: HE guard interval and LTF type. - * (0) 1xLTF+1.6us, (1) 2xLTF+0.8us, - * (2) 2xLTF+1.6us, (3) 4xLTF+3.2us - */ -#define RATE_MCS_HE_GI_LTF_POS 20 -#define RATE_MCS_HE_GI_LTF_MSK (3 << RATE_MCS_HE_GI_LTF_POS) - -/* Bit 22-23: HE type. (0) SU, (1) SU_EXT, (2) MU, (3) trigger based */ -#define RATE_MCS_HE_TYPE_POS 22 -#define RATE_MCS_HE_TYPE_MSK (3 << RATE_MCS_HE_TYPE_POS) - -/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */ -#define RATE_MCS_DUP_POS 24 -#define RATE_MCS_DUP_MSK (3 << RATE_MCS_DUP_POS) - -/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */ -#define RATE_MCS_LDPC_POS 27 -#define RATE_MCS_LDPC_MSK (1 << RATE_MCS_LDPC_POS) - - -/* Link Quality definitions */ - -/* # entries in rate scale table to support Tx retries */ -#define LQ_MAX_RETRY_NUM 16 - -/* Link quality command flags bit fields */ - -/* Bit 0: (0) Don't use RTS (1) Use RTS */ -#define LQ_FLAG_USE_RTS_POS 0 -#define LQ_FLAG_USE_RTS_MSK (1 << LQ_FLAG_USE_RTS_POS) - -/* Bit 1-3: LQ command color. Used to match responses to LQ commands */ -#define LQ_FLAG_COLOR_POS 1 -#define LQ_FLAG_COLOR_MSK (7 << LQ_FLAG_COLOR_POS) -#define LQ_FLAG_COLOR_GET(_f) (((_f) & LQ_FLAG_COLOR_MSK) >>\ - LQ_FLAG_COLOR_POS) -#define LQ_FLAGS_COLOR_INC(_c) ((((_c) + 1) << LQ_FLAG_COLOR_POS) &\ - LQ_FLAG_COLOR_MSK) -#define LQ_FLAG_COLOR_SET(_f, _c) ((_c) | ((_f) & ~LQ_FLAG_COLOR_MSK)) - -/* Bit 4-5: Tx RTS BW Signalling - * (0) No RTS BW signalling - * (1) Static BW signalling - * (2) Dynamic BW signalling - */ -#define LQ_FLAG_RTS_BW_SIG_POS 4 -#define LQ_FLAG_RTS_BW_SIG_NONE (0 << LQ_FLAG_RTS_BW_SIG_POS) -#define LQ_FLAG_RTS_BW_SIG_STATIC (1 << LQ_FLAG_RTS_BW_SIG_POS) -#define LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << LQ_FLAG_RTS_BW_SIG_POS) - -/* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection - * Dyanmic BW selection allows Tx with narrower BW then requested in rates - */ -#define LQ_FLAG_DYNAMIC_BW_POS 6 -#define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS) - -/* Single Stream Tx Parameters (lq_cmd->ss_params) - * Flags to control a smart FW decision about whether BFER/STBC/SISO will be - * used for single stream Tx. - */ - -/* Bit 0-1: Max STBC streams allowed. Can be 0-3. - * (0) - No STBC allowed - * (1) - 2x1 STBC allowed (HT/VHT) - * (2) - 4x2 STBC allowed (HT/VHT) - * (3) - 3x2 STBC allowed (HT only) - * All our chips are at most 2 antennas so only (1) is valid for now. - */ -#define LQ_SS_STBC_ALLOWED_POS 0 -#define LQ_SS_STBC_ALLOWED_MSK (3 << LQ_SS_STBC_ALLOWED_MSK) - -/* 2x1 STBC is allowed */ -#define LQ_SS_STBC_1SS_ALLOWED (1 << LQ_SS_STBC_ALLOWED_POS) - -/* Bit 2: Beamformer (VHT only) is allowed */ -#define LQ_SS_BFER_ALLOWED_POS 2 -#define LQ_SS_BFER_ALLOWED (1 << LQ_SS_BFER_ALLOWED_POS) - -/* Bit 3: Force BFER or STBC for testing - * If this is set: - * If BFER is allowed then force the ucode to choose BFER else - * If STBC is allowed then force the ucode to choose STBC over SISO - */ -#define LQ_SS_FORCE_POS 3 -#define LQ_SS_FORCE (1 << LQ_SS_FORCE_POS) - -/* Bit 31: ss_params field is valid. Used for FW backward compatibility - * with other drivers which don't support the ss_params API yet - */ -#define LQ_SS_PARAMS_VALID_POS 31 -#define LQ_SS_PARAMS_VALID (1 << LQ_SS_PARAMS_VALID_POS) - -/** - * struct iwl_lq_cmd - link quality command - * @sta_id: station to update - * @reduced_tpc: reduced transmit power control value - * @control: not used - * @flags: combination of LQ_FLAG_* - * @mimo_delim: the first SISO index in rs_table, which separates MIMO - * and SISO rates - * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD). - * Should be ANT_[ABC] - * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC] - * @initial_rate_index: first index from rs_table per AC category - * @agg_time_limit: aggregation max time threshold in usec/100, meaning - * value of 100 is one usec. Range is 100 to 8000 - * @agg_disable_start_th: try-count threshold for starting aggregation. - * If a frame has higher try-count, it should not be selected for - * starting an aggregation sequence. - * @agg_frame_cnt_limit: max frame count in an aggregation. - * 0: no limit - * 1: no aggregation (one frame per aggregation) - * 2 - 0x3f: maximal number of frames (up to 3f == 63) - * @reserved2: reserved - * @rs_table: array of rates for each TX try, each is rate_n_flags, - * meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP - * @ss_params: single stream features. declare whether STBC or BFER are allowed. - */ -struct iwl_lq_cmd { - u8 sta_id; - u8 reduced_tpc; - __le16 control; - /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */ - u8 flags; - u8 mimo_delim; - u8 single_stream_ant_msk; - u8 dual_stream_ant_msk; - u8 initial_rate_index[AC_NUM]; - /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */ - __le16 agg_time_limit; - u8 agg_disable_start_th; - u8 agg_frame_cnt_limit; - __le32 reserved2; - __le32 rs_table[LQ_MAX_RETRY_NUM]; - __le32 ss_params; -}; /* LINK_QUALITY_CMD_API_S_VER_1 */ - -#endif /* __fw_api_rs_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h deleted file mode 100644 index 59038ade08d8..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ /dev/null @@ -1,574 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __fw_api_rx_h__ -#define __fw_api_rx_h__ - -/* API for pre-9000 hardware */ - -#define IWL_RX_INFO_PHY_CNT 8 -#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1 -#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff -#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00 -#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000 -#define IWL_RX_INFO_ENERGY_ANT_A_POS 0 -#define IWL_RX_INFO_ENERGY_ANT_B_POS 8 -#define IWL_RX_INFO_ENERGY_ANT_C_POS 16 - -enum iwl_mac_context_info { - MAC_CONTEXT_INFO_NONE, - MAC_CONTEXT_INFO_GSCAN, -}; - -/** - * struct iwl_rx_phy_info - phy info - * (REPLY_RX_PHY_CMD = 0xc0) - * @non_cfg_phy_cnt: non configurable DSP phy data byte count - * @cfg_phy_cnt: configurable DSP phy data byte count - * @stat_id: configurable DSP phy data set ID - * @reserved1: reserved - * @system_timestamp: GP2 at on air rise - * @timestamp: TSF at on air rise - * @beacon_time_stamp: beacon at on-air rise - * @phy_flags: general phy flags: band, modulation, ... - * @channel: channel number - * @non_cfg_phy: for various implementations of non_cfg_phy - * @rate_n_flags: RATE_MCS_* - * @byte_count: frame's byte-count - * @frame_time: frame's time on the air, based on byte count and frame rate - * calculation - * @mac_active_msk: what MACs were active when the frame was received - * @mac_context_info: additional info on the context in which the frame was - * received as defined in &enum iwl_mac_context_info - * - * Before each Rx, the device sends this data. It contains PHY information - * about the reception of the packet. - */ -struct iwl_rx_phy_info { - u8 non_cfg_phy_cnt; - u8 cfg_phy_cnt; - u8 stat_id; - u8 reserved1; - __le32 system_timestamp; - __le64 timestamp; - __le32 beacon_time_stamp; - __le16 phy_flags; - __le16 channel; - __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT]; - __le32 rate_n_flags; - __le32 byte_count; - u8 mac_active_msk; - u8 mac_context_info; - __le16 frame_time; -} __packed; - -/* - * TCP offload Rx assist info - * - * bits 0:3 - reserved - * bits 4:7 - MIC CRC length - * bits 8:12 - MAC header length - * bit 13 - Padding indication - * bit 14 - A-AMSDU indication - * bit 15 - Offload enabled - */ -enum iwl_csum_rx_assist_info { - CSUM_RXA_RESERVED_MASK = 0x000f, - CSUM_RXA_MICSIZE_MASK = 0x00f0, - CSUM_RXA_HEADERLEN_MASK = 0x1f00, - CSUM_RXA_PADD = BIT(13), - CSUM_RXA_AMSDU = BIT(14), - CSUM_RXA_ENA = BIT(15) -}; - -/** - * struct iwl_rx_mpdu_res_start - phy info - * @byte_count: byte count of the frame - * @assist: see &enum iwl_csum_rx_assist_info - */ -struct iwl_rx_mpdu_res_start { - __le16 byte_count; - __le16 assist; -} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */ - -/** - * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags - * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band - * @RX_RES_PHY_FLAGS_MOD_CCK: modulation is CCK - * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short - * @RX_RES_PHY_FLAGS_NARROW_BAND: narrow band (<20 MHz) receive - * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received - * @RX_RES_PHY_FLAGS_ANTENNA_POS: antenna bit position - * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU - * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame - * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble - * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame - */ -enum iwl_rx_phy_flags { - RX_RES_PHY_FLAGS_BAND_24 = BIT(0), - RX_RES_PHY_FLAGS_MOD_CCK = BIT(1), - RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2), - RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3), - RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4), - RX_RES_PHY_FLAGS_ANTENNA_POS = 4, - RX_RES_PHY_FLAGS_AGG = BIT(7), - RX_RES_PHY_FLAGS_OFDM_HT = BIT(8), - RX_RES_PHY_FLAGS_OFDM_GF = BIT(9), - RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10), -}; - -/** - * enum iwl_mvm_rx_status - written by fw for each Rx packet - * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine - * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow - * @RX_MPDU_RES_STATUS_SRC_STA_FOUND: station was found - * @RX_MPDU_RES_STATUS_KEY_VALID: key was valid - * @RX_MPDU_RES_STATUS_KEY_PARAM_OK: key parameters were usable - * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed - * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked - * in the driver. - * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine - * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or - * alg = CCM only. Checks replay attack for 11w frames. Relevant only if - * %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set. - * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted - * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP - * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM - * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP - * @RX_MPDU_RES_STATUS_SEC_EXT_ENC: this frame is encrypted using extension - * algorithm - * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC - * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted - * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm - * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted - * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP: extended IV (set with TKIP) - * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT: key ID comparison done - * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame - * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw - * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors - * @RX_MPDU_RES_STATUS_STA_ID_MSK: station ID mask - * @RX_MDPU_RES_STATUS_STA_ID_SHIFT: station ID bit shift - * @RX_MPDU_RES_STATUS_FILTERING_MSK: filter status - * @RX_MPDU_RES_STATUS2_FILTERING_MSK: filter status 2 - */ -enum iwl_mvm_rx_status { - RX_MPDU_RES_STATUS_CRC_OK = BIT(0), - RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1), - RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2), - RX_MPDU_RES_STATUS_KEY_VALID = BIT(3), - RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4), - RX_MPDU_RES_STATUS_ICV_OK = BIT(5), - RX_MPDU_RES_STATUS_MIC_OK = BIT(6), - RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), - RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7), - RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8), - RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), - RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), - RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), - RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), - RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), - RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), - RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), - RX_MPDU_RES_STATUS_DEC_DONE = BIT(11), - RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13), - RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14), - RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15), - RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16), - RX_MPDU_RES_STATUS_CSUM_OK = BIT(17), - RX_MDPU_RES_STATUS_STA_ID_SHIFT = 24, - RX_MPDU_RES_STATUS_STA_ID_MSK = 0x1f << RX_MDPU_RES_STATUS_STA_ID_SHIFT, - RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000), - RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), -}; - -/* 9000 series API */ -enum iwl_rx_mpdu_mac_flags1 { - IWL_RX_MDPU_MFLG1_ADDRTYPE_MASK = 0x03, - IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_MASK = 0xf0, - /* shift should be 4, but the length is measured in 2-byte - * words, so shifting only by 3 gives a byte result - */ - IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_SHIFT = 3, -}; - -enum iwl_rx_mpdu_mac_flags2 { - /* in 2-byte words */ - IWL_RX_MPDU_MFLG2_HDR_LEN_MASK = 0x1f, - IWL_RX_MPDU_MFLG2_PAD = 0x20, - IWL_RX_MPDU_MFLG2_AMSDU = 0x40, -}; - -enum iwl_rx_mpdu_amsdu_info { - IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK = 0x7f, - IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x80, -}; - -enum iwl_rx_l3_proto_values { - IWL_RX_L3_TYPE_NONE, - IWL_RX_L3_TYPE_IPV4, - IWL_RX_L3_TYPE_IPV4_FRAG, - IWL_RX_L3_TYPE_IPV6_FRAG, - IWL_RX_L3_TYPE_IPV6, - IWL_RX_L3_TYPE_IPV6_IN_IPV4, - IWL_RX_L3_TYPE_ARP, - IWL_RX_L3_TYPE_EAPOL, -}; - -#define IWL_RX_L3_PROTO_POS 4 - -enum iwl_rx_l3l4_flags { - IWL_RX_L3L4_IP_HDR_CSUM_OK = BIT(0), - IWL_RX_L3L4_TCP_UDP_CSUM_OK = BIT(1), - IWL_RX_L3L4_TCP_FIN_SYN_RST_PSH = BIT(2), - IWL_RX_L3L4_TCP_ACK = BIT(3), - IWL_RX_L3L4_L3_PROTO_MASK = 0xf << IWL_RX_L3_PROTO_POS, - IWL_RX_L3L4_L4_PROTO_MASK = 0xf << 8, - IWL_RX_L3L4_RSS_HASH_MASK = 0xf << 12, -}; - -enum iwl_rx_mpdu_status { - IWL_RX_MPDU_STATUS_CRC_OK = BIT(0), - IWL_RX_MPDU_STATUS_OVERRUN_OK = BIT(1), - IWL_RX_MPDU_STATUS_SRC_STA_FOUND = BIT(2), - IWL_RX_MPDU_STATUS_KEY_VALID = BIT(3), - IWL_RX_MPDU_STATUS_KEY_PARAM_OK = BIT(4), - IWL_RX_MPDU_STATUS_ICV_OK = BIT(5), - IWL_RX_MPDU_STATUS_MIC_OK = BIT(6), - IWL_RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), - IWL_RX_MPDU_STATUS_SEC_MASK = 0x7 << 8, - IWL_RX_MPDU_STATUS_SEC_NONE = 0x0 << 8, - IWL_RX_MPDU_STATUS_SEC_WEP = 0x1 << 8, - IWL_RX_MPDU_STATUS_SEC_CCM = 0x2 << 8, - IWL_RX_MPDU_STATUS_SEC_TKIP = 0x3 << 8, - IWL_RX_MPDU_STATUS_SEC_EXT_ENC = 0x4 << 8, - IWL_RX_MPDU_STATUS_SEC_GCM = 0x5 << 8, - IWL_RX_MPDU_STATUS_DECRYPTED = BIT(11), - IWL_RX_MPDU_STATUS_WEP_MATCH = BIT(12), - IWL_RX_MPDU_STATUS_EXT_IV_MATCH = BIT(13), - IWL_RX_MPDU_STATUS_KEY_ID_MATCH = BIT(14), - IWL_RX_MPDU_STATUS_ROBUST_MNG_FRAME = BIT(15), -}; - -enum iwl_rx_mpdu_hash_filter { - IWL_RX_MPDU_HF_A1_HASH_MASK = 0x3f, - IWL_RX_MPDU_HF_FILTER_STATUS_MASK = 0xc0, -}; - -enum iwl_rx_mpdu_sta_id_flags { - IWL_RX_MPDU_SIF_STA_ID_MASK = 0x1f, - IWL_RX_MPDU_SIF_RRF_ABORT = 0x20, - IWL_RX_MPDU_SIF_FILTER_STATUS_MASK = 0xc0, -}; - -#define IWL_RX_REORDER_DATA_INVALID_BAID 0x7f - -enum iwl_rx_mpdu_reorder_data { - IWL_RX_MPDU_REORDER_NSSN_MASK = 0x00000fff, - IWL_RX_MPDU_REORDER_SN_MASK = 0x00fff000, - IWL_RX_MPDU_REORDER_SN_SHIFT = 12, - IWL_RX_MPDU_REORDER_BAID_MASK = 0x7f000000, - IWL_RX_MPDU_REORDER_BAID_SHIFT = 24, - IWL_RX_MPDU_REORDER_BA_OLD_SN = 0x80000000, -}; - -enum iwl_rx_mpdu_phy_info { - IWL_RX_MPDU_PHY_AMPDU = BIT(5), - IWL_RX_MPDU_PHY_AMPDU_TOGGLE = BIT(6), - IWL_RX_MPDU_PHY_SHORT_PREAMBLE = BIT(7), - IWL_RX_MPDU_PHY_TSF_OVERLOAD = BIT(8), -}; - -enum iwl_rx_mpdu_mac_info { - IWL_RX_MPDU_PHY_MAC_INDEX_MASK = 0x0f, - IWL_RX_MPDU_PHY_PHY_INDEX_MASK = 0xf0, -}; - -/** - * struct iwl_rx_mpdu_desc - RX MPDU descriptor - */ -struct iwl_rx_mpdu_desc { - /* DW2 */ - /** - * @mpdu_len: MPDU length - */ - __le16 mpdu_len; - /** - * @mac_flags1: &enum iwl_rx_mpdu_mac_flags1 - */ - u8 mac_flags1; - /** - * @mac_flags2: &enum iwl_rx_mpdu_mac_flags2 - */ - u8 mac_flags2; - /* DW3 */ - /** - * @amsdu_info: &enum iwl_rx_mpdu_amsdu_info - */ - u8 amsdu_info; - /** - * @phy_info: &enum iwl_rx_mpdu_phy_info - */ - __le16 phy_info; - /** - * @mac_phy_idx: MAC/PHY index - */ - u8 mac_phy_idx; - /* DW4 - carries csum data only when rpa_en == 1 */ - /** - * @raw_csum: raw checksum (alledgedly unreliable) - */ - __le16 raw_csum; - /** - * @l3l4_flags: &enum iwl_rx_l3l4_flags - */ - __le16 l3l4_flags; - /* DW5 */ - /** - * @status: &enum iwl_rx_mpdu_status - */ - __le16 status; - /** - * @hash_filter: hash filter value - */ - u8 hash_filter; - /** - * @sta_id_flags: &enum iwl_rx_mpdu_sta_id_flags - */ - u8 sta_id_flags; - /* DW6 */ - /** - * @reorder_data: &enum iwl_rx_mpdu_reorder_data - */ - __le32 reorder_data; - /* DW7 - carries rss_hash only when rpa_en == 1 */ - /** - * @rss_hash: RSS hash value - */ - __le32 rss_hash; - /* DW8 - carries filter_match only when rpa_en == 1 */ - /** - * @filter_match: filter match value - */ - __le32 filter_match; - /* DW9 */ - /** - * @rate_n_flags: RX rate/flags encoding - */ - __le32 rate_n_flags; - /* DW10 */ - /** - * @energy_a: energy chain A - */ - u8 energy_a; - /** - * @energy_b: energy chain B - */ - u8 energy_b; - /** - * @channel: channel number - */ - u8 channel; - /** - * @mac_context: MAC context mask - */ - u8 mac_context; - /* DW11 */ - /** - * @gp2_on_air_rise: GP2 timer value on air rise (INA) - */ - __le32 gp2_on_air_rise; - /* DW12 & DW13 */ - /** - * @tsf_on_air_rise: - * TSF value on air rise (INA), only valid if - * %IWL_RX_MPDU_PHY_TSF_OVERLOAD isn't set - */ - __le64 tsf_on_air_rise; -} __packed; - -struct iwl_frame_release { - u8 baid; - u8 reserved; - __le16 nssn; -}; - -enum iwl_rss_hash_func_en { - IWL_RSS_HASH_TYPE_IPV4_TCP, - IWL_RSS_HASH_TYPE_IPV4_UDP, - IWL_RSS_HASH_TYPE_IPV4_PAYLOAD, - IWL_RSS_HASH_TYPE_IPV6_TCP, - IWL_RSS_HASH_TYPE_IPV6_UDP, - IWL_RSS_HASH_TYPE_IPV6_PAYLOAD, -}; - -#define IWL_RSS_HASH_KEY_CNT 10 -#define IWL_RSS_INDIRECTION_TABLE_SIZE 128 -#define IWL_RSS_ENABLE 1 - -/** - * struct iwl_rss_config_cmd - RSS (Receive Side Scaling) configuration - * - * @flags: 1 - enable, 0 - disable - * @hash_mask: Type of RSS to use. Values are from %iwl_rss_hash_func_en - * @reserved: reserved - * @secret_key: 320 bit input of random key configuration from driver - * @indirection_table: indirection table - */ -struct iwl_rss_config_cmd { - __le32 flags; - u8 hash_mask; - u8 reserved[3]; - __le32 secret_key[IWL_RSS_HASH_KEY_CNT]; - u8 indirection_table[IWL_RSS_INDIRECTION_TABLE_SIZE]; -} __packed; /* RSS_CONFIG_CMD_API_S_VER_1 */ - -#define IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE 128 -#define IWL_MULTI_QUEUE_SYNC_SENDER_POS 0 -#define IWL_MULTI_QUEUE_SYNC_SENDER_MSK 0xf - -/** - * struct iwl_rxq_sync_cmd - RXQ notification trigger - * - * @flags: flags of the notification. bit 0:3 are the sender queue - * @rxq_mask: rx queues to send the notification on - * @count: number of bytes in payload, should be DWORD aligned - * @payload: data to send to rx queues - */ -struct iwl_rxq_sync_cmd { - __le32 flags; - __le32 rxq_mask; - __le32 count; - u8 payload[]; -} __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */ - -/** - * struct iwl_rxq_sync_notification - Notification triggered by RXQ - * sync command - * - * @count: number of bytes in payload - * @payload: data to send to rx queues - */ -struct iwl_rxq_sync_notification { - __le32 count; - u8 payload[]; -} __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */ - -/** - * enum iwl_mvm_rxq_notif_type - Internal message identifier - * - * @IWL_MVM_RXQ_EMPTY: empty sync notification - * @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA - */ -enum iwl_mvm_rxq_notif_type { - IWL_MVM_RXQ_EMPTY, - IWL_MVM_RXQ_NOTIF_DEL_BA, -}; - -/** - * struct iwl_mvm_internal_rxq_notif - Internal representation of the data sent - * in &iwl_rxq_sync_cmd. Should be DWORD aligned. - * FW is agnostic to the payload, so there are no endianity requirements. - * - * @type: value from &iwl_mvm_rxq_notif_type - * @sync: ctrl path is waiting for all notifications to be received - * @cookie: internal cookie to identify old notifications - * @data: payload - */ -struct iwl_mvm_internal_rxq_notif { - u16 type; - u16 sync; - u32 cookie; - u8 data[]; -} __packed; - -/** - * enum iwl_mvm_pm_event - type of station PM event - * @IWL_MVM_PM_EVENT_AWAKE: station woke up - * @IWL_MVM_PM_EVENT_ASLEEP: station went to sleep - * @IWL_MVM_PM_EVENT_UAPSD: station sent uAPSD trigger - * @IWL_MVM_PM_EVENT_PS_POLL: station sent PS-Poll - */ -enum iwl_mvm_pm_event { - IWL_MVM_PM_EVENT_AWAKE, - IWL_MVM_PM_EVENT_ASLEEP, - IWL_MVM_PM_EVENT_UAPSD, - IWL_MVM_PM_EVENT_PS_POLL, -}; /* PEER_PM_NTFY_API_E_VER_1 */ - -/** - * struct iwl_mvm_pm_state_notification - station PM state notification - * @sta_id: station ID of the station changing state - * @type: the new powersave state, see &enum iwl_mvm_pm_event - */ -struct iwl_mvm_pm_state_notification { - u8 sta_id; - u8 type; - /* private: */ - __le16 reserved; -} __packed; /* PEER_PM_NTFY_API_S_VER_1 */ - -#endif /* __fw_api_rx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h deleted file mode 100644 index 1cd7cc087936..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h +++ /dev/null @@ -1,792 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __fw_api_scan_h__ -#define __fw_api_scan_h__ - -/* Scan Commands, Responses, Notifications */ - -/* Max number of IEs for direct SSID scans in a command */ -#define PROBE_OPTION_MAX 20 - -/** - * struct iwl_ssid_ie - directed scan network information element - * - * Up to 20 of these may appear in REPLY_SCAN_CMD, - * selected by "type" bit field in struct iwl_scan_channel; - * each channel may select different ssids from among the 20 entries. - * SSID IEs get transmitted in reverse order of entry. - * - * @id: element ID - * @len: element length - * @ssid: element (SSID) data - */ -struct iwl_ssid_ie { - u8 id; - u8 len; - u8 ssid[IEEE80211_MAX_SSID_LEN]; -} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */ - -/* scan offload */ -#define IWL_SCAN_MAX_BLACKLIST_LEN 64 -#define IWL_SCAN_SHORT_BLACKLIST_LEN 16 -#define IWL_SCAN_MAX_PROFILES 11 -#define SCAN_OFFLOAD_PROBE_REQ_SIZE 512 - -/* Default watchdog (in MS) for scheduled scan iteration */ -#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000) - -#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) -#define CAN_ABORT_STATUS 1 - -#define IWL_FULL_SCAN_MULTIPLIER 5 -#define IWL_FAST_SCHED_SCAN_ITERATIONS 3 -#define IWL_MAX_SCHED_SCAN_PLANS 2 - -enum scan_framework_client { - SCAN_CLIENT_SCHED_SCAN = BIT(0), - SCAN_CLIENT_NETDETECT = BIT(1), - SCAN_CLIENT_ASSET_TRACKING = BIT(2), -}; - -/** - * struct iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S - * @ssid: MAC address to filter out - * @reported_rssi: AP rssi reported to the host - * @client_bitmap: clients ignore this entry - enum scan_framework_client - */ -struct iwl_scan_offload_blacklist { - u8 ssid[ETH_ALEN]; - u8 reported_rssi; - u8 client_bitmap; -} __packed; - -enum iwl_scan_offload_network_type { - IWL_NETWORK_TYPE_BSS = 1, - IWL_NETWORK_TYPE_IBSS = 2, - IWL_NETWORK_TYPE_ANY = 3, -}; - -enum iwl_scan_offload_band_selection { - IWL_SCAN_OFFLOAD_SELECT_2_4 = 0x4, - IWL_SCAN_OFFLOAD_SELECT_5_2 = 0x8, - IWL_SCAN_OFFLOAD_SELECT_ANY = 0xc, -}; - -/** - * struct iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S - * @ssid_index: index to ssid list in fixed part - * @unicast_cipher: encryption algorithm to match - bitmap - * @auth_alg: authentication algorithm to match - bitmap - * @network_type: enum iwl_scan_offload_network_type - * @band_selection: enum iwl_scan_offload_band_selection - * @client_bitmap: clients waiting for match - enum scan_framework_client - * @reserved: reserved - */ -struct iwl_scan_offload_profile { - u8 ssid_index; - u8 unicast_cipher; - u8 auth_alg; - u8 network_type; - u8 band_selection; - u8 client_bitmap; - u8 reserved[2]; -} __packed; - -/** - * struct iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1 - * @profiles: profiles to search for match - * @blacklist_len: length of blacklist - * @num_profiles: num of profiles in the list - * @match_notify: clients waiting for match found notification - * @pass_match: clients waiting for the results - * @active_clients: active clients bitmap - enum scan_framework_client - * @any_beacon_notify: clients waiting for match notification without match - * @reserved: reserved - */ -struct iwl_scan_offload_profile_cfg { - struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES]; - u8 blacklist_len; - u8 num_profiles; - u8 match_notify; - u8 pass_match; - u8 active_clients; - u8 any_beacon_notify; - u8 reserved[2]; -} __packed; - -/** - * struct iwl_scan_schedule_lmac - schedule of scan offload - * @delay: delay between iterations, in seconds. - * @iterations: num of scan iterations - * @full_scan_mul: number of partial scans before each full scan - */ -struct iwl_scan_schedule_lmac { - __le16 delay; - u8 iterations; - u8 full_scan_mul; -} __packed; /* SCAN_SCHEDULE_API_S */ - -enum iwl_scan_offload_complete_status { - IWL_SCAN_OFFLOAD_COMPLETED = 1, - IWL_SCAN_OFFLOAD_ABORTED = 2, -}; - -enum iwl_scan_ebs_status { - IWL_SCAN_EBS_SUCCESS, - IWL_SCAN_EBS_FAILED, - IWL_SCAN_EBS_CHAN_NOT_FOUND, - IWL_SCAN_EBS_INACTIVE, -}; - -/** - * struct iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S - * @tx_flags: combination of TX_CMD_FLG_* - * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is - * cleared. Combination of RATE_MCS_* - * @sta_id: index of destination station in FW station table - * @reserved: for alignment and future use - */ -struct iwl_scan_req_tx_cmd { - __le32 tx_flags; - __le32 rate_n_flags; - u8 sta_id; - u8 reserved[3]; -} __packed; - -enum iwl_scan_channel_flags_lmac { - IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27), - IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28), -}; - -/** - * struct iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2 - * @flags: bits 1-20: directed scan to i'th ssid - * other bits &enum iwl_scan_channel_flags_lmac - * @channel_num: channel number 1-13 etc - * @iter_count: scan iteration on this channel - * @iter_interval: interval in seconds between iterations on one channel - */ -struct iwl_scan_channel_cfg_lmac { - __le32 flags; - __le16 channel_num; - __le16 iter_count; - __le32 iter_interval; -} __packed; - -/* - * struct iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1 - * @offset: offset in the data block - * @len: length of the segment - */ -struct iwl_scan_probe_segment { - __le16 offset; - __le16 len; -} __packed; - -/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2 - * @mac_header: first (and common) part of the probe - * @band_data: band specific data - * @common_data: last (and common) part of the probe - * @buf: raw data block - */ -struct iwl_scan_probe_req { - struct iwl_scan_probe_segment mac_header; - struct iwl_scan_probe_segment band_data[2]; - struct iwl_scan_probe_segment common_data; - u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE]; -} __packed; - -enum iwl_scan_channel_flags { - IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0), - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1), - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2), -}; - -/* struct iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S - * @flags: enum iwl_scan_channel_flags - * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is - * involved. - * 1 - EBS is disabled. - * 2 - every second scan will be full scan(and so on). - */ -struct iwl_scan_channel_opt { - __le16 flags; - __le16 non_ebs_ratio; -} __packed; - -/** - * enum iwl_mvm_lmac_scan_flags - LMAC scan flags - * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses - * without filtering. - * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels - * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan - * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification - * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS: multiple SSID matching - * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented - * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report - * and DS parameter set IEs into probe requests. - * @IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL: use extended dwell time on channels - * 1, 6 and 11. - * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches - */ -enum iwl_mvm_lmac_scan_flags { - IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0), - IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1), - IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2), - IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3), - IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4), - IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5), - IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6), - IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL = BIT(7), - IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9), -}; - -enum iwl_scan_priority { - IWL_SCAN_PRIORITY_LOW, - IWL_SCAN_PRIORITY_MEDIUM, - IWL_SCAN_PRIORITY_HIGH, -}; - -enum iwl_scan_priority_ext { - IWL_SCAN_PRIORITY_EXT_0_LOWEST, - IWL_SCAN_PRIORITY_EXT_1, - IWL_SCAN_PRIORITY_EXT_2, - IWL_SCAN_PRIORITY_EXT_3, - IWL_SCAN_PRIORITY_EXT_4, - IWL_SCAN_PRIORITY_EXT_5, - IWL_SCAN_PRIORITY_EXT_6, - IWL_SCAN_PRIORITY_EXT_7_HIGHEST, -}; - -/** - * struct iwl_scan_req_lmac - SCAN_REQUEST_CMD_API_S_VER_1 - * @reserved1: for alignment and future use - * @n_channels: num of channels to scan - * @active_dwell: dwell time for active channels - * @passive_dwell: dwell time for passive channels - * @fragmented_dwell: dwell time for fragmented passive scan - * @extended_dwell: dwell time for channels 1, 6 and 11 (in certain cases) - * @reserved2: for alignment and future use - * @rx_chain_select: PHY_RX_CHAIN_* flags - * @scan_flags: &enum iwl_mvm_lmac_scan_flags - * @max_out_time: max time (in TU) to be out of associated channel - * @suspend_time: pause scan this long (TUs) when returning to service channel - * @flags: RXON flags - * @filter_flags: RXON filter - * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz - * @direct_scan: list of SSIDs for directed active scan - * @scan_prio: enum iwl_scan_priority - * @iter_num: number of scan iterations - * @delay: delay in seconds before first iteration - * @schedule: two scheduling plans. The first one is finite, the second one can - * be infinite. - * @channel_opt: channel optimization options, for full and partial scan - * @data: channel configuration and probe request packet. - */ -struct iwl_scan_req_lmac { - /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */ - __le32 reserved1; - u8 n_channels; - u8 active_dwell; - u8 passive_dwell; - u8 fragmented_dwell; - u8 extended_dwell; - u8 reserved2; - __le16 rx_chain_select; - __le32 scan_flags; - __le32 max_out_time; - __le32 suspend_time; - /* RX_ON_FLAGS_API_S_VER_1 */ - __le32 flags; - __le32 filter_flags; - struct iwl_scan_req_tx_cmd tx_cmd[2]; - struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; - __le32 scan_prio; - /* SCAN_REQ_PERIODIC_PARAMS_API_S */ - __le32 iter_num; - __le32 delay; - struct iwl_scan_schedule_lmac schedule[IWL_MAX_SCHED_SCAN_PLANS]; - struct iwl_scan_channel_opt channel_opt[2]; - u8 data[]; -} __packed; - -/** - * struct iwl_scan_results_notif - scan results for one channel - - * SCAN_RESULT_NTF_API_S_VER_3 - * @channel: which channel the results are from - * @band: 0 for 5.2 GHz, 1 for 2.4 GHz - * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request - * @num_probe_not_sent: # of request that weren't sent due to not enough time - * @duration: duration spent in channel, in usecs - */ -struct iwl_scan_results_notif { - u8 channel; - u8 band; - u8 probe_status; - u8 num_probe_not_sent; - __le32 duration; -} __packed; - -/** - * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels) - * SCAN_COMPLETE_NTF_API_S_VER_3 - * @scanned_channels: number of channels scanned (and number of valid results) - * @status: one of SCAN_COMP_STATUS_* - * @bt_status: BT on/off status - * @last_channel: last channel that was scanned - * @tsf_low: TSF timer (lower half) in usecs - * @tsf_high: TSF timer (higher half) in usecs - * @results: an array of scan results, only "scanned_channels" of them are valid - */ -struct iwl_lmac_scan_complete_notif { - u8 scanned_channels; - u8 status; - u8 bt_status; - u8 last_channel; - __le32 tsf_low; - __le32 tsf_high; - struct iwl_scan_results_notif results[]; -} __packed; - -/** - * struct iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2 - * @last_schedule_line: last schedule line executed (fast or regular) - * @last_schedule_iteration: last scan iteration executed before scan abort - * @status: &enum iwl_scan_offload_complete_status - * @ebs_status: EBS success status &enum iwl_scan_ebs_status - * @time_after_last_iter: time in seconds elapsed after last iteration - * @reserved: reserved - */ -struct iwl_periodic_scan_complete { - u8 last_schedule_line; - u8 last_schedule_iteration; - u8 status; - u8 ebs_status; - __le32 time_after_last_iter; - __le32 reserved; -} __packed; - -/* UMAC Scan API */ - -/* The maximum of either of these cannot exceed 8, because we use an - * 8-bit mask (see IWL_MVM_SCAN_MASK in mvm.h). - */ -#define IWL_MVM_MAX_UMAC_SCANS 8 -#define IWL_MVM_MAX_LMAC_SCANS 1 - -enum scan_config_flags { - SCAN_CONFIG_FLAG_ACTIVATE = BIT(0), - SCAN_CONFIG_FLAG_DEACTIVATE = BIT(1), - SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = BIT(2), - SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = BIT(3), - SCAN_CONFIG_FLAG_SET_TX_CHAINS = BIT(8), - SCAN_CONFIG_FLAG_SET_RX_CHAINS = BIT(9), - SCAN_CONFIG_FLAG_SET_AUX_STA_ID = BIT(10), - SCAN_CONFIG_FLAG_SET_ALL_TIMES = BIT(11), - SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = BIT(12), - SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = BIT(13), - SCAN_CONFIG_FLAG_SET_LEGACY_RATES = BIT(14), - SCAN_CONFIG_FLAG_SET_MAC_ADDR = BIT(15), - SCAN_CONFIG_FLAG_SET_FRAGMENTED = BIT(16), - SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = BIT(17), - SCAN_CONFIG_FLAG_SET_CAM_MODE = BIT(18), - SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = BIT(19), - SCAN_CONFIG_FLAG_SET_PROMISC_MODE = BIT(20), - SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = BIT(21), - SCAN_CONFIG_FLAG_SET_LMAC2_FRAGMENTED = BIT(22), - SCAN_CONFIG_FLAG_CLEAR_LMAC2_FRAGMENTED = BIT(23), - - /* Bits 26-31 are for num of channels in channel_array */ -#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26) -}; - -enum scan_config_rates { - /* OFDM basic rates */ - SCAN_CONFIG_RATE_6M = BIT(0), - SCAN_CONFIG_RATE_9M = BIT(1), - SCAN_CONFIG_RATE_12M = BIT(2), - SCAN_CONFIG_RATE_18M = BIT(3), - SCAN_CONFIG_RATE_24M = BIT(4), - SCAN_CONFIG_RATE_36M = BIT(5), - SCAN_CONFIG_RATE_48M = BIT(6), - SCAN_CONFIG_RATE_54M = BIT(7), - /* CCK basic rates */ - SCAN_CONFIG_RATE_1M = BIT(8), - SCAN_CONFIG_RATE_2M = BIT(9), - SCAN_CONFIG_RATE_5M = BIT(10), - SCAN_CONFIG_RATE_11M = BIT(11), - - /* Bits 16-27 are for supported rates */ -#define SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16) -}; - -enum iwl_channel_flags { - IWL_CHANNEL_FLAG_EBS = BIT(0), - IWL_CHANNEL_FLAG_ACCURATE_EBS = BIT(1), - IWL_CHANNEL_FLAG_EBS_ADD = BIT(2), - IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = BIT(3), -}; - -/** - * struct iwl_scan_dwell - * @active: default dwell time for active scan - * @passive: default dwell time for passive scan - * @fragmented: default dwell time for fragmented scan - * @extended: default dwell time for channels 1, 6 and 11 - */ -struct iwl_scan_dwell { - u8 active; - u8 passive; - u8 fragmented; - u8 extended; -} __packed; - -/** - * struct iwl_scan_config - * @flags: enum scan_config_flags - * @tx_chains: valid_tx antenna - ANT_* definitions - * @rx_chains: valid_rx antenna - ANT_* definitions - * @legacy_rates: default legacy rates - enum scan_config_rates - * @out_of_channel_time: default max out of serving channel time - * @suspend_time: default max suspend time - * @dwell: dwells for the scan - * @mac_addr: default mac address to be used in probes - * @bcast_sta_id: the index of the station in the fw - * @channel_flags: default channel flags - enum iwl_channel_flags - * scan_config_channel_flag - * @channel_array: default supported channels - */ -struct iwl_scan_config_v1 { - __le32 flags; - __le32 tx_chains; - __le32 rx_chains; - __le32 legacy_rates; - __le32 out_of_channel_time; - __le32 suspend_time; - struct iwl_scan_dwell dwell; - u8 mac_addr[ETH_ALEN]; - u8 bcast_sta_id; - u8 channel_flags; - u8 channel_array[]; -} __packed; /* SCAN_CONFIG_DB_CMD_API_S */ - -#define SCAN_TWO_LMACS 2 - -struct iwl_scan_config { - __le32 flags; - __le32 tx_chains; - __le32 rx_chains; - __le32 legacy_rates; - __le32 out_of_channel_time[SCAN_TWO_LMACS]; - __le32 suspend_time[SCAN_TWO_LMACS]; - struct iwl_scan_dwell dwell; - u8 mac_addr[ETH_ALEN]; - u8 bcast_sta_id; - u8 channel_flags; - u8 channel_array[]; -} __packed; /* SCAN_CONFIG_DB_CMD_API_S_3 */ - -/** - * enum iwl_umac_scan_flags - UMAC scan flags - * @IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request - * can be preempted by other scan requests with higher priority. - * The low priority scan will be resumed when the higher proirity scan is - * completed. - * @IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver - * when scan starts. - */ -enum iwl_umac_scan_flags { - IWL_UMAC_SCAN_FLAG_PREEMPTIVE = BIT(0), - IWL_UMAC_SCAN_FLAG_START_NOTIF = BIT(1), -}; - -enum iwl_umac_scan_uid_offsets { - IWL_UMAC_SCAN_UID_TYPE_OFFSET = 0, - IWL_UMAC_SCAN_UID_SEQ_OFFSET = 8, -}; - -enum iwl_umac_scan_general_flags { - IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC = BIT(0), - IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT = BIT(1), - IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL = BIT(2), - IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE = BIT(3), - IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = BIT(4), - IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = BIT(5), - IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6), - IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7), - IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8), - IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9), - IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = BIT(10), - IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED = BIT(11), -}; - -/** - * struct iwl_scan_channel_cfg_umac - * @flags: bitmap - 0-19: directed scan to i'th ssid. - * @channel_num: channel number 1-13 etc. - * @iter_count: repetition count for the channel. - * @iter_interval: interval between two scan iterations on one channel. - */ -struct iwl_scan_channel_cfg_umac { - __le32 flags; - u8 channel_num; - u8 iter_count; - __le16 iter_interval; -} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */ - -/** - * struct iwl_scan_umac_schedule - * @interval: interval in seconds between scan iterations - * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop - * @reserved: for alignment and future use - */ -struct iwl_scan_umac_schedule { - __le16 interval; - u8 iter_count; - u8 reserved; -} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */ - -/** - * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command - * parameters following channels configuration array. - * @schedule: two scheduling plans. - * @delay: delay in TUs before starting the first scan iteration - * @reserved: for future use and alignment - * @preq: probe request with IEs blocks - * @direct_scan: list of SSIDs for directed active scan - */ -struct iwl_scan_req_umac_tail { - /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */ - struct iwl_scan_umac_schedule schedule[IWL_MAX_SCHED_SCAN_PLANS]; - __le16 delay; - __le16 reserved; - /* SCAN_PROBE_PARAMS_API_S_VER_1 */ - struct iwl_scan_probe_req preq; - struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; -} __packed; - -/** - * struct iwl_scan_req_umac - * @flags: &enum iwl_umac_scan_flags - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @ooc_priority: out of channel priority - &enum iwl_scan_priority - * @general_flags: &enum iwl_umac_scan_general_flags - * @reserved2: for future use and alignment - * @scan_start_mac_id: report the scan start TSF time according to this mac TSF - * @extended_dwell: dwell time for channels 1, 6 and 11 - * @active_dwell: dwell time for active scan - * @passive_dwell: dwell time for passive scan - * @fragmented_dwell: dwell time for fragmented passive scan - * @max_out_time: max out of serving channel time, per LMAC - for CDB there - * are 2 LMACs - * @suspend_time: max suspend time, per LMAC - for CDB there are 2 LMACs - * @scan_priority: scan internal prioritization &enum iwl_scan_priority - * @channel_flags: &enum iwl_scan_channel_flags - * @n_channels: num of channels in scan request - * @reserved: for future use and alignment - * @data: &struct iwl_scan_channel_cfg_umac and - * &struct iwl_scan_req_umac_tail - */ -struct iwl_scan_req_umac { - __le32 flags; - __le32 uid; - __le32 ooc_priority; - /* SCAN_GENERAL_PARAMS_API_S_VER_4 */ - __le16 general_flags; - u8 reserved2; - u8 scan_start_mac_id; - u8 extended_dwell; - u8 active_dwell; - u8 passive_dwell; - u8 fragmented_dwell; - union { - struct { - __le32 max_out_time; - __le32 suspend_time; - __le32 scan_priority; - /* SCAN_CHANNEL_PARAMS_API_S_VER_4 */ - u8 channel_flags; - u8 n_channels; - __le16 reserved; - u8 data[]; - } v1; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ - struct { - __le32 max_out_time[SCAN_TWO_LMACS]; - __le32 suspend_time[SCAN_TWO_LMACS]; - __le32 scan_priority; - /* SCAN_CHANNEL_PARAMS_API_S_VER_4 */ - u8 channel_flags; - u8 n_channels; - __le16 reserved; - u8 data[]; - } v6; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_6 */ - }; -} __packed; - -#define IWL_SCAN_REQ_UMAC_SIZE sizeof(struct iwl_scan_req_umac) -#define IWL_SCAN_REQ_UMAC_SIZE_V1 (sizeof(struct iwl_scan_req_umac) - \ - 2 * sizeof(__le32)) - -/** - * struct iwl_umac_scan_abort - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @flags: reserved - */ -struct iwl_umac_scan_abort { - __le32 uid; - __le32 flags; -} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */ - -/** - * struct iwl_umac_scan_complete - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @last_schedule: last scheduling line - * @last_iter: last scan iteration number - * @status: &enum iwl_scan_offload_complete_status - * @ebs_status: &enum iwl_scan_ebs_status - * @time_from_last_iter: time elapsed from last iteration - * @reserved: for future use - */ -struct iwl_umac_scan_complete { - __le32 uid; - u8 last_schedule; - u8 last_iter; - u8 status; - u8 ebs_status; - __le32 time_from_last_iter; - __le32 reserved; -} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */ - -#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5 -/** - * struct iwl_scan_offload_profile_match - match information - * @bssid: matched bssid - * @reserved: reserved - * @channel: channel where the match occurred - * @energy: energy - * @matching_feature: feature matches - * @matching_channels: bitmap of channels that matched, referencing - * the channels passed in tue scan offload request - */ -struct iwl_scan_offload_profile_match { - u8 bssid[ETH_ALEN]; - __le16 reserved; - u8 channel; - u8 energy; - u8 matching_feature; - u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN]; -} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */ - -/** - * struct iwl_scan_offload_profiles_query - match results query response - * @matched_profiles: bitmap of matched profiles, referencing the - * matches passed in the scan offload request - * @last_scan_age: age of the last offloaded scan - * @n_scans_done: number of offloaded scans done - * @gp2_d0u: GP2 when D0U occurred - * @gp2_invoked: GP2 when scan offload was invoked - * @resume_while_scanning: not used - * @self_recovery: obsolete - * @reserved: reserved - * @matches: array of match information, one for each match - */ -struct iwl_scan_offload_profiles_query { - __le32 matched_profiles; - __le32 last_scan_age; - __le32 n_scans_done; - __le32 gp2_d0u; - __le32 gp2_invoked; - u8 resume_while_scanning; - u8 self_recovery; - __le16 reserved; - struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; -} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */ - -/** - * struct iwl_umac_scan_iter_complete_notif - notifies end of scanning iteration - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @scanned_channels: number of channels scanned and number of valid elements in - * results array - * @status: one of SCAN_COMP_STATUS_* - * @bt_status: BT on/off status - * @last_channel: last channel that was scanned - * @start_tsf: TSF timer in usecs of the scan start time for the mac specified - * in &struct iwl_scan_req_umac. - * @results: array of scan results, length in @scanned_channels - */ -struct iwl_umac_scan_iter_complete_notif { - __le32 uid; - u8 scanned_channels; - u8 status; - u8 bt_status; - u8 last_channel; - __le64 start_tsf; - struct iwl_scan_results_notif results[]; -} __packed; /* SCAN_ITER_COMPLETE_NTF_UMAC_API_S_VER_2 */ - -#endif diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h deleted file mode 100644 index 81f0a3463bac..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ /dev/null @@ -1,578 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_sta_h__ -#define __fw_api_sta_h__ - -/** - * enum iwl_sta_flags - flags for the ADD_STA host command - * @STA_FLG_REDUCED_TX_PWR_CTRL: reduced TX power (control frames) - * @STA_FLG_REDUCED_TX_PWR_DATA: reduced TX power (data frames) - * @STA_FLG_DISABLE_TX: set if TX should be disabled - * @STA_FLG_PS: set if STA is in Power Save - * @STA_FLG_INVALID: set if STA is invalid - * @STA_FLG_DLP_EN: Direct Link Protocol is enabled - * @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs - * @STA_FLG_DRAIN_FLOW: drain flow - * @STA_FLG_PAN: STA is for PAN interface - * @STA_FLG_CLASS_AUTH: station is authenticated - * @STA_FLG_CLASS_ASSOC: station is associated - * @STA_FLG_RTS_MIMO_PROT: station requires RTS MIMO protection (dynamic SMPS) - * @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU (mask) - * @STA_FLG_MAX_AGG_SIZE_SHIFT: maximal size for A-MPDU (bit shift) - * @STA_FLG_MAX_AGG_SIZE_8K: maximal size for A-MPDU (8k supported) - * @STA_FLG_MAX_AGG_SIZE_16K: maximal size for A-MPDU (16k supported) - * @STA_FLG_MAX_AGG_SIZE_32K: maximal size for A-MPDU (32k supported) - * @STA_FLG_MAX_AGG_SIZE_64K: maximal size for A-MPDU (64k supported) - * @STA_FLG_MAX_AGG_SIZE_128K: maximal size for A-MPDU (128k supported) - * @STA_FLG_MAX_AGG_SIZE_256K: maximal size for A-MPDU (256k supported) - * @STA_FLG_MAX_AGG_SIZE_512K: maximal size for A-MPDU (512k supported) - * @STA_FLG_MAX_AGG_SIZE_1024K: maximal size for A-MPDU (1024k supported) - * @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation - * @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is - * initialised by driver and can be updated by fw upon reception of - * action frames that can change the channel width. When cleared the fw - * will send all the frames in 20MHz even when FAT channel is requested. - * @STA_FLG_FAT_EN_20MHZ: no wide channels are supported, only 20 MHz - * @STA_FLG_FAT_EN_40MHZ: wide channels up to 40 MHz supported - * @STA_FLG_FAT_EN_80MHZ: wide channels up to 80 MHz supported - * @STA_FLG_FAT_EN_160MHZ: wide channels up to 160 MHz supported - * @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the - * driver and can be updated by fw upon reception of action frames. - * @STA_FLG_MIMO_EN_SISO: no support for MIMO - * @STA_FLG_MIMO_EN_MIMO2: 2 streams supported - * @STA_FLG_MIMO_EN_MIMO3: 3 streams supported - * @STA_FLG_MFP_EN: Management Frame Protection - * @STA_FLG_AGG_MPDU_DENS_MSK: A-MPDU density (mask) - * @STA_FLG_AGG_MPDU_DENS_SHIFT: A-MPDU density (bit shift) - * @STA_FLG_AGG_MPDU_DENS_2US: A-MPDU density (2 usec gap) - * @STA_FLG_AGG_MPDU_DENS_4US: A-MPDU density (4 usec gap) - * @STA_FLG_AGG_MPDU_DENS_8US: A-MPDU density (8 usec gap) - * @STA_FLG_AGG_MPDU_DENS_16US: A-MPDU density (16 usec gap) - */ -enum iwl_sta_flags { - STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3), - STA_FLG_REDUCED_TX_PWR_DATA = BIT(6), - - STA_FLG_DISABLE_TX = BIT(4), - - STA_FLG_PS = BIT(8), - STA_FLG_DRAIN_FLOW = BIT(12), - STA_FLG_PAN = BIT(13), - STA_FLG_CLASS_AUTH = BIT(14), - STA_FLG_CLASS_ASSOC = BIT(15), - STA_FLG_RTS_MIMO_PROT = BIT(17), - - STA_FLG_MAX_AGG_SIZE_SHIFT = 19, - STA_FLG_MAX_AGG_SIZE_8K = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_16K = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_32K = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_64K = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_128K = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_256K = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_512K = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_1024K = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_MSK = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), - - STA_FLG_AGG_MPDU_DENS_SHIFT = 23, - STA_FLG_AGG_MPDU_DENS_2US = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_4US = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_8US = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_16US = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_MSK = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), - - STA_FLG_FAT_EN_20MHZ = (0 << 26), - STA_FLG_FAT_EN_40MHZ = (1 << 26), - STA_FLG_FAT_EN_80MHZ = (2 << 26), - STA_FLG_FAT_EN_160MHZ = (3 << 26), - STA_FLG_FAT_EN_MSK = (3 << 26), - - STA_FLG_MIMO_EN_SISO = (0 << 28), - STA_FLG_MIMO_EN_MIMO2 = (1 << 28), - STA_FLG_MIMO_EN_MIMO3 = (2 << 28), - STA_FLG_MIMO_EN_MSK = (3 << 28), -}; - -/** - * enum iwl_sta_key_flag - key flags for the ADD_STA host command - * @STA_KEY_FLG_NO_ENC: no encryption - * @STA_KEY_FLG_WEP: WEP encryption algorithm - * @STA_KEY_FLG_CCM: CCMP encryption algorithm - * @STA_KEY_FLG_TKIP: TKIP encryption algorithm - * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support) - * @STA_KEY_FLG_GCMP: GCMP encryption algorithm - * @STA_KEY_FLG_CMAC: CMAC encryption algorithm - * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm - * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value - * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from - * station info array (1 - n 1X mode) - * @STA_KEY_FLG_KEYID_MSK: the index of the key - * @STA_KEY_FLG_KEYID_POS: key index bit position - * @STA_KEY_NOT_VALID: key is invalid - * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key - * @STA_KEY_FLG_KEY_32BYTES: for non-wep key set for 32 bytes key - * @STA_KEY_MULTICAST: set for multical key - * @STA_KEY_MFP: key is used for Management Frame Protection - */ -enum iwl_sta_key_flag { - STA_KEY_FLG_NO_ENC = (0 << 0), - STA_KEY_FLG_WEP = (1 << 0), - STA_KEY_FLG_CCM = (2 << 0), - STA_KEY_FLG_TKIP = (3 << 0), - STA_KEY_FLG_EXT = (4 << 0), - STA_KEY_FLG_GCMP = (5 << 0), - STA_KEY_FLG_CMAC = (6 << 0), - STA_KEY_FLG_ENC_UNKNOWN = (7 << 0), - STA_KEY_FLG_EN_MSK = (7 << 0), - - STA_KEY_FLG_WEP_KEY_MAP = BIT(3), - STA_KEY_FLG_KEYID_POS = 8, - STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS), - STA_KEY_NOT_VALID = BIT(11), - STA_KEY_FLG_WEP_13BYTES = BIT(12), - STA_KEY_FLG_KEY_32BYTES = BIT(12), - STA_KEY_MULTICAST = BIT(14), - STA_KEY_MFP = BIT(15), -}; - -/** - * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed - * @STA_MODIFY_QUEUE_REMOVAL: this command removes a queue - * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx - * @STA_MODIFY_UAPSD_ACS: this command modifies %uapsd_acs - * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid - * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid - * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count - * @STA_MODIFY_PROT_TH: modify RTS threshold - * @STA_MODIFY_QUEUES: modify the queues used by this station - */ -enum iwl_sta_modify_flag { - STA_MODIFY_QUEUE_REMOVAL = BIT(0), - STA_MODIFY_TID_DISABLE_TX = BIT(1), - STA_MODIFY_UAPSD_ACS = BIT(2), - STA_MODIFY_ADD_BA_TID = BIT(3), - STA_MODIFY_REMOVE_BA_TID = BIT(4), - STA_MODIFY_SLEEPING_STA_TX_COUNT = BIT(5), - STA_MODIFY_PROT_TH = BIT(6), - STA_MODIFY_QUEUES = BIT(7), -}; - -/** - * enum iwl_sta_mode - station command mode - * @STA_MODE_ADD: add new station - * @STA_MODE_MODIFY: modify the station - */ -enum iwl_sta_mode { - STA_MODE_ADD = 0, - STA_MODE_MODIFY = 1, -}; - -/** - * enum iwl_sta_sleep_flag - type of sleep of the station - * @STA_SLEEP_STATE_AWAKE: station is awake - * @STA_SLEEP_STATE_PS_POLL: station is PS-polling - * @STA_SLEEP_STATE_UAPSD: station uses U-APSD - * @STA_SLEEP_STATE_MOREDATA: set more-data bit on - * (last) released frame - */ -enum iwl_sta_sleep_flag { - STA_SLEEP_STATE_AWAKE = 0, - STA_SLEEP_STATE_PS_POLL = BIT(0), - STA_SLEEP_STATE_UAPSD = BIT(1), - STA_SLEEP_STATE_MOREDATA = BIT(2), -}; - -#define STA_KEY_MAX_NUM (16) -#define STA_KEY_IDX_INVALID (0xff) -#define STA_KEY_MAX_DATA_KEY_NUM (4) -#define IWL_MAX_GLOBAL_KEYS (4) -#define STA_KEY_LEN_WEP40 (5) -#define STA_KEY_LEN_WEP104 (13) - -/** - * struct iwl_mvm_keyinfo - key information - * @key_flags: type &enum iwl_sta_key_flag - * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection - * @reserved1: reserved - * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx - * @key_offset: key offset in the fw's key table - * @reserved2: reserved - * @key: 16-byte unicast decryption key - * @tx_secur_seq_cnt: initial RSC / PN needed for replay check - * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only - * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only - */ -struct iwl_mvm_keyinfo { - __le16 key_flags; - u8 tkip_rx_tsc_byte2; - u8 reserved1; - __le16 tkip_rx_ttak[5]; - u8 key_offset; - u8 reserved2; - u8 key[16]; - __le64 tx_secur_seq_cnt; - __le64 hw_tkip_mic_rx_key; - __le64 hw_tkip_mic_tx_key; -} __packed; - -#define IWL_ADD_STA_STATUS_MASK 0xFF -#define IWL_ADD_STA_BAID_VALID_MASK 0x8000 -#define IWL_ADD_STA_BAID_MASK 0x7F00 -#define IWL_ADD_STA_BAID_SHIFT 8 - -/** - * struct iwl_mvm_add_sta_cmd_v7 - Add/modify a station in the fw's sta table. - * ( REPLY_ADD_STA = 0x18 ) - * @add_modify: see &enum iwl_sta_mode - * @awake_acs: ACs to transmit data on while station is sleeping (for U-APSD) - * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable - * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. - * @mac_id_n_color: the Mac context this station belongs to, - * see &enum iwl_mvm_id_and_color - * @addr: station's MAC address - * @reserved2: reserved - * @sta_id: index of station in uCode's station table - * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave - * alone. 1 - modify, 0 - don't change. - * @reserved3: reserved - * @station_flags: look at &enum iwl_sta_flags - * @station_flags_msk: what of %station_flags have changed, - * also &enum iwl_sta_flags - * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) - * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set - * add_immediate_ba_ssn. - * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx) - * Set %STA_MODIFY_REMOVE_BA_TID to use this field - * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with - * add_immediate_ba_tid. - * @sleep_tx_count: number of packets to transmit to station even though it is - * asleep. Used to synchronise PS-poll and u-APSD responses while ucode - * keeps track of STA sleep state. - * @sleep_state_flags: Look at &enum iwl_sta_sleep_flag. - * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP - * mac-addr. - * @beamform_flags: beam forming controls - * @tfd_queue_msk: tfd queues used by this station - * - * The device contains an internal table of per-station information, with info - * on security keys, aggregation parameters, and Tx rates for initial Tx - * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD). - * - * ADD_STA sets up the table entry for one station, either creating a new - * entry, or modifying a pre-existing one. - */ -struct iwl_mvm_add_sta_cmd_v7 { - u8 add_modify; - u8 awake_acs; - __le16 tid_disable_tx; - __le32 mac_id_n_color; - u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ - __le16 reserved2; - u8 sta_id; - u8 modify_mask; - __le16 reserved3; - __le32 station_flags; - __le32 station_flags_msk; - u8 add_immediate_ba_tid; - u8 remove_immediate_ba_tid; - __le16 add_immediate_ba_ssn; - __le16 sleep_tx_count; - __le16 sleep_state_flags; - __le16 assoc_id; - __le16 beamform_flags; - __le32 tfd_queue_msk; -} __packed; /* ADD_STA_CMD_API_S_VER_7 */ - -/** - * enum iwl_sta_type - FW station types - * ( REPLY_ADD_STA = 0x18 ) - * @IWL_STA_LINK: Link station - normal RX and TX traffic. - * @IWL_STA_GENERAL_PURPOSE: General purpose. In AP mode used for beacons - * and probe responses. - * @IWL_STA_MULTICAST: multicast traffic, - * @IWL_STA_TDLS_LINK: TDLS link station - * @IWL_STA_AUX_ACTIVITY: auxilary station (scan, ROC and so on). - */ -enum iwl_sta_type { - IWL_STA_LINK, - IWL_STA_GENERAL_PURPOSE, - IWL_STA_MULTICAST, - IWL_STA_TDLS_LINK, - IWL_STA_AUX_ACTIVITY, -}; - -/** - * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table. - * ( REPLY_ADD_STA = 0x18 ) - * @add_modify: see &enum iwl_sta_mode - * @awake_acs: ACs to transmit data on while station is sleeping (for U-APSD) - * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable - * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. - * @mac_id_n_color: the Mac context this station belongs to, - * see &enum iwl_mvm_id_and_color - * @addr: station's MAC address - * @reserved2: reserved - * @sta_id: index of station in uCode's station table - * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave - * alone. 1 - modify, 0 - don't change. - * @reserved3: reserved - * @station_flags: look at &enum iwl_sta_flags - * @station_flags_msk: what of %station_flags have changed, - * also &enum iwl_sta_flags - * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) - * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set - * add_immediate_ba_ssn. - * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx) - * Set %STA_MODIFY_REMOVE_BA_TID to use this field - * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with - * add_immediate_ba_tid. - * @sleep_tx_count: number of packets to transmit to station even though it is - * asleep. Used to synchronise PS-poll and u-APSD responses while ucode - * keeps track of STA sleep state. - * @station_type: type of this station. See &enum iwl_sta_type. - * @sleep_state_flags: Look at &enum iwl_sta_sleep_flag. - * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP - * mac-addr. - * @beamform_flags: beam forming controls - * @tfd_queue_msk: tfd queues used by this station. - * Obselete for new TX API (9 and above). - * @rx_ba_window: aggregation window size - * @sp_length: the size of the SP as it appears in the WME IE - * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver - * enabled ACs. - * - * The device contains an internal table of per-station information, with info - * on security keys, aggregation parameters, and Tx rates for initial Tx - * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD). - * - * ADD_STA sets up the table entry for one station, either creating a new - * entry, or modifying a pre-existing one. - */ -struct iwl_mvm_add_sta_cmd { - u8 add_modify; - u8 awake_acs; - __le16 tid_disable_tx; - __le32 mac_id_n_color; - u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ - __le16 reserved2; - u8 sta_id; - u8 modify_mask; - __le16 reserved3; - __le32 station_flags; - __le32 station_flags_msk; - u8 add_immediate_ba_tid; - u8 remove_immediate_ba_tid; - __le16 add_immediate_ba_ssn; - __le16 sleep_tx_count; - u8 sleep_state_flags; - u8 station_type; - __le16 assoc_id; - __le16 beamform_flags; - __le32 tfd_queue_msk; - __le16 rx_ba_window; - u8 sp_length; - u8 uapsd_acs; -} __packed; /* ADD_STA_CMD_API_S_VER_10 */ - -/** - * struct iwl_mvm_add_sta_key_common - add/modify sta key common part - * ( REPLY_ADD_STA_KEY = 0x17 ) - * @sta_id: index of station in uCode's station table - * @key_offset: key offset in key storage - * @key_flags: type &enum iwl_sta_key_flag - * @key: key material data - * @rx_secur_seq_cnt: RX security sequence counter for the key - */ -struct iwl_mvm_add_sta_key_common { - u8 sta_id; - u8 key_offset; - __le16 key_flags; - u8 key[32]; - u8 rx_secur_seq_cnt[16]; -} __packed; - -/** - * struct iwl_mvm_add_sta_key_cmd_v1 - add/modify sta key - * @common: see &struct iwl_mvm_add_sta_key_common - * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection - * @reserved: reserved - * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx - */ -struct iwl_mvm_add_sta_key_cmd_v1 { - struct iwl_mvm_add_sta_key_common common; - u8 tkip_rx_tsc_byte2; - u8 reserved; - __le16 tkip_rx_ttak[5]; -} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */ - -/** - * struct iwl_mvm_add_sta_key_cmd - add/modify sta key - * @common: see &struct iwl_mvm_add_sta_key_common - * @rx_mic_key: TKIP RX unicast or multicast key - * @tx_mic_key: TKIP TX key - * @transmit_seq_cnt: TSC, transmit packet number - */ -struct iwl_mvm_add_sta_key_cmd { - struct iwl_mvm_add_sta_key_common common; - __le64 rx_mic_key; - __le64 tx_mic_key; - __le64 transmit_seq_cnt; -} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_2 */ - -/** - * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command - * @ADD_STA_SUCCESS: operation was executed successfully - * @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table - * @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session - * @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that - * doesn't exist. - */ -enum iwl_mvm_add_sta_rsp_status { - ADD_STA_SUCCESS = 0x1, - ADD_STA_STATIONS_OVERLOAD = 0x2, - ADD_STA_IMMEDIATE_BA_FAILURE = 0x4, - ADD_STA_MODIFY_NON_EXISTING_STA = 0x8, -}; - -/** - * struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table - * ( REMOVE_STA = 0x19 ) - * @sta_id: the station id of the station to be removed - * @reserved: reserved - */ -struct iwl_mvm_rm_sta_cmd { - u8 sta_id; - u8 reserved[3]; -} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */ - -/** - * struct iwl_mvm_mgmt_mcast_key_cmd_v1 - * ( MGMT_MCAST_KEY = 0x1f ) - * @ctrl_flags: &enum iwl_sta_key_flag - * @igtk: IGTK key material - * @k1: unused - * @k2: unused - * @sta_id: station ID that support IGTK - * @key_id: key ID - * @receive_seq_cnt: initial RSC/PN needed for replay check - */ -struct iwl_mvm_mgmt_mcast_key_cmd_v1 { - __le32 ctrl_flags; - u8 igtk[16]; - u8 k1[16]; - u8 k2[16]; - __le32 key_id; - __le32 sta_id; - __le64 receive_seq_cnt; -} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */ - -/** - * struct iwl_mvm_mgmt_mcast_key_cmd - * ( MGMT_MCAST_KEY = 0x1f ) - * @ctrl_flags: &enum iwl_sta_key_flag - * @igtk: IGTK master key - * @sta_id: station ID that support IGTK - * @key_id: key ID - * @receive_seq_cnt: initial RSC/PN needed for replay check - */ -struct iwl_mvm_mgmt_mcast_key_cmd { - __le32 ctrl_flags; - u8 igtk[32]; - __le32 key_id; - __le32 sta_id; - __le64 receive_seq_cnt; -} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_2 */ - -struct iwl_mvm_wep_key { - u8 key_index; - u8 key_offset; - __le16 reserved1; - u8 key_size; - u8 reserved2[3]; - u8 key[16]; -} __packed; - -struct iwl_mvm_wep_key_cmd { - __le32 mac_id_n_color; - u8 num_keys; - u8 decryption_type; - u8 flags; - u8 reserved; - struct iwl_mvm_wep_key wep_key[0]; -} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ - -/** - * struct iwl_mvm_eosp_notification - EOSP notification from firmware - * @remain_frame_count: # of frames remaining, non-zero if SP was cut - * short by GO absence - * @sta_id: station ID - */ -struct iwl_mvm_eosp_notification { - __le32 remain_frame_count; - __le32 sta_id; -} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */ - -#endif /* __fw_api_sta_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h deleted file mode 100644 index c7531da508fd..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h +++ /dev/null @@ -1,479 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __fw_api_stats_h__ -#define __fw_api_stats_h__ -#include "fw-api-mac.h" - -struct mvm_statistics_dbg { - __le32 burst_check; - __le32 burst_count; - __le32 wait_for_silence_timeout_cnt; - u8 reserved[12]; -} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ - -struct mvm_statistics_div { - __le32 tx_on_a; - __le32 tx_on_b; - __le32 exec_time; - __le32 probe_time; - __le32 rssi_ant; - __le32 reserved2; -} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ - -/** - * struct mvm_statistics_rx_non_phy - * @bogus_cts: CTS received when not expecting CTS - * @bogus_ack: ACK received when not expecting ACK - * @non_channel_beacons: beacons with our bss id but not on our serving channel - * @channel_beacons: beacons with our bss id and in our serving channel - * @num_missed_bcon: number of missed beacons - * @adc_rx_saturation_time: count in 0.8us units the time the ADC was in - * saturation - * @ina_detection_search_time: total time (in 0.8us) searched for INA - * @beacon_silence_rssi_a: RSSI silence after beacon frame - * @beacon_silence_rssi_b: RSSI silence after beacon frame - * @beacon_silence_rssi_c: RSSI silence after beacon frame - * @interference_data_flag: flag for interference data availability. 1 when data - * is available. - * @channel_load: counts RX Enable time in uSec - * @beacon_rssi_a: beacon RSSI on anntena A - * @beacon_rssi_b: beacon RSSI on antenna B - * @beacon_rssi_c: beacon RSSI on antenna C - * @beacon_energy_a: beacon energy on antenna A - * @beacon_energy_b: beacon energy on antenna B - * @beacon_energy_c: beacon energy on antenna C - * @num_bt_kills: number of BT "kills" (frame TX aborts) - * @mac_id: mac ID - */ -struct mvm_statistics_rx_non_phy { - __le32 bogus_cts; - __le32 bogus_ack; - __le32 non_channel_beacons; - __le32 channel_beacons; - __le32 num_missed_bcon; - __le32 adc_rx_saturation_time; - __le32 ina_detection_search_time; - __le32 beacon_silence_rssi_a; - __le32 beacon_silence_rssi_b; - __le32 beacon_silence_rssi_c; - __le32 interference_data_flag; - __le32 channel_load; - __le32 beacon_rssi_a; - __le32 beacon_rssi_b; - __le32 beacon_rssi_c; - __le32 beacon_energy_a; - __le32 beacon_energy_b; - __le32 beacon_energy_c; - __le32 num_bt_kills; - __le32 mac_id; -} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_4 */ - -struct mvm_statistics_rx_non_phy_v3 { - __le32 bogus_cts; /* CTS received when not expecting CTS */ - __le32 bogus_ack; /* ACK received when not expecting ACK */ - __le32 non_bssid_frames; /* number of frames with BSSID that - * doesn't belong to the STA BSSID */ - __le32 filtered_frames; /* count frames that were dumped in the - * filtering process */ - __le32 non_channel_beacons; /* beacons with our bss id but not on - * our serving channel */ - __le32 channel_beacons; /* beacons with our bss id and in our - * serving channel */ - __le32 num_missed_bcon; /* number of missed beacons */ - __le32 adc_rx_saturation_time; /* count in 0.8us units the time the - * ADC was in saturation */ - __le32 ina_detection_search_time;/* total time (in 0.8us) searched - * for INA */ - __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ - __le32 interference_data_flag; /* flag for interference data - * availability. 1 when data is - * available. */ - __le32 channel_load; /* counts RX Enable time in uSec */ - __le32 dsp_false_alarms; /* DSP false alarm (both OFDM - * and CCK) counter */ - __le32 beacon_rssi_a; - __le32 beacon_rssi_b; - __le32 beacon_rssi_c; - __le32 beacon_energy_a; - __le32 beacon_energy_b; - __le32 beacon_energy_c; - __le32 num_bt_kills; - __le32 mac_id; - __le32 directed_data_mpdu; -} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ - -struct mvm_statistics_rx_phy { - __le32 unresponded_rts; - __le32 rxe_frame_lmt_overrun; - __le32 sent_ba_rsp_cnt; - __le32 dsp_self_kill; - __le32 reserved; -} __packed; /* STATISTICS_RX_PHY_API_S_VER_3 */ - -struct mvm_statistics_rx_phy_v2 { - __le32 ina_cnt; - __le32 fina_cnt; - __le32 plcp_err; - __le32 crc32_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 false_alarm_cnt; - __le32 fina_sync_err_cnt; - __le32 sfd_timeout; - __le32 fina_timeout; - __le32 unresponded_rts; - __le32 rxe_frame_lmt_overrun; - __le32 sent_ack_cnt; - __le32 sent_cts_cnt; - __le32 sent_ba_rsp_cnt; - __le32 dsp_self_kill; - __le32 mh_format_err; - __le32 re_acq_main_rssi_sum; - __le32 reserved; -} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ - -struct mvm_statistics_rx_ht_phy_v1 { - __le32 plcp_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 crc32_err; - __le32 mh_format_err; - __le32 agg_crc32_good; - __le32 agg_mpdu_cnt; - __le32 agg_cnt; - __le32 unsupport_mcs; -} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ - -struct mvm_statistics_rx_ht_phy { - __le32 mh_format_err; - __le32 agg_mpdu_cnt; - __le32 agg_cnt; - __le32 unsupport_mcs; -} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_2 */ - -struct mvm_statistics_tx_non_phy_v3 { - __le32 preamble_cnt; - __le32 rx_detected_cnt; - __le32 bt_prio_defer_cnt; - __le32 bt_prio_kill_cnt; - __le32 few_bytes_cnt; - __le32 cts_timeout; - __le32 ack_timeout; - __le32 expected_ack_cnt; - __le32 actual_ack_cnt; - __le32 dump_msdu_cnt; - __le32 burst_abort_next_frame_mismatch_cnt; - __le32 burst_abort_missing_next_frame_cnt; - __le32 cts_timeout_collision; - __le32 ack_or_ba_timeout_collision; -} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_3 */ - -struct mvm_statistics_tx_non_phy { - __le32 bt_prio_defer_cnt; - __le32 bt_prio_kill_cnt; - __le32 few_bytes_cnt; - __le32 cts_timeout; - __le32 ack_timeout; - __le32 dump_msdu_cnt; - __le32 burst_abort_next_frame_mismatch_cnt; - __le32 burst_abort_missing_next_frame_cnt; - __le32 cts_timeout_collision; - __le32 ack_or_ba_timeout_collision; -} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_4 */ - -#define MAX_CHAINS 3 - -struct mvm_statistics_tx_non_phy_agg { - __le32 ba_timeout; - __le32 ba_reschedule_frames; - __le32 scd_query_agg_frame_cnt; - __le32 scd_query_no_agg; - __le32 scd_query_agg; - __le32 scd_query_mismatch; - __le32 frame_not_ready; - __le32 underrun; - __le32 bt_prio_kill; - __le32 rx_ba_rsp_cnt; - __s8 txpower[MAX_CHAINS]; - __s8 reserved; - __le32 reserved2; -} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ - -struct mvm_statistics_tx_channel_width { - __le32 ext_cca_narrow_ch20[1]; - __le32 ext_cca_narrow_ch40[2]; - __le32 ext_cca_narrow_ch80[3]; - __le32 ext_cca_narrow_ch160[4]; - __le32 last_tx_ch_width_indx; - __le32 rx_detected_per_ch_width[4]; - __le32 success_per_ch_width[4]; - __le32 fail_per_ch_width[4]; -}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ - -struct mvm_statistics_tx_v4 { - struct mvm_statistics_tx_non_phy_v3 general; - struct mvm_statistics_tx_non_phy_agg agg; - struct mvm_statistics_tx_channel_width channel_width; -} __packed; /* STATISTICS_TX_API_S_VER_4 */ - -struct mvm_statistics_tx { - struct mvm_statistics_tx_non_phy general; - struct mvm_statistics_tx_non_phy_agg agg; - struct mvm_statistics_tx_channel_width channel_width; -} __packed; /* STATISTICS_TX_API_S_VER_5 */ - - -struct mvm_statistics_bt_activity { - __le32 hi_priority_tx_req_cnt; - __le32 hi_priority_tx_denied_cnt; - __le32 lo_priority_tx_req_cnt; - __le32 lo_priority_tx_denied_cnt; - __le32 hi_priority_rx_req_cnt; - __le32 hi_priority_rx_denied_cnt; - __le32 lo_priority_rx_req_cnt; - __le32 lo_priority_rx_denied_cnt; -} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ - -struct mvm_statistics_general_common_v19 { - __le32 radio_temperature; - __le32 radio_voltage; - struct mvm_statistics_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct mvm_statistics_div slow_div; - __le32 rx_enable_counter; - /* - * num_of_sos_states: - * count the number of times we have to re-tune - * in order to get out of bad PHY status - */ - __le32 num_of_sos_states; - __le32 beacon_filtered; - __le32 missed_beacons; - u8 beacon_filter_average_energy; - u8 beacon_filter_reason; - u8 beacon_filter_current_energy; - u8 beacon_filter_reserved; - __le32 beacon_filter_delta_time; - struct mvm_statistics_bt_activity bt_activity; - __le64 rx_time; - __le64 on_time_rf; - __le64 on_time_scan; - __le64 tx_time; -} __packed; - -struct mvm_statistics_general_common { - __le32 radio_temperature; - struct mvm_statistics_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct mvm_statistics_div slow_div; - __le32 rx_enable_counter; - /* - * num_of_sos_states: - * count the number of times we have to re-tune - * in order to get out of bad PHY status - */ - __le32 num_of_sos_states; - __le32 beacon_filtered; - __le32 missed_beacons; - u8 beacon_filter_average_energy; - u8 beacon_filter_reason; - u8 beacon_filter_current_energy; - u8 beacon_filter_reserved; - __le32 beacon_filter_delta_time; - struct mvm_statistics_bt_activity bt_activity; - __le64 rx_time; - __le64 on_time_rf; - __le64 on_time_scan; - __le64 tx_time; -} __packed; /* STATISTICS_GENERAL_API_S_VER_10 */ - -struct mvm_statistics_general_v8 { - struct mvm_statistics_general_common_v19 common; - __le32 beacon_counter[NUM_MAC_INDEX]; - u8 beacon_average_energy[NUM_MAC_INDEX]; - u8 reserved[4 - (NUM_MAC_INDEX % 4)]; -} __packed; /* STATISTICS_GENERAL_API_S_VER_8 */ - -struct mvm_statistics_general_cdb_v9 { - struct mvm_statistics_general_common_v19 common; - __le32 beacon_counter[NUM_MAC_INDEX_CDB]; - u8 beacon_average_energy[NUM_MAC_INDEX_CDB]; - u8 reserved[4 - (NUM_MAC_INDEX_CDB % 4)]; -} __packed; /* STATISTICS_GENERAL_API_S_VER_9 */ - -struct mvm_statistics_general_cdb { - struct mvm_statistics_general_common common; - __le32 beacon_counter[MAC_INDEX_AUX]; - u8 beacon_average_energy[MAC_INDEX_AUX]; - u8 reserved[8 - MAC_INDEX_AUX]; -} __packed; /* STATISTICS_GENERAL_API_S_VER_10 */ - -/** - * struct mvm_statistics_load - RX statistics for multi-queue devices - * @air_time: accumulated air time, per mac - * @byte_count: accumulated byte count, per mac - * @pkt_count: accumulated packet count, per mac - * @avg_energy: average RSSI, per station - */ -struct mvm_statistics_load { - __le32 air_time[MAC_INDEX_AUX]; - __le32 byte_count[MAC_INDEX_AUX]; - __le32 pkt_count[MAC_INDEX_AUX]; - u8 avg_energy[IWL_MVM_STATION_COUNT]; -} __packed; /* STATISTICS_RX_MAC_STATION_S_VER_3 */ - -struct mvm_statistics_load_v1 { - __le32 air_time[NUM_MAC_INDEX]; - __le32 byte_count[NUM_MAC_INDEX]; - __le32 pkt_count[NUM_MAC_INDEX]; - u8 avg_energy[IWL_MVM_STATION_COUNT]; -} __packed; /* STATISTICS_RX_MAC_STATION_S_VER_1 */ - -struct mvm_statistics_rx { - struct mvm_statistics_rx_phy ofdm; - struct mvm_statistics_rx_phy cck; - struct mvm_statistics_rx_non_phy general; - struct mvm_statistics_rx_ht_phy ofdm_ht; -} __packed; /* STATISTICS_RX_API_S_VER_4 */ - -struct mvm_statistics_rx_v3 { - struct mvm_statistics_rx_phy_v2 ofdm; - struct mvm_statistics_rx_phy_v2 cck; - struct mvm_statistics_rx_non_phy_v3 general; - struct mvm_statistics_rx_ht_phy_v1 ofdm_ht; -} __packed; /* STATISTICS_RX_API_S_VER_3 */ - -/* - * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) - * - * By default, uCode issues this notification after receiving a beacon - * while associated. To disable this behavior, set DISABLE_NOTIF flag in the - * STATISTICS_CMD (0x9c), below. - */ - -struct iwl_notif_statistics_v10 { - __le32 flag; - struct mvm_statistics_rx_v3 rx; - struct mvm_statistics_tx_v4 tx; - struct mvm_statistics_general_v8 general; -} __packed; /* STATISTICS_NTFY_API_S_VER_10 */ - -struct iwl_notif_statistics_v11 { - __le32 flag; - struct mvm_statistics_rx_v3 rx; - struct mvm_statistics_tx_v4 tx; - struct mvm_statistics_general_v8 general; - struct mvm_statistics_load_v1 load_stats; -} __packed; /* STATISTICS_NTFY_API_S_VER_11 */ - -struct iwl_notif_statistics_cdb { - __le32 flag; - struct mvm_statistics_rx rx; - struct mvm_statistics_tx tx; - struct mvm_statistics_general_cdb general; - struct mvm_statistics_load load_stats; -} __packed; /* STATISTICS_NTFY_API_S_VER_13 */ - -/** - * enum iwl_statistics_notif_flags - flags used in statistics notification - * @IWL_STATISTICS_REPLY_FLG_CLEAR: statistics were cleared after this report - */ -enum iwl_statistics_notif_flags { - IWL_STATISTICS_REPLY_FLG_CLEAR = 0x1, -}; - -/** - * enum iwl_statistics_cmd_flags - flags used in statistics command - * @IWL_STATISTICS_FLG_CLEAR: request to clear statistics after the report - * that's sent after this command - * @IWL_STATISTICS_FLG_DISABLE_NOTIF: disable unilateral statistics - * notifications - */ -enum iwl_statistics_cmd_flags { - IWL_STATISTICS_FLG_CLEAR = 0x1, - IWL_STATISTICS_FLG_DISABLE_NOTIF = 0x2, -}; - -/** - * struct iwl_statistics_cmd - statistics config command - * @flags: flags from &enum iwl_statistics_cmd_flags - */ -struct iwl_statistics_cmd { - __le32 flags; -} __packed; /* STATISTICS_CMD_API_S_VER_1 */ - -#endif /* __fw_api_stats_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h deleted file mode 100644 index 8658a983c463..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h +++ /dev/null @@ -1,398 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __fw_api_tof_h__ -#define __fw_api_tof_h__ - -/* ToF sub-group command IDs */ -enum iwl_mvm_tof_sub_grp_ids { - TOF_RANGE_REQ_CMD = 0x1, - TOF_CONFIG_CMD = 0x2, - TOF_RANGE_ABORT_CMD = 0x3, - TOF_RANGE_REQ_EXT_CMD = 0x4, - TOF_RESPONDER_CONFIG_CMD = 0x5, - TOF_NW_INITIATED_RES_SEND_CMD = 0x6, - TOF_NEIGHBOR_REPORT_REQ_CMD = 0x7, - TOF_NEIGHBOR_REPORT_RSP_NOTIF = 0xFC, - TOF_NW_INITIATED_REQ_RCVD_NOTIF = 0xFD, - TOF_RANGE_RESPONSE_NOTIF = 0xFE, - TOF_MCSI_DEBUG_NOTIF = 0xFB, -}; - -/** - * struct iwl_tof_config_cmd - ToF configuration - * @tof_disabled: 0 enabled, 1 - disabled - * @one_sided_disabled: 0 enabled, 1 - disabled - * @is_debug_mode: 1 debug mode, 0 - otherwise - * @is_buf_required: 1 channel estimation buffer required, 0 - otherwise - */ -struct iwl_tof_config_cmd { - __le32 sub_grp_cmd_id; - u8 tof_disabled; - u8 one_sided_disabled; - u8 is_debug_mode; - u8 is_buf_required; -} __packed; - -/** - * struct iwl_tof_responder_config_cmd - ToF AP mode (for debug) - * @burst_period: future use: (currently hard coded in the LMAC) - * The interval between two sequential bursts. - * @min_delta_ftm: future use: (currently hard coded in the LMAC) - * The minimum delay between two sequential FTM Responses - * in the same burst. - * @burst_duration: future use: (currently hard coded in the LMAC) - * The total time for all FTMs handshake in the same burst. - * Affect the time events duration in the LMAC. - * @num_of_burst_exp: future use: (currently hard coded in the LMAC) - * The number of bursts for the current ToF request. Affect - * the number of events allocations in the current iteration. - * @get_ch_est: for xVT only, NA for driver - * @abort_responder: when set to '1' - Responder will terminate its activity - * (all other fields in the command are ignored) - * @recv_sta_req_params: 1 - Responder will ignore the other Responder's - * params and use the recomended Initiator params. - * 0 - otherwise - * @channel_num: current AP Channel - * @bandwidth: current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz - * @rate: current AP rate - * @ctrl_ch_position: coding of the control channel position relative to - * the center frequency: - * - * 40 MHz - * 0 below center, 1 above center - * - * 80 MHz - * bits [0..1] - * * 0 the near 20MHz to the center, - * * 1 the far 20MHz to the center - * bit[2] - * as above 40MHz - * @ftm_per_burst: FTMs per Burst - * @ftm_resp_ts_avail: '0' - we don't measure over the Initial FTM Response, - * '1' - we measure over the Initial FTM Response - * @asap_mode: ASAP / Non ASAP mode for the current WLS station - * @sta_id: index of the AP STA when in AP mode - * @tsf_timer_offset_msecs: The dictated time offset (mSec) from the AP's TSF - * @toa_offset: Artificial addition [0.1nsec] for the ToA - to be used for debug - * purposes, simulating station movement by adding various values - * to this field - * @bssid: Current AP BSSID - */ -struct iwl_tof_responder_config_cmd { - __le32 sub_grp_cmd_id; - __le16 burst_period; - u8 min_delta_ftm; - u8 burst_duration; - u8 num_of_burst_exp; - u8 get_ch_est; - u8 abort_responder; - u8 recv_sta_req_params; - u8 channel_num; - u8 bandwidth; - u8 rate; - u8 ctrl_ch_position; - u8 ftm_per_burst; - u8 ftm_resp_ts_avail; - u8 asap_mode; - u8 sta_id; - __le16 tsf_timer_offset_msecs; - __le16 toa_offset; - u8 bssid[ETH_ALEN]; -} __packed; - -/** - * struct iwl_tof_range_request_ext_cmd - extended range req for WLS - * @tsf_timer_offset_msec: the recommended time offset (mSec) from the AP's TSF - * @reserved: reserved - * @min_delta_ftm: Minimal time between two consecutive measurements, - * in units of 100us. 0 means no preference by station - * @ftm_format_and_bw20M: FTM Channel Spacing/Format for 20MHz: recommended - * value be sent to the AP - * @ftm_format_and_bw40M: FTM Channel Spacing/Format for 40MHz: recommended - * value to be sent to the AP - * @ftm_format_and_bw80M: FTM Channel Spacing/Format for 80MHz: recommended - * value to be sent to the AP - */ -struct iwl_tof_range_req_ext_cmd { - __le32 sub_grp_cmd_id; - __le16 tsf_timer_offset_msec; - __le16 reserved; - u8 min_delta_ftm; - u8 ftm_format_and_bw20M; - u8 ftm_format_and_bw40M; - u8 ftm_format_and_bw80M; -} __packed; - -#define IWL_MVM_TOF_MAX_APS 21 - -/** - * struct iwl_tof_range_req_ap_entry - AP configuration parameters - * @channel_num: Current AP Channel - * @bandwidth: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz - * @tsf_delta_direction: TSF relatively to the subject AP - * @ctrl_ch_position: Coding of the control channel position relative to the - * center frequency. - * 40MHz 0 below center, 1 above center - * 80MHz bits [0..1]: 0 the near 20MHz to the center, - * 1 the far 20MHz to the center - * bit[2] as above 40MHz - * @bssid: AP's bss id - * @measure_type: Measurement type: 0 - two sided, 1 - One sided - * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of the - * number of measurement iterations (min 2^0 = 1, max 2^14) - * @burst_period: Recommended value to be sent to the AP. Measurement - * periodicity In units of 100ms. ignored if num_of_bursts = 0 - * @samples_per_burst: 2-sided: the number of FTMs pairs in single Burst (1-31) - * 1-sided: how many rts/cts pairs should be used per burst. - * @retries_per_sample: Max number of retries that the LMAC should send - * in case of no replies by the AP. - * @tsf_delta: TSF Delta in units of microseconds. - * The difference between the AP TSF and the device local clock. - * @location_req: Location Request Bit[0] LCI should be sent in the FTMR - * Bit[1] Civic should be sent in the FTMR - * @asap_mode: 0 - non asap mode, 1 - asap mode (not relevant for one sided) - * @enable_dyn_ack: Enable Dynamic ACK BW. - * 0 Initiator interact with regular AP - * 1 Initiator interact with Responder machine: need to send the - * Initiator Acks with HT 40MHz / 80MHz, since the Responder should - * use it for its ch est measurement (this flag will be set when we - * configure the opposite machine to be Responder). - * @rssi: Last received value - * leagal values: -128-0 (0x7f). above 0x0 indicating an invalid value. - */ -struct iwl_tof_range_req_ap_entry { - u8 channel_num; - u8 bandwidth; - u8 tsf_delta_direction; - u8 ctrl_ch_position; - u8 bssid[ETH_ALEN]; - u8 measure_type; - u8 num_of_bursts; - __le16 burst_period; - u8 samples_per_burst; - u8 retries_per_sample; - __le32 tsf_delta; - u8 location_req; - u8 asap_mode; - u8 enable_dyn_ack; - s8 rssi; -} __packed; - -/** - * enum iwl_tof_response_mode - * @IWL_MVM_TOF_RESPOSE_ASAP: report each AP measurement separately as soon as - * possible (not supported for this release) - * @IWL_MVM_TOF_RESPOSE_TIMEOUT: report all AP measurements as a batch upon - * timeout expiration - * @IWL_MVM_TOF_RESPOSE_COMPLETE: report all AP measurements as a batch at the - * earlier of: measurements completion / timeout - * expiration. - */ -enum iwl_tof_response_mode { - IWL_MVM_TOF_RESPOSE_ASAP = 1, - IWL_MVM_TOF_RESPOSE_TIMEOUT, - IWL_MVM_TOF_RESPOSE_COMPLETE, -}; - -/** - * struct iwl_tof_range_req_cmd - start measurement cmd - * @request_id: A Token incremented per request. The same Token will be - * sent back in the range response - * @initiator: 0- NW initiated, 1 - Client Initiated - * @one_sided_los_disable: '0'- run ML-Algo for both ToF/OneSided, - * '1' - run ML-Algo for ToF only - * @req_timeout: Requested timeout of the response in units of 100ms. - * This is equivalent to the session time configured to the - * LMAC in Initiator Request - * @report_policy: Supported partially for this release: For current release - - * the range report will be uploaded as a batch when ready or - * when the session is done (successfully / partially). - * one of iwl_tof_response_mode. - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) - * @macaddr_random: '0' Use default source MAC address (i.e. p2_p), - * '1' Use MAC Address randomization according to the below - * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. - * Bits set to 1 shall be randomized by the UMAC - * @ap: per-AP request data - */ -struct iwl_tof_range_req_cmd { - __le32 sub_grp_cmd_id; - u8 request_id; - u8 initiator; - u8 one_sided_los_disable; - u8 req_timeout; - u8 report_policy; - u8 los_det_disable; - u8 num_of_ap; - u8 macaddr_random; - u8 macaddr_template[ETH_ALEN]; - u8 macaddr_mask[ETH_ALEN]; - struct iwl_tof_range_req_ap_entry ap[IWL_MVM_TOF_MAX_APS]; -} __packed; - -/** - * struct iwl_tof_gen_resp_cmd - generic ToF response - */ -struct iwl_tof_gen_resp_cmd { - __le32 sub_grp_cmd_id; - u8 data[]; -} __packed; - -/** - * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response) - * @bssid: BSSID of the AP - * @measure_status: current APs measurement status, one of - * &enum iwl_tof_entry_status. - * @measure_bw: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz - * @rtt: The Round Trip Time that took for the last measurement for - * current AP [nSec] - * @rtt_variance: The Variance of the RTT values measured for current AP - * @rtt_spread: The Difference between the maximum and the minimum RTT - * values measured for current AP in the current session [nsec] - * @rssi: RSSI as uploaded in the Channel Estimation notification - * @rssi_spread: The Difference between the maximum and the minimum RSSI values - * measured for current AP in the current session - * @reserved: reserved - * @range: Measured range [cm] - * @range_variance: Measured range variance [cm] - * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was - * uploaded by the LMAC - */ -struct iwl_tof_range_rsp_ap_entry_ntfy { - u8 bssid[ETH_ALEN]; - u8 measure_status; - u8 measure_bw; - __le32 rtt; - __le32 rtt_variance; - __le32 rtt_spread; - s8 rssi; - u8 rssi_spread; - __le16 reserved; - __le32 range; - __le32 range_variance; - __le32 timestamp; -} __packed; - -/** - * struct iwl_tof_range_rsp_ntfy - - * @request_id: A Token ID of the corresponding Range request - * @request_status: status of current measurement session - * @last_in_batch: reprot policy (when not all responses are uploaded at once) - * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) - * @ap: per-AP data - */ -struct iwl_tof_range_rsp_ntfy { - u8 request_id; - u8 request_status; - u8 last_in_batch; - u8 num_of_aps; - struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_MVM_TOF_MAX_APS]; -} __packed; - -#define IWL_MVM_TOF_MCSI_BUF_SIZE (245) -/** - * struct iwl_tof_mcsi_notif - used for debug - * @token: token ID for the current session - * @role: '0' - initiator, '1' - responder - * @reserved: reserved - * @initiator_bssid: initiator machine - * @responder_bssid: responder machine - * @mcsi_buffer: debug data - */ -struct iwl_tof_mcsi_notif { - u8 token; - u8 role; - __le16 reserved; - u8 initiator_bssid[ETH_ALEN]; - u8 responder_bssid[ETH_ALEN]; - u8 mcsi_buffer[IWL_MVM_TOF_MCSI_BUF_SIZE * 4]; -} __packed; - -/** - * struct iwl_tof_neighbor_report_notif - * @bssid: BSSID of the AP which sent the report - * @request_token: same token as the corresponding request - * @status: - * @report_ie_len: the length of the response frame starting from the Element ID - * @data: the IEs - */ -struct iwl_tof_neighbor_report { - u8 bssid[ETH_ALEN]; - u8 request_token; - u8 status; - __le16 report_ie_len; - u8 data[]; -} __packed; - -/** - * struct iwl_tof_range_abort_cmd - * @request_id: corresponds to a range request - * @reserved: reserved - */ -struct iwl_tof_range_abort_cmd { - __le32 sub_grp_cmd_id; - u8 request_id; - u8 reserved[3]; -} __packed; - -#endif diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h deleted file mode 100644 index 97d7eed32622..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ /dev/null @@ -1,917 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_tx_h__ -#define __fw_api_tx_h__ - -/** - * enum iwl_tx_flags - bitmasks for tx_flags in TX command - * @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame - * @TX_CMD_FLG_WRITE_TX_POWER: update current tx power value in the mgmt frame - * @TX_CMD_FLG_ACK: expect ACK from receiving station - * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command. - * Otherwise, use rate_n_flags from the TX command - * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected - * Must set TX_CMD_FLG_ACK with this flag. - * @TX_CMD_FLG_TXOP_PROT: TXOP protection requested - * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence - * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence - * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC) - * @TX_CMD_FLG_BT_PRIO_POS: the position of the BT priority (bit 11 is ignored - * on old firmwares). - * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame - * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control. - * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command - * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU - * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame - * Should be set for beacons and probe responses - * @TX_CMD_FLG_CALIB: activate PA TX power calibrations - * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count - * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header. - * Should be set for 26/30 length MAC headers - * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW - * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation - * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id - * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped - * @TX_CMD_FLG_EXEC_PAPD: execute PAPD - * @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power - * @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk - */ -enum iwl_tx_flags { - TX_CMD_FLG_PROT_REQUIRE = BIT(0), - TX_CMD_FLG_WRITE_TX_POWER = BIT(1), - TX_CMD_FLG_ACK = BIT(3), - TX_CMD_FLG_STA_RATE = BIT(4), - TX_CMD_FLG_BAR = BIT(6), - TX_CMD_FLG_TXOP_PROT = BIT(7), - TX_CMD_FLG_VHT_NDPA = BIT(8), - TX_CMD_FLG_HT_NDPA = BIT(9), - TX_CMD_FLG_CSI_FDBK2HOST = BIT(10), - TX_CMD_FLG_BT_PRIO_POS = 11, - TX_CMD_FLG_BT_DIS = BIT(12), - TX_CMD_FLG_SEQ_CTL = BIT(13), - TX_CMD_FLG_MORE_FRAG = BIT(14), - TX_CMD_FLG_TSF = BIT(16), - TX_CMD_FLG_CALIB = BIT(17), - TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18), - TX_CMD_FLG_MH_PAD = BIT(20), - TX_CMD_FLG_RESP_TO_DRV = BIT(21), - TX_CMD_FLG_TKIP_MIC_DONE = BIT(23), - TX_CMD_FLG_DUR = BIT(25), - TX_CMD_FLG_FW_DROP = BIT(26), - TX_CMD_FLG_EXEC_PAPD = BIT(27), - TX_CMD_FLG_PAPD_TYPE = BIT(28), - TX_CMD_FLG_HCCA_CHUNK = BIT(31) -}; /* TX_FLAGS_BITS_API_S_VER_1 */ - -/** - * enum iwl_tx_cmd_flags - bitmasks for tx_flags in TX command for a000 - * @IWL_TX_FLAGS_CMD_RATE: use rate from the TX command - * @IWL_TX_FLAGS_ENCRYPT_DIS: frame should not be encrypted, even if it belongs - * to a secured STA - * @IWL_TX_FLAGS_HIGH_PRI: high priority frame (like EAPOL) - can affect rate - * selection, retry limits and BT kill - */ -enum iwl_tx_cmd_flags { - IWL_TX_FLAGS_CMD_RATE = BIT(0), - IWL_TX_FLAGS_ENCRYPT_DIS = BIT(1), - IWL_TX_FLAGS_HIGH_PRI = BIT(2), -}; /* TX_FLAGS_BITS_API_S_VER_3 */ - -/** - * enum iwl_tx_pm_timeouts - pm timeout values in TX command - * @PM_FRAME_NONE: no need to suspend sleep mode - * @PM_FRAME_MGMT: fw suspend sleep mode for 100TU - * @PM_FRAME_ASSOC: fw suspend sleep mode for 10sec - */ -enum iwl_tx_pm_timeouts { - PM_FRAME_NONE = 0, - PM_FRAME_MGMT = 2, - PM_FRAME_ASSOC = 3, -}; - -#define TX_CMD_SEC_MSK 0x07 -#define TX_CMD_SEC_WEP_KEY_IDX_POS 6 -#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 - -/** - * enum iwl_tx_cmd_sec_ctrl - bitmasks for security control in TX command - * @TX_CMD_SEC_WEP: WEP encryption algorithm. - * @TX_CMD_SEC_CCM: CCM encryption algorithm. - * @TX_CMD_SEC_TKIP: TKIP encryption algorithm. - * @TX_CMD_SEC_EXT: extended cipher algorithm. - * @TX_CMD_SEC_GCMP: GCMP encryption algorithm. - * @TX_CMD_SEC_KEY128: set for 104 bits WEP key. - * @TX_CMD_SEC_KEY_FROM_TABLE: for a non-WEP key, set if the key should be taken - * from the table instead of from the TX command. - * If the key is taken from the key table its index should be given by the - * first byte of the TX command key field. - */ -enum iwl_tx_cmd_sec_ctrl { - TX_CMD_SEC_WEP = 0x01, - TX_CMD_SEC_CCM = 0x02, - TX_CMD_SEC_TKIP = 0x03, - TX_CMD_SEC_EXT = 0x04, - TX_CMD_SEC_GCMP = 0x05, - TX_CMD_SEC_KEY128 = 0x08, - TX_CMD_SEC_KEY_FROM_TABLE = 0x10, -}; - -/* - * TX command Frame life time in us - to be written in pm_frame_timeout - */ -#define TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF -#define TX_CMD_LIFE_TIME_DEFAULT 2000000 /* 2000 ms*/ -#define TX_CMD_LIFE_TIME_PROBE_RESP 40000 /* 40 ms */ -#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0 - -/* - * TID for non QoS frames - to be written in tid_tspec - */ -#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT - -/* - * Limits on the retransmissions - to be written in {data,rts}_retry_limit - */ -#define IWL_DEFAULT_TX_RETRY 15 -#define IWL_MGMT_DFAULT_RETRY_LIMIT 3 -#define IWL_RTS_DFAULT_RETRY_LIMIT 60 -#define IWL_BAR_DFAULT_RETRY_LIMIT 60 -#define IWL_LOW_RETRY_LIMIT 7 - -/** - * enum iwl_tx_offload_assist_flags_pos - set %iwl_tx_cmd offload_assist values - * @TX_CMD_OFFLD_IP_HDR: offset to start of IP header (in words) - * from mac header end. For normal case it is 4 words for SNAP. - * note: tx_cmd, mac header and pad are not counted in the offset. - * This is used to help the offload in case there is tunneling such as - * IPv6 in IPv4, in such case the ip header offset should point to the - * inner ip header and IPv4 checksum of the external header should be - * calculated by driver. - * @TX_CMD_OFFLD_L4_EN: enable TCP/UDP checksum - * @TX_CMD_OFFLD_L3_EN: enable IP header checksum - * @TX_CMD_OFFLD_MH_SIZE: size of the mac header in words. Includes the IV - * field. Doesn't include the pad. - * @TX_CMD_OFFLD_PAD: mark 2-byte pad was inserted after the mac header for - * alignment - * @TX_CMD_OFFLD_AMSDU: mark TX command is A-MSDU - */ -enum iwl_tx_offload_assist_flags_pos { - TX_CMD_OFFLD_IP_HDR = 0, - TX_CMD_OFFLD_L4_EN = 6, - TX_CMD_OFFLD_L3_EN = 7, - TX_CMD_OFFLD_MH_SIZE = 8, - TX_CMD_OFFLD_PAD = 13, - TX_CMD_OFFLD_AMSDU = 14, -}; - -#define IWL_TX_CMD_OFFLD_MH_MASK 0x1f -#define IWL_TX_CMD_OFFLD_IP_HDR_MASK 0x3f - -/* TODO: complete documentation for try_cnt and btkill_cnt */ -/** - * struct iwl_tx_cmd - TX command struct to FW - * ( TX_CMD = 0x1c ) - * @len: in bytes of the payload, see below for details - * @offload_assist: TX offload configuration - * @tx_flags: combination of TX_CMD_FLG_* - * @scratch: scratch buffer used by the device - * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is - * cleared. Combination of RATE_MCS_* - * @sta_id: index of destination station in FW station table - * @sec_ctl: security control, TX_CMD_SEC_* - * @initial_rate_index: index into the the rate table for initial TX attempt. - * Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames. - * @reserved2: reserved - * @key: security key - * @reserved3: reserved - * @life_time: frame life time (usecs??) - * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt + - * btkill_cnd + reserved), first 32 bits. "0" disables usage. - * @dram_msb_ptr: upper bits of the scratch physical address - * @rts_retry_limit: max attempts for RTS - * @data_retry_limit: max attempts to send the data packet - * @tid_tspec: TID/tspec - * @pm_frame_timeout: PM TX frame timeout - * @reserved4: reserved - * @payload: payload (same as @hdr) - * @hdr: 802.11 header (same as @payload) - * - * The byte count (both len and next_frame_len) includes MAC header - * (24/26/30/32 bytes) - * + 2 bytes pad if 26/30 header size - * + 8 byte IV for CCM or TKIP (not used for WEP) - * + Data payload - * + 8-byte MIC (not used for CCM/WEP) - * It does not include post-MAC padding, i.e., - * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes. - * Range of len: 14-2342 bytes. - * - * After the struct fields the MAC header is placed, plus any padding, - * and then the actial payload. - */ -struct iwl_tx_cmd { - __le16 len; - __le16 offload_assist; - __le32 tx_flags; - struct { - u8 try_cnt; - u8 btkill_cnt; - __le16 reserved; - } scratch; /* DRAM_SCRATCH_API_U_VER_1 */ - __le32 rate_n_flags; - u8 sta_id; - u8 sec_ctl; - u8 initial_rate_index; - u8 reserved2; - u8 key[16]; - __le32 reserved3; - __le32 life_time; - __le32 dram_lsb_ptr; - u8 dram_msb_ptr; - u8 rts_retry_limit; - u8 data_retry_limit; - u8 tid_tspec; - __le16 pm_frame_timeout; - __le16 reserved4; - u8 payload[0]; - struct ieee80211_hdr hdr[0]; -} __packed; /* TX_CMD_API_S_VER_6 */ - -struct iwl_dram_sec_info { - __le32 pn_low; - __le16 pn_high; - __le16 aux_info; -} __packed; /* DRAM_SEC_INFO_API_S_VER_1 */ - -/** - * struct iwl_tx_cmd_gen2 - TX command struct to FW for a000 devices - * ( TX_CMD = 0x1c ) - * @len: in bytes of the payload, see below for details - * @offload_assist: TX offload configuration - * @flags: combination of &enum iwl_tx_cmd_flags - * @dram_info: FW internal DRAM storage - * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is - * cleared. Combination of RATE_MCS_* - * @hdr: 802.11 header - */ -struct iwl_tx_cmd_gen2 { - __le16 len; - __le16 offload_assist; - __le32 flags; - struct iwl_dram_sec_info dram_info; - __le32 rate_n_flags; - struct ieee80211_hdr hdr[0]; -} __packed; /* TX_CMD_API_S_VER_7 */ - -/* - * TX response related data - */ - -/* - * enum iwl_tx_status - status that is returned by the fw after attempts to Tx - * @TX_STATUS_SUCCESS: - * @TX_STATUS_DIRECT_DONE: - * @TX_STATUS_POSTPONE_DELAY: - * @TX_STATUS_POSTPONE_FEW_BYTES: - * @TX_STATUS_POSTPONE_BT_PRIO: - * @TX_STATUS_POSTPONE_QUIET_PERIOD: - * @TX_STATUS_POSTPONE_CALC_TTAK: - * @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: - * @TX_STATUS_FAIL_SHORT_LIMIT: - * @TX_STATUS_FAIL_LONG_LIMIT: - * @TX_STATUS_FAIL_UNDERRUN: - * @TX_STATUS_FAIL_DRAIN_FLOW: - * @TX_STATUS_FAIL_RFKILL_FLUSH: - * @TX_STATUS_FAIL_LIFE_EXPIRE: - * @TX_STATUS_FAIL_DEST_PS: - * @TX_STATUS_FAIL_HOST_ABORTED: - * @TX_STATUS_FAIL_BT_RETRY: - * @TX_STATUS_FAIL_STA_INVALID: - * @TX_TATUS_FAIL_FRAG_DROPPED: - * @TX_STATUS_FAIL_TID_DISABLE: - * @TX_STATUS_FAIL_FIFO_FLUSHED: - * @TX_STATUS_FAIL_SMALL_CF_POLL: - * @TX_STATUS_FAIL_FW_DROP: - * @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and - * STA table - * @TX_FRAME_STATUS_INTERNAL_ABORT: - * @TX_MODE_MSK: - * @TX_MODE_NO_BURST: - * @TX_MODE_IN_BURST_SEQ: - * @TX_MODE_FIRST_IN_BURST: - * @TX_QUEUE_NUM_MSK: - * - * Valid only if frame_count =1 - * TODO: complete documentation - */ -enum iwl_tx_status { - TX_STATUS_MSK = 0x000000ff, - TX_STATUS_SUCCESS = 0x01, - TX_STATUS_DIRECT_DONE = 0x02, - /* postpone TX */ - TX_STATUS_POSTPONE_DELAY = 0x40, - TX_STATUS_POSTPONE_FEW_BYTES = 0x41, - TX_STATUS_POSTPONE_BT_PRIO = 0x42, - TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, - TX_STATUS_POSTPONE_CALC_TTAK = 0x44, - /* abort TX */ - TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, - TX_STATUS_FAIL_SHORT_LIMIT = 0x82, - TX_STATUS_FAIL_LONG_LIMIT = 0x83, - TX_STATUS_FAIL_UNDERRUN = 0x84, - TX_STATUS_FAIL_DRAIN_FLOW = 0x85, - TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, - TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, - TX_STATUS_FAIL_DEST_PS = 0x88, - TX_STATUS_FAIL_HOST_ABORTED = 0x89, - TX_STATUS_FAIL_BT_RETRY = 0x8a, - TX_STATUS_FAIL_STA_INVALID = 0x8b, - TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, - TX_STATUS_FAIL_TID_DISABLE = 0x8d, - TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, - TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f, - TX_STATUS_FAIL_FW_DROP = 0x90, - TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91, - TX_STATUS_INTERNAL_ABORT = 0x92, - TX_MODE_MSK = 0x00000f00, - TX_MODE_NO_BURST = 0x00000000, - TX_MODE_IN_BURST_SEQ = 0x00000100, - TX_MODE_FIRST_IN_BURST = 0x00000200, - TX_QUEUE_NUM_MSK = 0x0001f000, - TX_NARROW_BW_MSK = 0x00060000, - TX_NARROW_BW_1DIV2 = 0x00020000, - TX_NARROW_BW_1DIV4 = 0x00040000, - TX_NARROW_BW_1DIV8 = 0x00060000, -}; - -/* - * enum iwl_tx_agg_status - TX aggregation status - * @AGG_TX_STATE_STATUS_MSK: - * @AGG_TX_STATE_TRANSMITTED: - * @AGG_TX_STATE_UNDERRUN: - * @AGG_TX_STATE_BT_PRIO: - * @AGG_TX_STATE_FEW_BYTES: - * @AGG_TX_STATE_ABORT: - * @AGG_TX_STATE_LAST_SENT_TTL: - * @AGG_TX_STATE_LAST_SENT_TRY_CNT: - * @AGG_TX_STATE_LAST_SENT_BT_KILL: - * @AGG_TX_STATE_SCD_QUERY: - * @AGG_TX_STATE_TEST_BAD_CRC32: - * @AGG_TX_STATE_RESPONSE: - * @AGG_TX_STATE_DUMP_TX: - * @AGG_TX_STATE_DELAY_TX: - * @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries - * occur if tx failed for this frame when it was a member of a previous - * aggregation block). If rate scaling is used, retry count indicates the - * rate table entry used for all frames in the new agg. - *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for - * this frame - * - * TODO: complete documentation - */ -enum iwl_tx_agg_status { - AGG_TX_STATE_STATUS_MSK = 0x00fff, - AGG_TX_STATE_TRANSMITTED = 0x000, - AGG_TX_STATE_UNDERRUN = 0x001, - AGG_TX_STATE_BT_PRIO = 0x002, - AGG_TX_STATE_FEW_BYTES = 0x004, - AGG_TX_STATE_ABORT = 0x008, - AGG_TX_STATE_LAST_SENT_TTL = 0x010, - AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020, - AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040, - AGG_TX_STATE_SCD_QUERY = 0x080, - AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100, - AGG_TX_STATE_RESPONSE = 0x1ff, - AGG_TX_STATE_DUMP_TX = 0x200, - AGG_TX_STATE_DELAY_TX = 0x400, - AGG_TX_STATE_TRY_CNT_POS = 12, - AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS, -}; - -#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL| \ - AGG_TX_STATE_LAST_SENT_TRY_CNT| \ - AGG_TX_STATE_LAST_SENT_BT_KILL) - -/* - * The mask below describes a status where we are absolutely sure that the MPDU - * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've - * written the bytes to the TXE, but we know nothing about what the DSP did. - */ -#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \ - AGG_TX_STATE_ABORT | \ - AGG_TX_STATE_SCD_QUERY) - -/* - * REPLY_TX = 0x1c (response) - * - * This response may be in one of two slightly different formats, indicated - * by the frame_count field: - * - * 1) No aggregation (frame_count == 1). This reports Tx results for a single - * frame. Multiple attempts, at various bit rates, may have been made for - * this frame. - * - * 2) Aggregation (frame_count > 1). This reports Tx results for two or more - * frames that used block-acknowledge. All frames were transmitted at - * same rate. Rate scaling may have been used if first frame in this new - * agg block failed in previous agg block(s). - * - * Note that, for aggregation, ACK (block-ack) status is not delivered - * here; block-ack has not been received by the time the device records - * this status. - * This status relates to reasons the tx might have been blocked or aborted - * within the device, rather than whether it was received successfully by - * the destination station. - */ - -/** - * struct agg_tx_status - per packet TX aggregation status - * @status: See &enum iwl_tx_agg_status - * @sequence: Sequence # for this frame's Tx cmd (not SSN!) - */ -struct agg_tx_status { - __le16 status; - __le16 sequence; -} __packed; - -/* - * definitions for initial rate index field - * bits [3:0] initial rate index - * bits [6:4] rate table color, used for the initial rate - * bit-7 invalid rate indication - */ -#define TX_RES_INIT_RATE_INDEX_MSK 0x0f -#define TX_RES_RATE_TABLE_COLOR_POS 4 -#define TX_RES_RATE_TABLE_COLOR_MSK 0x70 -#define TX_RES_INV_RATE_INDEX_MSK 0x80 -#define TX_RES_RATE_TABLE_COL_GET(_f) (((_f) & TX_RES_RATE_TABLE_COLOR_MSK) >>\ - TX_RES_RATE_TABLE_COLOR_POS) - -#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f) -#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4) - -/** - * struct iwl_mvm_tx_resp_v3 - notifies that fw is TXing a packet - * ( REPLY_TX = 0x1c ) - * @frame_count: 1 no aggregation, >1 aggregation - * @bt_kill_count: num of times blocked by bluetooth (unused for agg) - * @failure_rts: num of failures due to unsuccessful RTS - * @failure_frame: num failures due to no ACK (unused for agg) - * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the - * Tx of all the batch. RATE_MCS_* - * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. - * for agg: RTS + CTS + aggregation tx time + block-ack time. - * in usec. - * @pa_status: tx power info - * @pa_integ_res_a: tx power info - * @pa_integ_res_b: tx power info - * @pa_integ_res_c: tx power info - * @measurement_req_id: tx power info - * @reduced_tpc: transmit power reduction used - * @reserved: reserved - * @tfd_info: TFD information set by the FH - * @seq_ctl: sequence control from the Tx cmd - * @byte_cnt: byte count from the Tx cmd - * @tlc_info: TLC rate info - * @ra_tid: bits [3:0] = ra, bits [7:4] = tid - * @frame_ctrl: frame control - * @status: for non-agg: frame status TX_STATUS_* - * for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields - * follow this one, up to frame_count. Length in @frame_count. - * - * After the array of statuses comes the SSN of the SCD. Look at - * %iwl_mvm_get_scd_ssn for more details. - */ -struct iwl_mvm_tx_resp_v3 { - u8 frame_count; - u8 bt_kill_count; - u8 failure_rts; - u8 failure_frame; - __le32 initial_rate; - __le16 wireless_media_time; - - u8 pa_status; - u8 pa_integ_res_a[3]; - u8 pa_integ_res_b[3]; - u8 pa_integ_res_c[3]; - __le16 measurement_req_id; - u8 reduced_tpc; - u8 reserved; - - __le32 tfd_info; - __le16 seq_ctl; - __le16 byte_cnt; - u8 tlc_info; - u8 ra_tid; - __le16 frame_ctrl; - struct agg_tx_status status[]; -} __packed; /* TX_RSP_API_S_VER_3 */ - -/** - * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet - * ( REPLY_TX = 0x1c ) - * @frame_count: 1 no aggregation, >1 aggregation - * @bt_kill_count: num of times blocked by bluetooth (unused for agg) - * @failure_rts: num of failures due to unsuccessful RTS - * @failure_frame: num failures due to no ACK (unused for agg) - * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the - * Tx of all the batch. RATE_MCS_* - * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. - * for agg: RTS + CTS + aggregation tx time + block-ack time. - * in usec. - * @pa_status: tx power info - * @pa_integ_res_a: tx power info - * @pa_integ_res_b: tx power info - * @pa_integ_res_c: tx power info - * @measurement_req_id: tx power info - * @reduced_tpc: transmit power reduction used - * @reserved: reserved - * @tfd_info: TFD information set by the FH - * @seq_ctl: sequence control from the Tx cmd - * @byte_cnt: byte count from the Tx cmd - * @tlc_info: TLC rate info - * @ra_tid: bits [3:0] = ra, bits [7:4] = tid - * @frame_ctrl: frame control - * @tx_queue: TX queue for this response - * @reserved2: reserved for padding/alignment - * @status: for non-agg: frame status TX_STATUS_* - * For version 6 TX response isn't received for aggregation at all. - * - * After the array of statuses comes the SSN of the SCD. Look at - * %iwl_mvm_get_scd_ssn for more details. - */ -struct iwl_mvm_tx_resp { - u8 frame_count; - u8 bt_kill_count; - u8 failure_rts; - u8 failure_frame; - __le32 initial_rate; - __le16 wireless_media_time; - - u8 pa_status; - u8 pa_integ_res_a[3]; - u8 pa_integ_res_b[3]; - u8 pa_integ_res_c[3]; - __le16 measurement_req_id; - u8 reduced_tpc; - u8 reserved; - - __le32 tfd_info; - __le16 seq_ctl; - __le16 byte_cnt; - u8 tlc_info; - u8 ra_tid; - __le16 frame_ctrl; - __le16 tx_queue; - __le16 reserved2; - struct agg_tx_status status; -} __packed; /* TX_RSP_API_S_VER_6 */ - -/** - * struct iwl_mvm_ba_notif - notifies about reception of BA - * ( BA_NOTIF = 0xc5 ) - * @sta_addr: MAC address - * @reserved: reserved - * @sta_id: Index of recipient (BA-sending) station in fw's station table - * @tid: tid of the session - * @seq_ctl: sequence control field - * @bitmap: the bitmap of the BA notification as seen in the air - * @scd_flow: the tx queue this BA relates to - * @scd_ssn: the index of the last contiguously sent packet - * @txed: number of Txed frames in this batch - * @txed_2_done: number of Acked frames in this batch - * @reduced_txp: power reduced according to TPC. This is the actual value and - * not a copy from the LQ command. Thus, if not the first rate was used - * for Tx-ing then this value will be set to 0 by FW. - * @reserved1: reserved - */ -struct iwl_mvm_ba_notif { - u8 sta_addr[ETH_ALEN]; - __le16 reserved; - - u8 sta_id; - u8 tid; - __le16 seq_ctl; - __le64 bitmap; - __le16 scd_flow; - __le16 scd_ssn; - u8 txed; - u8 txed_2_done; - u8 reduced_txp; - u8 reserved1; -} __packed; - -/** - * struct iwl_mvm_compressed_ba_tfd - progress of a TFD queue - * @q_num: TFD queue number - * @tfd_index: Index of first un-acked frame in the TFD queue - * @scd_queue: For debug only - the physical queue the TFD queue is bound to - * @tid: TID of the queue (0-7) - * @reserved: reserved for alignment - */ -struct iwl_mvm_compressed_ba_tfd { - __le16 q_num; - __le16 tfd_index; - u8 scd_queue; - u8 tid; - u8 reserved[2]; -} __packed; /* COMPRESSED_BA_TFD_API_S_VER_1 */ - -/** - * struct iwl_mvm_compressed_ba_ratid - progress of a RA TID queue - * @q_num: RA TID queue number - * @tid: TID of the queue - * @ssn: BA window current SSN - */ -struct iwl_mvm_compressed_ba_ratid { - u8 q_num; - u8 tid; - __le16 ssn; -} __packed; /* COMPRESSED_BA_RATID_API_S_VER_1 */ - -/* - * enum iwl_mvm_ba_resp_flags - TX aggregation status - * @IWL_MVM_BA_RESP_TX_AGG: generated due to BA - * @IWL_MVM_BA_RESP_TX_BAR: generated due to BA after BAR - * @IWL_MVM_BA_RESP_TX_AGG_FAIL: aggregation didn't receive BA - * @IWL_MVM_BA_RESP_TX_UNDERRUN: aggregation got underrun - * @IWL_MVM_BA_RESP_TX_BT_KILL: aggregation got BT-kill - * @IWL_MVM_BA_RESP_TX_DSP_TIMEOUT: aggregation didn't finish within the - * expected time - */ -enum iwl_mvm_ba_resp_flags { - IWL_MVM_BA_RESP_TX_AGG, - IWL_MVM_BA_RESP_TX_BAR, - IWL_MVM_BA_RESP_TX_AGG_FAIL, - IWL_MVM_BA_RESP_TX_UNDERRUN, - IWL_MVM_BA_RESP_TX_BT_KILL, - IWL_MVM_BA_RESP_TX_DSP_TIMEOUT -}; - -/** - * struct iwl_mvm_compressed_ba_notif - notifies about reception of BA - * ( BA_NOTIF = 0xc5 ) - * @flags: status flag, see the &iwl_mvm_ba_resp_flags - * @sta_id: Index of recipient (BA-sending) station in fw's station table - * @reduced_txp: power reduced according to TPC. This is the actual value and - * not a copy from the LQ command. Thus, if not the first rate was used - * for Tx-ing then this value will be set to 0 by FW. - * @initial_rate: TLC rate info, initial rate index, TLC table color - * @retry_cnt: retry count - * @query_byte_cnt: SCD query byte count - * @query_frame_cnt: SCD query frame count - * @txed: number of frames sent in the aggregation (all-TIDs) - * @done: number of frames that were Acked by the BA (all-TIDs) - * @reserved: reserved (for alignment) - * @wireless_time: Wireless-media time - * @tx_rate: the rate the aggregation was sent at - * @tfd_cnt: number of TFD-Q elements - * @ra_tid_cnt: number of RATID-Q elements - * @tfd: array of TFD queue status updates. See &iwl_mvm_compressed_ba_tfd - * for details. - * @ra_tid: array of RA-TID queue status updates. For debug purposes only. See - * &iwl_mvm_compressed_ba_ratid for more details. - */ -struct iwl_mvm_compressed_ba_notif { - __le32 flags; - u8 sta_id; - u8 reduced_txp; - u8 initial_rate; - u8 retry_cnt; - __le32 query_byte_cnt; - __le16 query_frame_cnt; - __le16 txed; - __le16 done; - __le16 reserved; - __le32 wireless_time; - __le32 tx_rate; - __le16 tfd_cnt; - __le16 ra_tid_cnt; - struct iwl_mvm_compressed_ba_tfd tfd[1]; - struct iwl_mvm_compressed_ba_ratid ra_tid[0]; -} __packed; /* COMPRESSED_BA_RES_API_S_VER_4 */ - -/** - * struct iwl_mac_beacon_cmd_v6 - beacon template command - * @tx: the tx commands associated with the beacon frame - * @template_id: currently equal to the mac context id of the coresponding - * mac. - * @tim_idx: the offset of the tim IE in the beacon - * @tim_size: the length of the tim IE - * @frame: the template of the beacon frame - */ -struct iwl_mac_beacon_cmd_v6 { - struct iwl_tx_cmd tx; - __le32 template_id; - __le32 tim_idx; - __le32 tim_size; - struct ieee80211_hdr frame[0]; -} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_6 */ - -/** - * struct iwl_mac_beacon_cmd_data - data of beacon template with offloaded CSA - * @template_id: currently equal to the mac context id of the coresponding - * mac. - * @tim_idx: the offset of the tim IE in the beacon - * @tim_size: the length of the tim IE - * @ecsa_offset: offset to the ECSA IE if present - * @csa_offset: offset to the CSA IE if present - * @frame: the template of the beacon frame - */ -struct iwl_mac_beacon_cmd_data { - __le32 template_id; - __le32 tim_idx; - __le32 tim_size; - __le32 ecsa_offset; - __le32 csa_offset; - struct ieee80211_hdr frame[0]; -}; - -/** - * struct iwl_mac_beacon_cmd_v7 - beacon template command with offloaded CSA - * @tx: the tx commands associated with the beacon frame - * @data: see &iwl_mac_beacon_cmd_data - */ -struct iwl_mac_beacon_cmd_v7 { - struct iwl_tx_cmd tx; - struct iwl_mac_beacon_cmd_data data; -} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_7 */ - -/** - * struct iwl_mac_beacon_cmd - beacon template command with offloaded CSA - * @byte_cnt: byte count of the beacon frame - * @flags: for future use - * @reserved: reserved - * @data: see &iwl_mac_beacon_cmd_data - */ -struct iwl_mac_beacon_cmd { - __le16 byte_cnt; - __le16 flags; - __le64 reserved; - struct iwl_mac_beacon_cmd_data data; -} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_8 */ - -struct iwl_beacon_notif { - struct iwl_mvm_tx_resp beacon_notify_hdr; - __le64 tsf; - __le32 ibss_mgr_status; -} __packed; - -/** - * struct iwl_extended_beacon_notif - notifies about beacon transmission - * @beacon_notify_hdr: tx response command associated with the beacon - * @tsf: last beacon tsf - * @ibss_mgr_status: whether IBSS is manager - * @gp2: last beacon time in gp2 - */ -struct iwl_extended_beacon_notif { - struct iwl_mvm_tx_resp beacon_notify_hdr; - __le64 tsf; - __le32 ibss_mgr_status; - __le32 gp2; -} __packed; /* BEACON_NTFY_API_S_VER_5 */ - -/** - * enum iwl_dump_control - dump (flush) control flags - * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty - * and the TFD queues are empty. - */ -enum iwl_dump_control { - DUMP_TX_FIFO_FLUSH = BIT(1), -}; - -/** - * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command - * @queues_ctl: bitmap of queues to flush - * @flush_ctl: control flags - * @reserved: reserved - */ -struct iwl_tx_path_flush_cmd_v1 { - __le32 queues_ctl; - __le16 flush_ctl; - __le16 reserved; -} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */ - -/** - * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command - * @sta_id: station ID to flush - * @tid_mask: TID mask to flush - * @reserved: reserved - */ -struct iwl_tx_path_flush_cmd { - __le32 sta_id; - __le16 tid_mask; - __le16 reserved; -} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_2 */ - -/* Available options for the SCD_QUEUE_CFG HCMD */ -enum iwl_scd_cfg_actions { - SCD_CFG_DISABLE_QUEUE = 0x0, - SCD_CFG_ENABLE_QUEUE = 0x1, - SCD_CFG_UPDATE_QUEUE_TID = 0x2, -}; - -/** - * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command - * @token: unused - * @sta_id: station id - * @tid: TID - * @scd_queue: scheduler queue to confiug - * @action: 1 queue enable, 0 queue disable, 2 change txq's tid owner - * Value is one of &enum iwl_scd_cfg_actions options - * @aggregate: 1 aggregated queue, 0 otherwise - * @tx_fifo: &enum iwl_mvm_tx_fifo - * @window: BA window size - * @ssn: SSN for the BA agreement - * @reserved: reserved - */ -struct iwl_scd_txq_cfg_cmd { - u8 token; - u8 sta_id; - u8 tid; - u8 scd_queue; - u8 action; - u8 aggregate; - u8 tx_fifo; - u8 window; - __le16 ssn; - __le16 reserved; -} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */ - -/** - * struct iwl_scd_txq_cfg_rsp - * @token: taken from the command - * @sta_id: station id from the command - * @tid: tid from the command - * @scd_queue: scd_queue from the command - */ -struct iwl_scd_txq_cfg_rsp { - u8 token; - u8 sta_id; - u8 tid; - u8 scd_queue; -} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */ - -#endif /* __fw_api_tx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index bfc865ad8904..69336f38ac58 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -68,2692 +68,32 @@ #ifndef __fw_api_h__ #define __fw_api_h__ -#include "fw-api-rs.h" -#include "fw-api-rx.h" -#include "fw-api-tx.h" -#include "fw-api-sta.h" -#include "fw-api-mac.h" -#include "fw-api-power.h" -#include "fw-api-d3.h" -#include "fw-api-coex.h" -#include "fw-api-scan.h" -#include "fw-api-stats.h" -#include "fw-api-tof.h" - -/* Tx queue numbers for non-DQA mode */ -enum { - IWL_MVM_OFFCHANNEL_QUEUE = 8, - IWL_MVM_CMD_QUEUE = 9, -}; - -/* - * DQA queue numbers - * - * @IWL_MVM_DQA_CMD_QUEUE: a queue reserved for sending HCMDs to the FW - * @IWL_MVM_DQA_AUX_QUEUE: a queue reserved for aux frames - * @IWL_MVM_DQA_P2P_DEVICE_QUEUE: a queue reserved for P2P device frames - * @IWL_MVM_DQA_GCAST_QUEUE: a queue reserved for P2P GO/SoftAP GCAST frames - * @IWL_MVM_DQA_BSS_CLIENT_QUEUE: a queue reserved for BSS activity, to ensure - * that we are never left without the possibility to connect to an AP. - * @IWL_MVM_DQA_MIN_MGMT_QUEUE: first TXQ in pool for MGMT and non-QOS frames. - * Each MGMT queue is mapped to a single STA - * MGMT frames are frames that return true on ieee80211_is_mgmt() - * @IWL_MVM_DQA_MAX_MGMT_QUEUE: last TXQ in pool for MGMT frames - * @IWL_MVM_DQA_AP_PROBE_RESP_QUEUE: a queue reserved for P2P GO/SoftAP probe - * responses - * @IWL_MVM_DQA_MIN_DATA_QUEUE: first TXQ in pool for DATA frames. - * DATA frames are intended for !ieee80211_is_mgmt() frames, but if - * the MGMT TXQ pool is exhausted, mgmt frames can be sent on DATA queues - * as well - * @IWL_MVM_DQA_MAX_DATA_QUEUE: last TXQ in pool for DATA frames - */ -enum iwl_mvm_dqa_txq { - IWL_MVM_DQA_CMD_QUEUE = 0, - IWL_MVM_DQA_AUX_QUEUE = 1, - IWL_MVM_DQA_P2P_DEVICE_QUEUE = 2, - IWL_MVM_DQA_GCAST_QUEUE = 3, - IWL_MVM_DQA_BSS_CLIENT_QUEUE = 4, - IWL_MVM_DQA_MIN_MGMT_QUEUE = 5, - IWL_MVM_DQA_MAX_MGMT_QUEUE = 8, - IWL_MVM_DQA_AP_PROBE_RESP_QUEUE = 9, - IWL_MVM_DQA_MIN_DATA_QUEUE = 10, - IWL_MVM_DQA_MAX_DATA_QUEUE = 31, -}; - -enum iwl_mvm_tx_fifo { - IWL_MVM_TX_FIFO_BK = 0, - IWL_MVM_TX_FIFO_BE, - IWL_MVM_TX_FIFO_VI, - IWL_MVM_TX_FIFO_VO, - IWL_MVM_TX_FIFO_MCAST = 5, - IWL_MVM_TX_FIFO_CMD = 7, -}; - - -/** - * enum iwl_legacy_cmds - legacy group command IDs - */ -enum iwl_legacy_cmds { - /** - * @MVM_ALIVE: - * Alive data from the firmware, as described in - * &struct mvm_alive_resp_v3 or &struct mvm_alive_resp. - */ - MVM_ALIVE = 0x1, - - /** - * @REPLY_ERROR: Cause an error in the firmware, for testing purposes. - */ - REPLY_ERROR = 0x2, - - /** - * @ECHO_CMD: Send data to the device to have it returned immediately. - */ - ECHO_CMD = 0x3, - - /** - * @INIT_COMPLETE_NOTIF: Notification that initialization is complete. - */ - INIT_COMPLETE_NOTIF = 0x4, - - /** - * @PHY_CONTEXT_CMD: - * Add/modify/remove a PHY context, using &struct iwl_phy_context_cmd. - */ - PHY_CONTEXT_CMD = 0x8, - - /** - * @DBG_CFG: Debug configuration command. - */ - DBG_CFG = 0x9, - - /** - * @ANTENNA_COUPLING_NOTIFICATION: - * Antenna coupling data, &struct iwl_mvm_antenna_coupling_notif - */ - ANTENNA_COUPLING_NOTIFICATION = 0xa, - - /** - * @SCAN_ITERATION_COMPLETE_UMAC: - * Firmware indicates a scan iteration completed, using - * &struct iwl_umac_scan_iter_complete_notif. - */ - SCAN_ITERATION_COMPLETE_UMAC = 0xb5, - - /** - * @SCAN_CFG_CMD: - * uses &struct iwl_scan_config_v1 or &struct iwl_scan_config - */ - SCAN_CFG_CMD = 0xc, - - /** - * @SCAN_REQ_UMAC: uses &struct iwl_scan_req_umac - */ - SCAN_REQ_UMAC = 0xd, - - /** - * @SCAN_ABORT_UMAC: uses &struct iwl_umac_scan_abort - */ - SCAN_ABORT_UMAC = 0xe, - - /** - * @SCAN_COMPLETE_UMAC: uses &struct iwl_umac_scan_complete - */ - SCAN_COMPLETE_UMAC = 0xf, - - /** - * @BA_WINDOW_STATUS_NOTIFICATION_ID: - * uses &struct iwl_ba_window_status_notif - */ - BA_WINDOW_STATUS_NOTIFICATION_ID = 0x13, - - /** - * @ADD_STA_KEY: - * &struct iwl_mvm_add_sta_key_cmd_v1 or - * &struct iwl_mvm_add_sta_key_cmd. - */ - ADD_STA_KEY = 0x17, - - /** - * @ADD_STA: - * &struct iwl_mvm_add_sta_cmd or &struct iwl_mvm_add_sta_cmd_v7. - */ - ADD_STA = 0x18, - - /** - * @REMOVE_STA: &struct iwl_mvm_rm_sta_cmd - */ - REMOVE_STA = 0x19, - - /** - * @FW_GET_ITEM_CMD: uses &struct iwl_fw_get_item_cmd - */ - FW_GET_ITEM_CMD = 0x1a, - - /** - * @TX_CMD: uses &struct iwl_tx_cmd or &struct iwl_tx_cmd_gen2, - * response in &struct iwl_mvm_tx_resp or - * &struct iwl_mvm_tx_resp_v3 - */ - TX_CMD = 0x1c, - - /** - * @TXPATH_FLUSH: &struct iwl_tx_path_flush_cmd - */ - TXPATH_FLUSH = 0x1e, - - /** - * @MGMT_MCAST_KEY: - * &struct iwl_mvm_mgmt_mcast_key_cmd or - * &struct iwl_mvm_mgmt_mcast_key_cmd_v1 - */ - MGMT_MCAST_KEY = 0x1f, - - /* scheduler config */ - /** - * @SCD_QUEUE_CFG: &struct iwl_scd_txq_cfg_cmd for older hardware, - * &struct iwl_tx_queue_cfg_cmd with &struct iwl_tx_queue_cfg_rsp - * for newer (A000) hardware. - */ - SCD_QUEUE_CFG = 0x1d, - - /** - * @WEP_KEY: uses &struct iwl_mvm_wep_key_cmd - */ - WEP_KEY = 0x20, - - /** - * @SHARED_MEM_CFG: - * retrieve shared memory configuration - response in - * &struct iwl_shared_mem_cfg - */ - SHARED_MEM_CFG = 0x25, - - /** - * @TDLS_CHANNEL_SWITCH_CMD: uses &struct iwl_tdls_channel_switch_cmd - */ - TDLS_CHANNEL_SWITCH_CMD = 0x27, - - /** - * @TDLS_CHANNEL_SWITCH_NOTIFICATION: - * uses &struct iwl_tdls_channel_switch_notif - */ - TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa, - - /** - * @TDLS_CONFIG_CMD: - * &struct iwl_tdls_config_cmd, response in &struct iwl_tdls_config_res - */ - TDLS_CONFIG_CMD = 0xa7, - - /** - * @MAC_CONTEXT_CMD: &struct iwl_mac_ctx_cmd - */ - MAC_CONTEXT_CMD = 0x28, - - /** - * @TIME_EVENT_CMD: - * &struct iwl_time_event_cmd, response in &struct iwl_time_event_resp - */ - TIME_EVENT_CMD = 0x29, /* both CMD and response */ - - /** - * @TIME_EVENT_NOTIFICATION: &struct iwl_time_event_notif - */ - TIME_EVENT_NOTIFICATION = 0x2a, - - /** - * @BINDING_CONTEXT_CMD: - * &struct iwl_binding_cmd or &struct iwl_binding_cmd_v1 - */ - BINDING_CONTEXT_CMD = 0x2b, - - /** - * @TIME_QUOTA_CMD: &struct iwl_time_quota_cmd - */ - TIME_QUOTA_CMD = 0x2c, - - /** - * @NON_QOS_TX_COUNTER_CMD: - * command is &struct iwl_nonqos_seq_query_cmd - */ - NON_QOS_TX_COUNTER_CMD = 0x2d, - - /** - * @LQ_CMD: using &struct iwl_lq_cmd - */ - LQ_CMD = 0x4e, - - /** - * @FW_PAGING_BLOCK_CMD: - * &struct iwl_fw_paging_cmd - */ - FW_PAGING_BLOCK_CMD = 0x4f, - - /** - * @SCAN_OFFLOAD_REQUEST_CMD: uses &struct iwl_scan_req_lmac - */ - SCAN_OFFLOAD_REQUEST_CMD = 0x51, - - /** - * @SCAN_OFFLOAD_ABORT_CMD: abort the scan - no further contents - */ - SCAN_OFFLOAD_ABORT_CMD = 0x52, - - /** - * @HOT_SPOT_CMD: uses &struct iwl_hs20_roc_req - */ - HOT_SPOT_CMD = 0x53, - - /** - * @SCAN_OFFLOAD_COMPLETE: - * notification, &struct iwl_periodic_scan_complete - */ - SCAN_OFFLOAD_COMPLETE = 0x6D, - - /** - * @SCAN_OFFLOAD_UPDATE_PROFILES_CMD: - * update scan offload (scheduled scan) profiles/blacklist/etc. - */ - SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, - - /** - * @MATCH_FOUND_NOTIFICATION: scan match found - */ - MATCH_FOUND_NOTIFICATION = 0xd9, - - /** - * @SCAN_ITERATION_COMPLETE: - * uses &struct iwl_lmac_scan_complete_notif - */ - SCAN_ITERATION_COMPLETE = 0xe7, - - /* Phy */ - /** - * @PHY_CONFIGURATION_CMD: &struct iwl_phy_cfg_cmd - */ - PHY_CONFIGURATION_CMD = 0x6a, - - /** - * @CALIB_RES_NOTIF_PHY_DB: &struct iwl_calib_res_notif_phy_db - */ - CALIB_RES_NOTIF_PHY_DB = 0x6b, - - /** - * @PHY_DB_CMD: &struct iwl_phy_db_cmd - */ - PHY_DB_CMD = 0x6c, - - /** - * @TOF_CMD: &struct iwl_tof_config_cmd - */ - TOF_CMD = 0x10, - - /** - * @TOF_NOTIFICATION: &struct iwl_tof_gen_resp_cmd - */ - TOF_NOTIFICATION = 0x11, - - /** - * @POWER_TABLE_CMD: &struct iwl_device_power_cmd - */ - POWER_TABLE_CMD = 0x77, - - /** - * @PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION: - * &struct iwl_uapsd_misbehaving_ap_notif - */ - PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78, - - /** - * @LTR_CONFIG: &struct iwl_ltr_config_cmd - */ - LTR_CONFIG = 0xee, - - /** - * @REPLY_THERMAL_MNG_BACKOFF: - * Thermal throttling command - */ - REPLY_THERMAL_MNG_BACKOFF = 0x7e, - - /** - * @DC2DC_CONFIG_CMD: - * Set/Get DC2DC frequency tune - * Command is &struct iwl_dc2dc_config_cmd, - * response is &struct iwl_dc2dc_config_resp - */ - DC2DC_CONFIG_CMD = 0x83, - - /** - * @NVM_ACCESS_CMD: using &struct iwl_nvm_access_cmd - */ - NVM_ACCESS_CMD = 0x88, - - /** - * @BEACON_NOTIFICATION: &struct iwl_extended_beacon_notif - */ - BEACON_NOTIFICATION = 0x90, - - /** - * @BEACON_TEMPLATE_CMD: - * Uses one of &struct iwl_mac_beacon_cmd_v6, - * &struct iwl_mac_beacon_cmd_v7 or &struct iwl_mac_beacon_cmd - * depending on the device version. - */ - BEACON_TEMPLATE_CMD = 0x91, - /** - * @TX_ANT_CONFIGURATION_CMD: &struct iwl_tx_ant_cfg_cmd - */ - TX_ANT_CONFIGURATION_CMD = 0x98, - - /** - * @STATISTICS_CMD: - * one of &struct iwl_statistics_cmd, - * &struct iwl_notif_statistics_v11, - * &struct iwl_notif_statistics_v10, - * &struct iwl_notif_statistics_cdb - */ - STATISTICS_CMD = 0x9c, - - /** - * @STATISTICS_NOTIFICATION: - * one of &struct iwl_notif_statistics_v10, - * &struct iwl_notif_statistics_v11, - * &struct iwl_notif_statistics_cdb - */ - STATISTICS_NOTIFICATION = 0x9d, - - /** - * @EOSP_NOTIFICATION: - * Notify that a service period ended, - * &struct iwl_mvm_eosp_notification - */ - EOSP_NOTIFICATION = 0x9e, - - /** - * @REDUCE_TX_POWER_CMD: - * &struct iwl_dev_tx_power_cmd_v3 or &struct iwl_dev_tx_power_cmd - */ - REDUCE_TX_POWER_CMD = 0x9f, - - /** - * @CARD_STATE_NOTIFICATION: - * Card state (RF/CT kill) notification, - * uses &struct iwl_card_state_notif - */ - CARD_STATE_NOTIFICATION = 0xa1, - - /** - * @MISSED_BEACONS_NOTIFICATION: &struct iwl_missed_beacons_notif - */ - MISSED_BEACONS_NOTIFICATION = 0xa2, - - /** - * @MAC_PM_POWER_TABLE: using &struct iwl_mac_power_cmd - */ - MAC_PM_POWER_TABLE = 0xa9, - - /** - * @MFUART_LOAD_NOTIFICATION: &struct iwl_mfuart_load_notif - */ - MFUART_LOAD_NOTIFICATION = 0xb1, - - /** - * @RSS_CONFIG_CMD: &struct iwl_rss_config_cmd - */ - RSS_CONFIG_CMD = 0xb3, - - /** - * @REPLY_RX_PHY_CMD: &struct iwl_rx_phy_info - */ - REPLY_RX_PHY_CMD = 0xc0, - - /** - * @REPLY_RX_MPDU_CMD: - * &struct iwl_rx_mpdu_res_start or &struct iwl_rx_mpdu_desc - */ - REPLY_RX_MPDU_CMD = 0xc1, - - /** - * @FRAME_RELEASE: - * Frame release (reorder helper) notification, uses - * &struct iwl_frame_release - */ - FRAME_RELEASE = 0xc3, - - /** - * @BA_NOTIF: - * BlockAck notification, uses &struct iwl_mvm_compressed_ba_notif - * or &struct iwl_mvm_ba_notif depending on the HW - */ - BA_NOTIF = 0xc5, - - /* Location Aware Regulatory */ - /** - * @MCC_UPDATE_CMD: using &struct iwl_mcc_update_cmd - */ - MCC_UPDATE_CMD = 0xc8, - - /** - * @MCC_CHUB_UPDATE_CMD: using &struct iwl_mcc_chub_notif - */ - MCC_CHUB_UPDATE_CMD = 0xc9, - - /** - * @MARKER_CMD: trace marker command, uses &struct iwl_mvm_marker - */ - MARKER_CMD = 0xcb, - - /** - * @BT_PROFILE_NOTIFICATION: &struct iwl_bt_coex_profile_notif - */ - BT_PROFILE_NOTIFICATION = 0xce, - - /** - * @BT_CONFIG: &struct iwl_bt_coex_cmd - */ - BT_CONFIG = 0x9b, - - /** - * @BT_COEX_UPDATE_CORUN_LUT: - * &struct iwl_bt_coex_corun_lut_update_cmd - */ - BT_COEX_UPDATE_CORUN_LUT = 0x5b, - - /** - * @BT_COEX_UPDATE_REDUCED_TXP: - * &struct iwl_bt_coex_reduced_txp_update_cmd - */ - BT_COEX_UPDATE_REDUCED_TXP = 0x5c, - - /** - * @BT_COEX_CI: &struct iwl_bt_coex_ci_cmd - */ - BT_COEX_CI = 0x5d, - - /** - * @REPLY_SF_CFG_CMD: &struct iwl_sf_cfg_cmd - */ - REPLY_SF_CFG_CMD = 0xd1, - /** - * @REPLY_BEACON_FILTERING_CMD: &struct iwl_beacon_filter_cmd - */ - REPLY_BEACON_FILTERING_CMD = 0xd2, - - /** - * @DTS_MEASUREMENT_NOTIFICATION: - * &struct iwl_dts_measurement_notif_v1 or - * &struct iwl_dts_measurement_notif_v2 - */ - DTS_MEASUREMENT_NOTIFICATION = 0xdd, - - /** - * @LDBG_CONFIG_CMD: configure continuous trace recording - */ - LDBG_CONFIG_CMD = 0xf6, - - /** - * @DEBUG_LOG_MSG: Debugging log data from firmware - */ - DEBUG_LOG_MSG = 0xf7, - - /** - * @BCAST_FILTER_CMD: &struct iwl_bcast_filter_cmd - */ - BCAST_FILTER_CMD = 0xcf, - - /** - * @MCAST_FILTER_CMD: &struct iwl_mcast_filter_cmd - */ - MCAST_FILTER_CMD = 0xd0, - - /** - * @D3_CONFIG_CMD: &struct iwl_d3_manager_config - */ - D3_CONFIG_CMD = 0xd3, - - /** - * @PROT_OFFLOAD_CONFIG_CMD: Depending on firmware, uses one of - * &struct iwl_proto_offload_cmd_v1, &struct iwl_proto_offload_cmd_v2, - * &struct iwl_proto_offload_cmd_v3_small, - * &struct iwl_proto_offload_cmd_v3_large - */ - PROT_OFFLOAD_CONFIG_CMD = 0xd4, - - /** - * @OFFLOADS_QUERY_CMD: - * No data in command, response in &struct iwl_wowlan_status - */ - OFFLOADS_QUERY_CMD = 0xd5, - - /** - * @REMOTE_WAKE_CONFIG_CMD: &struct iwl_wowlan_remote_wake_config - */ - REMOTE_WAKE_CONFIG_CMD = 0xd6, - - /** - * @D0I3_END_CMD: End D0i3/D3 state, no command data - */ - D0I3_END_CMD = 0xed, - - /** - * @WOWLAN_PATTERNS: &struct iwl_wowlan_patterns_cmd - */ - WOWLAN_PATTERNS = 0xe0, - - /** - * @WOWLAN_CONFIGURATION: &struct iwl_wowlan_config_cmd - */ - WOWLAN_CONFIGURATION = 0xe1, - - /** - * @WOWLAN_TSC_RSC_PARAM: &struct iwl_wowlan_rsc_tsc_params_cmd - */ - WOWLAN_TSC_RSC_PARAM = 0xe2, - - /** - * @WOWLAN_TKIP_PARAM: &struct iwl_wowlan_tkip_params_cmd - */ - WOWLAN_TKIP_PARAM = 0xe3, - - /** - * @WOWLAN_KEK_KCK_MATERIAL: &struct iwl_wowlan_kek_kck_material_cmd - */ - WOWLAN_KEK_KCK_MATERIAL = 0xe4, - - /** - * @WOWLAN_GET_STATUSES: response in &struct iwl_wowlan_status - */ - WOWLAN_GET_STATUSES = 0xe5, - - /** - * @SCAN_OFFLOAD_PROFILES_QUERY_CMD: - * No command data, response is &struct iwl_scan_offload_profiles_query - */ - SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56, -}; - -/* Please keep this enum *SORTED* by hex value. - * Needed for binary search, otherwise a warning will be triggered. - */ -enum iwl_mac_conf_subcmd_ids { - LINK_QUALITY_MEASUREMENT_CMD = 0x1, - LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF = 0xFE, - CHANNEL_SWITCH_NOA_NOTIF = 0xFF, -}; - -/** - * enum iwl_phy_ops_subcmd_ids - PHY group commands - */ -enum iwl_phy_ops_subcmd_ids { - /** - * @CMD_DTS_MEASUREMENT_TRIGGER_WIDE: - * Uses either &struct iwl_dts_measurement_cmd or - * &struct iwl_ext_dts_measurement_cmd - */ - CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, - - /** - * @CTDP_CONFIG_CMD: &struct iwl_mvm_ctdp_cmd - */ - CTDP_CONFIG_CMD = 0x03, - - /** - * @TEMP_REPORTING_THRESHOLDS_CMD: &struct temp_report_ths_cmd - */ - TEMP_REPORTING_THRESHOLDS_CMD = 0x04, - - /** - * @GEO_TX_POWER_LIMIT: &struct iwl_geo_tx_power_profiles_cmd - */ - GEO_TX_POWER_LIMIT = 0x05, - - /** - * @CT_KILL_NOTIFICATION: &struct ct_kill_notif - */ - CT_KILL_NOTIFICATION = 0xFE, - - /** - * @DTS_MEASUREMENT_NOTIF_WIDE: - * &struct iwl_dts_measurement_notif_v1 or - * &struct iwl_dts_measurement_notif_v2 - */ - DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, -}; - -/** - * enum iwl_system_subcmd_ids - system group command IDs - */ -enum iwl_system_subcmd_ids { - /** - * @SHARED_MEM_CFG_CMD: - * response in &struct iwl_shared_mem_cfg or - * &struct iwl_shared_mem_cfg_v2 - */ - SHARED_MEM_CFG_CMD = 0x0, - - /** - * @INIT_EXTENDED_CFG_CMD: &struct iwl_init_extended_cfg_cmd - */ - INIT_EXTENDED_CFG_CMD = 0x03, -}; - -/** - * enum iwl_data_path_subcmd_ids - data path group commands - */ -enum iwl_data_path_subcmd_ids { - /** - * @DQA_ENABLE_CMD: &struct iwl_dqa_enable_cmd - */ - DQA_ENABLE_CMD = 0x0, - - /** - * @UPDATE_MU_GROUPS_CMD: &struct iwl_mu_group_mgmt_cmd - */ - UPDATE_MU_GROUPS_CMD = 0x1, - - /** - * @TRIGGER_RX_QUEUES_NOTIF_CMD: &struct iwl_rxq_sync_cmd - */ - TRIGGER_RX_QUEUES_NOTIF_CMD = 0x2, - - /** - * @STA_PM_NOTIF: &struct iwl_mvm_pm_state_notification - */ - STA_PM_NOTIF = 0xFD, - - /** - * @MU_GROUP_MGMT_NOTIF: &struct iwl_mu_group_mgmt_notif - */ - MU_GROUP_MGMT_NOTIF = 0xFE, - - /** - * @RX_QUEUES_NOTIFICATION: &struct iwl_rxq_sync_notification - */ - RX_QUEUES_NOTIFICATION = 0xFF, -}; - -/** - * enum iwl_prot_offload_subcmd_ids - protocol offload commands - */ -enum iwl_prot_offload_subcmd_ids { - /** - * @STORED_BEACON_NTF: &struct iwl_stored_beacon_notif - */ - STORED_BEACON_NTF = 0xFF, -}; - -/** - * enum iwl_regulatory_and_nvm_subcmd_ids - regulatory/NVM commands - */ -enum iwl_regulatory_and_nvm_subcmd_ids { - /** - * @NVM_ACCESS_COMPLETE: &struct iwl_nvm_access_complete_cmd - */ - NVM_ACCESS_COMPLETE = 0x0, - - /** - * @NVM_GET_INFO: - * Command is &struct iwl_nvm_get_info, - * response is &struct iwl_nvm_get_info_rsp - */ - NVM_GET_INFO = 0x2, -}; - -/** - * enum iwl_debug_cmds - debug commands - */ -enum iwl_debug_cmds { - /** - * @LMAC_RD_WR: - * LMAC memory read/write, using &struct iwl_dbg_mem_access_cmd and - * &struct iwl_dbg_mem_access_rsp - */ - LMAC_RD_WR = 0x0, - /** - * @UMAC_RD_WR: - * UMAC memory read/write, using &struct iwl_dbg_mem_access_cmd and - * &struct iwl_dbg_mem_access_rsp - */ - UMAC_RD_WR = 0x1, - /** - * @MFU_ASSERT_DUMP_NTF: - * &struct iwl_mfu_assert_dump_notif - */ - MFU_ASSERT_DUMP_NTF = 0xFE, -}; - -/** - * enum iwl_mvm_command_groups - command groups for the firmware - * @LEGACY_GROUP: legacy group, uses command IDs from &enum iwl_legacy_cmds - * @LONG_GROUP: legacy group with long header, also uses command IDs - * from &enum iwl_legacy_cmds - * @SYSTEM_GROUP: system group, uses command IDs from - * &enum iwl_system_subcmd_ids - * @MAC_CONF_GROUP: MAC configuration group, uses command IDs from - * &enum iwl_mac_conf_subcmd_ids - * @PHY_OPS_GROUP: PHY operations group, uses command IDs from - * &enum iwl_phy_ops_subcmd_ids - * @DATA_PATH_GROUP: data path group, uses command IDs from - * &enum iwl_data_path_subcmd_ids - * @NAN_GROUP: NAN group, uses command IDs from &enum iwl_nan_subcmd_ids - * @TOF_GROUP: TOF group, uses command IDs from &enum iwl_tof_subcmd_ids - * @PROT_OFFLOAD_GROUP: protocol offload group, uses command IDs from - * &enum iwl_prot_offload_subcmd_ids - * @REGULATORY_AND_NVM_GROUP: regulatory/NVM group, uses command IDs from - * &enum iwl_regulatory_and_nvm_subcmd_ids - * @DEBUG_GROUP: Debug group, uses command IDs from &enum iwl_debug_cmds - */ -enum iwl_mvm_command_groups { - LEGACY_GROUP = 0x0, - LONG_GROUP = 0x1, - SYSTEM_GROUP = 0x2, - MAC_CONF_GROUP = 0x3, - PHY_OPS_GROUP = 0x4, - DATA_PATH_GROUP = 0x5, - PROT_OFFLOAD_GROUP = 0xb, - REGULATORY_AND_NVM_GROUP = 0xc, - DEBUG_GROUP = 0xf, -}; - -/** - * struct iwl_cmd_response - generic response struct for most commands - * @status: status of the command asked, changes for each one - */ -struct iwl_cmd_response { - __le32 status; -}; - -/* - * struct iwl_dqa_enable_cmd - * @cmd_queue: the TXQ number of the command queue - */ -struct iwl_dqa_enable_cmd { - __le32 cmd_queue; -} __packed; /* DQA_CONTROL_CMD_API_S_VER_1 */ - -/* - * struct iwl_tx_ant_cfg_cmd - * @valid: valid antenna configuration - */ -struct iwl_tx_ant_cfg_cmd { - __le32 valid; -} __packed; - -/** - * struct iwl_calib_ctrl - Calibration control struct. - * Sent as part of the phy configuration command. - * @flow_trigger: bitmap for which calibrations to perform according to - * flow triggers, using &enum iwl_calib_cfg - * @event_trigger: bitmap for which calibrations to perform according to - * event triggers, using &enum iwl_calib_cfg - */ -struct iwl_calib_ctrl { - __le32 flow_trigger; - __le32 event_trigger; -} __packed; - -/* This enum defines the bitmap of various calibrations to enable in both - * init ucode and runtime ucode through CALIBRATION_CFG_CMD. - */ -enum iwl_calib_cfg { - IWL_CALIB_CFG_XTAL_IDX = BIT(0), - IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(1), - IWL_CALIB_CFG_VOLTAGE_READ_IDX = BIT(2), - IWL_CALIB_CFG_PAPD_IDX = BIT(3), - IWL_CALIB_CFG_TX_PWR_IDX = BIT(4), - IWL_CALIB_CFG_DC_IDX = BIT(5), - IWL_CALIB_CFG_BB_FILTER_IDX = BIT(6), - IWL_CALIB_CFG_LO_LEAKAGE_IDX = BIT(7), - IWL_CALIB_CFG_TX_IQ_IDX = BIT(8), - IWL_CALIB_CFG_TX_IQ_SKEW_IDX = BIT(9), - IWL_CALIB_CFG_RX_IQ_IDX = BIT(10), - IWL_CALIB_CFG_RX_IQ_SKEW_IDX = BIT(11), - IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(12), - IWL_CALIB_CFG_CHAIN_NOISE_IDX = BIT(13), - IWL_CALIB_CFG_DISCONNECTED_ANT_IDX = BIT(14), - IWL_CALIB_CFG_ANT_COUPLING_IDX = BIT(15), - IWL_CALIB_CFG_DAC_IDX = BIT(16), - IWL_CALIB_CFG_ABS_IDX = BIT(17), - IWL_CALIB_CFG_AGC_IDX = BIT(18), -}; - -/** - * struct iwl_phy_cfg_cmd - Phy configuration command - * @phy_cfg: PHY configuration value, uses &enum iwl_fw_phy_cfg - * @calib_control: calibration control data - */ -struct iwl_phy_cfg_cmd { - __le32 phy_cfg; - struct iwl_calib_ctrl calib_control; -} __packed; - -#define PHY_CFG_RADIO_TYPE (BIT(0) | BIT(1)) -#define PHY_CFG_RADIO_STEP (BIT(2) | BIT(3)) -#define PHY_CFG_RADIO_DASH (BIT(4) | BIT(5)) -#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7)) -#define PHY_CFG_TX_CHAIN_A BIT(8) -#define PHY_CFG_TX_CHAIN_B BIT(9) -#define PHY_CFG_TX_CHAIN_C BIT(10) -#define PHY_CFG_RX_CHAIN_A BIT(12) -#define PHY_CFG_RX_CHAIN_B BIT(13) -#define PHY_CFG_RX_CHAIN_C BIT(14) - - -/** - * enum iwl_nvm_access_op - NVM access opcode - * @IWL_NVM_READ: read NVM - * @IWL_NVM_WRITE: write NVM - */ -enum iwl_nvm_access_op { - IWL_NVM_READ = 0, - IWL_NVM_WRITE = 1, -}; - -/** - * enum iwl_nvm_access_target - target of the NVM_ACCESS_CMD - * @NVM_ACCESS_TARGET_CACHE: access the cache - * @NVM_ACCESS_TARGET_OTP: access the OTP - * @NVM_ACCESS_TARGET_EEPROM: access the EEPROM - */ -enum iwl_nvm_access_target { - NVM_ACCESS_TARGET_CACHE = 0, - NVM_ACCESS_TARGET_OTP = 1, - NVM_ACCESS_TARGET_EEPROM = 2, -}; - -/** - * enum iwl_nvm_section_type - section types for NVM_ACCESS_CMD - * @NVM_SECTION_TYPE_SW: software section - * @NVM_SECTION_TYPE_REGULATORY: regulatory section - * @NVM_SECTION_TYPE_CALIBRATION: calibration section - * @NVM_SECTION_TYPE_PRODUCTION: production section - * @NVM_SECTION_TYPE_MAC_OVERRIDE: MAC override section - * @NVM_SECTION_TYPE_PHY_SKU: PHY SKU section - * @NVM_MAX_NUM_SECTIONS: number of sections - */ -enum iwl_nvm_section_type { - NVM_SECTION_TYPE_SW = 1, - NVM_SECTION_TYPE_REGULATORY = 3, - NVM_SECTION_TYPE_CALIBRATION = 4, - NVM_SECTION_TYPE_PRODUCTION = 5, - NVM_SECTION_TYPE_MAC_OVERRIDE = 11, - NVM_SECTION_TYPE_PHY_SKU = 12, - NVM_MAX_NUM_SECTIONS = 13, -}; - -/** - * struct iwl_nvm_access_cmd - Request the device to send an NVM section - * @op_code: &enum iwl_nvm_access_op - * @target: &enum iwl_nvm_access_target - * @type: &enum iwl_nvm_section_type - * @offset: offset in bytes into the section - * @length: in bytes, to read/write - * @data: if write operation, the data to write. On read its empty - */ -struct iwl_nvm_access_cmd { - u8 op_code; - u8 target; - __le16 type; - __le16 offset; - __le16 length; - u8 data[]; -} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */ - -#define CONT_REC_COMMAND_SIZE 80 -#define ENABLE_CONT_RECORDING 0x15 -#define DISABLE_CONT_RECORDING 0x16 - -/* - * struct iwl_continuous_record_mode - recording mode - */ -struct iwl_continuous_record_mode { - __le16 enable_recording; -} __packed; - -/* - * struct iwl_continuous_record_cmd - enable/disable continuous recording - */ -struct iwl_continuous_record_cmd { - struct iwl_continuous_record_mode record_mode; - u8 pad[CONT_REC_COMMAND_SIZE - - sizeof(struct iwl_continuous_record_mode)]; -} __packed; - -/** - * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD - * @offset: offset in bytes into the section - * @length: in bytes, either how much was written or read - * @type: NVM_SECTION_TYPE_* - * @status: 0 for success, fail otherwise - * @data: if read operation, the data returned. Empty on write. - */ -struct iwl_nvm_access_resp { - __le16 offset; - __le16 length; - __le16 type; - __le16 status; - u8 data[]; -} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */ - -/* MVM_ALIVE 0x1 */ - -/* alive response is_valid values */ -#define ALIVE_RESP_UCODE_OK BIT(0) -#define ALIVE_RESP_RFKILL BIT(1) - -/* alive response ver_type values */ -enum { - FW_TYPE_HW = 0, - FW_TYPE_PROT = 1, - FW_TYPE_AP = 2, - FW_TYPE_WOWLAN = 3, - FW_TYPE_TIMING = 4, - FW_TYPE_WIPAN = 5 -}; - -/* alive response ver_subtype values */ -enum { - FW_SUBTYPE_FULL_FEATURE = 0, - FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */ - FW_SUBTYPE_REDUCED = 2, - FW_SUBTYPE_ALIVE_ONLY = 3, - FW_SUBTYPE_WOWLAN = 4, - FW_SUBTYPE_AP_SUBTYPE = 5, - FW_SUBTYPE_WIPAN = 6, - FW_SUBTYPE_INITIALIZE = 9 -}; - -#define IWL_ALIVE_STATUS_ERR 0xDEAD -#define IWL_ALIVE_STATUS_OK 0xCAFE - -#define IWL_ALIVE_FLG_RFKILL BIT(0) - -struct iwl_lmac_alive { - __le32 ucode_minor; - __le32 ucode_major; - u8 ver_subtype; - u8 ver_type; - u8 mac; - u8 opt; - __le32 timestamp; - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 log_event_table_ptr; /* SRAM address for LMAC event log */ - __le32 cpu_register_ptr; - __le32 dbgm_config_ptr; - __le32 alive_counter_ptr; - __le32 scd_base_ptr; /* SRAM address for SCD */ - __le32 st_fwrd_addr; /* pointer to Store and forward */ - __le32 st_fwrd_size; -} __packed; /* UCODE_ALIVE_NTFY_API_S_VER_3 */ - -struct iwl_umac_alive { - __le32 umac_minor; /* UMAC version: minor */ - __le32 umac_major; /* UMAC version: major */ - __le32 error_info_addr; /* SRAM address for UMAC error log */ - __le32 dbg_print_buff_addr; -} __packed; /* UMAC_ALIVE_DATA_API_S_VER_2 */ - -struct mvm_alive_resp_v3 { - __le16 status; - __le16 flags; - struct iwl_lmac_alive lmac_data; - struct iwl_umac_alive umac_data; -} __packed; /* ALIVE_RES_API_S_VER_3 */ - -struct mvm_alive_resp { - __le16 status; - __le16 flags; - struct iwl_lmac_alive lmac_data[2]; - struct iwl_umac_alive umac_data; -} __packed; /* ALIVE_RES_API_S_VER_4 */ - -/* Error response/notification */ -enum { - FW_ERR_UNKNOWN_CMD = 0x0, - FW_ERR_INVALID_CMD_PARAM = 0x1, - FW_ERR_SERVICE = 0x2, - FW_ERR_ARC_MEMORY = 0x3, - FW_ERR_ARC_CODE = 0x4, - FW_ERR_WATCH_DOG = 0x5, - FW_ERR_WEP_GRP_KEY_INDX = 0x10, - FW_ERR_WEP_KEY_SIZE = 0x11, - FW_ERR_OBSOLETE_FUNC = 0x12, - FW_ERR_UNEXPECTED = 0xFE, - FW_ERR_FATAL = 0xFF -}; - -/** - * struct iwl_error_resp - FW error indication - * ( REPLY_ERROR = 0x2 ) - * @error_type: one of FW_ERR_* - * @cmd_id: the command ID for which the error occured - * @reserved1: reserved - * @bad_cmd_seq_num: sequence number of the erroneous command - * @error_service: which service created the error, applicable only if - * error_type = 2, otherwise 0 - * @timestamp: TSF in usecs. - */ -struct iwl_error_resp { - __le32 error_type; - u8 cmd_id; - u8 reserved1; - __le16 bad_cmd_seq_num; - __le32 error_service; - __le64 timestamp; -} __packed; - - -/* Common PHY, MAC and Bindings definitions */ -#define MAX_MACS_IN_BINDING (3) -#define MAX_BINDINGS (4) - -/** - * enum iwl_mvm_id_and_color - ID and color fields in context dword - * @FW_CTXT_ID_POS: position of the ID - * @FW_CTXT_ID_MSK: mask of the ID - * @FW_CTXT_COLOR_POS: position of the color - * @FW_CTXT_COLOR_MSK: mask of the color - * @FW_CTXT_INVALID: value used to indicate unused/invalid - */ -enum iwl_mvm_id_and_color { - FW_CTXT_ID_POS = 0, - FW_CTXT_ID_MSK = 0xff << FW_CTXT_ID_POS, - FW_CTXT_COLOR_POS = 8, - FW_CTXT_COLOR_MSK = 0xff << FW_CTXT_COLOR_POS, - FW_CTXT_INVALID = 0xffffffff, -}; - -#define FW_CMD_ID_AND_COLOR(_id, _color) ((_id << FW_CTXT_ID_POS) |\ - (_color << FW_CTXT_COLOR_POS)) - -/* Possible actions on PHYs, MACs and Bindings */ -enum iwl_phy_ctxt_action { - FW_CTXT_ACTION_STUB = 0, - FW_CTXT_ACTION_ADD, - FW_CTXT_ACTION_MODIFY, - FW_CTXT_ACTION_REMOVE, - FW_CTXT_ACTION_NUM -}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */ - -/* Time Events */ - -/* Time Event types, according to MAC type */ -enum iwl_time_event_type { - /* BSS Station Events */ - TE_BSS_STA_AGGRESSIVE_ASSOC, - TE_BSS_STA_ASSOC, - TE_BSS_EAP_DHCP_PROT, - TE_BSS_QUIET_PERIOD, - - /* P2P Device Events */ - TE_P2P_DEVICE_DISCOVERABLE, - TE_P2P_DEVICE_LISTEN, - TE_P2P_DEVICE_ACTION_SCAN, - TE_P2P_DEVICE_FULL_SCAN, - - /* P2P Client Events */ - TE_P2P_CLIENT_AGGRESSIVE_ASSOC, - TE_P2P_CLIENT_ASSOC, - TE_P2P_CLIENT_QUIET_PERIOD, - - /* P2P GO Events */ - TE_P2P_GO_ASSOC_PROT, - TE_P2P_GO_REPETITIVET_NOA, - TE_P2P_GO_CT_WINDOW, - - /* WiDi Sync Events */ - TE_WIDI_TX_SYNC, - - /* Channel Switch NoA */ - TE_CHANNEL_SWITCH_PERIOD, - - TE_MAX -}; /* MAC_EVENT_TYPE_API_E_VER_1 */ - - - -/* Time event - defines for command API v1 */ - -/* - * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed. - * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only - * the first fragment is scheduled. - * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only - * the first 2 fragments are scheduled. - * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any - * number of fragments are valid. - * - * Other than the constant defined above, specifying a fragmentation value 'x' - * means that the event can be fragmented but only the first 'x' will be - * scheduled. - */ -enum { - TE_V1_FRAG_NONE = 0, - TE_V1_FRAG_SINGLE = 1, - TE_V1_FRAG_DUAL = 2, - TE_V1_FRAG_ENDLESS = 0xffffffff -}; - -/* If a Time Event can be fragmented, this is the max number of fragments */ -#define TE_V1_FRAG_MAX_MSK 0x0fffffff -/* Repeat the time event endlessly (until removed) */ -#define TE_V1_REPEAT_ENDLESS 0xffffffff -/* If a Time Event has bounded repetitions, this is the maximal value */ -#define TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff - -/* Time Event dependencies: none, on another TE, or in a specific time */ -enum { - TE_V1_INDEPENDENT = 0, - TE_V1_DEP_OTHER = BIT(0), - TE_V1_DEP_TSF = BIT(1), - TE_V1_EVENT_SOCIOPATHIC = BIT(2), -}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */ - -/* - * @TE_V1_NOTIF_NONE: no notifications - * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start - * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end - * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use - * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use. - * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start - * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end - * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use. - * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use. - * - * Supported Time event notifications configuration. - * A notification (both event and fragment) includes a status indicating weather - * the FW was able to schedule the event or not. For fragment start/end - * notification the status is always success. There is no start/end fragment - * notification for monolithic events. - */ -enum { - TE_V1_NOTIF_NONE = 0, - TE_V1_NOTIF_HOST_EVENT_START = BIT(0), - TE_V1_NOTIF_HOST_EVENT_END = BIT(1), - TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2), - TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3), - TE_V1_NOTIF_HOST_FRAG_START = BIT(4), - TE_V1_NOTIF_HOST_FRAG_END = BIT(5), - TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6), - TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7), -}; /* MAC_EVENT_ACTION_API_E_VER_2 */ - -/* Time event - defines for command API */ - -/* - * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed. - * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only - * the first fragment is scheduled. - * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only - * the first 2 fragments are scheduled. - * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any - * number of fragments are valid. - * - * Other than the constant defined above, specifying a fragmentation value 'x' - * means that the event can be fragmented but only the first 'x' will be - * scheduled. - */ -enum { - TE_V2_FRAG_NONE = 0, - TE_V2_FRAG_SINGLE = 1, - TE_V2_FRAG_DUAL = 2, - TE_V2_FRAG_MAX = 0xfe, - TE_V2_FRAG_ENDLESS = 0xff -}; - -/* Repeat the time event endlessly (until removed) */ -#define TE_V2_REPEAT_ENDLESS 0xff -/* If a Time Event has bounded repetitions, this is the maximal value */ -#define TE_V2_REPEAT_MAX 0xfe - -#define TE_V2_PLACEMENT_POS 12 -#define TE_V2_ABSENCE_POS 15 - -/** - * enum iwl_time_event_policy - Time event policy values - * A notification (both event and fragment) includes a status indicating weather - * the FW was able to schedule the event or not. For fragment start/end - * notification the status is always success. There is no start/end fragment - * notification for monolithic events. - * - * @TE_V2_DEFAULT_POLICY: independent, social, present, unoticable - * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start - * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end - * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use - * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use. - * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start - * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end - * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use. - * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use. - * @T2_V2_START_IMMEDIATELY: start time event immediately - * @TE_V2_DEP_OTHER: depends on another time event - * @TE_V2_DEP_TSF: depends on a specific time - * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC - * @TE_V2_ABSENCE: are we present or absent during the Time Event. - */ -enum iwl_time_event_policy { - TE_V2_DEFAULT_POLICY = 0x0, - - /* notifications (event start/stop, fragment start/stop) */ - TE_V2_NOTIF_HOST_EVENT_START = BIT(0), - TE_V2_NOTIF_HOST_EVENT_END = BIT(1), - TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2), - TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3), - - TE_V2_NOTIF_HOST_FRAG_START = BIT(4), - TE_V2_NOTIF_HOST_FRAG_END = BIT(5), - TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6), - TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7), - T2_V2_START_IMMEDIATELY = BIT(11), - - /* placement characteristics */ - TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS), - TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1), - TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2), - - /* are we present or absent during the Time Event. */ - TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS), -}; - -/** - * struct iwl_time_event_cmd - configuring Time Events - * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also - * with version 1. determined by IWL_UCODE_TLV_FLAGS) - * ( TIME_EVENT_CMD = 0x29 ) - * @id_and_color: ID and color of the relevant MAC, - * &enum iwl_mvm_id_and_color - * @action: action to perform, one of &enum iwl_phy_ctxt_action - * @id: this field has two meanings, depending on the action: - * If the action is ADD, then it means the type of event to add. - * For all other actions it is the unique event ID assigned when the - * event was added by the FW. - * @apply_time: When to start the Time Event (in GP2) - * @max_delay: maximum delay to event's start (apply time), in TU - * @depends_on: the unique ID of the event we depend on (if any) - * @interval: interval between repetitions, in TU - * @duration: duration of event in TU - * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS - * @max_frags: maximal number of fragments the Time Event can be divided to - * @policy: defines whether uCode shall notify the host or other uCode modules - * on event and/or fragment start and/or end - * using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF - * TE_EVENT_SOCIOPATHIC - * using TE_ABSENCE and using TE_NOTIF_*, - * &enum iwl_time_event_policy - */ -struct iwl_time_event_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - __le32 id; - /* MAC_TIME_EVENT_DATA_API_S_VER_2 */ - __le32 apply_time; - __le32 max_delay; - __le32 depends_on; - __le32 interval; - __le32 duration; - u8 repeat; - u8 max_frags; - __le16 policy; -} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */ - -/** - * struct iwl_time_event_resp - response structure to iwl_time_event_cmd - * @status: bit 0 indicates success, all others specify errors - * @id: the Time Event type - * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE - * @id_and_color: ID and color of the relevant MAC, - * &enum iwl_mvm_id_and_color - */ -struct iwl_time_event_resp { - __le32 status; - __le32 id; - __le32 unique_id; - __le32 id_and_color; -} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */ - -/** - * struct iwl_time_event_notif - notifications of time event start/stop - * ( TIME_EVENT_NOTIFICATION = 0x2a ) - * @timestamp: action timestamp in GP2 - * @session_id: session's unique id - * @unique_id: unique id of the Time Event itself - * @id_and_color: ID and color of the relevant MAC - * @action: &enum iwl_time_event_policy - * @status: true if scheduled, false otherwise (not executed) - */ -struct iwl_time_event_notif { - __le32 timestamp; - __le32 session_id; - __le32 unique_id; - __le32 id_and_color; - __le32 action; - __le32 status; -} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */ - - -/* Bindings and Time Quota */ - -/** - * struct iwl_binding_cmd_v1 - configuring bindings - * ( BINDING_CONTEXT_CMD = 0x2b ) - * @id_and_color: ID and color of the relevant Binding, - * &enum iwl_mvm_id_and_color - * @action: action to perform, one of FW_CTXT_ACTION_* - * @macs: array of MAC id and colors which belong to the binding, - * &enum iwl_mvm_id_and_color - * @phy: PHY id and color which belongs to the binding, - * &enum iwl_mvm_id_and_color - */ -struct iwl_binding_cmd_v1 { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* BINDING_DATA_API_S_VER_1 */ - __le32 macs[MAX_MACS_IN_BINDING]; - __le32 phy; -} __packed; /* BINDING_CMD_API_S_VER_1 */ - -/** - * struct iwl_binding_cmd - configuring bindings - * ( BINDING_CONTEXT_CMD = 0x2b ) - * @id_and_color: ID and color of the relevant Binding, - * &enum iwl_mvm_id_and_color - * @action: action to perform, one of FW_CTXT_ACTION_* - * @macs: array of MAC id and colors which belong to the binding - * &enum iwl_mvm_id_and_color - * @phy: PHY id and color which belongs to the binding - * &enum iwl_mvm_id_and_color - * @lmac_id: the lmac id the binding belongs to - */ -struct iwl_binding_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* BINDING_DATA_API_S_VER_1 */ - __le32 macs[MAX_MACS_IN_BINDING]; - __le32 phy; - __le32 lmac_id; -} __packed; /* BINDING_CMD_API_S_VER_2 */ - -#define IWL_BINDING_CMD_SIZE_V1 sizeof(struct iwl_binding_cmd_v1) -#define IWL_LMAC_24G_INDEX 0 -#define IWL_LMAC_5G_INDEX 1 - -/* The maximal number of fragments in the FW's schedule session */ -#define IWL_MVM_MAX_QUOTA 128 - -/** - * struct iwl_time_quota_data - configuration of time quota per binding - * @id_and_color: ID and color of the relevant Binding, - * &enum iwl_mvm_id_and_color - * @quota: absolute time quota in TU. The scheduler will try to divide the - * remainig quota (after Time Events) according to this quota. - * @max_duration: max uninterrupted context duration in TU - */ -struct iwl_time_quota_data { - __le32 id_and_color; - __le32 quota; - __le32 max_duration; -} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */ - -/** - * struct iwl_time_quota_cmd - configuration of time quota between bindings - * ( TIME_QUOTA_CMD = 0x2c ) - * @quotas: allocations per binding - * Note: on non-CDB the fourth one is the auxilary mac and is - * essentially zero. - * On CDB the fourth one is a regular binding. - */ -struct iwl_time_quota_cmd { - struct iwl_time_quota_data quotas[MAX_BINDINGS]; -} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */ - - -/* PHY context */ - -/* Supported bands */ -#define PHY_BAND_5 (0) -#define PHY_BAND_24 (1) - -/* Supported channel width, vary if there is VHT support */ -#define PHY_VHT_CHANNEL_MODE20 (0x0) -#define PHY_VHT_CHANNEL_MODE40 (0x1) -#define PHY_VHT_CHANNEL_MODE80 (0x2) -#define PHY_VHT_CHANNEL_MODE160 (0x3) - -/* - * Control channel position: - * For legacy set bit means upper channel, otherwise lower. - * For VHT - bit-2 marks if the control is lower/upper relative to center-freq - * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0. - * center_freq - * | - * 40Mhz |_______|_______| - * 80Mhz |_______|_______|_______|_______| - * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______| - * code 011 010 001 000 | 100 101 110 111 - */ -#define PHY_VHT_CTRL_POS_1_BELOW (0x0) -#define PHY_VHT_CTRL_POS_2_BELOW (0x1) -#define PHY_VHT_CTRL_POS_3_BELOW (0x2) -#define PHY_VHT_CTRL_POS_4_BELOW (0x3) -#define PHY_VHT_CTRL_POS_1_ABOVE (0x4) -#define PHY_VHT_CTRL_POS_2_ABOVE (0x5) -#define PHY_VHT_CTRL_POS_3_ABOVE (0x6) -#define PHY_VHT_CTRL_POS_4_ABOVE (0x7) - -/* - * @band: PHY_BAND_* - * @channel: channel number - * @width: PHY_[VHT|LEGACY]_CHANNEL_* - * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_* - */ -struct iwl_fw_channel_info { - u8 band; - u8 channel; - u8 width; - u8 ctrl_pos; -} __packed; - -#define PHY_RX_CHAIN_DRIVER_FORCE_POS (0) -#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \ - (0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS) -#define PHY_RX_CHAIN_VALID_POS (1) -#define PHY_RX_CHAIN_VALID_MSK \ - (0x7 << PHY_RX_CHAIN_VALID_POS) -#define PHY_RX_CHAIN_FORCE_SEL_POS (4) -#define PHY_RX_CHAIN_FORCE_SEL_MSK \ - (0x7 << PHY_RX_CHAIN_FORCE_SEL_POS) -#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS (7) -#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \ - (0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS) -#define PHY_RX_CHAIN_CNT_POS (10) -#define PHY_RX_CHAIN_CNT_MSK \ - (0x3 << PHY_RX_CHAIN_CNT_POS) -#define PHY_RX_CHAIN_MIMO_CNT_POS (12) -#define PHY_RX_CHAIN_MIMO_CNT_MSK \ - (0x3 << PHY_RX_CHAIN_MIMO_CNT_POS) -#define PHY_RX_CHAIN_MIMO_FORCE_POS (14) -#define PHY_RX_CHAIN_MIMO_FORCE_MSK \ - (0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS) - -/* TODO: fix the value, make it depend on firmware at runtime? */ -#define NUM_PHY_CTX 3 - -/* TODO: complete missing documentation */ -/** - * struct iwl_phy_context_cmd - config of the PHY context - * ( PHY_CONTEXT_CMD = 0x8 ) - * @id_and_color: ID and color of the relevant Binding - * @action: action to perform, one of FW_CTXT_ACTION_* - * @apply_time: 0 means immediate apply and context switch. - * other value means apply new params after X usecs - * @tx_param_color: ??? - * @ci: channel info - * @txchain_info: ??? - * @rxchain_info: ??? - * @acquisition_data: ??? - * @dsp_cfg_flags: set to 0 - */ -struct iwl_phy_context_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* PHY_CONTEXT_DATA_API_S_VER_1 */ - __le32 apply_time; - __le32 tx_param_color; - struct iwl_fw_channel_info ci; - __le32 txchain_info; - __le32 rxchain_info; - __le32 acquisition_data; - __le32 dsp_cfg_flags; -} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */ - -/* - * Aux ROC command - * - * Command requests the firmware to create a time event for a certain duration - * and remain on the given channel. This is done by using the Aux framework in - * the FW. - * The command was first used for Hot Spot issues - but can be used regardless - * to Hot Spot. - * - * ( HOT_SPOT_CMD 0x53 ) - * - * @id_and_color: ID and color of the MAC - * @action: action to perform, one of FW_CTXT_ACTION_* - * @event_unique_id: If the action FW_CTXT_ACTION_REMOVE then the - * event_unique_id should be the id of the time event assigned by ucode. - * Otherwise ignore the event_unique_id. - * @sta_id_and_color: station id and color, resumed during "Remain On Channel" - * activity. - * @channel_info: channel info - * @node_addr: Our MAC Address - * @reserved: reserved for alignment - * @apply_time: GP2 value to start (should always be the current GP2 value) - * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max - * time by which start of the event is allowed to be postponed. - * @duration: event duration in TU To calculate event duration: - * timeEventDuration = min(duration, remainingQuota) - */ -struct iwl_hs20_roc_req { - /* COMMON_INDEX_HDR_API_S_VER_1 hdr */ - __le32 id_and_color; - __le32 action; - __le32 event_unique_id; - __le32 sta_id_and_color; - struct iwl_fw_channel_info channel_info; - u8 node_addr[ETH_ALEN]; - __le16 reserved; - __le32 apply_time; - __le32 apply_time_max_delay; - __le32 duration; -} __packed; /* HOT_SPOT_CMD_API_S_VER_1 */ - -/* - * values for AUX ROC result values - */ -enum iwl_mvm_hot_spot { - HOT_SPOT_RSP_STATUS_OK, - HOT_SPOT_RSP_STATUS_TOO_MANY_EVENTS, - HOT_SPOT_MAX_NUM_OF_SESSIONS, -}; - -/* - * Aux ROC command response - * - * In response to iwl_hs20_roc_req the FW sends this command to notify the - * driver the uid of the timevent. - * - * ( HOT_SPOT_CMD 0x53 ) - * - * @event_unique_id: Unique ID of time event assigned by ucode - * @status: Return status 0 is success, all the rest used for specific errors - */ -struct iwl_hs20_roc_res { - __le32 event_unique_id; - __le32 status; -} __packed; /* HOT_SPOT_RSP_API_S_VER_1 */ - -/** - * struct iwl_radio_version_notif - information on the radio version - * ( RADIO_VERSION_NOTIFICATION = 0x68 ) - * @radio_flavor: radio flavor - * @radio_step: radio version step - * @radio_dash: radio version dash - */ -struct iwl_radio_version_notif { - __le32 radio_flavor; - __le32 radio_step; - __le32 radio_dash; -} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */ - -enum iwl_card_state_flags { - CARD_ENABLED = 0x00, - HW_CARD_DISABLED = 0x01, - SW_CARD_DISABLED = 0x02, - CT_KILL_CARD_DISABLED = 0x04, - HALT_CARD_DISABLED = 0x08, - CARD_DISABLED_MSK = 0x0f, - CARD_IS_RX_ON = 0x10, -}; - -/** - * struct iwl_radio_version_notif - information on the radio version - * ( CARD_STATE_NOTIFICATION = 0xa1 ) - * @flags: %iwl_card_state_flags - */ -struct iwl_card_state_notif { - __le32 flags; -} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ - -/** - * struct iwl_missed_beacons_notif - information on missed beacons - * ( MISSED_BEACONS_NOTIFICATION = 0xa2 ) - * @mac_id: interface ID - * @consec_missed_beacons_since_last_rx: number of consecutive missed - * beacons since last RX. - * @consec_missed_beacons: number of consecutive missed beacons - * @num_expected_beacons: number of expected beacons - * @num_recvd_beacons: number of received beacons - */ -struct iwl_missed_beacons_notif { - __le32 mac_id; - __le32 consec_missed_beacons_since_last_rx; - __le32 consec_missed_beacons; - __le32 num_expected_beacons; - __le32 num_recvd_beacons; -} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */ - -/** - * struct iwl_mfuart_load_notif - mfuart image version & status - * ( MFUART_LOAD_NOTIFICATION = 0xb1 ) - * @installed_ver: installed image version - * @external_ver: external image version - * @status: MFUART loading status - * @duration: MFUART loading time - * @image_size: MFUART image size in bytes -*/ -struct iwl_mfuart_load_notif { - __le32 installed_ver; - __le32 external_ver; - __le32 status; - __le32 duration; - /* image size valid only in v2 of the command */ - __le32 image_size; -} __packed; /*MFU_LOADER_NTFY_API_S_VER_2*/ - -/** - * struct iwl_mfu_assert_dump_notif - mfuart dump logs - * ( MFU_ASSERT_DUMP_NTF = 0xfe ) - * @assert_id: mfuart assert id that cause the notif - * @curr_reset_num: number of asserts since uptime - * @index_num: current chunk id - * @parts_num: total number of chunks - * @data_size: number of data bytes sent - * @data: data buffer - */ -struct iwl_mfu_assert_dump_notif { - __le32 assert_id; - __le32 curr_reset_num; - __le16 index_num; - __le16 parts_num; - __le32 data_size; - __le32 data[0]; -} __packed; /*MFU_DUMP_ASSERT_API_S_VER_1*/ - -#define MAX_PORT_ID_NUM 2 -#define MAX_MCAST_FILTERING_ADDRESSES 256 - -/** - * struct iwl_mcast_filter_cmd - configure multicast filter. - * @filter_own: Set 1 to filter out multicast packets sent by station itself - * @port_id: Multicast MAC addresses array specifier. This is a strange way - * to identify network interface adopted in host-device IF. - * It is used by FW as index in array of addresses. This array has - * MAX_PORT_ID_NUM members. - * @count: Number of MAC addresses in the array - * @pass_all: Set 1 to pass all multicast packets. - * @bssid: current association BSSID. - * @reserved: reserved - * @addr_list: Place holder for array of MAC addresses. - * IMPORTANT: add padding if necessary to ensure DWORD alignment. - */ -struct iwl_mcast_filter_cmd { - u8 filter_own; - u8 port_id; - u8 count; - u8 pass_all; - u8 bssid[6]; - u8 reserved[2]; - u8 addr_list[0]; -} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */ - -#define MAX_BCAST_FILTERS 8 -#define MAX_BCAST_FILTER_ATTRS 2 - -/** - * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet - * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start. - * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e. - * start of ip payload). - */ -enum iwl_mvm_bcast_filter_attr_offset { - BCAST_FILTER_OFFSET_PAYLOAD_START = 0, - BCAST_FILTER_OFFSET_IP_END = 1, -}; - -/** - * struct iwl_fw_bcast_filter_attr - broadcast filter attribute - * @offset_type: &enum iwl_mvm_bcast_filter_attr_offset. - * @offset: starting offset of this pattern. - * @reserved1: reserved - * @val: value to match - big endian (MSB is the first - * byte to match from offset pos). - * @mask: mask to match (big endian). - */ -struct iwl_fw_bcast_filter_attr { - u8 offset_type; - u8 offset; - __le16 reserved1; - __be32 val; - __be32 mask; -} __packed; /* BCAST_FILTER_ATT_S_VER_1 */ - -/** - * enum iwl_mvm_bcast_filter_frame_type - filter frame type - * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames. - * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames - */ -enum iwl_mvm_bcast_filter_frame_type { - BCAST_FILTER_FRAME_TYPE_ALL = 0, - BCAST_FILTER_FRAME_TYPE_IPV4 = 1, -}; - -/** - * struct iwl_fw_bcast_filter - broadcast filter - * @discard: discard frame (1) or let it pass (0). - * @frame_type: &enum iwl_mvm_bcast_filter_frame_type. - * @reserved1: reserved - * @num_attrs: number of valid attributes in this filter. - * @attrs: attributes of this filter. a filter is considered matched - * only when all its attributes are matched (i.e. AND relationship) - */ -struct iwl_fw_bcast_filter { - u8 discard; - u8 frame_type; - u8 num_attrs; - u8 reserved1; - struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS]; -} __packed; /* BCAST_FILTER_S_VER_1 */ - -#define BA_WINDOW_STREAMS_MAX 16 -#define BA_WINDOW_STATUS_TID_MSK 0x000F -#define BA_WINDOW_STATUS_STA_ID_POS 4 -#define BA_WINDOW_STATUS_STA_ID_MSK 0x01F0 -#define BA_WINDOW_STATUS_VALID_MSK BIT(9) - -/** - * struct iwl_ba_window_status_notif - reordering window's status notification - * @bitmap: bitmap of received frames [start_seq_num + 0]..[start_seq_num + 63] - * @ra_tid: bit 3:0 - TID, bit 8:4 - STA_ID, bit 9 - valid - * @start_seq_num: the start sequence number of the bitmap - * @mpdu_rx_count: the number of received MPDUs since entering D0i3 - */ -struct iwl_ba_window_status_notif { - __le64 bitmap[BA_WINDOW_STREAMS_MAX]; - __le16 ra_tid[BA_WINDOW_STREAMS_MAX]; - __le32 start_seq_num[BA_WINDOW_STREAMS_MAX]; - __le16 mpdu_rx_count[BA_WINDOW_STREAMS_MAX]; -} __packed; /* BA_WINDOW_STATUS_NTFY_API_S_VER_1 */ - -/** - * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration. - * @default_discard: default action for this mac (discard (1) / pass (0)). - * @reserved1: reserved - * @attached_filters: bitmap of relevant filters for this mac. - */ -struct iwl_fw_bcast_mac { - u8 default_discard; - u8 reserved1; - __le16 attached_filters; -} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */ - -/** - * struct iwl_bcast_filter_cmd - broadcast filtering configuration - * @disable: enable (0) / disable (1) - * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS) - * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER) - * @reserved1: reserved - * @filters: broadcast filters - * @macs: broadcast filtering configuration per-mac - */ -struct iwl_bcast_filter_cmd { - u8 disable; - u8 max_bcast_filters; - u8 max_macs; - u8 reserved1; - struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS]; - struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER]; -} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */ - -/* - * enum iwl_mvm_marker_id - maker ids - * - * The ids for different type of markers to insert into the usniffer logs - */ -enum iwl_mvm_marker_id { - MARKER_ID_TX_FRAME_LATENCY = 1, -}; /* MARKER_ID_API_E_VER_1 */ - -/** - * struct iwl_mvm_marker - mark info into the usniffer logs - * - * (MARKER_CMD = 0xcb) - * - * Mark the UTC time stamp into the usniffer logs together with additional - * metadata, so the usniffer output can be parsed. - * In the command response the ucode will return the GP2 time. - * - * @dw_len: The amount of dwords following this byte including this byte. - * @marker_id: A unique marker id (iwl_mvm_marker_id). - * @reserved: reserved. - * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC - * @metadata: additional meta data that will be written to the unsiffer log - */ -struct iwl_mvm_marker { - u8 dw_len; - u8 marker_id; - __le16 reserved; - __le64 timestamp; - __le32 metadata[0]; -} __packed; /* MARKER_API_S_VER_1 */ - -/* - * enum iwl_dc2dc_config_id - flag ids - * - * Ids of dc2dc configuration flags - */ -enum iwl_dc2dc_config_id { - DCDC_LOW_POWER_MODE_MSK_SET = 0x1, /* not used */ - DCDC_FREQ_TUNE_SET = 0x2, -}; /* MARKER_ID_API_E_VER_1 */ - -/** - * struct iwl_dc2dc_config_cmd - configure dc2dc values - * - * (DC2DC_CONFIG_CMD = 0x83) - * - * Set/Get & configure dc2dc values. - * The command always returns the current dc2dc values. - * - * @flags: set/get dc2dc - * @enable_low_power_mode: not used. - * @dc2dc_freq_tune0: frequency divider - digital domain - * @dc2dc_freq_tune1: frequency divider - analog domain - */ -struct iwl_dc2dc_config_cmd { - __le32 flags; - __le32 enable_low_power_mode; /* not used */ - __le32 dc2dc_freq_tune0; - __le32 dc2dc_freq_tune1; -} __packed; /* DC2DC_CONFIG_CMD_API_S_VER_1 */ - -/** - * struct iwl_dc2dc_config_resp - response for iwl_dc2dc_config_cmd - * - * Current dc2dc values returned by the FW. - * - * @dc2dc_freq_tune0: frequency divider - digital domain - * @dc2dc_freq_tune1: frequency divider - analog domain - */ -struct iwl_dc2dc_config_resp { - __le32 dc2dc_freq_tune0; - __le32 dc2dc_freq_tune1; -} __packed; /* DC2DC_CONFIG_RESP_API_S_VER_1 */ - -/*********************************** - * Smart Fifo API - ***********************************/ -/* Smart Fifo state */ -enum iwl_sf_state { - SF_LONG_DELAY_ON = 0, /* should never be called by driver */ - SF_FULL_ON, - SF_UNINIT, - SF_INIT_OFF, - SF_HW_NUM_STATES -}; - -/* Smart Fifo possible scenario */ -enum iwl_sf_scenario { - SF_SCENARIO_SINGLE_UNICAST, - SF_SCENARIO_AGG_UNICAST, - SF_SCENARIO_MULTICAST, - SF_SCENARIO_BA_RESP, - SF_SCENARIO_TX_RESP, - SF_NUM_SCENARIO -}; - -#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */ -#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */ - -/* smart FIFO default values */ -#define SF_W_MARK_SISO 6144 -#define SF_W_MARK_MIMO2 8192 -#define SF_W_MARK_MIMO3 6144 -#define SF_W_MARK_LEGACY 4096 -#define SF_W_MARK_SCAN 4096 - -/* SF Scenarios timers for default configuration (aligned to 32 uSec) */ -#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */ -#define SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */ - -/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */ -#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ -#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ -#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */ -#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */ -#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */ -#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */ -#define SF_BA_IDLE_TIMER 320 /* 300 uSec */ -#define SF_BA_AGING_TIMER 2016 /* 2 mSec */ -#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */ -#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */ - -#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */ - -#define SF_CFG_DUMMY_NOTIF_OFF BIT(16) - -/** - * struct iwl_sf_cfg_cmd - Smart Fifo configuration command. - * @state: smart fifo state, types listed in &enum iwl_sf_state. - * @watermark: Minimum allowed availabe free space in RXF for transient state. - * @long_delay_timeouts: aging and idle timer values for each scenario - * in long delay state. - * @full_on_timeouts: timer values for each scenario in full on state. - */ -struct iwl_sf_cfg_cmd { - __le32 state; - __le32 watermark[SF_TRANSIENT_STATES_NUMBER]; - __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; - __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; -} __packed; /* SF_CFG_API_S_VER_2 */ - -/*********************************** - * Location Aware Regulatory (LAR) API - MCC updates - ***********************************/ - -/** - * struct iwl_mcc_update_cmd_v1 - Request the device to update geographic - * regulatory profile according to the given MCC (Mobile Country Code). - * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. - * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the - * MCC in the cmd response will be the relevant MCC in the NVM. - * @mcc: given mobile country code - * @source_id: the source from where we got the MCC, see iwl_mcc_source - * @reserved: reserved for alignment - */ -struct iwl_mcc_update_cmd_v1 { - __le16 mcc; - u8 source_id; - u8 reserved; -} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_1 */ - -/** - * struct iwl_mcc_update_cmd - Request the device to update geographic - * regulatory profile according to the given MCC (Mobile Country Code). - * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. - * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the - * MCC in the cmd response will be the relevant MCC in the NVM. - * @mcc: given mobile country code - * @source_id: the source from where we got the MCC, see iwl_mcc_source - * @reserved: reserved for alignment - * @key: integrity key for MCC API OEM testing - * @reserved2: reserved - */ -struct iwl_mcc_update_cmd { - __le16 mcc; - u8 source_id; - u8 reserved; - __le32 key; - u8 reserved2[20]; -} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_2 */ - -/** - * struct iwl_mcc_update_resp_v1 - response to MCC_UPDATE_CMD. - * Contains the new channel control profile map, if changed, and the new MCC - * (mobile country code). - * The new MCC may be different than what was requested in MCC_UPDATE_CMD. - * @status: see &enum iwl_mcc_update_status - * @mcc: the new applied MCC - * @cap: capabilities for all channels which matches the MCC - * @source_id: the MCC source, see iwl_mcc_source - * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 - * channels, depending on platform) - * @channels: channel control data map, DWORD for each channel. Only the first - * 16bits are used. - */ -struct iwl_mcc_update_resp_v1 { - __le32 status; - __le16 mcc; - u8 cap; - u8 source_id; - __le32 n_channels; - __le32 channels[0]; -} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */ - -/** - * struct iwl_mcc_update_resp - response to MCC_UPDATE_CMD. - * Contains the new channel control profile map, if changed, and the new MCC - * (mobile country code). - * The new MCC may be different than what was requested in MCC_UPDATE_CMD. - * @status: see &enum iwl_mcc_update_status - * @mcc: the new applied MCC - * @cap: capabilities for all channels which matches the MCC - * @source_id: the MCC source, see iwl_mcc_source - * @time: time elapsed from the MCC test start (in 30 seconds TU) - * @reserved: reserved. - * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 - * channels, depending on platform) - * @channels: channel control data map, DWORD for each channel. Only the first - * 16bits are used. - */ -struct iwl_mcc_update_resp { - __le32 status; - __le16 mcc; - u8 cap; - u8 source_id; - __le16 time; - __le16 reserved; - __le32 n_channels; - __le32 channels[0]; -} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */ - -/** - * struct iwl_mcc_chub_notif - chub notifies of mcc change - * (MCC_CHUB_UPDATE_CMD = 0xc9) - * The Chub (Communication Hub, CommsHUB) is a HW component that connects to - * the cellular and connectivity cores that gets updates of the mcc, and - * notifies the ucode directly of any mcc change. - * The ucode requests the driver to request the device to update geographic - * regulatory profile according to the given MCC (Mobile Country Code). - * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. - * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the - * MCC in the cmd response will be the relevant MCC in the NVM. - * @mcc: given mobile country code - * @source_id: identity of the change originator, see iwl_mcc_source - * @reserved1: reserved for alignment - */ -struct iwl_mcc_chub_notif { - __le16 mcc; - u8 source_id; - u8 reserved1; -} __packed; /* LAR_MCC_NOTIFY_S */ - -enum iwl_mcc_update_status { - MCC_RESP_NEW_CHAN_PROFILE, - MCC_RESP_SAME_CHAN_PROFILE, - MCC_RESP_INVALID, - MCC_RESP_NVM_DISABLED, - MCC_RESP_ILLEGAL, - MCC_RESP_LOW_PRIORITY, - MCC_RESP_TEST_MODE_ACTIVE, - MCC_RESP_TEST_MODE_NOT_ACTIVE, - MCC_RESP_TEST_MODE_DENIAL_OF_SERVICE, -}; - -enum iwl_mcc_source { - MCC_SOURCE_OLD_FW = 0, - MCC_SOURCE_ME = 1, - MCC_SOURCE_BIOS = 2, - MCC_SOURCE_3G_LTE_HOST = 3, - MCC_SOURCE_3G_LTE_DEVICE = 4, - MCC_SOURCE_WIFI = 5, - MCC_SOURCE_RESERVED = 6, - MCC_SOURCE_DEFAULT = 7, - MCC_SOURCE_UNINITIALIZED = 8, - MCC_SOURCE_MCC_API = 9, - MCC_SOURCE_GET_CURRENT = 0x10, - MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11, -}; - -/* DTS measurements */ - -enum iwl_dts_measurement_flags { - DTS_TRIGGER_CMD_FLAGS_TEMP = BIT(0), - DTS_TRIGGER_CMD_FLAGS_VOLT = BIT(1), -}; - -/** - * struct iwl_dts_measurement_cmd - request DTS temp and/or voltage measurements - * - * @flags: indicates which measurements we want as specified in - * &enum iwl_dts_measurement_flags - */ -struct iwl_dts_measurement_cmd { - __le32 flags; -} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_CMD_S */ - -/** -* enum iwl_dts_control_measurement_mode - DTS measurement type -* @DTS_AUTOMATIC: Automatic mode (full SW control). Provide temperature read -* back (latest value. Not waiting for new value). Use automatic -* SW DTS configuration. -* @DTS_REQUEST_READ: Request DTS read. Configure DTS with manual settings, -* trigger DTS reading and provide read back temperature read -* when available. -* @DTS_OVER_WRITE: over-write the DTS temperatures in the SW until next read -* @DTS_DIRECT_WITHOUT_MEASURE: DTS returns its latest temperature result, -* without measurement trigger. -*/ -enum iwl_dts_control_measurement_mode { - DTS_AUTOMATIC = 0, - DTS_REQUEST_READ = 1, - DTS_OVER_WRITE = 2, - DTS_DIRECT_WITHOUT_MEASURE = 3, -}; - -/** -* enum iwl_dts_used - DTS to use or used for measurement in the DTS request -* @DTS_USE_TOP: Top -* @DTS_USE_CHAIN_A: chain A -* @DTS_USE_CHAIN_B: chain B -* @DTS_USE_CHAIN_C: chain C -* @XTAL_TEMPERATURE: read temperature from xtal -*/ -enum iwl_dts_used { - DTS_USE_TOP = 0, - DTS_USE_CHAIN_A = 1, - DTS_USE_CHAIN_B = 2, - DTS_USE_CHAIN_C = 3, - XTAL_TEMPERATURE = 4, -}; - -/** -* enum iwl_dts_bit_mode - bit-mode to use in DTS request read mode -* @DTS_BIT6_MODE: bit 6 mode -* @DTS_BIT8_MODE: bit 8 mode -*/ -enum iwl_dts_bit_mode { - DTS_BIT6_MODE = 0, - DTS_BIT8_MODE = 1, -}; - -/** - * struct iwl_ext_dts_measurement_cmd - request extended DTS temp measurements - * @control_mode: see &enum iwl_dts_control_measurement_mode - * @temperature: used when over write DTS mode is selected - * @sensor: set temperature sensor to use. See &enum iwl_dts_used - * @avg_factor: average factor to DTS in request DTS read mode - * @bit_mode: value defines the DTS bit mode to use. See &enum iwl_dts_bit_mode - * @step_duration: step duration for the DTS - */ -struct iwl_ext_dts_measurement_cmd { - __le32 control_mode; - __le32 temperature; - __le32 sensor; - __le32 avg_factor; - __le32 bit_mode; - __le32 step_duration; -} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */ - -/** - * struct iwl_dts_measurement_notif_v1 - measurements notification - * - * @temp: the measured temperature - * @voltage: the measured voltage - */ -struct iwl_dts_measurement_notif_v1 { - __le32 temp; - __le32 voltage; -} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_1*/ - -/** - * struct iwl_dts_measurement_notif_v2 - measurements notification - * - * @temp: the measured temperature - * @voltage: the measured voltage - * @threshold_idx: the trip index that was crossed - */ -struct iwl_dts_measurement_notif_v2 { - __le32 temp; - __le32 voltage; - __le32 threshold_idx; -} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_2 */ - -/** - * struct ct_kill_notif - CT-kill entry notification - * - * @temperature: the current temperature in celsius - * @reserved: reserved - */ -struct ct_kill_notif { - __le16 temperature; - __le16 reserved; -} __packed; /* GRP_PHY_CT_KILL_NTF */ - -/** -* enum ctdp_cmd_operation - CTDP command operations -* @CTDP_CMD_OPERATION_START: update the current budget -* @CTDP_CMD_OPERATION_STOP: stop ctdp -* @CTDP_CMD_OPERATION_REPORT: get the average budget -*/ -enum iwl_mvm_ctdp_cmd_operation { - CTDP_CMD_OPERATION_START = 0x1, - CTDP_CMD_OPERATION_STOP = 0x2, - CTDP_CMD_OPERATION_REPORT = 0x4, -};/* CTDP_CMD_OPERATION_TYPE_E */ - -/** - * struct iwl_mvm_ctdp_cmd - track and manage the FW power consumption budget - * - * @operation: see &enum iwl_mvm_ctdp_cmd_operation - * @budget: the budget in milliwatt - * @window_size: defined in API but not used - */ -struct iwl_mvm_ctdp_cmd { - __le32 operation; - __le32 budget; - __le32 window_size; -} __packed; - -#define IWL_MAX_DTS_TRIPS 8 - -/** - * struct temp_report_ths_cmd - set temperature thresholds - * - * @num_temps: number of temperature thresholds passed - * @thresholds: array with the thresholds to be configured - */ -struct temp_report_ths_cmd { - __le32 num_temps; - __le16 thresholds[IWL_MAX_DTS_TRIPS]; -} __packed; /* GRP_PHY_TEMP_REPORTING_THRESHOLDS_CMD */ - -/*********************************** - * TDLS API - ***********************************/ - -/* Type of TDLS request */ -enum iwl_tdls_channel_switch_type { - TDLS_SEND_CHAN_SW_REQ = 0, - TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH, - TDLS_MOVE_CH, -}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */ - -/** - * struct iwl_tdls_channel_switch_timing - Switch timing in TDLS channel-switch - * @frame_timestamp: GP2 timestamp of channel-switch request/response packet - * received from peer - * @max_offchan_duration: What amount of microseconds out of a DTIM is given - * to the TDLS off-channel communication. For instance if the DTIM is - * 200TU and the TDLS peer is to be given 25% of the time, the value - * given will be 50TU, or 50 * 1024 if translated into microseconds. - * @switch_time: switch time the peer sent in its channel switch timing IE - * @switch_timeout: switch timeout the peer sent in its channel switch timing IE - */ -struct iwl_tdls_channel_switch_timing { - __le32 frame_timestamp; /* GP2 time of peer packet Rx */ - __le32 max_offchan_duration; /* given in micro-seconds */ - __le32 switch_time; /* given in micro-seconds */ - __le32 switch_timeout; /* given in micro-seconds */ -} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */ - -#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200 - -/** - * struct iwl_tdls_channel_switch_frame - TDLS channel switch frame template - * - * A template representing a TDLS channel-switch request or response frame - * - * @switch_time_offset: offset to the channel switch timing IE in the template - * @tx_cmd: Tx parameters for the frame - * @data: frame data - */ -struct iwl_tdls_channel_switch_frame { - __le32 switch_time_offset; - struct iwl_tx_cmd tx_cmd; - u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE]; -} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */ - -/** - * struct iwl_tdls_channel_switch_cmd - TDLS channel switch command - * - * The command is sent to initiate a channel switch and also in response to - * incoming TDLS channel-switch request/response packets from remote peers. - * - * @switch_type: see &enum iwl_tdls_channel_switch_type - * @peer_sta_id: station id of TDLS peer - * @ci: channel we switch to - * @timing: timing related data for command - * @frame: channel-switch request/response template, depending to switch_type - */ -struct iwl_tdls_channel_switch_cmd { - u8 switch_type; - __le32 peer_sta_id; - struct iwl_fw_channel_info ci; - struct iwl_tdls_channel_switch_timing timing; - struct iwl_tdls_channel_switch_frame frame; -} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */ - -/** - * struct iwl_tdls_channel_switch_notif - TDLS channel switch start notification - * - * @status: non-zero on success - * @offchannel_duration: duration given in microseconds - * @sta_id: peer currently performing the channel-switch with - */ -struct iwl_tdls_channel_switch_notif { - __le32 status; - __le32 offchannel_duration; - __le32 sta_id; -} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */ - -/** - * struct iwl_tdls_sta_info - TDLS station info - * - * @sta_id: station id of the TDLS peer - * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx - * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer - * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise - */ -struct iwl_tdls_sta_info { - u8 sta_id; - u8 tx_to_peer_tid; - __le16 tx_to_peer_ssn; - __le32 is_initiator; -} __packed; /* TDLS_STA_INFO_VER_1 */ - -/** - * struct iwl_tdls_config_cmd - TDLS basic config command - * - * @id_and_color: MAC id and color being configured - * @tdls_peer_count: amount of currently connected TDLS peers - * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx - * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP - * @sta_info: per-station info. Only the first tdls_peer_count entries are set - * @pti_req_data_offset: offset of network-level data for the PTI template - * @pti_req_tx_cmd: Tx parameters for PTI request template - * @pti_req_template: PTI request template data - */ -struct iwl_tdls_config_cmd { - __le32 id_and_color; /* mac id and color */ - u8 tdls_peer_count; - u8 tx_to_ap_tid; - __le16 tx_to_ap_ssn; - struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT]; - - __le32 pti_req_data_offset; - struct iwl_tx_cmd pti_req_tx_cmd; - u8 pti_req_template[0]; -} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */ - -/** - * struct iwl_tdls_config_sta_info_res - TDLS per-station config information - * - * @sta_id: station id of the TDLS peer - * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to - * the peer - */ -struct iwl_tdls_config_sta_info_res { - __le16 sta_id; - __le16 tx_to_peer_last_seq; -} __packed; /* TDLS_STA_INFO_RSP_VER_1 */ - -/** - * struct iwl_tdls_config_res - TDLS config information from FW - * - * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP - * @sta_info: per-station TDLS config information - */ -struct iwl_tdls_config_res { - __le32 tx_to_ap_last_seq; - struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT]; -} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */ - -/** - * struct iwl_mu_group_mgmt_cmd - VHT MU-MIMO group configuration - * - * @reserved: reserved - * @membership_status: a bitmap of MU groups - * @user_position:the position of station in a group. If the station is in the - * group then bits (group * 2) is the position -1 - */ -struct iwl_mu_group_mgmt_cmd { - __le32 reserved; - __le32 membership_status[2]; - __le32 user_position[4]; -} __packed; /* MU_GROUP_ID_MNG_TABLE_API_S_VER_1 */ - -/** - * struct iwl_mu_group_mgmt_notif - VHT MU-MIMO group id notification - * - * @membership_status: a bitmap of MU groups - * @user_position: the position of station in a group. If the station is in the - * group then bits (group * 2) is the position -1 - */ -struct iwl_mu_group_mgmt_notif { - __le32 membership_status[2]; - __le32 user_position[4]; -} __packed; /* MU_GROUP_MNG_NTFY_API_S_VER_1 */ - -#define MAX_STORED_BEACON_SIZE 600 - -/** - * struct iwl_stored_beacon_notif - Stored beacon notification - * - * @system_time: system time on air rise - * @tsf: TSF on air rise - * @beacon_timestamp: beacon on air rise - * @band: band, matches &RX_RES_PHY_FLAGS_BAND_24 definition - * @channel: channel this beacon was received on - * @rates: rate in ucode internal format - * @byte_count: frame's byte count - * @data: beacon data, length in @byte_count - */ -struct iwl_stored_beacon_notif { - __le32 system_time; - __le64 tsf; - __le32 beacon_timestamp; - __le16 band; - __le16 channel; - __le32 rates; - __le32 byte_count; - u8 data[MAX_STORED_BEACON_SIZE]; -} __packed; /* WOWLAN_STROED_BEACON_INFO_S_VER_2 */ - -#define LQM_NUMBER_OF_STATIONS_IN_REPORT 16 - -enum iwl_lqm_cmd_operatrions { - LQM_CMD_OPERATION_START_MEASUREMENT = 0x01, - LQM_CMD_OPERATION_STOP_MEASUREMENT = 0x02, -}; - -enum iwl_lqm_status { - LQM_STATUS_SUCCESS = 0, - LQM_STATUS_TIMEOUT = 1, - LQM_STATUS_ABORT = 2, -}; - -/** - * struct iwl_link_qual_msrmnt_cmd - Link Quality Measurement command - * @cmd_operation: command operation to be performed (start or stop) - * as defined above. - * @mac_id: MAC ID the measurement applies to. - * @measurement_time: time of the total measurement to be performed, in uSec. - * @timeout: maximum time allowed until a response is sent, in uSec. - */ -struct iwl_link_qual_msrmnt_cmd { - __le32 cmd_operation; - __le32 mac_id; - __le32 measurement_time; - __le32 timeout; -} __packed /* LQM_CMD_API_S_VER_1 */; - -/** - * struct iwl_link_qual_msrmnt_notif - Link Quality Measurement notification - * - * @frequent_stations_air_time: an array containing the total air time - * (in uSec) used by the most frequently transmitting stations. - * @number_of_stations: the number of uniqe stations included in the array - * (a number between 0 to 16) - * @total_air_time_other_stations: the total air time (uSec) used by all the - * stations which are not included in the above report. - * @time_in_measurement_window: the total time in uSec in which a measurement - * took place. - * @tx_frame_dropped: the number of TX frames dropped due to retry limit during - * measurement - * @mac_id: MAC ID the measurement applies to. - * @status: return status. may be one of the LQM_STATUS_* defined above. - * @reserved: reserved. - */ -struct iwl_link_qual_msrmnt_notif { - __le32 frequent_stations_air_time[LQM_NUMBER_OF_STATIONS_IN_REPORT]; - __le32 number_of_stations; - __le32 total_air_time_other_stations; - __le32 time_in_measurement_window; - __le32 tx_frame_dropped; - __le32 mac_id; - __le32 status; - u8 reserved[12]; -} __packed; /* LQM_MEASUREMENT_COMPLETE_NTF_API_S_VER1 */ - -/** - * struct iwl_channel_switch_noa_notif - Channel switch NOA notification - * - * @id_and_color: ID and color of the MAC - */ -struct iwl_channel_switch_noa_notif { - __le32 id_and_color; -} __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */ - -/* Operation types for the debug mem access */ -enum { - DEBUG_MEM_OP_READ = 0, - DEBUG_MEM_OP_WRITE = 1, - DEBUG_MEM_OP_WRITE_BYTES = 2, -}; - -#define DEBUG_MEM_MAX_SIZE_DWORDS 32 - -/** - * struct iwl_dbg_mem_access_cmd - Request the device to read/write memory - * @op: DEBUG_MEM_OP_* - * @addr: address to read/write from/to - * @len: in dwords, to read/write - * @data: for write opeations, contains the source buffer - */ -struct iwl_dbg_mem_access_cmd { - __le32 op; - __le32 addr; - __le32 len; - __le32 data[]; -} __packed; /* DEBUG_(U|L)MAC_RD_WR_CMD_API_S_VER_1 */ - -/* Status responses for the debug mem access */ -enum { - DEBUG_MEM_STATUS_SUCCESS = 0x0, - DEBUG_MEM_STATUS_FAILED = 0x1, - DEBUG_MEM_STATUS_LOCKED = 0x2, - DEBUG_MEM_STATUS_HIDDEN = 0x3, - DEBUG_MEM_STATUS_LENGTH = 0x4, -}; - -/** - * struct iwl_dbg_mem_access_rsp - Response to debug mem commands - * @status: DEBUG_MEM_STATUS_* - * @len: read dwords (0 for write operations) - * @data: contains the read DWs - */ -struct iwl_dbg_mem_access_rsp { - __le32 status; - __le32 len; - __le32 data[]; -} __packed; /* DEBUG_(U|L)MAC_RD_WR_RSP_API_S_VER_1 */ - -/** - * struct iwl_nvm_access_complete_cmd - NVM_ACCESS commands are completed - * @reserved: reserved - */ -struct iwl_nvm_access_complete_cmd { - __le32 reserved; -} __packed; /* NVM_ACCESS_COMPLETE_CMD_API_S_VER_1 */ - -/** - * enum iwl_extended_cfg_flag - commands driver may send before - * finishing init flow - * @IWL_INIT_DEBUG_CFG: driver is going to send debug config command - * @IWL_INIT_NVM: driver is going to send NVM_ACCESS commands - * @IWL_INIT_PHY: driver is going to send PHY_DB commands - */ -enum iwl_extended_cfg_flags { - IWL_INIT_DEBUG_CFG, - IWL_INIT_NVM, - IWL_INIT_PHY, -}; - -/** - * struct iwl_extended_cfg_cmd - mark what commands ucode should wait for - * before finishing init flows - * @init_flags: values from iwl_extended_cfg_flags - */ -struct iwl_init_extended_cfg_cmd { - __le32 init_flags; -} __packed; /* INIT_EXTENDED_CFG_CMD_API_S_VER_1 */ - -/* - * struct iwl_nvm_get_info - request to get NVM data - */ -struct iwl_nvm_get_info { - __le32 reserved; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_S_VER_1 */ - -/** - * struct iwl_nvm_get_info_general - general NVM data - * @flags: 1 - empty, 0 - valid - * @nvm_version: nvm version - * @board_type: board type - * @reserved: reserved - */ -struct iwl_nvm_get_info_general { - __le32 flags; - __le16 nvm_version; - u8 board_type; - u8 reserved; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_GENERAL_S_VER_1 */ - -/** - * struct iwl_nvm_get_info_sku - mac information - * @enable_24g: band 2.4G enabled - * @enable_5g: band 5G enabled - * @enable_11n: 11n enabled - * @enable_11ac: 11ac enabled - * @mimo_disable: MIMO enabled - * @ext_crypto: Extended crypto enabled - */ -struct iwl_nvm_get_info_sku { - __le32 enable_24g; - __le32 enable_5g; - __le32 enable_11n; - __le32 enable_11ac; - __le32 mimo_disable; - __le32 ext_crypto; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_MAC_SKU_SECTION_S_VER_1 */ - -/** - * struct iwl_nvm_get_info_phy - phy information - * @tx_chains: BIT 0 chain A, BIT 1 chain B - * @rx_chains: BIT 0 chain A, BIT 1 chain B - */ -struct iwl_nvm_get_info_phy { - __le32 tx_chains; - __le32 rx_chains; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_PHY_SKU_SECTION_S_VER_1 */ - -#define IWL_NUM_CHANNELS (51) - -/** - * struct iwl_nvm_get_info_regulatory - regulatory information - * @lar_enabled: is LAR enabled - * @channel_profile: regulatory data of this channel - * @reserved: reserved - */ -struct iwl_nvm_get_info_regulatory { - __le32 lar_enabled; - __le16 channel_profile[IWL_NUM_CHANNELS]; - __le16 reserved; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_1 */ - -/** - * struct iwl_nvm_get_info_rsp - response to get NVM data - * @general: general NVM data - * @mac_sku: data relating to MAC sku - * @phy_sku: data relating to PHY sku - * @regulatory: regulatory data - */ -struct iwl_nvm_get_info_rsp { - struct iwl_nvm_get_info_general general; - struct iwl_nvm_get_info_sku mac_sku; - struct iwl_nvm_get_info_phy phy_sku; - struct iwl_nvm_get_info_regulatory regulatory; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_RSP_S_VER_1 */ - -/** - * struct iwl_mvm_antenna_coupling_notif - antenna coupling notification - * @isolation: antenna isolation value - */ -struct iwl_mvm_antenna_coupling_notif { - __le32 isolation; -} __packed; +#include "fw/api/tdls.h" +#include "fw/api/mac-cfg.h" +#include "fw/api/offload.h" +#include "fw/api/context.h" +#include "fw/api/time-event.h" +#include "fw/api/datapath.h" +#include "fw/api/phy.h" +#include "fw/api/config.h" +#include "fw/api/alive.h" +#include "fw/api/binding.h" +#include "fw/api/cmdhdr.h" +#include "fw/api/coex.h" +#include "fw/api/commands.h" +#include "fw/api/d3.h" +#include "fw/api/filter.h" +#include "fw/api/mac.h" +#include "fw/api/nvm-reg.h" +#include "fw/api/phy-ctxt.h" +#include "fw/api/power.h" +#include "fw/api/rs.h" +#include "fw/api/rx.h" +#include "fw/api/scan.h" +#include "fw/api/sf.h" +#include "fw/api/sta.h" +#include "fw/api/stats.h" +#include "fw/api/tof.h" +#include "fw/api/tx.h" #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 3c4d82045c1c..6b385cf10555 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -82,10 +82,10 @@ #include "iwl-io.h" #include "iwl-prph.h" #include "rs.h" -#include "fw-api-scan.h" +#include "fw/api/scan.h" #include "time-event.h" #include "fw-api.h" -#include "fw-api-scan.h" +#include "fw/api/scan.h" #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" MODULE_DESCRIPTION(DRV_DESCRIPTION); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index fb9eaf003ea5..7ee8e9077baf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -251,7 +251,7 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { - enum iwl_phy_ctxt_action action = FW_CTXT_ACTION_MODIFY; + enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY; lockdep_assert_held(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index c05e5ac565ae..c11fe2621d51 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -75,7 +75,7 @@ #include "iwl-debug.h" #include "mvm.h" #include "iwl-modparams.h" -#include "fw-api-power.h" +#include "fw/api/power.h" #define POWER_KEEP_ALIVE_PERIOD_SEC 25 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 35e813bdfbe5..cb44e4114655 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -69,7 +69,7 @@ #include #include "mvm.h" -#include "fw-api-scan.h" +#include "fw/api/scan.h" #include "iwl-io.h" #define IWL_DENSE_EBS_SCAN_RATIO 5 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c index 634175b2480c..2d0b8a391308 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c @@ -61,7 +61,7 @@ * *****************************************************************************/ #include "mvm.h" -#include "fw-api-tof.h" +#include "fw/api/tof.h" #define IWL_MVM_TOF_RANGE_REQ_MAX_ID 256 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h index 8c3421c9991d..2ff560aa1a82 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tof.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h @@ -63,7 +63,7 @@ #ifndef __tof_h__ #define __tof_h__ -#include "fw-api-tof.h" +#include "fw/api/tof.h" struct iwl_mvm_tof_data { struct iwl_tof_config_cmd tof_cfg; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 3ccd16f26b91..2ea74abad73d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -71,7 +71,7 @@ #include "iwl-prph.h" #include "iwl-csr.h" #include "mvm.h" -#include "fw-api-rs.h" +#include "fw/api/rs.h" /* * Will return 0 even if the cmd failed when RFKILL is asserted unless diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index a3795ba0d7b9..5e85f29220f6 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -55,7 +55,7 @@ #include "iwl-csr.h" #include "iwl-io.h" #include "internal.h" -#include "mvm/fw-api.h" +#include "fw/api/tx.h" /* * iwl_pcie_gen2_tx_stop - Stop all Tx DMA channels diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index de50418adae5..073793a53d8e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -43,8 +43,7 @@ #include "iwl-scd.h" #include "iwl-op-mode.h" #include "internal.h" -/* FIXME: need to abstract out TX command (once we know what it looks like) */ -#include "dvm/commands.h" +#include "fw/api/tx.h" #define IWL_TX_CRC_SIZE 4 #define IWL_TX_DELIMITER_SIZE 4 @@ -2367,7 +2366,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, tb1_len = ALIGN(len, 4); /* Tell NIC about any 2-byte padding after MAC header */ if (tb1_len != len) - tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; + tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_MH_PAD); } else { tb1_len = len; } -- cgit v1.2.3 From b37ca87f93e475eb41aa6dfd3675e5bffdf67c0a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Jun 2017 12:19:22 +0200 Subject: iwlwifi: fw api: fix various kernel-doc warnings Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h | 22 +++++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/fw/api/txq.h | 3 ++- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h b/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h index fd97cccaedb8..ea4a3f04a83a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h @@ -112,15 +112,24 @@ static inline u32 iwl_cmd_id(u8 opcode, u8 groupid, u8 version) #define IWL_ALWAYS_LONG_GROUP 1 /** - * struct iwl_cmd_header + * struct iwl_cmd_header - (short) command header format * * This header format appears in the beginning of each command sent from the * driver, and each response/notification received from uCode. */ struct iwl_cmd_header { - u8 cmd; /* Command ID: REPLY_RXON, etc. */ + /** + * @cmd: Command ID: REPLY_RXON, etc. + */ + u8 cmd; + /** + * @group_id: group ID, for commands with groups + */ u8 group_id; - /* + /** + * @sequence: + * Sequence number for the command. + * * The driver sets up the sequence number to values of its choosing. * uCode does not use this value, but passes it back to the driver * when sending the response to each driver-originated command, so @@ -150,6 +159,13 @@ struct iwl_cmd_header { * driver, and each response/notification received from uCode. * this is the wide version that contains more information about the command * like length, version and command type + * + * @cmd: command ID, like in &struct iwl_cmd_header + * @group_id: group ID, like in &struct iwl_cmd_header + * @sequence: sequence, like in &struct iwl_cmd_header + * @length: length of the command + * @reserved: reserved + * @version: command version */ struct iwl_cmd_header_wide { u8 cmd; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h index 7e7ebe82729b..805e161641aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h @@ -112,7 +112,7 @@ enum iwl_mvm_tx_fifo { }; /** - * iwl_tx_queue_cfg_actions - TXQ config options + * enum iwl_tx_queue_cfg_actions - TXQ config options * @TX_QUEUE_CFG_ENABLE_QUEUE: enable a queue * @TX_QUEUE_CFG_TFD_SHORT_FORMAT: use short TFD format */ @@ -145,6 +145,7 @@ struct iwl_tx_queue_cfg_cmd { * @queue_number: queue number assigned to this RA -TID * @flags: set on failure * @write_pointer: initial value for write pointer + * @reserved: reserved */ struct iwl_tx_queue_cfg_rsp { __le16 queue_number; -- cgit v1.2.3 From 7d6222e27013a18f36e54f40a66e209a859fc1a8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Jun 2017 09:18:22 +0200 Subject: iwlwifi: mvm: add and use iwl_mvm_has_unified_ucode() This may need to be refined later, but for now using this, even with the TODO, is better than checking "has new TX API". Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 8 ++++---- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 6 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index e6f6de2500db..58f846610e5d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -457,7 +457,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) }; int ret; - if (iwl_mvm_has_new_tx_api(mvm)) + if (iwl_mvm_has_unified_ucode(mvm)) return iwl_run_unified_mvm_ucode(mvm, true); lockdep_assert_held(&mvm->mutex); @@ -1034,7 +1034,7 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) { int ret; - if (iwl_mvm_has_new_tx_api(mvm)) + if (iwl_mvm_has_unified_ucode(mvm)) return iwl_run_unified_mvm_ucode(mvm, false); ret = iwl_run_init_mvm_ucode(mvm, false); @@ -1101,8 +1101,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) goto error; - /* Send phy db control command and then phy db calibration*/ - if (!iwl_mvm_has_new_tx_api(mvm)) { + if (!iwl_mvm_has_unified_ucode(mvm)) { + /* Send phy db control command and then phy db calibration */ ret = iwl_send_phy_db_data(mvm->phy_db); if (ret) goto error; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 6fa2c44e6edd..e9440ef3022f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1232,6 +1232,12 @@ static inline bool iwl_mvm_has_new_tx_api(struct iwl_mvm *mvm) return mvm->trans->cfg->use_tfh; } +static inline bool iwl_mvm_has_unified_ucode(struct iwl_mvm *mvm) +{ + /* TODO - better define this */ + return mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_A000; +} + static inline bool iwl_mvm_is_cdb_supported(struct iwl_mvm *mvm) { /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 6b385cf10555..4d591fdc61f2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -645,7 +645,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->last_agg_queue = IWL_MVM_DQA_MAX_DATA_QUEUE; } mvm->sf_state = SF_UNINIT; - if (iwl_mvm_has_new_tx_api(mvm)) + if (iwl_mvm_has_unified_ucode(mvm)) iwl_fw_set_current_image(&mvm->fwrt, IWL_UCODE_REGULAR); else iwl_fw_set_current_image(&mvm->fwrt, IWL_UCODE_INIT); -- cgit v1.2.3 From c8c017a6ff933ff88f3abf7d924e717284e218af Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Jun 2017 09:42:28 +0200 Subject: iwlwifi: mvm: check family instead of new TX API for workarounds There are two workarounds because RSS is currently broken on A000 devices due to firmware issues, but checking for the new TX API doesn't really make sense. Check the hardware family instead of the new TX API - there's nothing better to check since it's just a temporary workaround. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 3 ++- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 58f846610e5d..e04bf2f7c1ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1118,7 +1118,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm) /* Init RSS configuration */ /* TODO - remove a000 disablement when we have RXQ config API */ - if (iwl_mvm_has_new_rx_api(mvm) && !iwl_mvm_has_new_tx_api(mvm)) { + if (iwl_mvm_has_new_rx_api(mvm) && + mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_A000) { ret = iwl_send_rss_cfg_cmd(mvm); if (ret) { IWL_ERR(mvm, "Failed to configure RSS queues: %d\n", diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index ab6c6bf79565..44f144c58720 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -4280,7 +4280,8 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); /* TODO - remove a000 disablement when we have RXQ config API */ - if (!iwl_mvm_has_new_rx_api(mvm) || iwl_mvm_has_new_tx_api(mvm)) + if (!iwl_mvm_has_new_rx_api(mvm) || + mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_A000) return; notif->cookie = mvm->queue_sync_cookie; -- cgit v1.2.3 From c67a3d05024fad00ac160e868967a1e711073993 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 7 Jun 2017 09:31:13 +0200 Subject: iwlwifi: mvm: byte-swap constant instead of variable Convention has it to byte-swap the constant instead of the variable when doing bit checks. This also generates better code when the swap is actually needed, since the constant can be swapped at compile-time. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 13733252c1fb..6b8e57b7234a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -851,7 +851,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, rcu_read_lock(); - if (le16_to_cpu(desc->status) & IWL_RX_MPDU_STATUS_SRC_STA_FOUND) { + if (desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_SRC_STA_FOUND)) { u8 id = desc->sta_id_flags & IWL_RX_MPDU_SIF_STA_ID_MASK; if (!WARN_ON_ONCE(id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))) { -- cgit v1.2.3 From 57bcc81d52a264847944f6e2b812588fbab09e5d Mon Sep 17 00:00:00 2001 From: Seraphime Kirkovski Date: Wed, 7 Jun 2017 01:20:27 +0200 Subject: iwlwifi: dvm: remove unused defines Those constants have been unused for quite some time now. Signed-off-by: Seraphime Kirkovski Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/dvm/commands.h | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h index 2ab2773655a8..ede47e3c5971 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h @@ -1437,22 +1437,6 @@ struct agg_tx_status { __le16 sequence; } __packed; -/* - * definitions for initial rate index field - * bits [3:0] initial rate index - * bits [6:4] rate table color, used for the initial rate - * bit-7 invalid rate indication - * i.e. rate was not chosen from rate table - * or rate table color was changed during frame retries - * refer tlc rate info - */ - -#define IWL50_TX_RES_INIT_RATE_INDEX_POS 0 -#define IWL50_TX_RES_INIT_RATE_INDEX_MSK 0x0f -#define IWL50_TX_RES_RATE_TABLE_COLOR_POS 4 -#define IWL50_TX_RES_RATE_TABLE_COLOR_MSK 0x70 -#define IWL50_TX_RES_INV_RATE_INDEX_MSK 0x80 - /* refer to ra_tid */ #define IWLAGN_TX_RES_TID_POS 0 #define IWLAGN_TX_RES_TID_MSK 0x0f -- cgit v1.2.3 From 09856582bffba3b4a3f6286d53be16c2a569c3ef Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Thu, 1 Jun 2017 15:19:45 +0300 Subject: iwlwifi: mvm: refactor beacon template command code We currently support 3 different versions of the beacon template command and the code does some tricks in order to reuse what is possible across these versions. But it is a bit complicated to read and soon there will be one more variation that the driver needs implement, which would complicate it even further. Refactor the way we send beacon template commands, which increases the code size a bit, but makes it much easier to read. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/tx.h | 31 ++-- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 202 +++++++++++++--------- 2 files changed, 138 insertions(+), 95 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index d20baedead98..95dbed609f3e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -766,7 +766,8 @@ struct iwl_mac_beacon_cmd_v6 { } __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_6 */ /** - * struct iwl_mac_beacon_cmd_data - data of beacon template with offloaded CSA + * struct iwl_mac_beacon_cmd_v7 - beacon template command with offloaded CSA + * @tx: the tx commands associated with the beacon frame * @template_id: currently equal to the mac context id of the coresponding * mac. * @tim_idx: the offset of the tim IE in the beacon @@ -775,23 +776,14 @@ struct iwl_mac_beacon_cmd_v6 { * @csa_offset: offset to the CSA IE if present * @frame: the template of the beacon frame */ -struct iwl_mac_beacon_cmd_data { +struct iwl_mac_beacon_cmd_v7 { + struct iwl_tx_cmd tx; __le32 template_id; __le32 tim_idx; __le32 tim_size; __le32 ecsa_offset; __le32 csa_offset; struct ieee80211_hdr frame[0]; -}; - -/** - * struct iwl_mac_beacon_cmd_v7 - beacon template command with offloaded CSA - * @tx: the tx commands associated with the beacon frame - * @data: see &iwl_mac_beacon_cmd_data - */ -struct iwl_mac_beacon_cmd_v7 { - struct iwl_tx_cmd tx; - struct iwl_mac_beacon_cmd_data data; } __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_7 */ /** @@ -799,13 +791,24 @@ struct iwl_mac_beacon_cmd_v7 { * @byte_cnt: byte count of the beacon frame * @flags: for future use * @reserved: reserved - * @data: see &iwl_mac_beacon_cmd_data + * @template_id: currently equal to the mac context id of the coresponding + * mac. + * @tim_idx: the offset of the tim IE in the beacon + * @tim_size: the length of the tim IE + * @ecsa_offset: offset to the ECSA IE if present + * @csa_offset: offset to the CSA IE if present + * @frame: the template of the beacon frame */ struct iwl_mac_beacon_cmd { __le16 byte_cnt; __le16 flags; __le64 reserved; - struct iwl_mac_beacon_cmd_data data; + __le32 template_id; + __le32 tim_idx; + __le32 tim_size; + __le32 ecsa_offset; + __le32 csa_offset; + struct ieee80211_hdr frame[0]; } __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_8 */ struct iwl_beacon_notif { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index d130bdd76368..1546d54e2ebd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1048,83 +1048,26 @@ static u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) return ie - beacon; } -static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct sk_buff *beacon) +static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct sk_buff *beacon, + struct iwl_tx_cmd *tx) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_host_cmd cmd = { - .id = BEACON_TEMPLATE_CMD, - .flags = CMD_ASYNC, - }; - union { - struct iwl_mac_beacon_cmd_v6 beacon_cmd_v6; - struct iwl_mac_beacon_cmd_v7 beacon_cmd; - } u = {}; - struct iwl_mac_beacon_cmd beacon_cmd = {}; struct ieee80211_tx_info *info; - u32 beacon_skb_len; u32 rate, tx_flags; - if (WARN_ON(!beacon)) - return -EINVAL; - - beacon_skb_len = beacon->len; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD)) { - u32 csa_offset, ecsa_offset; - - csa_offset = iwl_mvm_find_ie_offset(beacon->data, - WLAN_EID_CHANNEL_SWITCH, - beacon_skb_len); - ecsa_offset = - iwl_mvm_find_ie_offset(beacon->data, - WLAN_EID_EXT_CHANSWITCH_ANN, - beacon_skb_len); - - if (iwl_mvm_has_new_tx_api(mvm)) { - beacon_cmd.data.template_id = - cpu_to_le32((u32)mvmvif->id); - beacon_cmd.data.ecsa_offset = cpu_to_le32(ecsa_offset); - beacon_cmd.data.csa_offset = cpu_to_le32(csa_offset); - beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon_skb_len); - if (vif->type == NL80211_IFTYPE_AP) - iwl_mvm_mac_ctxt_set_tim(mvm, - &beacon_cmd.data.tim_idx, - &beacon_cmd.data.tim_size, - beacon->data, - beacon_skb_len); - cmd.len[0] = sizeof(beacon_cmd); - cmd.data[0] = &beacon_cmd; - goto send; - - } else { - u.beacon_cmd.data.ecsa_offset = - cpu_to_le32(ecsa_offset); - u.beacon_cmd.data.csa_offset = cpu_to_le32(csa_offset); - cmd.len[0] = sizeof(u.beacon_cmd); - cmd.data[0] = &u; - } - } else { - cmd.len[0] = sizeof(u.beacon_cmd_v6); - cmd.data[0] = &u; - } - - /* TODO: for now the beacon template id is set to be the mac context id. - * Might be better to handle it as another resource ... */ - u.beacon_cmd_v6.template_id = cpu_to_le32((u32)mvmvif->id); info = IEEE80211_SKB_CB(beacon); /* Set up TX command fields */ - u.beacon_cmd_v6.tx.len = cpu_to_le16((u16)beacon_skb_len); - u.beacon_cmd_v6.tx.sta_id = mvmvif->bcast_sta.sta_id; - u.beacon_cmd_v6.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); + tx->len = cpu_to_le16((u16)beacon->len); + tx->sta_id = mvmvif->bcast_sta.sta_id; + tx->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF; tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) << TX_CMD_FLG_BT_PRIO_POS; - u.beacon_cmd_v6.tx.tx_flags = cpu_to_le32(tx_flags); + tx->tx_flags = cpu_to_le32(tx_flags); if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) { @@ -1133,7 +1076,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, mvm->mgmt_last_antenna_idx); } - u.beacon_cmd_v6.tx.rate_n_flags = + tx->rate_n_flags = cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS); @@ -1141,29 +1084,126 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, rate = IWL_FIRST_OFDM_RATE; } else { rate = IWL_FIRST_CCK_RATE; - u.beacon_cmd_v6.tx.rate_n_flags |= - cpu_to_le32(RATE_MCS_CCK_MSK); + tx->rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK); } - u.beacon_cmd_v6.tx.rate_n_flags |= - cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); - /* Set up TX beacon command fields */ - if (vif->type == NL80211_IFTYPE_AP) - iwl_mvm_mac_ctxt_set_tim(mvm, &u.beacon_cmd_v6.tim_idx, - &u.beacon_cmd_v6.tim_size, - beacon->data, - beacon_skb_len); + tx->rate_n_flags |= cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); +} + +static int iwl_mvm_mac_ctxt_send_beacon_cmd(struct iwl_mvm *mvm, + struct sk_buff *beacon, + void *data, int len) +{ + struct iwl_host_cmd cmd = { + .id = BEACON_TEMPLATE_CMD, + .flags = CMD_ASYNC, + }; -send: - /* Submit command */ + cmd.len[0] = len; + cmd.data[0] = data; cmd.dataflags[0] = 0; - cmd.len[1] = beacon_skb_len; + cmd.len[1] = beacon->len; cmd.data[1] = beacon->data; cmd.dataflags[1] = IWL_HCMD_DFL_DUP; return iwl_mvm_send_cmd(mvm, &cmd); } +static int iwl_mvm_mac_ctxt_send_beacon_v6(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct sk_buff *beacon) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_beacon_cmd_v6 beacon_cmd = {}; + + iwl_mvm_mac_ctxt_set_tx(mvm, vif, beacon, &beacon_cmd.tx); + + beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); + + if (vif->type == NL80211_IFTYPE_AP) + iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd.tim_idx, + &beacon_cmd.tim_size, + beacon->data, beacon->len); + + return iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd, + sizeof(beacon_cmd)); +} + +static int iwl_mvm_mac_ctxt_send_beacon_v7(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct sk_buff *beacon) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_beacon_cmd_v7 beacon_cmd = {}; + + iwl_mvm_mac_ctxt_set_tx(mvm, vif, beacon, &beacon_cmd.tx); + + beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); + + if (vif->type == NL80211_IFTYPE_AP) + iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd.tim_idx, + &beacon_cmd.tim_size, + beacon->data, beacon->len); + + beacon_cmd.csa_offset = + cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, + WLAN_EID_CHANNEL_SWITCH, + beacon->len)); + beacon_cmd.ecsa_offset = + cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, + WLAN_EID_EXT_CHANSWITCH_ANN, + beacon->len)); + + return iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd, + sizeof(beacon_cmd)); +} + +static int iwl_mvm_mac_ctxt_send_beacon_v8(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct sk_buff *beacon) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_beacon_cmd beacon_cmd = {}; + + beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len); + beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); + + if (vif->type == NL80211_IFTYPE_AP) + iwl_mvm_mac_ctxt_set_tim(mvm, + &beacon_cmd.tim_idx, + &beacon_cmd.tim_size, + beacon->data, beacon->len); + + beacon_cmd.csa_offset = + cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, + WLAN_EID_CHANNEL_SWITCH, + beacon->len)); + beacon_cmd.ecsa_offset = + cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, + WLAN_EID_EXT_CHANSWITCH_ANN, + beacon->len)); + + return iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd, + sizeof(beacon_cmd)); +} + +static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct sk_buff *beacon) +{ + if (WARN_ON(!beacon)) + return -EINVAL; + + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD)) + return iwl_mvm_mac_ctxt_send_beacon_v6(mvm, vif, beacon); + + if (!iwl_mvm_has_new_tx_api(mvm)) + return iwl_mvm_mac_ctxt_send_beacon_v7(mvm, vif, beacon); + + return iwl_mvm_mac_ctxt_send_beacon_v8(mvm, vif, beacon); +} + /* The beacon template for the AP/GO/IBSS has changed and needs update */ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -- cgit v1.2.3 From cf6c6ea352faadb15d1373d890bf857080b218a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 13 Jun 2017 13:18:48 +0300 Subject: iwlwifi: mvm: fix the FIFO numbers in A000 devices The FIFO numbering is different in A000 devices. This means that we routed BE packets to BK FIFO. Fix this. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/txq.h | 12 ++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 9 ++++++++- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 8 ++++++++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 2 +- 5 files changed, 32 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h index 805e161641aa..87c1e36a9c5a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h @@ -111,6 +111,18 @@ enum iwl_mvm_tx_fifo { IWL_MVM_TX_FIFO_CMD = 7, }; +enum iwl_gen2_tx_fifo { + IWL_GEN2_TX_FIFO_CMD = 0, + IWL_GEN2_EDCA_TX_FIFO_BK, + IWL_GEN2_EDCA_TX_FIFO_BE, + IWL_GEN2_EDCA_TX_FIFO_VI, + IWL_GEN2_EDCA_TX_FIFO_VO, + IWL_GEN2_TRIG_TX_FIFO_BK, + IWL_GEN2_TRIG_TX_FIFO_BE, + IWL_GEN2_TRIG_TX_FIFO_VI, + IWL_GEN2_TRIG_TX_FIFO_VO, +}; + /** * enum iwl_tx_queue_cfg_actions - TXQ config options * @TX_QUEUE_CFG_ENABLE_QUEUE: enable a queue diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 1546d54e2ebd..604db47c8c21 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -80,6 +80,13 @@ const u8 iwl_mvm_ac_to_tx_fifo[] = { IWL_MVM_TX_FIFO_BK, }; +const u8 iwl_mvm_ac_to_gen2_tx_fifo[] = { + IWL_GEN2_EDCA_TX_FIFO_VO, + IWL_GEN2_EDCA_TX_FIFO_VI, + IWL_GEN2_EDCA_TX_FIFO_BE, + IWL_GEN2_EDCA_TX_FIFO_BK, +}; + struct iwl_mvm_mac_iface_iterator_data { struct iwl_mvm *mvm; struct ieee80211_vif *vif; @@ -774,7 +781,7 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u8 txf = iwl_mvm_ac_to_tx_fifo[i]; + u8 txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, i); cmd->ac[txf].cw_min = cpu_to_le16(mvmvif->queue_params[i].cw_min); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index e9440ef3022f..74948f875a3f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1291,6 +1291,14 @@ static inline bool iwl_mvm_is_ctdp_supported(struct iwl_mvm *mvm) } extern const u8 iwl_mvm_ac_to_tx_fifo[]; +extern const u8 iwl_mvm_ac_to_gen2_tx_fifo[]; + +static inline u8 iwl_mvm_mac_ac_to_tx_fifo(struct iwl_mvm *mvm, + enum ieee80211_ac_numbers ac) +{ + return iwl_mvm_has_new_tx_api(mvm) ? + iwl_mvm_ac_to_gen2_tx_fifo[ac] : iwl_mvm_ac_to_tx_fifo[ac]; +} struct iwl_rate_info { u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 4df5f13fcdae..4adc8fecfaef 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -315,7 +315,7 @@ static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac], mvmsta->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac], 0, + iwl_mvm_mac_ac_to_tx_fifo(mvm, ac), 0, wdg_timeout); mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]); } @@ -745,7 +745,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_trans_txq_scd_cfg cfg = { - .fifo = iwl_mvm_ac_to_tx_fifo[ac], + .fifo = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac), .sta_id = mvmsta->sta_id, .tid = tid, .frame_limit = IWL_FRAME_LIMIT, @@ -1303,7 +1303,7 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, u16 seq = IEEE80211_SEQ_TO_SN(tid_data->seq_number); cfg.tid = i; - cfg.fifo = iwl_mvm_ac_to_tx_fifo[ac]; + cfg.fifo = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac); cfg.aggregate = (txq_id >= IWL_MVM_DQA_MIN_DATA_QUEUE || txq_id == IWL_MVM_DQA_BSS_CLIENT_QUEUE); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index f263a1902e27..cccff1073e3b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -751,7 +751,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, max_amsdu_len = sta->max_amsdu_len; /* the Tx FIFO to which this A-MSDU will be routed */ - txf = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; + txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, tid_to_mac80211_ac[tid]); /* * Don't send an AMSDU that will be longer than the TXF. -- cgit v1.2.3 From bec9522aa57421984cb4e1e87eee195bae1ccb25 Mon Sep 17 00:00:00 2001 From: Sharon Dvir Date: Mon, 12 Jun 2017 11:40:33 +0300 Subject: iwlwifi: mvm: fix uninitialized var while waiting for queues to empty While waiting for queues to empty, If txq_id == IWL_MVM_INVALID_QUEUE for all txq_ids, ret is used uninitialized. Found by Klocwork. Fixes: d6d517b7730c ("iwlwifi: add wait for tx queue empty") Signed-off-by: Sharon Dvir Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 4adc8fecfaef..b196acd18252 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1619,10 +1619,11 @@ static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm, int iwl_mvm_wait_sta_queues_empty(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvm_sta) { - int i, ret; + int i; for (i = 0; i < ARRAY_SIZE(mvm_sta->tid_data); i++) { u16 txq_id; + int ret; spin_lock_bh(&mvm_sta->lock); txq_id = mvm_sta->tid_data[i].txq_id; @@ -1633,10 +1634,10 @@ int iwl_mvm_wait_sta_queues_empty(struct iwl_mvm *mvm, ret = iwl_trans_wait_txq_empty(mvm->trans, txq_id); if (ret) - break; + return ret; } - return ret; + return 0; } int iwl_mvm_rm_sta(struct iwl_mvm *mvm, -- cgit v1.2.3 From c1b68c194ed72a5df55e7344b19c50ccb357f690 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 14 Jun 2017 15:25:39 +0300 Subject: iwlwifi: pcie: fix A-MSDU on gen2 devices The return status check of iwl_pcie_gen2_build_amsdu was buggy. Fix it. Fixes: 6ffe5de35b05 ("iwlwifi: pcie: add AMSDU to gen2") Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 5e85f29220f6..5dc785d4c167 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -422,9 +422,9 @@ struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, hdr_len = ieee80211_hdrlen(hdr->frame_control); if (amsdu) { - if (!iwl_pcie_gen2_build_amsdu(trans, skb, tfd, - tb1_len + IWL_FIRST_TB_SIZE, - hdr_len, dev_cmd)) + if (iwl_pcie_gen2_build_amsdu(trans, skb, tfd, + tb1_len + IWL_FIRST_TB_SIZE, + hdr_len, dev_cmd)) goto out_err; /* -- cgit v1.2.3 From f6aa45f67aaa03bd294040508218bc72c5958ad6 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Mon, 5 Jun 2017 16:39:39 +0300 Subject: iwlwifi: mvm: support fw reading empty OTP If the OTP is empty, the NVM_GET_INFO command returns with flags' bit(0) on. This means the FW returns the default values for working with. This is allowed, so use this returned data. Fixes: e9e1ba3dbf00 ("iwlwifi: mvm: support getting nvm data from firmware") Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h | 10 +++++++++- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 7 ++----- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index d4c01f3dce32..00bc7a25dece 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -163,9 +163,17 @@ struct iwl_nvm_get_info { __le32 reserved; } __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_S_VER_1 */ +/** + * enum iwl_nvm_info_general_flags - flags in NVM_GET_INFO resp + * @NVM_GENERAL_FLAGS_EMPTY_OTP: 1 if OTP is empty + */ +enum iwl_nvm_info_general_flags { + NVM_GENERAL_FLAGS_EMPTY_OTP = BIT(0), +}; + /** * struct iwl_nvm_get_info_general - general NVM data - * @flags: 1 - empty, 0 - valid + * @flags: bit 0: 1 - empty, 0 - non-empty * @nvm_version: nvm version * @board_type: board type * @reserved: reserved diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index dac7e542a190..5cc749261ce3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -576,11 +576,8 @@ int iwl_mvm_nvm_get_from_fw(struct iwl_mvm *mvm) } rsp = (void *)hcmd.resp_pkt->data; - if (le32_to_cpu(rsp->general.flags)) { - IWL_ERR(mvm, "Invalid NVM data from FW\n"); - ret = -EINVAL; - goto out; - } + if (le32_to_cpu(rsp->general.flags) & NVM_GENERAL_FLAGS_EMPTY_OTP) + IWL_INFO(mvm, "OTP is empty\n"); mvm->nvm_data = kzalloc(sizeof(*mvm->nvm_data) + sizeof(struct ieee80211_channel) * -- cgit v1.2.3 From f2e66c8df0d0f10c70ed7f5e14a939714e9ee6a9 Mon Sep 17 00:00:00 2001 From: Mordechai Goodstein Date: Tue, 13 Jun 2017 17:17:27 +0300 Subject: iwlwifi: implement fseq version mismatch warning During init, the FW checks whether the FSEQ value matches what it expects. If it doesn't match, we print a warning to let integrators clearly know that something is wrong. This can happen if another core (i.e. not WiFi) has updated the FSEQ version. This notification is only sent by the FW in production, for development firmwares, an assertion is triggered instead. Signed-off-by: Luca Coelho Signed-off-by: Mordechai Goodstein --- drivers/net/wireless/intel/iwlwifi/Makefile | 1 + drivers/net/wireless/intel/iwlwifi/fw/api/alive.h | 16 ++++ .../net/wireless/intel/iwlwifi/fw/api/commands.h | 7 ++ drivers/net/wireless/intel/iwlwifi/fw/common_rx.c | 88 ++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 3 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 +- 6 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/common_rx.c (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index fd12b7394c5c..5dcb4a848dba 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -12,6 +12,7 @@ iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/a000.o iwlwifi-objs += iwl-trans.o iwlwifi-objs += fw/notif-wait.o iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o fw/dbg.o +iwlwifi-$(CONFIG_IWLMVM) += fw/common_rx.o iwlwifi-objs += $(iwlwifi-m) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h index 6af6a9b32b69..3684a3e180e5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h @@ -187,4 +187,20 @@ struct iwl_card_state_notif { __le32 flags; } __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ +/** + * struct iwl_fseq_ver_mismatch_nty - Notification about version + * + * This notification does not have a direct impact on the init flow. + * It means that another core (not WiFi) has initiated the FSEQ flow + * and updated the FSEQ version. The driver only prints an error when + * this occurs. + * + * @aux_read_fseq_ver: auxiliary read FSEQ version + * @wifi_fseq_ver: FSEQ version (embedded in WiFi) + */ +struct iwl_fseq_ver_mismatch_ntf { + __le32 aux_read_fseq_ver; + __le32 wifi_fseq_ver; +} __packed; /* FSEQ_VER_MISMATCH_NTFY_API_S_VER_1 */ + #endif /* __iwl_fw_api_alive_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 34fceb26447d..c7b8cffdf281 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -652,6 +652,13 @@ enum iwl_system_subcmd_ids { * @INIT_EXTENDED_CFG_CMD: &struct iwl_init_extended_cfg_cmd */ INIT_EXTENDED_CFG_CMD = 0x03, + + /** + * @FSEQ_VER_MISMATCH_NTF: Notification about fseq version + * mismatch during init. The format is specified in + * &struct iwl_fseq_ver_mismatch_ntf. + */ + FSEQ_VER_MISMATCH_NTF = 0xFF, }; #endif /* __iwl_fw_api_commands_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/common_rx.c b/drivers/net/wireless/intel/iwlwifi/fw/common_rx.c new file mode 100644 index 000000000000..6f75985eea66 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/common_rx.c @@ -0,0 +1,88 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include "iwl-drv.h" +#include "runtime.h" +#include "fw/api/commands.h" +#include "fw/api/alive.h" + +static void iwl_fwrt_fseq_ver_mismatch(struct iwl_fw_runtime *fwrt, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_fseq_ver_mismatch_ntf *fseq = (void *)pkt->data; + + IWL_ERR(fwrt, "FSEQ version mismatch (aux: %d, wifi: %d)\n", + __le32_to_cpu(fseq->aux_read_fseq_ver), + __le32_to_cpu(fseq->wifi_fseq_ver)); +} + +void iwl_fwrt_handle_notification(struct iwl_fw_runtime *fwrt, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); + + switch (cmd) { + case WIDE_ID(SYSTEM_GROUP, FSEQ_VER_MISMATCH_NTF): + iwl_fwrt_fseq_ver_mismatch(fwrt, rxb); + break; + default: + break; + } +} +IWL_EXPORT_SYMBOL(iwl_fwrt_handle_notification); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 75575290a3e4..66bea6545690 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -150,4 +150,7 @@ void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt); void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt); +void iwl_fwrt_handle_notification(struct iwl_fw_runtime *fwrt, + struct iwl_rx_cmd_buffer *rxb); + #endif /* __iwl_fw_runtime_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 4d591fdc61f2..d0ffdf5fa3a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1006,8 +1006,10 @@ static void iwl_mvm_rx_common(struct iwl_mvm *mvm, list_add_tail(&entry->list, &mvm->async_handlers_list); spin_unlock(&mvm->async_handlers_lock); schedule_work(&mvm->async_handlers_wk); - break; + return; } + + iwl_fwrt_handle_notification(&mvm->fwrt, rxb); } static void iwl_mvm_rx(struct iwl_op_mode *op_mode, -- cgit v1.2.3 From 6667e6589a02df8f27f8eebfabd8891c9d74152c Mon Sep 17 00:00:00 2001 From: "Zamir, Roee" Date: Wed, 14 Jun 2017 13:53:44 +0300 Subject: iwlwifi: mvm: add compile-time option to disable EBS For testing purposes, we may want to disable EBS scans at compile time. Signed-off-by: Roee Zamir Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/constants.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index 21845034d80d..a922a351c916 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -140,5 +140,6 @@ #define IWL_MVM_RS_TPC_SR_FORCE_INCREASE 75 /* percent */ #define IWL_MVM_RS_TPC_SR_NO_INCREASE 85 /* percent */ #define IWL_MVM_RS_TPC_TX_POWER_STEP 3 +#define IWL_MVM_ENABLE_EBS 1 #endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index cb44e4114655..50983615dce6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -743,7 +743,7 @@ static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm, * 4. it's not a p2p find operation. */ return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && - mvm->last_ebs_successful && + mvm->last_ebs_successful && IWL_MVM_ENABLE_EBS && vif->type != NL80211_IFTYPE_P2P_DEVICE); } -- cgit v1.2.3 From 9ad8fd0b4a24e64c34130bdfed95bfbf382c8e59 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 20 Jun 2017 15:10:31 +0200 Subject: iwlwifi: pcie: rename iwl_trans_check_hw_rf_kill() to pcie Rename this function to the more appropriate iwl_pcie_check_hw_rf_kill() since it's only a function in the pcie code and cannot be called from any other place. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index f16c1bb9bf94..87712aeac31f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -805,11 +805,11 @@ static int iwl_pci_resume(struct device *device) /* * Enable rfkill interrupt (in order to keep track of the rfkill * status). Must be locked to avoid processing a possible rfkill - * interrupt while in iwl_trans_check_hw_rf_kill(). + * interrupt while in iwl_pcie_check_hw_rf_kill(). */ mutex_lock(&trans_pcie->mutex); iwl_enable_rfkill_int(trans); - iwl_trans_check_hw_rf_kill(trans); + iwl_pcie_check_hw_rf_kill(trans); mutex_unlock(&trans_pcie->mutex); return 0; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index fa315d84e98e..a8ffd4ca8cd8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -791,7 +791,7 @@ void iwl_pcie_enable_rx_wake(struct iwl_trans *trans, bool enable); void iwl_pcie_apm_config(struct iwl_trans *trans); int iwl_pcie_prepare_card_hw(struct iwl_trans *trans); void iwl_pcie_synchronize_irqs(struct iwl_trans *trans); -bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans); +bool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans); void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans, bool was_in_rfkill); void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index b84b78293e7b..c59f4581e972 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -307,7 +307,7 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, mutex_lock(&trans_pcie->mutex); /* If platform's RF_KILL switch is NOT set to KILL */ - hw_rfkill = iwl_trans_check_hw_rf_kill(trans); + hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); if (hw_rfkill && !run_in_rfkill) { ret = -ERFKILL; goto out; @@ -340,7 +340,7 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, goto out; /* re-check RF-Kill state since we may have missed the interrupt */ - hw_rfkill = iwl_trans_check_hw_rf_kill(trans); + hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); if (hw_rfkill && !run_in_rfkill) ret = -ERFKILL; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 92b3a55d0fbc..3fac3f29a139 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -986,7 +986,7 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, &first_ucode_section); } -bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans) +bool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); bool hw_rfkill = iwl_is_rfkill_set(trans); @@ -1252,7 +1252,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, mutex_lock(&trans_pcie->mutex); /* If platform's RF_KILL switch is NOT set to KILL */ - hw_rfkill = iwl_trans_check_hw_rf_kill(trans); + hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); if (hw_rfkill && !run_in_rfkill) { ret = -ERFKILL; goto out; @@ -1300,7 +1300,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, ret = iwl_pcie_load_given_ucode(trans, fw); /* re-check RF-Kill state since we may have missed the interrupt */ - hw_rfkill = iwl_trans_check_hw_rf_kill(trans); + hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); if (hw_rfkill && !run_in_rfkill) ret = -ERFKILL; @@ -1663,7 +1663,7 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) trans_pcie->is_down = false; /* ...rfkill can call stop_device and set it false if needed */ - iwl_trans_check_hw_rf_kill(trans); + iwl_pcie_check_hw_rf_kill(trans); /* Make sure we sync here, because we'll need full access later */ if (low_power) -- cgit v1.2.3 From 960f864b7b628f67bb57aeb071066fd2fe092bba Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 20 Jun 2017 15:18:43 +0200 Subject: iwlwifi: mvm: require AP_LINK_PS for TVQM Since the TXQ timer freeze code will not properly handle the large TVQM queue numbers, warn if we get into that code when we have TVQM. Also, just to catch this earlier, warn if the firmware image doesn't support AP_LINK_PS but we're running on HW using TVQM. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 44f144c58720..a2e0acc23adc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -445,8 +445,18 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); if (iwl_mvm_has_new_rx_api(mvm)) ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_STA_PM_NOTIF)) + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_STA_PM_NOTIF)) { ieee80211_hw_set(hw, AP_LINK_PS); + } else if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) { + /* + * we absolutely need this for the new TX API since that comes + * with many more queues than the current code can deal with + * for station powersave + */ + return -EINVAL; + } if (mvm->trans->num_rx_queues > 1) ieee80211_hw_set(hw, USES_RSS); @@ -2399,6 +2409,14 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, unsigned long txqs = 0, tids = 0; int tid; + /* + * If we have TVQM then we get too high queue numbers - luckily + * we really shouldn't get here with that because such hardware + * should have firmware supporting buffer station offload. + */ + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return; + spin_lock_bh(&mvmsta->lock); for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; -- cgit v1.2.3 From 7426ee33a29b3215357986378c77bb9949518154 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 20 Jun 2017 11:22:07 +0200 Subject: iwlwifi: mvm: simplify bufferable MMPDU check There's no need to spell out the cases when we can just use ieee80211_is_bufferable_mmpdu(). Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index a2e0acc23adc..b8373923cfa9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -816,9 +816,7 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */ if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER && ieee80211_is_mgmt(hdr->frame_control) && - !ieee80211_is_deauth(hdr->frame_control) && - !ieee80211_is_disassoc(hdr->frame_control) && - !ieee80211_is_action(hdr->frame_control))) + !ieee80211_is_bufferable_mmpdu(hdr->frame_control))) sta = NULL; if (sta) { -- cgit v1.2.3 From d197358b755b56bd12c01044b6373986590878f6 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Thu, 22 Jun 2017 16:00:25 +0300 Subject: iwlwifi: mvm: rename p2p-specific sta functions to include p2p in the names The iwl_mvm_add_bcast_sta() and the iwl_mvm_rm_bcast_sta() functions are only called in P2P flows. Add _p2p_ to the function names to make this explicit. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index b8373923cfa9..d0fd46f9597c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1428,7 +1428,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, if (ret) goto out_unref_phy; - ret = iwl_mvm_add_bcast_sta(mvm, vif); + ret = iwl_mvm_add_p2p_bcast_sta(mvm, vif); if (ret) goto out_unbind; @@ -1558,7 +1558,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { mvm->p2p_device_vif = NULL; - iwl_mvm_rm_bcast_sta(mvm, vif); + iwl_mvm_rm_p2p_bcast_sta(mvm, vif); iwl_mvm_binding_remove_vif(mvm, vif); iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); mvmvif->phy_ctxt = NULL; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index b196acd18252..5c707c62f1dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2106,7 +2106,7 @@ int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) * @mvm: the mvm component * @vif: the interface to which the broadcast station is added * @bsta: the broadcast station to add. */ -int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +int iwl_mvm_add_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; @@ -2137,7 +2137,7 @@ void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) * Send the FW a request to remove the station from it's internal data * structures, and in addition remove it from the local data structure. */ -int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +int iwl_mvm_rm_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { int ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 05fecbe87da4..6f1d358f3008 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -533,9 +533,9 @@ void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm); int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_add_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_rm_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, -- cgit v1.2.3 From c8f54701bdbfcc78ebbc0d10f47a8a1b803f101f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 19 Jun 2017 23:50:31 +0200 Subject: iwlwifi: mvm: remove non-DQA mode All the firmware versions the driver supports enable DQA, and thus the only way to get non-DQA mode is to modify the source. Remove this mode to simplify the code. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/txq.h | 6 - drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 16 +- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 164 +------- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 89 +---- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 40 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 31 +- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 437 +++++---------------- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 14 +- .../net/wireless/intel/iwlwifi/mvm/time-event.c | 5 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 133 ++----- 10 files changed, 158 insertions(+), 777 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h index 87c1e36a9c5a..87b4434224a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h @@ -62,12 +62,6 @@ #ifndef __iwl_fw_api_txq_h__ #define __iwl_fw_api_txq_h__ -/* Tx queue numbers for non-DQA mode */ -enum { - IWL_MVM_OFFCHANNEL_QUEUE = 8, - IWL_MVM_CMD_QUEUE = 9, -}; - /* * DQA queue numbers * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index e04bf2f7c1ba..ff44e1aa035e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -331,10 +331,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, */ memset(&mvm->queue_info, 0, sizeof(mvm->queue_info)); - if (iwl_mvm_is_dqa_supported(mvm)) - mvm->queue_info[IWL_MVM_DQA_CMD_QUEUE].hw_queue_refcount = 1; - else - mvm->queue_info[IWL_MVM_CMD_QUEUE].hw_queue_refcount = 1; + mvm->queue_info[IWL_MVM_DQA_CMD_QUEUE].hw_queue_refcount = 1; for (i = 0; i < IEEE80211_MAX_QUEUES; i++) atomic_set(&mvm->mac80211_queue_stop_count[i], 0); @@ -1137,14 +1134,9 @@ int iwl_mvm_up(struct iwl_mvm *mvm) /* reset quota debouncing buffer - 0xff will yield invalid data */ memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); - /* Enable DQA-mode if required */ - if (iwl_mvm_is_dqa_supported(mvm)) { - ret = iwl_mvm_send_dqa_cmd(mvm); - if (ret) - goto error; - } else { - IWL_DEBUG_FW(mvm, "Working in non-DQA mode\n"); - } + ret = iwl_mvm_send_dqa_cmd(mvm); + if (ret) + goto error; /* Add auxiliary station for scanning */ ret = iwl_mvm_add_aux_sta(mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 604db47c8c21..8fe955d58c6e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -241,32 +241,17 @@ static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac, data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif); } -static void iwl_mvm_mac_sta_hw_queues_iter(void *_data, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_hw_queues_iface_iterator_data *data = _data; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - - /* Mark the queues used by the sta */ - data->used_hw_queues |= mvmsta->tfd_queue_msk; -} - unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, struct ieee80211_vif *exclude_vif) { - u8 sta_id; struct iwl_mvm_hw_queues_iface_iterator_data data = { .exclude_vif = exclude_vif, .used_hw_queues = BIT(IWL_MVM_OFFCHANNEL_QUEUE) | - BIT(mvm->aux_queue), + BIT(mvm->aux_queue) | + BIT(IWL_MVM_DQA_GCAST_QUEUE), }; - if (iwl_mvm_is_dqa_supported(mvm)) - data.used_hw_queues |= BIT(IWL_MVM_DQA_GCAST_QUEUE); - else - data.used_hw_queues |= BIT(IWL_MVM_CMD_QUEUE); - lockdep_assert_held(&mvm->mutex); /* mark all VIF used hw queues */ @@ -274,26 +259,6 @@ unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, iwl_mvm_iface_hw_queues_iter, &data); - /* - * for DQA, the hw_queue in mac80211 is never really used for - * real traffic (only the few queue IDs covered above), so - * we can reuse the real HW queue IDs the stations use - */ - if (iwl_mvm_is_dqa_supported(mvm)) - return data.used_hw_queues; - - /* don't assign the same hw queues as TDLS stations */ - ieee80211_iterate_stations_atomic(mvm->hw, - iwl_mvm_mac_sta_hw_queues_iter, - &data); - - /* - * Some TDLS stations may be removed but are in the process of being - * drained. Don't touch their queues. - */ - for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) - data.used_hw_queues |= mvm->tfd_drained[sta_id]; - return data.used_hw_queues; } @@ -344,8 +309,7 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, NUM_TSF_IDS); } -static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_mac_iface_iterator_data data = { @@ -361,6 +325,8 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, int ret, i, queue_limit; unsigned long used_hw_queues; + lockdep_assert_held(&mvm->mutex); + /* * Allocate a MAC ID and a TSF for this MAC, along with the queues * and other resources. @@ -444,19 +410,14 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, return 0; } - if (iwl_mvm_is_dqa_supported(mvm)) { - /* - * queues in mac80211 almost entirely independent of - * the ones here - no real limit - */ - queue_limit = IEEE80211_MAX_QUEUES; - BUILD_BUG_ON(IEEE80211_MAX_QUEUES > - BITS_PER_BYTE * - sizeof(mvm->hw_queue_to_mac80211[0])); - } else { - /* need to not use too many in this case */ - queue_limit = mvm->first_agg_queue; - } + /* + * queues in mac80211 almost entirely independent of + * the ones here - no real limit + */ + queue_limit = IEEE80211_MAX_QUEUES; + BUILD_BUG_ON(IEEE80211_MAX_QUEUES > + BITS_PER_BYTE * + sizeof(mvm->hw_queue_to_mac80211[0])); /* * Find available queues, and allocate them to the ACs. When in @@ -478,27 +439,12 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, /* Allocate the CAB queue for softAP and GO interfaces */ if (vif->type == NL80211_IFTYPE_AP) { - u8 queue; - - if (!iwl_mvm_is_dqa_supported(mvm)) { - queue = find_first_zero_bit(&used_hw_queues, - mvm->first_agg_queue); - - if (queue >= mvm->first_agg_queue) { - IWL_ERR(mvm, "Failed to allocate cab queue\n"); - ret = -EIO; - goto exit_fail; - } - } else { - queue = IWL_MVM_DQA_GCAST_QUEUE; - } - /* * For TVQM this will be overwritten later with the FW assigned * queue value (when queue is enabled). */ - mvmvif->cab_queue = queue; - vif->cab_queue = queue; + mvmvif->cab_queue = IWL_MVM_DQA_GCAST_QUEUE; + vif->cab_queue = IWL_MVM_DQA_GCAST_QUEUE; } else { vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; } @@ -519,78 +465,6 @@ exit_fail: return ret; } -int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, false, false); - u32 ac; - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_mac_ctxt_allocate_resources(mvm, vif); - if (ret) - return ret; - - /* If DQA is supported - queues will be enabled when needed */ - if (iwl_mvm_is_dqa_supported(mvm)) - return 0; - - switch (vif->type) { - case NL80211_IFTYPE_P2P_DEVICE: - iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_TX_FIFO_VO, 0, wdg_timeout); - break; - case NL80211_IFTYPE_AP: - iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, vif->cab_queue, - IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); - /* fall through */ - default: - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac], - vif->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac], 0, - wdg_timeout); - break; - } - - return 0; -} - -void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - int ac; - - lockdep_assert_held(&mvm->mutex); - - /* - * If DQA is supported - queues were already disabled, since in - * DQA-mode the queues are a property of the STA and not of the - * vif, and at this point the STA was already deleted - */ - if (iwl_mvm_is_dqa_supported(mvm)) - return; - - switch (vif->type) { - case NL80211_IFTYPE_P2P_DEVICE: - iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MAX_TID_COUNT, 0); - - break; - case NL80211_IFTYPE_AP: - iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue, - IWL_MAX_TID_COUNT, 0); - /* fall through */ - default: - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - iwl_mvm_disable_txq(mvm, vif->hw_queue[ac], - vif->hw_queue[ac], - IWL_MAX_TID_COUNT, 0); - } -} - static void iwl_mvm_ack_rates(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum nl80211_band band, @@ -914,18 +788,12 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, { struct iwl_mac_ctx_cmd cmd = {}; u32 tfd_queue_msk = 0; - int ret, i; + int ret; WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - if (!iwl_mvm_is_dqa_supported(mvm)) { - for (i = 0; i < IEEE80211_NUM_ACS; i++) - if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) - tfd_queue_msk |= BIT(vif->hw_queue[i]); - } - cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC | MAC_FILTER_IN_CONTROL_AND_MGMT | MAC_FILTER_IN_BEACON | diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index d0fd46f9597c..aad75f1cea31 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -464,10 +464,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) if (mvm->trans->max_skb_frags) hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; - if (!iwl_mvm_is_dqa_supported(mvm)) - hw->queues = mvm->first_agg_queue; - else - hw->queues = IEEE80211_MAX_QUEUES; + hw->queues = IEEE80211_MAX_QUEUES; hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | IEEE80211_RADIOTAP_MCS_HAVE_STBC; @@ -1067,9 +1064,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) iwl_mvm_reset_phy_ctxts(mvm); memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); - memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); memset(mvm->sta_deferred_frames, 0, sizeof(mvm->sta_deferred_frames)); - memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained)); memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); @@ -1372,17 +1367,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, goto out_release; } - if (iwl_mvm_is_dqa_supported(mvm)) { - /* - * Only queue for this station is the mcast queue, - * which shouldn't be in TFD mask anyway - */ - ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->mcast_sta, - 0, vif->type, - IWL_STA_MULTICAST); - if (ret) - goto out_release; - } + /* + * Only queue for this station is the mcast queue, + * which shouldn't be in TFD mask anyway + */ + ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->mcast_sta, + 0, vif->type, + IWL_STA_MULTICAST); + if (ret) + goto out_release; iwl_mvm_vif_dbgfs_register(mvm, vif); goto out_unlock; @@ -1456,8 +1449,6 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, out_release: if (vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count--; - - iwl_mvm_mac_ctxt_release(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); @@ -1469,40 +1460,6 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif); - - if (tfd_msk && !iwl_mvm_is_dqa_supported(mvm)) { - /* - * mac80211 first removes all the stations of the vif and - * then removes the vif. When it removes a station it also - * flushes the AMPDU session. So by now, all the AMPDU sessions - * of all the stations of this vif are closed, and the queues - * of these AMPDU sessions are properly closed. - * We still need to take care of the shared queues of the vif. - * Flush them here. - * For DQA mode there is no need - broacast and multicast queue - * are flushed separately. - */ - mutex_lock(&mvm->mutex); - iwl_mvm_flush_tx_path(mvm, tfd_msk, 0); - mutex_unlock(&mvm->mutex); - - /* - * There are transports that buffer a few frames in the host. - * For these, the flush above isn't enough since while we were - * flushing, the transport might have sent more frames to the - * device. To solve this, wait here until the transport is - * empty. Technically, this could have replaced the flush - * above, but flush is much faster than draining. So flush - * first, and drain to make sure we have no frames in the - * transport anymore. - * If a station still had frames on the shared queues, it is - * already marked as draining, so to complete the draining, we - * just need to wait until the transport is empty. - */ - iwl_trans_wait_tx_queues_empty(mvm->trans, tfd_msk); - } - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { /* * Flush the ROC worker which will flush the OFFCHANNEL queue. @@ -1510,14 +1467,6 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, * queue are sent in ROC session. */ flush_work(&mvm->roc_done_wk); - } else { - /* - * By now, all the AC queues are empty. The AGG queues are - * empty too. We already got all the Tx responses for all the - * packets in the queues. The drain work can have been - * triggered. Flush it. - */ - flush_work(&mvm->sta_drained_wk); } } @@ -1571,7 +1520,6 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, iwl_mvm_mac_ctxt_remove(mvm, vif); out_release: - iwl_mvm_mac_ctxt_release(mvm, vif); mutex_unlock(&mvm->mutex); } @@ -2419,11 +2367,6 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - if (!iwl_mvm_is_dqa_supported(mvm) && - tid_data->state != IWL_AGG_ON && - tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) - continue; - if (tid_data->txq_id == IWL_MVM_INVALID_QUEUE) continue; @@ -2437,9 +2380,6 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, switch (cmd) { case STA_NOTIFY_SLEEP: - if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) - ieee80211_sta_block_awake(hw, sta, true); - for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT) ieee80211_sta_set_buffered(sta, tid, true); @@ -2632,9 +2572,6 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) return -EINVAL; - /* if a STA is being removed, reuse its ID */ - flush_work(&mvm->sta_drained_wk); - /* * If we are in a STA removal flow and in DQA mode: * @@ -2649,8 +2586,7 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, * make sure the worker is no longer handling frames for this STA. */ if (old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST && - iwl_mvm_is_dqa_supported(mvm)) { + new_state == IEEE80211_STA_NOTEXIST) { iwl_mvm_purge_deferred_tx_frames(mvm, mvm_sta); flush_work(&mvm->add_stream_wk); @@ -4032,8 +3968,7 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, return; /* Make sure we're done with the deferred traffic before flushing */ - if (iwl_mvm_is_dqa_supported(mvm)) - flush_work(&mvm->add_stream_wk); + flush_work(&mvm->add_stream_wk); mutex_lock(&mvm->mutex); mvmvif = iwl_mvm_vif_from_mac80211(vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 74948f875a3f..4e38dae9e5c0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -121,6 +121,9 @@ */ #define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3 +/* offchannel queue towards mac80211 */ +#define IWL_MVM_OFFCHANNEL_QUEUE 0 + extern const struct ieee80211_ops iwl_mvm_hw_ops; /** @@ -783,11 +786,7 @@ struct iwl_mvm { /* data related to data path */ struct iwl_rx_phy_info last_phy_info; struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT]; - struct work_struct sta_drained_wk; unsigned long sta_deferred_frames[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)]; - unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)]; - atomic_t pending_frames[IWL_MVM_STATION_COUNT]; - u32 tfd_drained[IWL_MVM_STATION_COUNT]; u8 rx_ba_sessions; /* configured by mac80211 */ @@ -960,9 +959,6 @@ struct iwl_mvm { u16 probe_queue; u16 p2p_dev_queue; - u8 first_agg_queue; - u8 last_agg_queue; - /* Indicate if device power save is allowed */ u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */ unsigned int max_amsdu_len; /* used for debugfs only */ @@ -1125,12 +1121,6 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); } -static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DQA_SUPPORT); -} - static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm) { /* For now we only use this mode to differentiate between @@ -1469,7 +1459,6 @@ u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef); /* MAC (virtual interface) programming */ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool force_assoc_off, const u8 *bssid_override); @@ -1720,10 +1709,6 @@ bool iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue, u8 sta_id, u8 tid, unsigned int timeout); -/* - * Disable a TXQ. - * Note that in non-DQA mode the %mac80211_queue and %tid params are ignored. - */ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, u8 tid, u8 flags); int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, u8 minq, u8 maxq); @@ -1733,25 +1718,8 @@ int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, u8 minq, u8 maxq); */ static inline u32 iwl_mvm_flushable_queues(struct iwl_mvm *mvm) { - u32 cmd_queue = iwl_mvm_is_dqa_supported(mvm) ? IWL_MVM_DQA_CMD_QUEUE : - IWL_MVM_CMD_QUEUE; - return ((BIT(mvm->cfg->base_params->num_of_queues) - 1) & - ~BIT(cmd_queue)); -} - -static inline -void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u8 fifo, u16 ssn, unsigned int wdg_timeout) -{ - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = fifo, - .tid = IWL_MAX_TID_COUNT, - .aggregate = false, - .frame_limit = IWL_FRAME_LIMIT, - }; - - iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); + ~BIT(IWL_MVM_DQA_CMD_QUEUE)); } static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index d0ffdf5fa3a5..c387d5095bfe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -623,27 +623,10 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->fw_restart = iwlwifi_mod_params.fw_restart ? -1 : 0; - if (!iwl_mvm_is_dqa_supported(mvm)) { - mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1; - - if (mvm->cfg->base_params->num_of_queues == 16) { - mvm->aux_queue = 11; - mvm->first_agg_queue = 12; - BUILD_BUG_ON(BITS_PER_BYTE * - sizeof(mvm->hw_queue_to_mac80211[0]) < 12); - } else { - mvm->aux_queue = 15; - mvm->first_agg_queue = 16; - BUILD_BUG_ON(BITS_PER_BYTE * - sizeof(mvm->hw_queue_to_mac80211[0]) < 16); - } - } else { - mvm->aux_queue = IWL_MVM_DQA_AUX_QUEUE; - mvm->probe_queue = IWL_MVM_DQA_AP_PROBE_RESP_QUEUE; - mvm->p2p_dev_queue = IWL_MVM_DQA_P2P_DEVICE_QUEUE; - mvm->first_agg_queue = IWL_MVM_DQA_MIN_DATA_QUEUE; - mvm->last_agg_queue = IWL_MVM_DQA_MAX_DATA_QUEUE; - } + mvm->aux_queue = IWL_MVM_DQA_AUX_QUEUE; + mvm->probe_queue = IWL_MVM_DQA_AP_PROBE_RESP_QUEUE; + mvm->p2p_dev_queue = IWL_MVM_DQA_P2P_DEVICE_QUEUE; + mvm->sf_state = SF_UNINIT; if (iwl_mvm_has_unified_ucode(mvm)) iwl_fw_set_current_image(&mvm->fwrt, IWL_UCODE_REGULAR); @@ -662,7 +645,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); - INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work); INIT_DELAYED_WORK(&mvm->scan_timeout_dwork, iwl_mvm_scan_timeout_wk); @@ -714,10 +696,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.command_groups = iwl_mvm_groups; trans_cfg.command_groups_size = ARRAY_SIZE(iwl_mvm_groups); - if (iwl_mvm_is_dqa_supported(mvm)) - trans_cfg.cmd_queue = IWL_MVM_DQA_CMD_QUEUE; - else - trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; + trans_cfg.cmd_queue = IWL_MVM_DQA_CMD_QUEUE; trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; trans_cfg.scd_set_active = true; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 5c707c62f1dc..5506d6ac66d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -284,60 +284,6 @@ unlock: rcu_read_unlock(); } -static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - unsigned long used_hw_queues; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, NULL, true, false); - u32 ac; - - lockdep_assert_held(&mvm->mutex); - - used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, NULL); - - /* Find available queues, and allocate them to the ACs */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - u8 queue = find_first_zero_bit(&used_hw_queues, - mvm->first_agg_queue); - - if (queue >= mvm->first_agg_queue) { - IWL_ERR(mvm, "Failed to allocate STA queue\n"); - return -EBUSY; - } - - __set_bit(queue, &used_hw_queues); - mvmsta->hw_queue[ac] = queue; - } - - /* Found a place for all queues - enable them */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac], - mvmsta->hw_queue[ac], - iwl_mvm_mac_ac_to_tx_fifo(mvm, ac), 0, - wdg_timeout); - mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]); - } - - return 0; -} - -static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - unsigned long sta_msk; - int i; - - lockdep_assert_held(&mvm->mutex); - - /* disable the TDLS STA-specific queues */ - sta_msk = mvmsta->tfd_queue_msk; - for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE) - iwl_mvm_disable_txq(mvm, i, i, IWL_MAX_TID_COUNT, 0); -} - /* Disable aggregations for a bitmap of TIDs for a given station */ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue, unsigned long disable_agg_tids, @@ -1317,8 +1263,6 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_READY; } } - - atomic_set(&mvm->pending_frames[mvm_sta->sta_id], 0); } int iwl_mvm_add_sta(struct iwl_mvm *mvm, @@ -1343,9 +1287,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, spin_lock_init(&mvm_sta->lock); - /* In DQA mode, if this is a HW restart, re-alloc existing queues */ - if (iwl_mvm_is_dqa_supported(mvm) && - test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* if this is a HW restart re-alloc existing queues */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { iwl_mvm_realloc_queues_after_restart(mvm, mvm_sta); goto update_fw; } @@ -1363,33 +1306,15 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, mvm_sta->sta_type = sta->tdls ? IWL_STA_TDLS_LINK : IWL_STA_LINK; /* HW restart, don't assume the memory has been zeroed */ - atomic_set(&mvm->pending_frames[sta_id], 0); mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */ mvm_sta->tfd_queue_msk = 0; - /* - * Allocate new queues for a TDLS station, unless we're in DQA mode, - * and then they'll be allocated dynamically - */ - if (!iwl_mvm_is_dqa_supported(mvm) && sta->tdls) { - ret = iwl_mvm_tdls_sta_init(mvm, sta); - if (ret) - return ret; - } else if (!iwl_mvm_is_dqa_supported(mvm)) { - for (i = 0; i < IEEE80211_NUM_ACS; i++) - if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) - mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); - } - /* for HW restart - reset everything but the sequence number */ for (i = 0; i <= IWL_MAX_TID_COUNT; i++) { u16 seq = mvm_sta->tid_data[i].seq_number; memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i])); mvm_sta->tid_data[i].seq_number = seq; - if (!iwl_mvm_is_dqa_supported(mvm)) - continue; - /* * Mark all queues for this STA as unallocated and defer TX * frames until the queue is allocated @@ -1423,7 +1348,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, mvm_sta->dup_data = dup_data; } - if (iwl_mvm_is_dqa_supported(mvm) && !iwl_mvm_has_new_tx_api(mvm)) { + if (!iwl_mvm_has_new_tx_api(mvm)) { ret = iwl_mvm_reserve_sta_stream(mvm, sta, ieee80211_vif_type_p2p(vif)); if (ret) @@ -1449,8 +1374,6 @@ update_fw: return 0; err: - if (!iwl_mvm_is_dqa_supported(mvm) && sta->tdls) - iwl_mvm_tdls_sta_deinit(mvm, sta); return ret; } @@ -1523,79 +1446,6 @@ static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id) return 0; } -void iwl_mvm_sta_drained_wk(struct work_struct *wk) -{ - struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, sta_drained_wk); - u8 sta_id; - - /* - * The mutex is needed because of the SYNC cmd, but not only: if the - * work would run concurrently with iwl_mvm_rm_sta, it would run before - * iwl_mvm_rm_sta sets the station as busy, and exit. Then - * iwl_mvm_rm_sta would set the station as busy, and nobody will clean - * that later. - */ - mutex_lock(&mvm->mutex); - - for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) { - int ret; - struct ieee80211_sta *sta = - rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - - /* - * This station is in use or RCU-removed; the latter happens in - * managed mode, where mac80211 removes the station before we - * can remove it from firmware (we can only do that after the - * MAC is marked unassociated), and possibly while the deauth - * frame to disconnect from the AP is still queued. Then, the - * station pointer is -ENOENT when the last skb is reclaimed. - */ - if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT) - continue; - - if (PTR_ERR(sta) == -EINVAL) { - IWL_ERR(mvm, "Drained sta %d, but it is internal?\n", - sta_id); - continue; - } - - if (!sta) { - IWL_ERR(mvm, "Drained sta %d, but it was NULL?\n", - sta_id); - continue; - } - - WARN_ON(PTR_ERR(sta) != -EBUSY); - /* This station was removed and we waited until it got drained, - * we can now proceed and remove it. - */ - ret = iwl_mvm_rm_sta_common(mvm, sta_id); - if (ret) { - IWL_ERR(mvm, - "Couldn't remove sta %d after it was drained\n", - sta_id); - continue; - } - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); - clear_bit(sta_id, mvm->sta_drained); - - if (mvm->tfd_drained[sta_id]) { - unsigned long i, msk = mvm->tfd_drained[sta_id]; - - for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE) - iwl_mvm_disable_txq(mvm, i, i, - IWL_MAX_TID_COUNT, 0); - - mvm->tfd_drained[sta_id] = 0; - IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n", - sta_id, msk); - } - } - - mutex_unlock(&mvm->mutex); -} - static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_sta *mvm_sta) @@ -1654,79 +1504,65 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, if (iwl_mvm_has_new_rx_api(mvm)) kfree(mvm_sta->dup_data); - if ((vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id == sta_id) || - iwl_mvm_is_dqa_supported(mvm)){ - ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); - if (ret) - return ret; - /* flush its queues here since we are freeing mvm_sta */ - ret = iwl_mvm_flush_sta(mvm, mvm_sta, false, 0); - if (ret) - return ret; - if (iwl_mvm_has_new_tx_api(mvm)) { - ret = iwl_mvm_wait_sta_queues_empty(mvm, mvm_sta); - } else { - u32 q_mask = mvm_sta->tfd_queue_msk; + ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); + if (ret) + return ret; - ret = iwl_trans_wait_tx_queues_empty(mvm->trans, - q_mask); - } - if (ret) - return ret; - ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); - - /* If DQA is supported - the queues can be disabled now */ - if (iwl_mvm_is_dqa_supported(mvm)) { - iwl_mvm_disable_sta_queues(mvm, vif, mvm_sta); - /* - * If pending_frames is set at this point - it must be - * driver internal logic error, since queues are empty - * and removed successuly. - * warn on it but set it to 0 anyway to avoid station - * not being removed later in the function - */ - WARN_ON(atomic_xchg(&mvm->pending_frames[sta_id], 0)); - } + /* flush its queues here since we are freeing mvm_sta */ + ret = iwl_mvm_flush_sta(mvm, mvm_sta, false, 0); + if (ret) + return ret; + if (iwl_mvm_has_new_tx_api(mvm)) { + ret = iwl_mvm_wait_sta_queues_empty(mvm, mvm_sta); + } else { + u32 q_mask = mvm_sta->tfd_queue_msk; - /* If there is a TXQ still marked as reserved - free it */ - if (iwl_mvm_is_dqa_supported(mvm) && - mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) { - u8 reserved_txq = mvm_sta->reserved_queue; - enum iwl_mvm_queue_status *status; - - /* - * If no traffic has gone through the reserved TXQ - it - * is still marked as IWL_MVM_QUEUE_RESERVED, and - * should be manually marked as free again - */ - spin_lock_bh(&mvm->queue_info_lock); - status = &mvm->queue_info[reserved_txq].status; - if (WARN((*status != IWL_MVM_QUEUE_RESERVED) && - (*status != IWL_MVM_QUEUE_FREE), - "sta_id %d reserved txq %d status %d", - sta_id, reserved_txq, *status)) { - spin_unlock_bh(&mvm->queue_info_lock); - return -EINVAL; - } + ret = iwl_trans_wait_tx_queues_empty(mvm->trans, + q_mask); + } + if (ret) + return ret; + + ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); - *status = IWL_MVM_QUEUE_FREE; + iwl_mvm_disable_sta_queues(mvm, vif, mvm_sta); + + /* If there is a TXQ still marked as reserved - free it */ + if (mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) { + u8 reserved_txq = mvm_sta->reserved_queue; + enum iwl_mvm_queue_status *status; + + /* + * If no traffic has gone through the reserved TXQ - it + * is still marked as IWL_MVM_QUEUE_RESERVED, and + * should be manually marked as free again + */ + spin_lock_bh(&mvm->queue_info_lock); + status = &mvm->queue_info[reserved_txq].status; + if (WARN((*status != IWL_MVM_QUEUE_RESERVED) && + (*status != IWL_MVM_QUEUE_FREE), + "sta_id %d reserved txq %d status %d", + sta_id, reserved_txq, *status)) { spin_unlock_bh(&mvm->queue_info_lock); + return -EINVAL; } - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id == sta_id) { - /* if associated - we can't remove the AP STA now */ - if (vif->bss_conf.assoc) - return ret; + *status = IWL_MVM_QUEUE_FREE; + spin_unlock_bh(&mvm->queue_info_lock); + } + + if (vif->type == NL80211_IFTYPE_STATION && + mvmvif->ap_sta_id == sta_id) { + /* if associated - we can't remove the AP STA now */ + if (vif->bss_conf.assoc) + return ret; - /* unassoc - go ahead - remove the AP STA now */ - mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; + /* unassoc - go ahead - remove the AP STA now */ + mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; - /* clear d0i3_ap_sta_id if no longer relevant */ - if (mvm->d0i3_ap_sta_id == sta_id) - mvm->d0i3_ap_sta_id = IWL_MVM_INVALID_STA; - } + /* clear d0i3_ap_sta_id if no longer relevant */ + if (mvm->d0i3_ap_sta_id == sta_id) + mvm->d0i3_ap_sta_id = IWL_MVM_INVALID_STA; } /* @@ -1743,32 +1579,10 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, * calls the drain worker. */ spin_lock_bh(&mvm_sta->lock); + spin_unlock_bh(&mvm_sta->lock); - /* - * There are frames pending on the AC queues for this station. - * We need to wait until all the frames are drained... - */ - if (atomic_read(&mvm->pending_frames[sta_id])) { - rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], - ERR_PTR(-EBUSY)); - spin_unlock_bh(&mvm_sta->lock); - - /* disable TDLS sta queues on drain complete */ - if (sta->tdls) { - mvm->tfd_drained[sta_id] = mvm_sta->tfd_queue_msk; - IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n", sta_id); - } - - ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); - } else { - spin_unlock_bh(&mvm_sta->lock); - - if (!iwl_mvm_is_dqa_supported(mvm) && sta->tdls) - iwl_mvm_tdls_sta_deinit(mvm, sta); - - ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); - } + ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); return ret; } @@ -1867,7 +1681,7 @@ static void iwl_mvm_enable_aux_queue(struct iwl_mvm *mvm) IWL_MAX_TID_COUNT, wdg_timeout); mvm->aux_queue = queue; - } else if (iwl_mvm_is_dqa_supported(mvm)) { + } else { struct iwl_trans_txq_scd_cfg cfg = { .fifo = IWL_MVM_TX_FIFO_MCAST, .sta_id = mvm->aux_sta.sta_id, @@ -1878,9 +1692,6 @@ static void iwl_mvm_enable_aux_queue(struct iwl_mvm *mvm) iwl_mvm_enable_txq(mvm, mvm->aux_queue, mvm->aux_queue, 0, &cfg, wdg_timeout); - } else { - iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, - IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); } } @@ -1980,7 +1791,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) lockdep_assert_held(&mvm->mutex); - if (iwl_mvm_is_dqa_supported(mvm) && !iwl_mvm_has_new_tx_api(mvm)) { + if (!iwl_mvm_has_new_tx_api(mvm)) { if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) queue = mvm->probe_queue; @@ -2066,8 +1877,7 @@ int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) lockdep_assert_held(&mvm->mutex); - if (iwl_mvm_is_dqa_supported(mvm)) - iwl_mvm_free_bcast_sta_queues(mvm, vif); + iwl_mvm_free_bcast_sta_queues(mvm, vif); ret = iwl_mvm_rm_sta_common(mvm, mvmvif->bcast_sta.sta_id); if (ret) @@ -2078,23 +1888,10 @@ int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 qmask = 0; lockdep_assert_held(&mvm->mutex); - if (!iwl_mvm_is_dqa_supported(mvm)) { - qmask = iwl_mvm_mac_get_queues_mask(vif); - - /* - * The firmware defines the TFD queue mask to only be relevant - * for *unicast* queues, so the multicast (CAB) queue shouldn't - * be included. This only happens in NL80211_IFTYPE_AP vif type, - * so the next line will only have an effect there. - */ - qmask &= ~BIT(vif->cab_queue); - } - - return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask, + return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, 0, ieee80211_vif_type_p2p(vif), IWL_STA_GENERAL_PURPOSE); } @@ -2176,9 +1973,6 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) lockdep_assert_held(&mvm->mutex); - if (!iwl_mvm_is_dqa_supported(mvm)) - return 0; - if (WARN_ON(vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_ADHOC)) return -ENOTSUPP; @@ -2243,9 +2037,6 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) lockdep_assert_held(&mvm->mutex); - if (!iwl_mvm_is_dqa_supported(mvm)) - return 0; - iwl_mvm_flush_sta(mvm, &mvmvif->mcast_sta, true, 0); iwl_mvm_disable_txq(mvm, mvmvif->cab_queue, vif->cab_queue, @@ -2495,8 +2286,6 @@ int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, mvm_sta->tid_disable_agg &= ~BIT(tid); } else { /* In DQA-mode the queue isn't removed on agg termination */ - if (!iwl_mvm_is_dqa_supported(mvm)) - mvm_sta->tfd_queue_msk &= ~BIT(queue); mvm_sta->tid_disable_agg |= BIT(tid); } @@ -2599,19 +2388,17 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ret = -ENXIO; goto release_locks; } - } else if (iwl_mvm_is_dqa_supported(mvm) && - unlikely(mvm->queue_info[txq_id].status == + } else if (unlikely(mvm->queue_info[txq_id].status == IWL_MVM_QUEUE_SHARED)) { ret = -ENXIO; IWL_DEBUG_TX_QUEUES(mvm, "Can't start tid %d agg on shared queue!\n", tid); goto release_locks; - } else if (!iwl_mvm_is_dqa_supported(mvm) || - mvm->queue_info[txq_id].status != IWL_MVM_QUEUE_READY) { + } else if (mvm->queue_info[txq_id].status != IWL_MVM_QUEUE_READY) { txq_id = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, - mvm->first_agg_queue, - mvm->last_agg_queue); + IWL_MVM_DQA_MIN_DATA_QUEUE, + IWL_MVM_DQA_MAX_DATA_QUEUE); if (txq_id < 0) { ret = txq_id; IWL_ERR(mvm, "Failed to allocate agg queue\n"); @@ -2729,37 +2516,34 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, queue_status = mvm->queue_info[queue].status; spin_unlock_bh(&mvm->queue_info_lock); - /* In DQA mode, the existing queue might need to be reconfigured */ - if (iwl_mvm_is_dqa_supported(mvm)) { - /* Maybe there is no need to even alloc a queue... */ - if (mvm->queue_info[queue].status == IWL_MVM_QUEUE_READY) - alloc_queue = false; + /* Maybe there is no need to even alloc a queue... */ + if (mvm->queue_info[queue].status == IWL_MVM_QUEUE_READY) + alloc_queue = false; + /* + * Only reconfig the SCD for the queue if the window size has + * changed from current (become smaller) + */ + if (!alloc_queue && buf_size < mvmsta->max_agg_bufsize) { /* - * Only reconfig the SCD for the queue if the window size has - * changed from current (become smaller) + * If reconfiguring an existing queue, it first must be + * drained */ - if (!alloc_queue && buf_size < mvmsta->max_agg_bufsize) { - /* - * If reconfiguring an existing queue, it first must be - * drained - */ - ret = iwl_trans_wait_tx_queues_empty(mvm->trans, - BIT(queue)); - if (ret) { - IWL_ERR(mvm, - "Error draining queue before reconfig\n"); - return ret; - } + ret = iwl_trans_wait_tx_queues_empty(mvm->trans, + BIT(queue)); + if (ret) { + IWL_ERR(mvm, + "Error draining queue before reconfig\n"); + return ret; + } - ret = iwl_mvm_reconfig_scd(mvm, queue, cfg.fifo, - mvmsta->sta_id, tid, - buf_size, ssn); - if (ret) { - IWL_ERR(mvm, - "Error reconfiguring TXQ #%d\n", queue); - return ret; - } + ret = iwl_mvm_reconfig_scd(mvm, queue, cfg.fifo, + mvmsta->sta_id, tid, + buf_size, ssn); + if (ret) { + IWL_ERR(mvm, + "Error reconfiguring TXQ #%d\n", queue); + return ret; } } @@ -2855,18 +2639,6 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, "ssn = %d, next_recl = %d\n", tid_data->ssn, tid_data->next_reclaimed); - /* - * There are still packets for this RA / TID in the HW. - * Not relevant for DQA mode, since there is no need to disable - * the queue. - */ - if (!iwl_mvm_is_dqa_supported(mvm) && - tid_data->ssn != tid_data->next_reclaimed) { - tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA; - err = 0; - break; - } - tid_data->ssn = 0xffff; tid_data->state = IWL_AGG_OFF; spin_unlock_bh(&mvmsta->lock); @@ -2874,12 +2646,6 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - - if (!iwl_mvm_is_dqa_supported(mvm)) { - int mac_queue = vif->hw_queue[tid_to_mac80211_ac[tid]]; - - iwl_mvm_disable_txq(mvm, txq_id, mac_queue, tid, 0); - } return 0; case IWL_AGG_STARTING: case IWL_EMPTYING_HW_QUEUE_ADDBA: @@ -2949,13 +2715,6 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_drain_sta(mvm, mvmsta, false); iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - - if (!iwl_mvm_is_dqa_supported(mvm)) { - int mac_queue = vif->hw_queue[tid_to_mac80211_ac[tid]]; - - iwl_mvm_disable_txq(mvm, tid_data->txq_id, mac_queue, - tid, 0); - } } return 0; @@ -3574,15 +3333,6 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, u16 n_queued; tid_data = &mvmsta->tid_data[tid]; - if (WARN(!iwl_mvm_is_dqa_supported(mvm) && - tid_data->state != IWL_AGG_ON && - tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA, - "TID %d state is %d\n", - tid, tid_data->state)) { - spin_unlock_bh(&mvmsta->lock); - ieee80211_sta_eosp(sta); - return; - } n_queued = iwl_mvm_tid_queued(mvm, tid_data); if (n_queued > remaining) { @@ -3676,13 +3426,8 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, mvm_sta->disable_tx = disable; - /* - * Tell mac80211 to start/stop queuing tx for this station, - * but don't stop queuing if there are still pending frames - * for this station. - */ - if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) - ieee80211_sta_block_awake(mvm->hw, sta, disable); + /* Tell mac80211 to start/stop queuing tx for this station */ + ieee80211_sta_block_awake(mvm->hw, sta, disable); iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 6f1d358f3008..005037aa3122 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -222,16 +222,7 @@ struct iwl_mvm_vif; * we remove the STA of the AP. The flush can be done synchronously against the * fw. * Drain means that the fw will drop all the frames sent to a specific station. - * This is useful when a client (if we are IBSS / GO or AP) disassociates. In - * that case, we need to drain all the frames for that client from the AC queues - * that are shared with the other clients. Only then, we can remove the STA in - * the fw. In order to do so, we track the non-AMPDU packets for each station. - * If mac80211 removes a STA and if it still has non-AMPDU packets pending in - * the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all - * the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped - * (we know about it with its Tx response), we remove the station in fw and set - * it as %NULL in %fw_id_to_mac_id: this is the purpose of - * %iwl_mvm_sta_drained_wk. + * This is useful when a client (if we are IBSS / GO or AP) disassociates. */ /** @@ -371,7 +362,6 @@ struct iwl_mvm_rxq_dup_data { * struct iwl_mvm_sta - representation of a station in the driver * @sta_id: the index of the station in the fw (will be replaced by id_n_color) * @tfd_queue_msk: the tfd queues used by the station - * @hw_queue: per-AC mapping of the TFD queues used by station * @mac_id_n_color: the MAC context this station is linked to * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for * tid. @@ -409,7 +399,6 @@ struct iwl_mvm_rxq_dup_data { struct iwl_mvm_sta { u32 sta_id; u32 tfd_queue_msk; - u8 hw_queue[IEEE80211_NUM_ACS]; u32 mac_id_n_color; u16 tid_disable_agg; u8 max_agg_bufsize; @@ -548,7 +537,6 @@ int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm); -void iwl_mvm_sta_drained_wk(struct work_struct *wk); void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, struct ieee80211_sta *sta); void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index fcf2d1e4ff4e..65d8299108d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -129,10 +129,7 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk) * issue as it will have to complete before the next command is * executed, and a new time event means a new command. */ - if (iwl_mvm_is_dqa_supported(mvm)) - iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true, CMD_ASYNC); - else - iwl_mvm_flush_tx_path(mvm, queues, CMD_ASYNC); + iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true, CMD_ASYNC); } static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index cccff1073e3b..6d7d1a66af81 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -552,9 +552,6 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif; - if (!iwl_mvm_is_dqa_supported(mvm)) - return info->hw_queue; - mvmvif = iwl_mvm_vif_from_mac80211(info->control.vif); switch (info->control.vif->type) { @@ -653,8 +650,7 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) if (ap_sta_id != IWL_MVM_INVALID_STA) sta_id = ap_sta_id; - } else if (iwl_mvm_is_dqa_supported(mvm) && - info.control.vif->type == NL80211_IFTYPE_MONITOR) { + } else if (info.control.vif->type == NL80211_IFTYPE_MONITOR) { queue = mvm->aux_queue; } } @@ -673,17 +669,6 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) return -1; } - /* - * Increase the pending frames counter, so that later when a reply comes - * in and the counter is decreased - we don't start getting negative - * values. - * Note that we don't need to make sure it isn't agg'd, since we're - * TXing non-sta - * For DQA mode - we shouldn't increase it though - */ - if (!iwl_mvm_is_dqa_supported(mvm)) - atomic_inc(&mvm->pending_frames[sta_id]); - return 0; } @@ -994,22 +979,13 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, } } - if (iwl_mvm_is_dqa_supported(mvm) || is_ampdu) - txq_id = mvmsta->tid_data[tid].txq_id; - - if (sta->tdls && !iwl_mvm_is_dqa_supported(mvm)) { - /* default to TID 0 for non-QoS packets */ - u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid; - - txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]]; - } + txq_id = mvmsta->tid_data[tid].txq_id; WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); /* Check if TXQ needs to be allocated or re-activated */ if (unlikely(txq_id == IWL_MVM_INVALID_QUEUE || - !mvmsta->tid_data[tid].is_tid_active) && - iwl_mvm_is_dqa_supported(mvm)) { + !mvmsta->tid_data[tid].is_tid_active)) { /* If TXQ needs to be allocated... */ if (txq_id == IWL_MVM_INVALID_QUEUE) { iwl_mvm_tx_add_stream(mvm, mvmsta, tid, skb); @@ -1036,7 +1012,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, txq_id); } - if (iwl_mvm_is_dqa_supported(mvm) && !iwl_mvm_has_new_tx_api(mvm)) { + if (!iwl_mvm_has_new_tx_api(mvm)) { /* Keep track of the time of the last frame for this RA/TID */ mvm->queue_info[txq_id].last_frame_time[tid] = jiffies; @@ -1070,10 +1046,6 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, spin_unlock(&mvmsta->lock); - /* Increase pending frames count if this isn't AMPDU or DQA queue */ - if (!iwl_mvm_is_dqa_supported(mvm) && !is_ampdu) - atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); - return 0; drop_unlock_sta: @@ -1142,8 +1114,7 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, lockdep_assert_held(&mvmsta->lock); if ((tid_data->state == IWL_AGG_ON || - tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA || - iwl_mvm_is_dqa_supported(mvm)) && + tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && iwl_mvm_tid_queued(mvm, tid_data) == 0) { /* * Now that this aggregation or DQA queue is empty tell @@ -1177,13 +1148,6 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, IWL_DEBUG_TX_QUEUES(mvm, "Can continue DELBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); - if (!iwl_mvm_is_dqa_supported(mvm)) { - u8 mac80211_ac = tid_to_mac80211_ac[tid]; - - iwl_mvm_disable_txq(mvm, tid_data->txq_id, - vif->hw_queue[mac80211_ac], tid, - CMD_ASYNC); - } tid_data->state = IWL_AGG_OFF; ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; @@ -1381,10 +1345,10 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, info->flags |= IEEE80211_TX_STAT_ACK; break; case TX_STATUS_FAIL_DEST_PS: - /* In DQA, the FW should have stopped the queue and not + /* the FW should have stopped the queue and not * return this status */ - WARN_ON(iwl_mvm_is_dqa_supported(mvm)); + WARN_ON(1); info->flags |= IEEE80211_TX_STAT_TX_FILTERED; break; default: @@ -1440,26 +1404,21 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, ieee80211_tx_status(mvm->hw, skb); } - if (iwl_mvm_is_dqa_supported(mvm) || txq_id >= mvm->first_agg_queue) { - /* If this is an aggregation queue, we use the ssn since: - * ssn = wifi seq_num % 256. - * The seq_ctl is the sequence control of the packet to which - * this Tx response relates. But if there is a hole in the - * bitmap of the BA we received, this Tx response may allow to - * reclaim the hole and all the subsequent packets that were - * already acked. In that case, seq_ctl != ssn, and the next - * packet to be reclaimed will be ssn and not seq_ctl. In that - * case, several packets will be reclaimed even if - * frame_count = 1. - * - * The ssn is the index (% 256) of the latest packet that has - * treated (acked / dropped) + 1. - */ - next_reclaimed = ssn; - } else { - /* The next packet to be reclaimed is the one after this one */ - next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10); - } + /* This is an aggregation queue or might become one, so we use + * the ssn since: ssn = wifi seq_num % 256. + * The seq_ctl is the sequence control of the packet to which + * this Tx response relates. But if there is a hole in the + * bitmap of the BA we received, this Tx response may allow to + * reclaim the hole and all the subsequent packets that were + * already acked. In that case, seq_ctl != ssn, and the next + * packet to be reclaimed will be ssn and not seq_ctl. In that + * case, several packets will be reclaimed even if + * frame_count = 1. + * + * The ssn is the index (% 256) of the latest packet that has + * treated (acked / dropped) + 1. + */ + next_reclaimed = ssn; IWL_DEBUG_TX_REPLY(mvm, "TXQ %d status %s (0x%08x)\n", @@ -1542,49 +1501,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, mvmsta = NULL; } - /* - * If the txq is not an AMPDU queue, there is no chance we freed - * several skbs. Check that out... - */ - if (iwl_mvm_is_dqa_supported(mvm) || txq_id >= mvm->first_agg_queue) - goto out; - - /* We can't free more than one frame at once on a shared queue */ - WARN_ON(skb_freed > 1); - - /* If we have still frames for this STA nothing to do here */ - if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) - goto out; - - if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) { - - /* - * If there are no pending frames for this STA and - * the tx to this station is not disabled, notify - * mac80211 that this station can now wake up in its - * STA table. - * If mvmsta is not NULL, sta is valid. - */ - - spin_lock_bh(&mvmsta->lock); - - if (!mvmsta->disable_tx) - ieee80211_sta_block_awake(mvm->hw, sta, false); - - spin_unlock_bh(&mvmsta->lock); - } - - if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) { - /* - * We are draining and this was the last packet - pre_rcu_remove - * has been called already. We might be after the - * synchronize_net already. - * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues. - */ - set_bit(sta_id, mvm->sta_drained); - schedule_work(&mvm->sta_drained_wk); - } - out: rcu_read_unlock(); } @@ -1648,9 +1564,8 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta; int queue = SEQ_TO_QUEUE(sequence); - if (WARN_ON_ONCE(queue < mvm->first_agg_queue && - (!iwl_mvm_is_dqa_supported(mvm) || - (queue != IWL_MVM_DQA_BSS_CLIENT_QUEUE)))) + if (WARN_ON_ONCE(queue < IWL_MVM_DQA_MIN_DATA_QUEUE && + (queue != IWL_MVM_DQA_BSS_CLIENT_QUEUE))) return; if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS)) -- cgit v1.2.3 From d20e30af7e65811955af5bc2a268ae75cd14e99e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 25 Jun 2017 10:38:12 +0300 Subject: iwlwifi: mvm: don't retake the pointer to skb's CB We already have a such a pointer. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index aad75f1cea31..ef02919c2d6e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -805,7 +805,7 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, goto drop; } - if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && + if (info->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) && !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) goto drop; -- cgit v1.2.3 From 2ec50f574e8198b7d8ba677d1edcab566fa9e84f Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Mon, 31 Jul 2017 18:05:09 +0800 Subject: net-next: mediatek: add platform data to adapt into various hardware This patch is the preparation patch in order to adapt into various hardware through adding platform data which holds specific characteristics among MediaTek SoCs and introducing the unified clock handler for those distinct clock requirements depending on different features such as TRGMII and SGMII getting support on the target SoC. And finally, add enhancement with given the generic description for Kconfig and remove the unnecessary machine type dependency in Makefile. Signed-off-by: Sean Wang Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/Kconfig | 6 +-- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 75 ++++++++++++++++++++++++----- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 23 ++++++++- 3 files changed, 88 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig index 698bb89aa901..f9149d2a4694 100644 --- a/drivers/net/ethernet/mediatek/Kconfig +++ b/drivers/net/ethernet/mediatek/Kconfig @@ -7,11 +7,11 @@ config NET_VENDOR_MEDIATEK if NET_VENDOR_MEDIATEK config NET_MEDIATEK_SOC - tristate "MediaTek MT7623 Gigabit ethernet support" - depends on NET_VENDOR_MEDIATEK && (MACH_MT7623 || MACH_MT2701) + tristate "MediaTek SoC Gigabit Ethernet support" + depends on NET_VENDOR_MEDIATEK select PHYLIB ---help--- This driver supports the gigabit ethernet MACs in the - MediaTek MT2701/MT7623 chipset family. + MediaTek SoC family. endif #NET_VENDOR_MEDIATEK diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index f1886e1bdd82..0337fe99500a 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -185,7 +185,8 @@ static void mtk_phy_link_adjust(struct net_device *dev) break; }; - if (mac->id == 0 && !mac->trgmii) + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GMAC1_TRGMII) && + !mac->id && !mac->trgmii) mtk_gmac0_rgmii_adjust(mac->hw, dev->phydev->speed); if (dev->phydev->link) @@ -1834,9 +1835,36 @@ static void ethsys_reset(struct mtk_eth *eth, u32 reset_bits) mdelay(10); } +static void mtk_clk_disable(struct mtk_eth *eth) +{ + int clk; + + for (clk = MTK_CLK_MAX - 1; clk >= 0; clk--) + clk_disable_unprepare(eth->clks[clk]); +} + +static int mtk_clk_enable(struct mtk_eth *eth) +{ + int clk, ret; + + for (clk = 0; clk < MTK_CLK_MAX ; clk++) { + ret = clk_prepare_enable(eth->clks[clk]); + if (ret) + goto err_disable_clks; + } + + return 0; + +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(eth->clks[clk]); + + return ret; +} + static int mtk_hw_init(struct mtk_eth *eth) { - int i, val; + int i, val, ret; if (test_and_set_bit(MTK_HW_INIT, ð->state)) return 0; @@ -1844,10 +1872,10 @@ static int mtk_hw_init(struct mtk_eth *eth) pm_runtime_enable(eth->dev); pm_runtime_get_sync(eth->dev); - clk_prepare_enable(eth->clks[MTK_CLK_ETHIF]); - clk_prepare_enable(eth->clks[MTK_CLK_ESW]); - clk_prepare_enable(eth->clks[MTK_CLK_GP1]); - clk_prepare_enable(eth->clks[MTK_CLK_GP2]); + ret = mtk_clk_enable(eth); + if (ret) + goto err_disable_pm; + ethsys_reset(eth, RSTCTRL_FE); ethsys_reset(eth, RSTCTRL_PPE); @@ -1915,6 +1943,12 @@ static int mtk_hw_init(struct mtk_eth *eth) } return 0; + +err_disable_pm: + pm_runtime_put_sync(eth->dev); + pm_runtime_disable(eth->dev); + + return ret; } static int mtk_hw_deinit(struct mtk_eth *eth) @@ -1922,10 +1956,7 @@ static int mtk_hw_deinit(struct mtk_eth *eth) if (!test_and_clear_bit(MTK_HW_INIT, ð->state)) return 0; - clk_disable_unprepare(eth->clks[MTK_CLK_GP2]); - clk_disable_unprepare(eth->clks[MTK_CLK_GP1]); - clk_disable_unprepare(eth->clks[MTK_CLK_ESW]); - clk_disable_unprepare(eth->clks[MTK_CLK_ETHIF]); + mtk_clk_disable(eth); pm_runtime_put_sync(eth->dev); pm_runtime_disable(eth->dev); @@ -2403,6 +2434,7 @@ static int mtk_probe(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct device_node *mac_np; + const struct of_device_id *match; struct mtk_eth *eth; int err; int i; @@ -2411,6 +2443,9 @@ static int mtk_probe(struct platform_device *pdev) if (!eth) return -ENOMEM; + match = of_match_device(of_mtk_match, &pdev->dev); + eth->soc = (struct mtk_soc_data *)match->data; + eth->dev = &pdev->dev; eth->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(eth->base)) @@ -2447,7 +2482,12 @@ static int mtk_probe(struct platform_device *pdev) if (IS_ERR(eth->clks[i])) { if (PTR_ERR(eth->clks[i]) == -EPROBE_DEFER) return -EPROBE_DEFER; - return -ENODEV; + if (eth->soc->required_clks & BIT(i)) { + dev_err(&pdev->dev, "clock %s not found\n", + mtk_clks_source_name[i]); + return -EINVAL; + } + eth->clks[i] = NULL; } } @@ -2550,8 +2590,19 @@ static int mtk_remove(struct platform_device *pdev) return 0; } +static const struct mtk_soc_data mt2701_data = { + .caps = MTK_GMAC1_TRGMII, + .required_clks = MT7623_CLKS_BITMAP +}; + +static const struct mtk_soc_data mt7623_data = { + .caps = MTK_GMAC1_TRGMII, + .required_clks = MT7623_CLKS_BITMAP +}; + const struct of_device_id of_mtk_match[] = { - { .compatible = "mediatek,mt2701-eth" }, + { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data}, + { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, {}, }; MODULE_DEVICE_TABLE(of, of_mtk_match); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 5868a09f623a..8ade23dfcc77 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -443,6 +443,9 @@ enum mtk_clks_map { MTK_CLK_MAX }; +#define MT7623_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ + BIT(MTK_CLK_GP1) | BIT(MTK_CLK_GP2) | \ + BIT(MTK_CLK_TRGPLL)) enum mtk_dev_state { MTK_HW_INIT, MTK_RESETTING @@ -511,6 +514,21 @@ struct mtk_rx_ring { u32 crx_idx_reg; }; +#define MTK_TRGMII BIT(0) +#define MTK_GMAC1_TRGMII (BIT(1) | MTK_TRGMII) +#define MTK_HAS_CAPS(caps, _x) (((caps) & (_x)) == (_x)) + +/* struct mtk_soc_data - This is the structure holding all differences + * among various plaforms + * @caps Flags shown the extra capability for the SoC + * @required_clks Flags shown the bitmap for required clocks on + * the target SoC + */ +struct mtk_soc_data { + u32 caps; + u32 required_clks; +}; + /* currently no SoC has more than 2 macs */ #define MTK_MAX_DEVS 2 @@ -542,7 +560,8 @@ struct mtk_rx_ring { * @clks: clock array for all clocks required * @mii_bus: If there is a bus we need to create an instance for it * @pending_work: The workqueue used to reset the dma ring - * @state Initialization and runtime state of the device. + * @state Initialization and runtime state of the device + * @soc: Holding specific data among vaious SoCs */ struct mtk_eth { @@ -574,6 +593,8 @@ struct mtk_eth { struct mii_bus *mii_bus; struct work_struct pending_work; unsigned long state; + + const struct mtk_soc_data *soc; }; /* struct mtk_mac - the structure that holds the info about the MACs of the -- cgit v1.2.3 From 42c03844e93d564cf6c44dcb7d91c5dc602c4857 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Mon, 31 Jul 2017 18:05:10 +0800 Subject: net-next: mediatek: add support for MediaTek MT7622 SoC This patch adds the driver for ethernet controller on MT7622 SoC. It has the similar handling logic as the previously MT7623 does, but there are additions against with MT7623 SoC, the shared SGMII given for the dual GMACs and including 5-ports 10/100 embedded switch support (ESW) as the GMAC1 option, thus more clocks consumers for the extra feature are introduced here. So for ease portability and maintenance, those differences all are being kept inside the platform data as other drivers usually do. Currently testing successfully is done with those patches for the conditions such as GMAC2 with IP1001 PHY via RGMII and GMAC1/2 with RTL8211F PHY via SGMII. Signed-off-by: Sean Wang Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 67 ++++++++++++++++++++++++++++- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 54 ++++++++++++++++++++++- 2 files changed, 117 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 0337fe99500a..acf2b3b8009c 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -53,7 +53,8 @@ static const struct mtk_ethtool_stats { }; static const char * const mtk_clks_source_name[] = { - "ethif", "esw", "gp1", "gp2", "trgpll" + "ethif", "esw", "gp0", "gp1", "gp2", "trgpll", "sgmii_tx250m", + "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb", "sgmii_ck", "eth2pll" }; void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg) @@ -163,6 +164,47 @@ static void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth, int speed) mtk_w32(eth, val, TRGMII_TCK_CTRL); } +static void mtk_gmac_sgmii_hw_setup(struct mtk_eth *eth, int mac_id) +{ + u32 val; + + /* Setup the link timer and QPHY power up inside SGMIISYS */ + regmap_write(eth->sgmiisys, SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_DEFAULT); + + regmap_read(eth->sgmiisys, SGMSYS_SGMII_MODE, &val); + val |= SGMII_REMOTE_FAULT_DIS; + regmap_write(eth->sgmiisys, SGMSYS_SGMII_MODE, val); + + regmap_read(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, &val); + val |= SGMII_AN_RESTART; + regmap_write(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, val); + + regmap_read(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val &= ~SGMII_PHYA_PWD; + regmap_write(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, val); + + /* Determine MUX for which GMAC uses the SGMII interface */ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_DUAL_GMAC_SHARED_SGMII)) { + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + val &= ~SYSCFG0_SGMII_MASK; + val |= !mac_id ? SYSCFG0_SGMII_GMAC1 : SYSCFG0_SGMII_GMAC2; + regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val); + + dev_info(eth->dev, "setup shared sgmii for gmac=%d\n", + mac_id); + } + + /* Setup the GMAC1 going through SGMII path when SoC also support + * ESW on GMAC1 + */ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC1_ESW | MTK_GMAC1_SGMII) && + !mac_id) { + mtk_w32(eth, 0, MTK_MAC_MISC); + dev_info(eth->dev, "setup gmac1 going through sgmii"); + } +} + static void mtk_phy_link_adjust(struct net_device *dev) { struct mtk_mac *mac = netdev_priv(dev); @@ -270,6 +312,7 @@ static int mtk_phy_connect(struct net_device *dev) if (!np) return -ENODEV; + mac->ge_mode = 0; switch (of_get_phy_mode(np)) { case PHY_INTERFACE_MODE_TRGMII: mac->trgmii = true; @@ -277,7 +320,10 @@ static int mtk_phy_connect(struct net_device *dev) case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII: - mac->ge_mode = 0; + break; + case PHY_INTERFACE_MODE_SGMII: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) + mtk_gmac_sgmii_hw_setup(eth, mac->id); break; case PHY_INTERFACE_MODE_MII: mac->ge_mode = 1; @@ -2423,6 +2469,7 @@ static int mtk_get_chip_id(struct mtk_eth *eth, u32 *chip_id) static bool mtk_is_hwlro_supported(struct mtk_eth *eth) { switch (eth->chip_id) { + case MT7622_ETH: case MT7623_ETH: return true; } @@ -2462,6 +2509,16 @@ static int mtk_probe(struct platform_device *pdev) return PTR_ERR(eth->ethsys); } + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { + eth->sgmiisys = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "mediatek,sgmiisys"); + if (IS_ERR(eth->sgmiisys)) { + dev_err(&pdev->dev, "no sgmiisys regmap found\n"); + return PTR_ERR(eth->sgmiisys); + } + } + eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "mediatek,pctl"); if (IS_ERR(eth->pctl)) { @@ -2595,6 +2652,11 @@ static const struct mtk_soc_data mt2701_data = { .required_clks = MT7623_CLKS_BITMAP }; +static const struct mtk_soc_data mt7622_data = { + .caps = MTK_DUAL_GMAC_SHARED_SGMII | MTK_GMAC1_ESW, + .required_clks = MT7622_CLKS_BITMAP +}; + static const struct mtk_soc_data mt7623_data = { .caps = MTK_GMAC1_TRGMII, .required_clks = MT7623_CLKS_BITMAP @@ -2602,6 +2664,7 @@ static const struct mtk_soc_data mt7623_data = { const struct of_device_id of_mtk_match[] = { { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data}, + { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data}, { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, {}, }; diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 8ade23dfcc77..4594862e5a9b 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -302,6 +302,9 @@ #define PHY_IAC_REG_SHIFT 25 #define PHY_IAC_TIMEOUT HZ +#define MTK_MAC_MISC 0x1000c +#define MTK_MUX_TO_ESW BIT(0) + /* Mac control registers */ #define MTK_MAC_MCR(x) (0x10100 + (x * 0x100)) #define MAC_MCR_MAX_RX_1536 BIT(24) @@ -357,11 +360,15 @@ #define ETHSYS_CHIPID0_3 0x0 #define ETHSYS_CHIPID4_7 0x4 #define MT7623_ETH 7623 +#define MT7622_ETH 7622 /* ethernet subsystem config register */ #define ETHSYS_SYSCFG0 0x14 #define SYSCFG0_GE_MASK 0x3 #define SYSCFG0_GE_MODE(x, y) (x << (12 + (y * 2))) +#define SYSCFG0_SGMII_MASK (3 << 8) +#define SYSCFG0_SGMII_GMAC1 ((2 << 8) & GENMASK(9, 8)) +#define SYSCFG0_SGMII_GMAC2 ((3 << 8) & GENMASK(9, 8)) /* ethernet subsystem clock register */ #define ETHSYS_CLKCFG0 0x2c @@ -372,6 +379,23 @@ #define RSTCTRL_FE BIT(6) #define RSTCTRL_PPE BIT(31) +/* SGMII subsystem config registers */ +/* Register to auto-negotiation restart */ +#define SGMSYS_PCS_CONTROL_1 0x0 +#define SGMII_AN_RESTART BIT(9) + +/* Register to programmable link timer, the unit in 2 * 8ns */ +#define SGMSYS_PCS_LINK_TIMER 0x18 +#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & GENMASK(19, 0)) + +/* Register to control remote fault */ +#define SGMSYS_SGMII_MODE 0x20 +#define SGMII_REMOTE_FAULT_DIS BIT(8) + +/* Register to power up QPHY */ +#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8 +#define SGMII_PHYA_PWD BIT(4) + struct mtk_rx_dma { unsigned int rxd1; unsigned int rxd2; @@ -437,15 +461,31 @@ enum mtk_tx_flags { enum mtk_clks_map { MTK_CLK_ETHIF, MTK_CLK_ESW, + MTK_CLK_GP0, MTK_CLK_GP1, MTK_CLK_GP2, MTK_CLK_TRGPLL, + MTK_CLK_SGMII_TX_250M, + MTK_CLK_SGMII_RX_250M, + MTK_CLK_SGMII_CDR_REF, + MTK_CLK_SGMII_CDR_FB, + MTK_CLK_SGMII_CK, + MTK_CLK_ETH2PLL, MTK_CLK_MAX }; #define MT7623_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ BIT(MTK_CLK_GP1) | BIT(MTK_CLK_GP2) | \ BIT(MTK_CLK_TRGPLL)) +#define MT7622_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ + BIT(MTK_CLK_GP0) | BIT(MTK_CLK_GP1) | \ + BIT(MTK_CLK_GP2) | \ + BIT(MTK_CLK_SGMII_TX_250M) | \ + BIT(MTK_CLK_SGMII_RX_250M) | \ + BIT(MTK_CLK_SGMII_CDR_REF) | \ + BIT(MTK_CLK_SGMII_CDR_FB) | \ + BIT(MTK_CLK_SGMII_CK) | \ + BIT(MTK_CLK_ETH2PLL)) enum mtk_dev_state { MTK_HW_INIT, MTK_RESETTING @@ -516,9 +556,16 @@ struct mtk_rx_ring { #define MTK_TRGMII BIT(0) #define MTK_GMAC1_TRGMII (BIT(1) | MTK_TRGMII) +#define MTK_ESW BIT(4) +#define MTK_GMAC1_ESW (BIT(5) | MTK_ESW) +#define MTK_SGMII BIT(8) +#define MTK_GMAC1_SGMII (BIT(9) | MTK_SGMII) +#define MTK_GMAC2_SGMII (BIT(10) | MTK_SGMII) +#define MTK_DUAL_GMAC_SHARED_SGMII (BIT(11) | MTK_GMAC1_SGMII | \ + MTK_GMAC2_SGMII) #define MTK_HAS_CAPS(caps, _x) (((caps) & (_x)) == (_x)) -/* struct mtk_soc_data - This is the structure holding all differences +/* struct mtk_eth_data - This is the structure holding all differences * among various plaforms * @caps Flags shown the extra capability for the SoC * @required_clks Flags shown the bitmap for required clocks on @@ -547,6 +594,8 @@ struct mtk_soc_data { * @msg_enable: Ethtool msg level * @ethsys: The register map pointing at the range used to setup * MII modes + * @sgmiisys: The register map pointing at the range used to setup + * SGMII modes * @pctl: The register map pointing at the range used to setup * GMAC port drive/slew values * @dma_refcnt: track how many netdevs are using the DMA engine @@ -560,7 +609,7 @@ struct mtk_soc_data { * @clks: clock array for all clocks required * @mii_bus: If there is a bus we need to create an instance for it * @pending_work: The workqueue used to reset the dma ring - * @state Initialization and runtime state of the device + * @state: Initialization and runtime state of the device * @soc: Holding specific data among vaious SoCs */ @@ -577,6 +626,7 @@ struct mtk_eth { u32 msg_enable; unsigned long sysclk; struct regmap *ethsys; + struct regmap *sgmiisys; struct regmap *pctl; u32 chip_id; bool hwlro; -- cgit v1.2.3 From 3e3d647715d40173e359c91c69059b9cec7ba6ca Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 1 Aug 2017 12:14:36 +0200 Subject: ravb: add wake-on-lan support via magic packet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WoL is enabled in the suspend callback by setting MagicPacket detection and disabling all interrupts expect MagicPacket. In the resume path the driver needs to reset the hardware to rearm the WoL logic, this prevents the driver from simply restoring the registers and to take advantage of that ravb was not suspended to reduce resume time. To reset the hardware the driver closes the device, sets it in reset mode and reopens the device just like it would do in a normal suspend/resume scenario without WoL enabled, but it both closes and opens the device in the resume callback since the device needs to be reset for WoL to work. One quirk needed for WoL is that the module clock needs to be prevented from being switched off by Runtime PM. To keep the clock alive the suspend callback need to call clk_enable() directly to increase the usage count of the clock. Then when Runtime PM decreases the clock usage count it won't reach 0 and be switched off. Signed-off-by: Niklas Söderlund Acked-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/ravb.h | 2 + drivers/net/ethernet/renesas/ravb_main.c | 108 +++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index 0525bd696d5d..96a27b00c90e 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -991,6 +991,7 @@ struct ravb_private { struct net_device *ndev; struct platform_device *pdev; void __iomem *addr; + struct clk *clk; struct mdiobb_ctrl mdiobb; u32 num_rx_ring[NUM_RX_QUEUE]; u32 num_tx_ring[NUM_TX_QUEUE]; @@ -1033,6 +1034,7 @@ struct ravb_private { unsigned no_avb_link:1; unsigned avb_link_active_low:1; + unsigned wol_enabled:1; }; static inline u32 ravb_read(struct net_device *ndev, enum ravb_reg reg) diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 5931e859876c..6d10db1b5146 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -680,6 +680,9 @@ static void ravb_emac_interrupt_unlocked(struct net_device *ndev) ecsr = ravb_read(ndev, ECSR); ravb_write(ndev, ecsr, ECSR); /* clear interrupt */ + + if (ecsr & ECSR_MPD) + pm_wakeup_event(&priv->pdev->dev, 0); if (ecsr & ECSR_ICD) ndev->stats.tx_carrier_errors++; if (ecsr & ECSR_LCHNG) { @@ -1330,6 +1333,33 @@ static int ravb_get_ts_info(struct net_device *ndev, return 0; } +static void ravb_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct ravb_private *priv = netdev_priv(ndev); + + wol->supported = 0; + wol->wolopts = 0; + + if (priv->clk) { + wol->supported = WAKE_MAGIC; + wol->wolopts = priv->wol_enabled ? WAKE_MAGIC : 0; + } +} + +static int ravb_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct ravb_private *priv = netdev_priv(ndev); + + if (!priv->clk || wol->wolopts & ~WAKE_MAGIC) + return -EOPNOTSUPP; + + priv->wol_enabled = !!(wol->wolopts & WAKE_MAGIC); + + device_set_wakeup_enable(&priv->pdev->dev, priv->wol_enabled); + + return 0; +} + static const struct ethtool_ops ravb_ethtool_ops = { .nway_reset = ravb_nway_reset, .get_msglevel = ravb_get_msglevel, @@ -1343,6 +1373,8 @@ static const struct ethtool_ops ravb_ethtool_ops = { .get_ts_info = ravb_get_ts_info, .get_link_ksettings = ravb_get_link_ksettings, .set_link_ksettings = ravb_set_link_ksettings, + .get_wol = ravb_get_wol, + .set_wol = ravb_set_wol, }; static inline int ravb_hook_irq(unsigned int irq, irq_handler_t handler, @@ -2041,6 +2073,11 @@ static int ravb_probe(struct platform_device *pdev) priv->chip_id = chip_id; + /* Get clock, if not found that's OK but Wake-On-Lan is unavailable */ + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) + priv->clk = NULL; + /* Set function */ ndev->netdev_ops = &ravb_netdev_ops; ndev->ethtool_ops = &ravb_ethtool_ops; @@ -2107,6 +2144,9 @@ static int ravb_probe(struct platform_device *pdev) if (error) goto out_napi_del; + if (priv->clk) + device_set_wakeup_capable(&pdev->dev, 1); + /* Print device information */ netdev_info(ndev, "Base address at %#x, %pM, IRQ %d.\n", (u32)ndev->base_addr, ndev->dev_addr, ndev->irq); @@ -2160,15 +2200,66 @@ static int ravb_remove(struct platform_device *pdev) return 0; } +static int ravb_wol_setup(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + + /* Disable interrupts by clearing the interrupt masks. */ + ravb_write(ndev, 0, RIC0); + ravb_write(ndev, 0, RIC2); + ravb_write(ndev, 0, TIC); + + /* Only allow ECI interrupts */ + synchronize_irq(priv->emac_irq); + napi_disable(&priv->napi[RAVB_NC]); + napi_disable(&priv->napi[RAVB_BE]); + ravb_write(ndev, ECSIPR_MPDIP, ECSIPR); + + /* Enable MagicPacket */ + ravb_modify(ndev, ECMR, ECMR_MPDE, ECMR_MPDE); + + /* Increased clock usage so device won't be suspended */ + clk_enable(priv->clk); + + return enable_irq_wake(priv->emac_irq); +} + +static int ravb_wol_restore(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + int ret; + + napi_enable(&priv->napi[RAVB_NC]); + napi_enable(&priv->napi[RAVB_BE]); + + /* Disable MagicPacket */ + ravb_modify(ndev, ECMR, ECMR_MPDE, 0); + + ret = ravb_close(ndev); + if (ret < 0) + return ret; + + /* Restore clock usage count */ + clk_disable(priv->clk); + + return disable_irq_wake(priv->emac_irq); +} + static int __maybe_unused ravb_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); - int ret = 0; + struct ravb_private *priv = netdev_priv(ndev); + int ret; - if (netif_running(ndev)) { - netif_device_detach(ndev); + if (!netif_running(ndev)) + return 0; + + netif_device_detach(ndev); + + if (priv->wol_enabled) + ret = ravb_wol_setup(ndev); + else ret = ravb_close(ndev); - } return ret; } @@ -2179,6 +2270,10 @@ static int __maybe_unused ravb_resume(struct device *dev) struct ravb_private *priv = netdev_priv(ndev); int ret = 0; + /* If WoL is enabled set reset mode to rearm the WoL logic */ + if (priv->wol_enabled) + ravb_write(ndev, CCC_OPC_RESET, CCC); + /* All register have been reset to default values. * Restore all registers which where setup at probe time and * reopen device if it was running before system suspended. @@ -2202,6 +2297,11 @@ static int __maybe_unused ravb_resume(struct device *dev) ravb_write(ndev, priv->desc_bat_dma, DBAT); if (netif_running(ndev)) { + if (priv->wol_enabled) { + ret = ravb_wol_restore(ndev); + if (ret) + return ret; + } ret = ravb_open(ndev); if (ret < 0) return ret; -- cgit v1.2.3 From fbf3d034f2ff6264183cfa6845770e8cc2a986c8 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 1 Aug 2017 12:14:37 +0200 Subject: ravb: add workaround for clock when resuming with WoL enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The renesas-cpg-mssr clock driver are not yet aware of PSCI sleep where power is cut to the SoC. When resuming from this state with WoL enabled the enable count of the ravb clock is 1 and the clock driver thinks the clock is already on when PM core enables the clock and increments the enable count to 2. This will result in the ravb driver failing to talk to the hardware since the module clock is off. Work around this by forcing the enable count to 0 and then back to 2 when resuming with WoL enabled. This workaround should be reverted once the renesas-cpg-mssr clock driver becomes aware of this PSCI sleep behavior. Signed-off-by: Niklas Söderlund Acked-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/ravb_main.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 6d10db1b5146..fdf30bfa403b 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -2270,9 +2270,32 @@ static int __maybe_unused ravb_resume(struct device *dev) struct ravb_private *priv = netdev_priv(ndev); int ret = 0; - /* If WoL is enabled set reset mode to rearm the WoL logic */ - if (priv->wol_enabled) + if (priv->wol_enabled) { + /* Reduce the usecount of the clock to zero and then + * restore it to its original value. This is done to force + * the clock to be re-enabled which is a workaround + * for renesas-cpg-mssr driver which do not enable clocks + * when resuming from PSCI suspend/resume. + * + * Without this workaround the driver fails to communicate + * with the hardware if WoL was enabled when the system + * entered PSCI suspend. This is due to that if WoL is enabled + * we explicitly keep the clock from being turned off when + * suspending, but in PSCI sleep power is cut so the clock + * is disabled anyhow, the clock driver is not aware of this + * so the clock is not turned back on when resuming. + * + * TODO: once the renesas-cpg-mssr suspend/resume is working + * this clock dance should be removed. + */ + clk_disable(priv->clk); + clk_disable(priv->clk); + clk_enable(priv->clk); + clk_enable(priv->clk); + + /* Set reset mode to rearm the WoL logic */ ravb_write(ndev, CCC_OPC_RESET, CCC); + } /* All register have been reset to default values. * Restore all registers which where setup at probe time and -- cgit v1.2.3 From 06d7b5c3acee7b42cd9aa47165587b9c1fb992d3 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:32 -0400 Subject: net: dsa: qca8k: fix EEE init The qca8k obviously copied code from the sf2 driver as how to set EEE: if (e->eee_enabled) { p->eee_enabled = qca8k_eee_init(ds, port, phydev); if (!p->eee_enabled) ret = -EOPNOTSUPP; } But it did not use the same logic for the EEE init routine, which is "Returns 0 if EEE was not enabled, or 1 otherwise". This results in returning -EOPNOTSUPP on success and caching EEE enabled on failure. This patch fixes the returned value of qca8k_eee_init. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/qca8k.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index b3bee7eab45f..e076ab23d4df 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -666,11 +666,11 @@ qca8k_eee_init(struct dsa_switch *ds, int port, ret = phy_init_eee(phy, 0); if (ret) - return ret; + return 0; qca8k_eee_enable_set(ds, port, true); - return 0; + return 1; } static int -- cgit v1.2.3 From dca20989313aba3288e3a1d3a88007ccafb37e25 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:33 -0400 Subject: net: dsa: qca8k: enable EEE once If EEE is queried enabled, qca8k_set_eee calls qca8k_eee_enable_set twice (because it is already called in qca8k_eee_init). Fix that. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/qca8k.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index e076ab23d4df..9d6b5d2f7a4a 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -684,12 +684,13 @@ qca8k_set_eee(struct dsa_switch *ds, int port, p->eee_enabled = e->eee_enabled; - if (e->eee_enabled) { + if (!p->eee_enabled) { + qca8k_eee_enable_set(ds, port, false); + } else { p->eee_enabled = qca8k_eee_init(ds, port, phydev); if (!p->eee_enabled) ret = -EOPNOTSUPP; } - qca8k_eee_enable_set(ds, port, p->eee_enabled); return ret; } -- cgit v1.2.3 From a2444ab83eb4ada01a3b35749cfc581130027816 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:34 -0400 Subject: net: dsa: qca8k: do not cache unneeded EEE fields The qca8k driver is currently caching a bitfield of the supported member of a ethtool_eee private structure, which is unused. Only the eee_enabled field of the private ethtool_eee copy is updated, thus using p->advertised and p->lp_advertised is also erroneous. Remove the usage of these private ethtool_eee members and only rely on phy_ethtool_get_eee to assign the eee_active member. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/qca8k.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 9d6b5d2f7a4a..c316c55aabc6 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -658,12 +658,8 @@ static int qca8k_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy) { - struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - struct ethtool_eee *p = &priv->port_sts[port].eee; int ret; - p->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full); - ret = phy_init_eee(phy, 0); if (ret) return 0; @@ -705,12 +701,7 @@ qca8k_get_eee(struct dsa_switch *ds, int port, int ret; ret = phy_ethtool_get_eee(netdev->phydev, p); - if (!ret) - e->eee_active = - !!(p->supported & p->advertised & p->lp_advertised); - else - e->eee_active = 0; - + e->eee_active = p->eee_active; e->eee_enabled = p->eee_enabled; return ret; -- cgit v1.2.3 From 193da90e6075fceb48dc22d9cab0cea557fb0713 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:35 -0400 Subject: net: dsa: qca8k: empty qca8k_get_eee phy_ethtool_get_eee is already called by the DSA layer, thus remove the duplicated call in the qca8k driver. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/qca8k.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index c316c55aabc6..8cd4634c6985 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -695,16 +695,8 @@ static int qca8k_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) { - struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - struct ethtool_eee *p = &priv->port_sts[port].eee; - struct net_device *netdev = ds->ports[port].netdev; - int ret; - - ret = phy_ethtool_get_eee(netdev->phydev, p); - e->eee_active = p->eee_active; - e->eee_enabled = p->eee_enabled; - - return ret; + /* Nothing to do on the port's MAC */ + return 0; } static void -- cgit v1.2.3 From b11af0ce11d1ca3d6292cda01fc2609309247f7a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:36 -0400 Subject: net: dsa: bcm_sf2: remove unneeded supported flags The SF2 driver is masking the supported bitfield of its private copy of the ports' ethtool_eee structures. It is used nowhere, thus remove it. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 648f91b58d1e..aef475f1ce06 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -327,12 +327,8 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, static int bcm_sf2_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy) { - struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - struct ethtool_eee *p = &priv->port_sts[port].eee; int ret; - p->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full); - ret = phy_init_eee(phy, 0); if (ret) return 0; -- cgit v1.2.3 From 7f5c6d7cdda7b67b11659cb5d2cfbaf5210d2fa8 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:37 -0400 Subject: net: dsa: mv88e6xxx: call phy_init_eee It is safer to init the EEE before the DSA layer call phy_ethtool_set_eee, as sf2 and qca8k are doing. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 647d5d45c1d6..b531d4a3bab5 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -855,6 +855,12 @@ static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, struct mv88e6xxx_chip *chip = ds->priv; int err; + if (e->eee_enabled) { + err = phy_init_eee(phydev, 0); + if (err) + return err; + } + mutex_lock(&chip->reg_lock); err = mv88e6xxx_energy_detect_write(chip, port, e); mutex_unlock(&chip->reg_lock); -- cgit v1.2.3 From c48f7eb3021c993cfb15ee4963df2fd6683c2f0d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:38 -0400 Subject: net: dsa: call phy_init_eee in DSA layer All DSA drivers are calling phy_init_eee if eee_enabled is true. Move up this statement in the DSA layer to simplify the DSA drivers. qca8k does not require to cache the ethtool_eee structures from now on. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 9 +-------- drivers/net/dsa/mv88e6xxx/chip.c | 6 ------ drivers/net/dsa/qca8k.c | 31 ++----------------------------- drivers/net/dsa/qca8k.h | 1 - net/dsa/slave.c | 6 ++++++ 5 files changed, 9 insertions(+), 44 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index aef475f1ce06..9d10aac8f241 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -360,14 +360,7 @@ static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, struct ethtool_eee *p = &priv->port_sts[port].eee; p->eee_enabled = e->eee_enabled; - - if (!p->eee_enabled) { - bcm_sf2_eee_enable_set(ds, port, false); - } else { - p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev); - if (!p->eee_enabled) - return -EOPNOTSUPP; - } + bcm_sf2_eee_enable_set(ds, port, e->eee_enabled); return 0; } diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index b531d4a3bab5..647d5d45c1d6 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -855,12 +855,6 @@ static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, struct mv88e6xxx_chip *chip = ds->priv; int err; - if (e->eee_enabled) { - err = phy_init_eee(phydev, 0); - if (err) - return err; - } - mutex_lock(&chip->reg_lock); err = mv88e6xxx_energy_detect_write(chip, port, e); mutex_unlock(&chip->reg_lock); diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 8cd4634c6985..bfe0172ae6cc 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -654,41 +654,14 @@ qca8k_eee_enable_set(struct dsa_switch *ds, int port, bool enable) mutex_unlock(&priv->reg_mutex); } -static int -qca8k_eee_init(struct dsa_switch *ds, int port, - struct phy_device *phy) -{ - int ret; - - ret = phy_init_eee(phy, 0); - if (ret) - return 0; - - qca8k_eee_enable_set(ds, port, true); - - return 1; -} - static int qca8k_set_eee(struct dsa_switch *ds, int port, struct phy_device *phydev, struct ethtool_eee *e) { - struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - struct ethtool_eee *p = &priv->port_sts[port].eee; - int ret = 0; + qca8k_eee_enable_set(ds, port, e->eee_enabled); - p->eee_enabled = e->eee_enabled; - - if (!p->eee_enabled) { - qca8k_eee_enable_set(ds, port, false); - } else { - p->eee_enabled = qca8k_eee_init(ds, port, phydev); - if (!p->eee_enabled) - ret = -EOPNOTSUPP; - } - - return ret; + return 0; } static int diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h index 1ed4fac6cd6d..1cf8a920d4ff 100644 --- a/drivers/net/dsa/qca8k.h +++ b/drivers/net/dsa/qca8k.h @@ -156,7 +156,6 @@ enum qca8k_fdb_cmd { }; struct ar8xxx_port_status { - struct ethtool_eee eee; int enabled; }; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 7df55d597740..ad5caaf384d7 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -659,6 +659,12 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e) if (ret) return ret; + if (e->eee_enabled) { + ret = phy_init_eee(p->phy, 0); + if (ret) + return ret; + } + return phy_ethtool_set_eee(p->phy, e); } -- cgit v1.2.3 From 46587e4a312780a63132483bc8678c397eca6aff Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:39 -0400 Subject: net: dsa: remove PHY device argument from .set_eee The DSA switch operations for EEE are only meant to configure a port's MAC EEE settings. The port's PHY EEE settings are accessed by the DSA layer and must be made available via a proper PHY driver. In order to reduce this confusion, remove the phy_device argument from the .set_eee operation. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 1 - drivers/net/dsa/mv88e6xxx/chip.c | 2 +- drivers/net/dsa/qca8k.c | 14 +++----------- include/net/dsa.h | 1 - net/dsa/slave.c | 2 +- 5 files changed, 5 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 9d10aac8f241..ce886345d8d2 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -353,7 +353,6 @@ static int bcm_sf2_sw_get_eee(struct dsa_switch *ds, int port, } static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, - struct phy_device *phydev, struct ethtool_eee *e) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 647d5d45c1d6..aaa96487f21f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -850,7 +850,7 @@ static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, } static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, - struct phy_device *phydev, struct ethtool_eee *e) + struct ethtool_eee *e) { struct mv88e6xxx_chip *chip = ds->priv; int err; diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index bfe0172ae6cc..e209e229ed4c 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -637,8 +637,8 @@ qca8k_get_sset_count(struct dsa_switch *ds) return ARRAY_SIZE(ar8327_mib); } -static void -qca8k_eee_enable_set(struct dsa_switch *ds, int port, bool enable) +static int +qca8k_set_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); @@ -646,20 +646,12 @@ qca8k_eee_enable_set(struct dsa_switch *ds, int port, bool enable) mutex_lock(&priv->reg_mutex); reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL); - if (enable) + if (eee->eee_enabled) reg |= lpi_en; else reg &= ~lpi_en; qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); mutex_unlock(&priv->reg_mutex); -} - -static int -qca8k_set_eee(struct dsa_switch *ds, int port, - struct phy_device *phydev, - struct ethtool_eee *e) -{ - qca8k_eee_enable_set(ds, port, e->eee_enabled); return 0; } diff --git a/include/net/dsa.h b/include/net/dsa.h index 88da272d20d0..ce46db323394 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -335,7 +335,6 @@ struct dsa_switch_ops { * EEE setttings */ int (*set_eee)(struct dsa_switch *ds, int port, - struct phy_device *phydev, struct ethtool_eee *e); int (*get_eee)(struct dsa_switch *ds, int port, struct ethtool_eee *e); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index ad5caaf384d7..9ddc584e70b0 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -655,7 +655,7 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e) if (!ds->ops->set_eee) return -EOPNOTSUPP; - ret = ds->ops->set_eee(ds, p->dp->index, p->phy, e); + ret = ds->ops->set_eee(ds, p->dp->index, e); if (ret) return ret; -- cgit v1.2.3 From 5480db6985640a44ff904d4b6ef7ec668b785ec2 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:40 -0400 Subject: net: dsa: mv88e6xxx: remove EEE support The PHY's EEE settings are already accessed by the DSA layer through the Marvell PHY driver and there is nothing to be done for switch's MACs. Remove all EEE support from the mv88e6xxx driver and simply return 0 from the EEE ops. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 74 ++----------------------------- drivers/net/dsa/mv88e6xxx/chip.h | 6 --- drivers/net/dsa/mv88e6xxx/phy.c | 96 ---------------------------------------- drivers/net/dsa/mv88e6xxx/phy.h | 22 --------- drivers/net/dsa/mv88e6xxx/port.c | 17 ------- drivers/net/dsa/mv88e6xxx/port.h | 3 -- 6 files changed, 4 insertions(+), 214 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index aaa96487f21f..aa0c5493fb9d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -810,56 +810,18 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, mutex_unlock(&chip->reg_lock); } -static int mv88e6xxx_energy_detect_read(struct mv88e6xxx_chip *chip, int port, - struct ethtool_eee *eee) -{ - int err; - - if (!chip->info->ops->phy_energy_detect_read) - return -EOPNOTSUPP; - - /* assign eee->eee_enabled and eee->tx_lpi_enabled */ - err = chip->info->ops->phy_energy_detect_read(chip, port, eee); - if (err) - return err; - - /* assign eee->eee_active */ - return mv88e6xxx_port_status_eee(chip, port, eee); -} - -static int mv88e6xxx_energy_detect_write(struct mv88e6xxx_chip *chip, int port, - struct ethtool_eee *eee) -{ - if (!chip->info->ops->phy_energy_detect_write) - return -EOPNOTSUPP; - - return chip->info->ops->phy_energy_detect_write(chip, port, eee); -} - static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) { - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mutex_lock(&chip->reg_lock); - err = mv88e6xxx_energy_detect_read(chip, port, e); - mutex_unlock(&chip->reg_lock); - - return err; + /* Nothing to do on the port's MAC */ + return 0; } static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) { - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mutex_lock(&chip->reg_lock); - err = mv88e6xxx_energy_detect_write(chip, port, e); - mutex_unlock(&chip->reg_lock); - - return err; + /* Nothing to do on the port's MAC */ + return 0; } static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) @@ -2521,8 +2483,6 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2648,8 +2608,6 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, @@ -2719,8 +2677,6 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, @@ -2784,8 +2740,6 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2821,8 +2775,6 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2858,8 +2810,6 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2895,8 +2845,6 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, @@ -2933,8 +2881,6 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -2971,8 +2917,6 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, @@ -3006,8 +2950,6 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, @@ -3039,8 +2981,6 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -3142,8 +3082,6 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6352_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6352_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, @@ -3180,8 +3118,6 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, @@ -3220,8 +3156,6 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, - .phy_energy_detect_read = mv88e6390_phy_energy_detect_read, - .phy_energy_detect_write = mv88e6390_phy_energy_detect_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 9111e1316250..334f6f7544ba 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -239,12 +239,6 @@ struct mv88e6xxx_ops { struct mii_bus *bus, int addr, int reg, u16 val); - /* Copper Energy Detect operations */ - int (*phy_energy_detect_read)(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee); - int (*phy_energy_detect_write)(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee); - /* Priority Override Table operations */ int (*pot_clear)(struct mv88e6xxx_chip *chip); diff --git a/drivers/net/dsa/mv88e6xxx/phy.c b/drivers/net/dsa/mv88e6xxx/phy.c index 317ae89cfa68..436668bd50dc 100644 --- a/drivers/net/dsa/mv88e6xxx/phy.c +++ b/drivers/net/dsa/mv88e6xxx/phy.c @@ -246,99 +246,3 @@ int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip) { return mv88e6xxx_phy_ppu_enable(chip); } - -/* Page 0, Register 16: Copper Specific Control Register 1 */ - -int mv88e6352_phy_energy_detect_read(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee) -{ - u16 val; - int err; - - err = mv88e6xxx_phy_read(chip, phy, MV88E6XXX_PHY_CSCTL1, &val); - if (err) - return err; - - val &= MV88E6352_PHY_CSCTL1_ENERGY_DETECT_MASK; - - eee->eee_enabled = false; - eee->tx_lpi_enabled = false; - - switch (val) { - case MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP: - eee->tx_lpi_enabled = true; - /* fall through... */ - case MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV: - eee->eee_enabled = true; - } - - return 0; -} - -int mv88e6352_phy_energy_detect_write(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee) -{ - u16 val; - int err; - - err = mv88e6xxx_phy_read(chip, phy, MV88E6XXX_PHY_CSCTL1, &val); - if (err) - return err; - - val &= ~MV88E6352_PHY_CSCTL1_ENERGY_DETECT_MASK; - - if (eee->eee_enabled) - val |= MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV; - if (eee->tx_lpi_enabled) - val |= MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP; - - return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_CSCTL1, val); -} - -int mv88e6390_phy_energy_detect_read(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee) -{ - u16 val; - int err; - - err = mv88e6xxx_phy_read(chip, phy, MV88E6XXX_PHY_CSCTL1, &val); - if (err) - return err; - - val &= MV88E6390_PHY_CSCTL1_ENERGY_DETECT_MASK; - - eee->eee_enabled = false; - eee->tx_lpi_enabled = false; - - switch (val) { - case MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_AUTO: - case MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_SW: - eee->tx_lpi_enabled = true; - /* fall through... */ - case MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_AUTO: - case MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_SW: - eee->eee_enabled = true; - } - - return 0; -} - -int mv88e6390_phy_energy_detect_write(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee) -{ - u16 val; - int err; - - err = mv88e6xxx_phy_read(chip, phy, MV88E6XXX_PHY_CSCTL1, &val); - if (err) - return err; - - val &= ~MV88E6390_PHY_CSCTL1_ENERGY_DETECT_MASK; - - if (eee->eee_enabled) - val |= MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_AUTO; - if (eee->tx_lpi_enabled) - val |= MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_AUTO; - - return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_CSCTL1, val); -} diff --git a/drivers/net/dsa/mv88e6xxx/phy.h b/drivers/net/dsa/mv88e6xxx/phy.h index 988802799ad6..556b74a0502a 100644 --- a/drivers/net/dsa/mv88e6xxx/phy.h +++ b/drivers/net/dsa/mv88e6xxx/phy.h @@ -17,19 +17,6 @@ #define MV88E6XXX_PHY_PAGE 0x16 #define MV88E6XXX_PHY_PAGE_COPPER 0x00 -/* Page 0, Register 16: Copper Specific Control Register 1 */ -#define MV88E6XXX_PHY_CSCTL1 16 -#define MV88E6352_PHY_CSCTL1_ENERGY_DETECT_MASK 0x0300 -#define MV88E6352_PHY_CSCTL1_ENERGY_DETECT_OFF_MASK 0x0100 /* 0x */ -#define MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV 0x0200 -#define MV88E6352_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP 0x0300 -#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_MASK 0x0380 -#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_OFF_MASK 0x0180 /* 0xx */ -#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_AUTO 0x0200 -#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_RCV_SW 0x0280 -#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_AUTO 0x0300 -#define MV88E6390_PHY_CSCTL1_ENERGY_DETECT_SENSE_NLP_SW 0x0380 - /* PHY Registers accesses implementations */ int mv88e6165_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, int addr, int reg, u16 *val); @@ -53,13 +40,4 @@ void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip); void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip); int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip); -int mv88e6352_phy_energy_detect_read(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee); -int mv88e6352_phy_energy_detect_write(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee); -int mv88e6390_phy_energy_detect_read(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee); -int mv88e6390_phy_energy_detect_write(struct mv88e6xxx_chip *chip, int phy, - struct ethtool_eee *eee); - #endif /*_MV88E6XXX_PHY_H */ diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 2837a9128557..a7801f6668a5 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -35,23 +35,6 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, return mv88e6xxx_write(chip, addr, reg, val); } -/* Offset 0x00: Port Status Register */ - -int mv88e6xxx_port_status_eee(struct mv88e6xxx_chip *chip, int port, - struct ethtool_eee *eee) -{ - u16 val; - int err; - - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &val); - if (err) - return err; - - eee->eee_active = !!(val & MV88E6352_PORT_STS_EEE); - - return 0; -} - /* Offset 0x01: MAC (or PCS or Physical) Control Register * * Link, Duplex and Flow Control have one force bit, one value bit. diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 6fcab309cd85..b16d5f0e6e9c 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -241,9 +241,6 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, u16 val); -int mv88e6xxx_port_status_eee(struct mv88e6xxx_chip *chip, int port, - struct ethtool_eee *eee); - int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, -- cgit v1.2.3 From 08f500610f39809c107f206cba1f799c98c38054 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:41 -0400 Subject: net: dsa: rename switch EEE ops To avoid confusion with the PHY EEE settings, rename the .set_eee and .get_eee ops to respectively .set_mac_eee and .get_mac_eee. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 12 ++++++------ drivers/net/dsa/mv88e6xxx/chip.c | 12 ++++++------ drivers/net/dsa/qca8k.c | 9 ++++----- include/net/dsa.h | 10 +++++----- net/dsa/slave.c | 8 ++++---- 5 files changed, 25 insertions(+), 26 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index ce886345d8d2..6bbfa6ea1efb 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -338,8 +338,8 @@ static int bcm_sf2_eee_init(struct dsa_switch *ds, int port, return 1; } -static int bcm_sf2_sw_get_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +static int bcm_sf2_sw_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_eee *p = &priv->port_sts[port].eee; @@ -352,8 +352,8 @@ static int bcm_sf2_sw_get_eee(struct dsa_switch *ds, int port, return 0; } -static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +static int bcm_sf2_sw_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_eee *p = &priv->port_sts[port].eee; @@ -1011,8 +1011,8 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .set_wol = bcm_sf2_sw_set_wol, .port_enable = bcm_sf2_port_setup, .port_disable = bcm_sf2_port_disable, - .get_eee = bcm_sf2_sw_get_eee, - .set_eee = bcm_sf2_sw_set_eee, + .get_mac_eee = bcm_sf2_sw_get_mac_eee, + .set_mac_eee = bcm_sf2_sw_set_mac_eee, .port_bridge_join = b53_br_join, .port_bridge_leave = b53_br_leave, .port_stp_state_set = b53_br_set_stp_state, diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index aa0c5493fb9d..521738c4cd17 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -810,15 +810,15 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, mutex_unlock(&chip->reg_lock); } -static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +static int mv88e6xxx_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { /* Nothing to do on the port's MAC */ return 0; } -static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +static int mv88e6xxx_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { /* Nothing to do on the port's MAC */ return 0; @@ -3890,8 +3890,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .get_sset_count = mv88e6xxx_get_sset_count, .port_enable = mv88e6xxx_port_enable, .port_disable = mv88e6xxx_port_disable, - .set_eee = mv88e6xxx_set_eee, - .get_eee = mv88e6xxx_get_eee, + .get_mac_eee = mv88e6xxx_get_mac_eee, + .set_mac_eee = mv88e6xxx_set_mac_eee, .get_eeprom_len = mv88e6xxx_get_eeprom_len, .get_eeprom = mv88e6xxx_get_eeprom, .set_eeprom = mv88e6xxx_set_eeprom, diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index e209e229ed4c..36c169b0c705 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -638,7 +638,7 @@ qca8k_get_sset_count(struct dsa_switch *ds) } static int -qca8k_set_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) +qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); @@ -657,8 +657,7 @@ qca8k_set_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) } static int -qca8k_get_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) { /* Nothing to do on the port's MAC */ return 0; @@ -863,8 +862,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .phy_write = qca8k_phy_write, .get_ethtool_stats = qca8k_get_ethtool_stats, .get_sset_count = qca8k_get_sset_count, - .get_eee = qca8k_get_eee, - .set_eee = qca8k_set_eee, + .get_mac_eee = qca8k_get_mac_eee, + .set_mac_eee = qca8k_set_mac_eee, .port_enable = qca8k_port_enable, .port_disable = qca8k_port_disable, .port_stp_state_set = qca8k_port_stp_state_set, diff --git a/include/net/dsa.h b/include/net/dsa.h index ce46db323394..0b1a0622b33c 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -332,12 +332,12 @@ struct dsa_switch_ops { struct phy_device *phy); /* - * EEE setttings + * Port's MAC EEE settings */ - int (*set_eee)(struct dsa_switch *ds, int port, - struct ethtool_eee *e); - int (*get_eee)(struct dsa_switch *ds, int port, - struct ethtool_eee *e); + int (*set_mac_eee)(struct dsa_switch *ds, int port, + struct ethtool_eee *e); + int (*get_mac_eee)(struct dsa_switch *ds, int port, + struct ethtool_eee *e); /* EEPROM access */ int (*get_eeprom_len)(struct dsa_switch *ds); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 9ddc584e70b0..cc4bad3dadb4 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -652,10 +652,10 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e) if (!p->phy) return -ENODEV; - if (!ds->ops->set_eee) + if (!ds->ops->set_mac_eee) return -EOPNOTSUPP; - ret = ds->ops->set_eee(ds, p->dp->index, e); + ret = ds->ops->set_mac_eee(ds, p->dp->index, e); if (ret) return ret; @@ -678,10 +678,10 @@ static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e) if (!p->phy) return -ENODEV; - if (!ds->ops->get_eee) + if (!ds->ops->get_mac_eee) return -EOPNOTSUPP; - ret = ds->ops->get_eee(ds, p->dp->index, e); + ret = ds->ops->get_mac_eee(ds, p->dp->index, e); if (ret) return ret; -- cgit v1.2.3 From 956a25c9f120343a9b6ab6564539158ec7237181 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 31 Jul 2017 10:30:54 -0700 Subject: hyperv: netvsc: Neaten netvsc_send_pkt by using a temporary Repeated dereference of nvmsg.msg.v1_msg.send_rndis_pkt can be shortened by using a temporary. Do so. No change in object code. Miscellanea: o Use * const for rpkt and nvchan Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index c64934c64dca..9598220b3bcc 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -747,8 +747,10 @@ static inline int netvsc_send_pkt( struct sk_buff *skb) { struct nvsp_message nvmsg; - struct netvsc_channel *nvchan - = &net_device->chan_table[packet->q_idx]; + struct nvsp_1_message_send_rndis_packet * const rpkt = + &nvmsg.msg.v1_msg.send_rndis_pkt; + struct netvsc_channel * const nvchan = + &net_device->chan_table[packet->q_idx]; struct vmbus_channel *out_channel = nvchan->channel; struct net_device *ndev = hv_get_drvdata(device); struct netdev_queue *txq = netdev_get_tx_queue(ndev, packet->q_idx); @@ -757,21 +759,16 @@ static inline int netvsc_send_pkt( u32 ring_avail = hv_ringbuf_avail_percent(&out_channel->outbound); nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; - if (skb != NULL) { - /* 0 is RMC_DATA; */ - nvmsg.msg.v1_msg.send_rndis_pkt.channel_type = 0; - } else { - /* 1 is RMC_CONTROL; */ - nvmsg.msg.v1_msg.send_rndis_pkt.channel_type = 1; - } + if (skb) + rpkt->channel_type = 0; /* 0 is RMC_DATA */ + else + rpkt->channel_type = 1; /* 1 is RMC_CONTROL */ - nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_index = - packet->send_buf_index; + rpkt->send_buf_section_index = packet->send_buf_index; if (packet->send_buf_index == NETVSC_INVALID_INDEX) - nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_size = 0; + rpkt->send_buf_section_size = 0; else - nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_size = - packet->total_data_buflen; + rpkt->send_buf_section_size = packet->total_data_buflen; req_id = (ulong)skb; -- cgit v1.2.3 From aa6c16c43b73b0e4042d84fece85111da1a8087f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 1 Aug 2017 13:50:56 +0200 Subject: net: bcmgenet: drop COMPILE_TEST dependency The last patch added the dependency on 'OF && HAS_IOMEM' but left COMPILE_TEST as an alternative, which kind of defeats the purpose of adding the dependency, we still get randconfig build warnings: warning: (NET_DSA_BCM_SF2 && BCMGENET) selects MDIO_BCM_UNIMAC which has unmet direct dependencies (NETDEVICES && MDIO_BUS && HAS_IOMEM && OF_MDIO) For compile-testing purposes, we don't really need this anyway, as CONFIG_OF can be enabled on all architectures, and HAS_IOMEM is present on all architectures we do meaningful compile-testing on (the exception being arch/um). This makes both OF and HAS_IOMEM hard dependencies. Fixes: 5af74bb4fcf8 ("net: bcmgenet: Add dependency on HAS_IOMEM && OF") Signed-off-by: Arnd Bergmann Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 45775399cab6..1456cb18f830 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -61,7 +61,7 @@ config BCM63XX_ENET config BCMGENET tristate "Broadcom GENET internal MAC support" - depends on (OF && HAS_IOMEM) || COMPILE_TEST + depends on OF && HAS_IOMEM select MII select PHYLIB select FIXED_PHY -- cgit v1.2.3 From cf19a8c3d515d8c9d88d804437c6ac291eeaa2aa Mon Sep 17 00:00:00 2001 From: Derek Chickles Date: Tue, 1 Aug 2017 15:05:07 -0700 Subject: liquidio: set sriov_totalvfs correctly The file /sys/devices/pci000.../sriov_totalvfs is showing a wrong value. Fix it by calling pci_sriov_set_totalvfs() to set the total number of VFs available after calculations for the number of PF and VF queues are made. Signed-off-by: Derek Chickles Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 1d8fefa9ce64..39a8dca35ffa 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1825,6 +1825,11 @@ static int octeon_chip_specific_setup(struct octeon_device *oct) case OCTEON_CN23XX_PCIID_PF: oct->chip_id = OCTEON_CN23XX_PF_VID; ret = setup_cn23xx_octeon_pf_device(oct); +#ifdef CONFIG_PCI_IOV + if (!ret) + pci_sriov_set_totalvfs(oct->pci_dev, + oct->sriov_info.max_vfs); +#endif s = "CN23XX"; break; -- cgit v1.2.3 From 0c195567a8f6e82ea5535cd9f1d54a1626dd233e Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 1 Aug 2017 19:58:53 -0700 Subject: netvsc: transparent VF management This patch implements transparent fail over from synthetic NIC to SR-IOV virtual function NIC in Hyper-V environment. It is a better alternative to using bonding as is done now. Instead, the receive and transmit fail over is done internally inside the driver. Using bonding driver has lots of issues because it depends on the script being run early enough in the boot process and with sufficient information to make the association. This patch moves all that functionality into the kernel. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 12 ++ drivers/net/hyperv/netvsc_drv.c | 419 +++++++++++++++++++++++++++++++--------- 2 files changed, 342 insertions(+), 89 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index f2cef5aaed1f..c701b059c5ac 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -680,6 +680,15 @@ struct netvsc_ethtool_stats { unsigned long tx_busy; }; +struct netvsc_vf_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + struct u64_stats_sync syncp; + u32 tx_dropped; +}; + struct netvsc_reconfig { struct list_head list; u32 event; @@ -713,6 +722,9 @@ struct net_device_context { /* State to manage the associated VF interface. */ struct net_device __rcu *vf_netdev; + struct netvsc_vf_pcpu_stats __percpu *vf_stats; + struct work_struct vf_takeover; + struct work_struct vf_notify; /* 1: allocated, serial number is valid. 0: not allocated */ u32 vf_alloc; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 9453eef6d09f..c71728d82049 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -71,6 +72,7 @@ static void netvsc_set_multicast_list(struct net_device *net) static int netvsc_open(struct net_device *net) { struct net_device_context *ndev_ctx = netdev_priv(net); + struct net_device *vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); struct netvsc_device *nvdev = rtnl_dereference(ndev_ctx->nvdev); struct rndis_device *rdev; int ret = 0; @@ -87,15 +89,29 @@ static int netvsc_open(struct net_device *net) netif_tx_wake_all_queues(net); rdev = nvdev->extension; - if (!rdev->link_state && !ndev_ctx->datapath) + + if (!rdev->link_state) netif_carrier_on(net); - return ret; + if (vf_netdev) { + /* Setting synthetic device up transparently sets + * slave as up. If open fails, then slave will be + * still be offline (and not used). + */ + ret = dev_open(vf_netdev); + if (ret) + netdev_warn(net, + "unable to open slave: %s: %d\n", + vf_netdev->name, ret); + } + return 0; } static int netvsc_close(struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); + struct net_device *vf_netdev + = rtnl_dereference(net_device_ctx->vf_netdev); struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); int ret; u32 aread, i, msec = 10, retry = 0, retry_max = 20; @@ -141,6 +157,9 @@ static int netvsc_close(struct net_device *net) ret = -ETIMEDOUT; } + if (vf_netdev) + dev_close(vf_netdev); + return ret; } @@ -224,13 +243,11 @@ static inline int netvsc_get_tx_queue(struct net_device *ndev, * * TODO support XPS - but get_xps_queue not exported */ -static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, - void *accel_priv, select_queue_fallback_t fallback) +static u16 netvsc_pick_tx(struct net_device *ndev, struct sk_buff *skb) { - unsigned int num_tx_queues = ndev->real_num_tx_queues; int q_idx = sk_tx_queue_get(skb->sk); - if (q_idx < 0 || skb->ooo_okay) { + if (q_idx < 0 || skb->ooo_okay || q_idx >= ndev->real_num_tx_queues) { /* If forwarding a packet, we use the recorded queue when * available for better cache locality. */ @@ -240,12 +257,33 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, q_idx = netvsc_get_tx_queue(ndev, skb, q_idx); } - while (unlikely(q_idx >= num_tx_queues)) - q_idx -= num_tx_queues; - return q_idx; } +static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, + void *accel_priv, + select_queue_fallback_t fallback) +{ + struct net_device_context *ndc = netdev_priv(ndev); + struct net_device *vf_netdev; + u16 txq; + + rcu_read_lock(); + vf_netdev = rcu_dereference(ndc->vf_netdev); + if (vf_netdev) { + txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0; + qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping; + } else { + txq = netvsc_pick_tx(ndev, skb); + } + rcu_read_unlock(); + + while (unlikely(txq >= ndev->real_num_tx_queues)) + txq -= ndev->real_num_tx_queues; + + return txq; +} + static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, struct hv_page_buffer *pb) { @@ -367,6 +405,33 @@ static u32 net_checksum_info(struct sk_buff *skb) return TRANSPORT_INFO_NOT_IP; } +/* Send skb on the slave VF device. */ +static int netvsc_vf_xmit(struct net_device *net, struct net_device *vf_netdev, + struct sk_buff *skb) +{ + struct net_device_context *ndev_ctx = netdev_priv(net); + unsigned int len = skb->len; + int rc; + + skb->dev = vf_netdev; + skb->queue_mapping = qdisc_skb_cb(skb)->slave_dev_queue_mapping; + + rc = dev_queue_xmit(skb); + if (likely(rc == NET_XMIT_SUCCESS || rc == NET_XMIT_CN)) { + struct netvsc_vf_pcpu_stats *pcpu_stats + = this_cpu_ptr(ndev_ctx->vf_stats); + + u64_stats_update_begin(&pcpu_stats->syncp); + pcpu_stats->tx_packets++; + pcpu_stats->tx_bytes += len; + u64_stats_update_end(&pcpu_stats->syncp); + } else { + this_cpu_inc(ndev_ctx->vf_stats->tx_dropped); + } + + return rc; +} + static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); @@ -375,11 +440,20 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) unsigned int num_data_pgs; struct rndis_message *rndis_msg; struct rndis_packet *rndis_pkt; + struct net_device *vf_netdev; u32 rndis_msg_size; struct rndis_per_packet_info *ppi; u32 hash; struct hv_page_buffer pb[MAX_PAGE_BUFFER_COUNT]; + /* if VF is present and up then redirect packets + * already called with rcu_read_lock_bh + */ + vf_netdev = rcu_dereference_bh(net_device_ctx->vf_netdev); + if (vf_netdev && netif_running(vf_netdev) && + !netpoll_tx_running(net)) + return netvsc_vf_xmit(net, vf_netdev, skb); + /* We will atmost need two pages to describe the rndis * header. We can only transmit MAX_PAGE_BUFFER_COUNT number * of pages in a single packet. If skb is scattered around @@ -658,29 +732,18 @@ int netvsc_recv_callback(struct net_device *net, struct netvsc_device *net_device; u16 q_idx = channel->offermsg.offer.sub_channel_index; struct netvsc_channel *nvchan; - struct net_device *vf_netdev; struct sk_buff *skb; struct netvsc_stats *rx_stats; if (net->reg_state != NETREG_REGISTERED) return NVSP_STAT_FAIL; - /* - * If necessary, inject this packet into the VF interface. - * On Hyper-V, multicast and brodcast packets are only delivered - * to the synthetic interface (after subjecting these to - * policy filters on the host). Deliver these via the VF - * interface in the guest. - */ rcu_read_lock(); net_device = rcu_dereference(net_device_ctx->nvdev); if (unlikely(!net_device)) goto drop; nvchan = &net_device->chan_table[q_idx]; - vf_netdev = rcu_dereference(net_device_ctx->vf_netdev); - if (vf_netdev && (vf_netdev->flags & IFF_UP)) - net = vf_netdev; /* Allocate a skb - TODO direct I/O to pages? */ skb = netvsc_alloc_recv_skb(net, &nvchan->napi, @@ -692,8 +755,7 @@ drop: return NVSP_STAT_FAIL; } - if (net != vf_netdev) - skb_record_rx_queue(skb, q_idx); + skb_record_rx_queue(skb, q_idx); /* * Even if injecting the packet, record the statistics @@ -853,6 +915,7 @@ static int netvsc_set_link_ksettings(struct net_device *dev, static int netvsc_change_mtu(struct net_device *ndev, int mtu) { struct net_device_context *ndevctx = netdev_priv(ndev); + struct net_device *vf_netdev = rtnl_dereference(ndevctx->vf_netdev); struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev); struct hv_device *hdev = ndevctx->device_ctx; int orig_mtu = ndev->mtu; @@ -863,6 +926,13 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) if (!nvdev || nvdev->destroy) return -ENODEV; + /* Change MTU of underlying VF netdev first. */ + if (vf_netdev) { + ret = dev_set_mtu(vf_netdev, mtu); + if (ret) + return ret; + } + netif_device_detach(ndev); was_opened = rndis_filter_opened(nvdev); if (was_opened) @@ -883,6 +953,9 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) /* Attempt rollback to original MTU */ ndev->mtu = orig_mtu; rndis_filter_device_add(hdev, &device_info); + + if (vf_netdev) + dev_set_mtu(vf_netdev, orig_mtu); } if (was_opened) @@ -896,16 +969,56 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) return ret; } +static void netvsc_get_vf_stats(struct net_device *net, + struct netvsc_vf_pcpu_stats *tot) +{ + struct net_device_context *ndev_ctx = netdev_priv(net); + int i; + + memset(tot, 0, sizeof(*tot)); + + for_each_possible_cpu(i) { + const struct netvsc_vf_pcpu_stats *stats + = per_cpu_ptr(ndev_ctx->vf_stats, i); + u64 rx_packets, rx_bytes, tx_packets, tx_bytes; + unsigned int start; + + do { + start = u64_stats_fetch_begin_irq(&stats->syncp); + rx_packets = stats->rx_packets; + tx_packets = stats->tx_packets; + rx_bytes = stats->rx_bytes; + tx_bytes = stats->tx_bytes; + } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + + tot->rx_packets += rx_packets; + tot->tx_packets += tx_packets; + tot->rx_bytes += rx_bytes; + tot->tx_bytes += tx_bytes; + tot->tx_dropped += stats->tx_dropped; + } +} + static void netvsc_get_stats64(struct net_device *net, struct rtnl_link_stats64 *t) { struct net_device_context *ndev_ctx = netdev_priv(net); struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev); - int i; + struct netvsc_vf_pcpu_stats vf_tot; + int i; if (!nvdev) return; + netdev_stats_to_stats64(t, &net->stats); + + netvsc_get_vf_stats(net, &vf_tot); + t->rx_packets += vf_tot.rx_packets; + t->tx_packets += vf_tot.tx_packets; + t->rx_bytes += vf_tot.rx_bytes; + t->tx_bytes += vf_tot.tx_bytes; + t->tx_dropped += vf_tot.tx_dropped; + for (i = 0; i < nvdev->num_chn; i++) { const struct netvsc_channel *nvchan = &nvdev->chan_table[i]; const struct netvsc_stats *stats; @@ -934,12 +1047,6 @@ static void netvsc_get_stats64(struct net_device *net, t->rx_packets += packets; t->multicast += multicast; } - - t->tx_dropped = net->stats.tx_dropped; - t->tx_errors = net->stats.tx_errors; - - t->rx_dropped = net->stats.rx_dropped; - t->rx_errors = net->stats.rx_errors; } static int netvsc_set_mac_addr(struct net_device *ndev, void *p) @@ -980,9 +1087,16 @@ static const struct { { "tx_no_space", offsetof(struct netvsc_ethtool_stats, tx_no_space) }, { "tx_too_big", offsetof(struct netvsc_ethtool_stats, tx_too_big) }, { "tx_busy", offsetof(struct netvsc_ethtool_stats, tx_busy) }, +}, vf_stats[] = { + { "vf_rx_packets", offsetof(struct netvsc_vf_pcpu_stats, rx_packets) }, + { "vf_rx_bytes", offsetof(struct netvsc_vf_pcpu_stats, rx_bytes) }, + { "vf_tx_packets", offsetof(struct netvsc_vf_pcpu_stats, tx_packets) }, + { "vf_tx_bytes", offsetof(struct netvsc_vf_pcpu_stats, tx_bytes) }, + { "vf_tx_dropped", offsetof(struct netvsc_vf_pcpu_stats, tx_dropped) }, }; #define NETVSC_GLOBAL_STATS_LEN ARRAY_SIZE(netvsc_stats) +#define NETVSC_VF_STATS_LEN ARRAY_SIZE(vf_stats) /* 4 statistics per queue (rx/tx packets/bytes) */ #define NETVSC_QUEUE_STATS_LEN(dev) ((dev)->num_chn * 4) @@ -997,7 +1111,9 @@ static int netvsc_get_sset_count(struct net_device *dev, int string_set) switch (string_set) { case ETH_SS_STATS: - return NETVSC_GLOBAL_STATS_LEN + NETVSC_QUEUE_STATS_LEN(nvdev); + return NETVSC_GLOBAL_STATS_LEN + + NETVSC_VF_STATS_LEN + + NETVSC_QUEUE_STATS_LEN(nvdev); default: return -EINVAL; } @@ -1010,6 +1126,7 @@ static void netvsc_get_ethtool_stats(struct net_device *dev, struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev); const void *nds = &ndc->eth_stats; const struct netvsc_stats *qstats; + struct netvsc_vf_pcpu_stats sum; unsigned int start; u64 packets, bytes; int i, j; @@ -1020,6 +1137,10 @@ static void netvsc_get_ethtool_stats(struct net_device *dev, for (i = 0; i < NETVSC_GLOBAL_STATS_LEN; i++) data[i] = *(unsigned long *)(nds + netvsc_stats[i].offset); + netvsc_get_vf_stats(dev, &sum); + for (j = 0; j < NETVSC_VF_STATS_LEN; j++) + data[i++] = *(u64 *)((void *)&sum + vf_stats[j].offset); + for (j = 0; j < nvdev->num_chn; j++) { qstats = &nvdev->chan_table[j].tx_stats; @@ -1054,11 +1175,16 @@ static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data) switch (stringset) { case ETH_SS_STATS: - for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++) - memcpy(p + i * ETH_GSTRING_LEN, - netvsc_stats[i].name, ETH_GSTRING_LEN); + for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++) { + memcpy(p, netvsc_stats[i].name, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + for (i = 0; i < ARRAY_SIZE(vf_stats); i++) { + memcpy(p, vf_stats[i].name, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } - p += i * ETH_GSTRING_LEN; for (i = 0; i < nvdev->num_chn; i++) { sprintf(p, "tx_queue_%u_packets", i); p += ETH_GSTRING_LEN; @@ -1298,8 +1424,7 @@ static void netvsc_link_change(struct work_struct *w) case RNDIS_STATUS_MEDIA_CONNECT: if (rdev->link_state) { rdev->link_state = false; - if (!ndev_ctx->datapath) - netif_carrier_on(net); + netif_carrier_on(net); netif_tx_wake_all_queues(net); } else { notify = true; @@ -1386,6 +1511,104 @@ static struct net_device *get_netvsc_byref(struct net_device *vf_netdev) return NULL; } +/* Called when VF is injecting data into network stack. + * Change the associated network device from VF to netvsc. + * note: already called with rcu_read_lock + */ +static rx_handler_result_t netvsc_vf_handle_frame(struct sk_buff **pskb) +{ + struct sk_buff *skb = *pskb; + struct net_device *ndev = rcu_dereference(skb->dev->rx_handler_data); + struct net_device_context *ndev_ctx = netdev_priv(ndev); + struct netvsc_vf_pcpu_stats *pcpu_stats + = this_cpu_ptr(ndev_ctx->vf_stats); + + skb->dev = ndev; + + u64_stats_update_begin(&pcpu_stats->syncp); + pcpu_stats->rx_packets++; + pcpu_stats->rx_bytes += skb->len; + u64_stats_update_end(&pcpu_stats->syncp); + + return RX_HANDLER_ANOTHER; +} + +static int netvsc_vf_join(struct net_device *vf_netdev, + struct net_device *ndev) +{ + struct net_device_context *ndev_ctx = netdev_priv(ndev); + int ret; + + ret = netdev_rx_handler_register(vf_netdev, + netvsc_vf_handle_frame, ndev); + if (ret != 0) { + netdev_err(vf_netdev, + "can not register netvsc VF receive handler (err = %d)\n", + ret); + goto rx_handler_failed; + } + + ret = netdev_upper_dev_link(vf_netdev, ndev); + if (ret != 0) { + netdev_err(vf_netdev, + "can not set master device %s (err = %d)\n", + ndev->name, ret); + goto upper_link_failed; + } + + /* set slave flag before open to prevent IPv6 addrconf */ + vf_netdev->flags |= IFF_SLAVE; + + schedule_work(&ndev_ctx->vf_takeover); + + netdev_info(vf_netdev, "joined to %s\n", ndev->name); + return 0; + +upper_link_failed: + netdev_rx_handler_unregister(vf_netdev); +rx_handler_failed: + return ret; +} + +static void __netvsc_vf_setup(struct net_device *ndev, + struct net_device *vf_netdev) +{ + int ret; + + call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); + + /* Align MTU of VF with master */ + ret = dev_set_mtu(vf_netdev, ndev->mtu); + if (ret) + netdev_warn(vf_netdev, + "unable to change mtu to %u\n", ndev->mtu); + + if (netif_running(ndev)) { + ret = dev_open(vf_netdev); + if (ret) + netdev_warn(vf_netdev, + "unable to open: %d\n", ret); + } +} + +/* Setup VF as slave of the synthetic device. + * Runs in workqueue to avoid recursion in netlink callbacks. + */ +static void netvsc_vf_setup(struct work_struct *w) +{ + struct net_device_context *ndev_ctx + = container_of(w, struct net_device_context, vf_takeover); + struct net_device *ndev = hv_get_drvdata(ndev_ctx->device_ctx); + struct net_device *vf_netdev; + + rtnl_lock(); + vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); + if (vf_netdev) + __netvsc_vf_setup(ndev, vf_netdev); + + rtnl_unlock(); +} + static int netvsc_register_vf(struct net_device *vf_netdev) { struct net_device *ndev; @@ -1409,10 +1632,12 @@ static int netvsc_register_vf(struct net_device *vf_netdev) if (!netvsc_dev || rtnl_dereference(net_device_ctx->vf_netdev)) return NOTIFY_DONE; + if (netvsc_vf_join(vf_netdev, ndev) != 0) + return NOTIFY_DONE; + netdev_info(ndev, "VF registering: %s\n", vf_netdev->name); - /* - * Take a reference on the module. - */ + + /* Prevent this module from being unloaded while VF is registered */ try_module_get(THIS_MODULE); dev_hold(vf_netdev); @@ -1420,61 +1645,59 @@ static int netvsc_register_vf(struct net_device *vf_netdev) return NOTIFY_OK; } -static int netvsc_vf_up(struct net_device *vf_netdev) +/* Change datapath */ +static void netvsc_vf_update(struct work_struct *w) { - struct net_device *ndev; + struct net_device_context *ndev_ctx + = container_of(w, struct net_device_context, vf_notify); + struct net_device *ndev = hv_get_drvdata(ndev_ctx->device_ctx); struct netvsc_device *netvsc_dev; - struct net_device_context *net_device_ctx; - - ndev = get_netvsc_byref(vf_netdev); - if (!ndev) - return NOTIFY_DONE; - - net_device_ctx = netdev_priv(ndev); - netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); - - netdev_info(ndev, "VF up: %s\n", vf_netdev->name); - - /* - * Open the device before switching data path. - */ - rndis_filter_open(netvsc_dev); - - /* - * notify the host to switch the data path. - */ - netvsc_switch_datapath(ndev, true); - netdev_info(ndev, "Data path switched to VF: %s\n", vf_netdev->name); - - netif_carrier_off(ndev); + struct net_device *vf_netdev; + bool vf_is_up; - /* Now notify peers through VF device. */ - call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, vf_netdev); + rtnl_lock(); + vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); + if (!vf_netdev) + goto unlock; + + netvsc_dev = rtnl_dereference(ndev_ctx->nvdev); + if (!netvsc_dev) + goto unlock; + + vf_is_up = netif_running(vf_netdev); + if (vf_is_up != ndev_ctx->datapath) { + if (vf_is_up) { + netdev_info(ndev, "VF up: %s\n", vf_netdev->name); + rndis_filter_open(netvsc_dev); + netvsc_switch_datapath(ndev, true); + netdev_info(ndev, "Data path switched to VF: %s\n", + vf_netdev->name); + } else { + netdev_info(ndev, "VF down: %s\n", vf_netdev->name); + netvsc_switch_datapath(ndev, false); + rndis_filter_close(netvsc_dev); + netdev_info(ndev, "Data path switched from VF: %s\n", + vf_netdev->name); + } - return NOTIFY_OK; + /* Now notify peers through VF device. */ + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, ndev); + } +unlock: + rtnl_unlock(); } -static int netvsc_vf_down(struct net_device *vf_netdev) +static int netvsc_vf_notify(struct net_device *vf_netdev) { - struct net_device *ndev; - struct netvsc_device *netvsc_dev; struct net_device_context *net_device_ctx; + struct net_device *ndev; ndev = get_netvsc_byref(vf_netdev); if (!ndev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); - netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); - - netdev_info(ndev, "VF down: %s\n", vf_netdev->name); - netvsc_switch_datapath(ndev, false); - netdev_info(ndev, "Data path switched from VF: %s\n", vf_netdev->name); - rndis_filter_close(netvsc_dev); - netif_carrier_on(ndev); - - /* Now notify peers through netvsc device. */ - call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, ndev); + schedule_work(&net_device_ctx->vf_notify); return NOTIFY_OK; } @@ -1489,9 +1712,12 @@ static int netvsc_unregister_vf(struct net_device *vf_netdev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); + cancel_work_sync(&net_device_ctx->vf_takeover); + cancel_work_sync(&net_device_ctx->vf_notify); netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name); + netdev_upper_dev_unlink(vf_netdev, ndev); RCU_INIT_POINTER(net_device_ctx->vf_netdev, NULL); dev_put(vf_netdev); module_put(THIS_MODULE); @@ -1505,12 +1731,12 @@ static int netvsc_probe(struct hv_device *dev, struct net_device_context *net_device_ctx; struct netvsc_device_info device_info; struct netvsc_device *nvdev; - int ret; + int ret = -ENOMEM; net = alloc_etherdev_mq(sizeof(struct net_device_context), VRSS_CHANNEL_MAX); if (!net) - return -ENOMEM; + goto no_net; netif_carrier_off(net); @@ -1529,6 +1755,13 @@ static int netvsc_probe(struct hv_device *dev, spin_lock_init(&net_device_ctx->lock); INIT_LIST_HEAD(&net_device_ctx->reconfig_events); + INIT_WORK(&net_device_ctx->vf_takeover, netvsc_vf_setup); + INIT_WORK(&net_device_ctx->vf_notify, netvsc_vf_update); + + net_device_ctx->vf_stats + = netdev_alloc_pcpu_stats(struct netvsc_vf_pcpu_stats); + if (!net_device_ctx->vf_stats) + goto no_stats; net->netdev_ops = &device_ops; net->ethtool_ops = ðtool_ops; @@ -1546,10 +1779,9 @@ static int netvsc_probe(struct hv_device *dev, if (IS_ERR(nvdev)) { ret = PTR_ERR(nvdev); netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); - free_netdev(net); - hv_set_drvdata(dev, NULL); - return ret; + goto rndis_failed; } + memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); /* hw_features computed in rndis_filter_device_add */ @@ -1573,11 +1805,20 @@ static int netvsc_probe(struct hv_device *dev, ret = register_netdev(net); if (ret != 0) { pr_err("Unable to register netdev.\n"); - rndis_filter_device_remove(dev, nvdev); - free_netdev(net); + goto register_failed; } return ret; + +register_failed: + rndis_filter_device_remove(dev, nvdev); +rndis_failed: + free_percpu(net_device_ctx->vf_stats); +no_stats: + hv_set_drvdata(dev, NULL); + free_netdev(net); +no_net: + return ret; } static int netvsc_remove(struct hv_device *dev) @@ -1611,6 +1852,7 @@ static int netvsc_remove(struct hv_device *dev) hv_set_drvdata(dev, NULL); + free_percpu(ndev_ctx->vf_stats); free_netdev(net); return 0; } @@ -1665,9 +1907,8 @@ static int netvsc_netdev_event(struct notifier_block *this, case NETDEV_UNREGISTER: return netvsc_unregister_vf(event_dev); case NETDEV_UP: - return netvsc_vf_up(event_dev); case NETDEV_DOWN: - return netvsc_vf_down(event_dev); + return netvsc_vf_notify(event_dev); default: return NOTIFY_DONE; } -- cgit v1.2.3 From 9820355f6934ba7ba6218abd44df7ee12b65f029 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 2 Aug 2017 09:52:10 +0200 Subject: mlxsw: core: Use correct EMAD transaction ID in debug message 'trans->tid' is only assigned later in the function, resulting in a zero transaction ID. Use 'tid' instead. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index affe84eb4bff..9d5e7cf288be 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -667,7 +667,7 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core, int err; dev_dbg(mlxsw_core->bus_info->dev, "EMAD reg access (tid=%llx,reg_id=%x(%s),type=%s)\n", - trans->tid, reg->id, mlxsw_reg_id_str(reg->id), + tid, reg->id, mlxsw_reg_id_str(reg->id), mlxsw_core_reg_access_type_str(type)); skb = mlxsw_emad_alloc(mlxsw_core, reg->len); -- cgit v1.2.3 From 63e701c15c98741fc8df50eb0315991e6d8040dc Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 2 Aug 2017 09:56:02 +0200 Subject: rocker: Provide offload indication using nexthop flags We want to stop using the FIB info's flags to provide the offlaod indication and instead do that on a per-nexthop basis. Convert rocker to do just that. It only supports one nexthop per-route, so conversion is simple. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_ofdpa.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 600e30e8f0be..da4e26b53a52 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -2761,7 +2761,7 @@ static int ofdpa_fib4_add(struct rocker *rocker, fen_info->tb_id, 0); if (err) return err; - fib_info_offload_inc(fen_info->fi); + fen_info->fi->fib_nh->nh_flags |= RTNH_F_OFFLOAD; return 0; } @@ -2776,7 +2776,7 @@ static int ofdpa_fib4_del(struct rocker *rocker, ofdpa_port = ofdpa_port_dev_lower_find(fen_info->fi->fib_dev, rocker); if (!ofdpa_port) return 0; - fib_info_offload_dec(fen_info->fi); + fen_info->fi->fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; return ofdpa_port_fib_ipv4(ofdpa_port, htonl(fen_info->dst), fen_info->dst_len, fen_info->fi, fen_info->tb_id, OFDPA_OP_FLAG_REMOVE); @@ -2803,7 +2803,7 @@ static void ofdpa_fib4_abort(struct rocker *rocker) rocker); if (!ofdpa_port) continue; - fib_info_offload_dec(flow_entry->fi); + flow_entry->fi->fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; ofdpa_flow_tbl_del(ofdpa_port, OFDPA_OP_FLAG_REMOVE, flow_entry); } -- cgit v1.2.3 From 3984d1a89fe7cba04cdcc6084dc1a2024f0190b6 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 2 Aug 2017 09:56:03 +0200 Subject: mlxsw: spectrum_router: Provide offload indication using nexthop flags In a similar fashion to previous patch, use the nexthop flags to provide offload indication instead of the FIB info's flags. In case a nexthop in a multipath route can't be offloaded (gateway's MAC can't be resolved, for example), then its offload flag isn't set. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Tested-by: David Ahern Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 38 ++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index add03fa34a2d..4cdeedf9c61f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2103,13 +2103,47 @@ mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry) } } +static void +mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; + int i; + + if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) { + nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD; + return; + } + + for (i = 0; i < nh_grp->count; i++) { + struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; + + if (nh->offloaded) + nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD; + else + nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; + } +} + +static void +mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; + int i; + + for (i = 0; i < nh_grp->count; i++) { + struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; + + nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; + } +} + static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) { fib_entry->offloaded = true; switch (fib_entry->fib_node->fib->proto) { case MLXSW_SP_L3_PROTO_IPV4: - fib_info_offload_inc(fib_entry->nh_group->key.fi); + mlxsw_sp_fib4_entry_offload_set(fib_entry); break; case MLXSW_SP_L3_PROTO_IPV6: WARN_ON_ONCE(1); @@ -2121,7 +2155,7 @@ mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) { switch (fib_entry->fib_node->fib->proto) { case MLXSW_SP_L3_PROTO_IPV4: - fib_info_offload_dec(fib_entry->nh_group->key.fi); + mlxsw_sp_fib4_entry_offload_unset(fib_entry); break; case MLXSW_SP_L3_PROTO_IPV6: WARN_ON_ONCE(1); -- cgit v1.2.3 From 1353ee707362478527d54cd0b25d3ac17c47168d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 2 Aug 2017 09:56:04 +0200 Subject: mlxsw: spectrum_router: Don't check state when refreshing offload indication Previous patch removed the reliance on the counter in the FIB info to set the offload indication, so we no longer need to keep an offload state on each FIB entry and can just set or unset the RTNH_F_OFFLOAD flag in each nexthop. This is also necessary because we're going to need to refresh the offload indication whenever the nexthop group associated with the FIB entry is refreshed. Current check would prevent us from marking a newly resolved nexthop as offloaded if the FIB entry is already marked as offloaded. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Tested-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 4cdeedf9c61f..40aecbc116ac 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -394,7 +394,6 @@ struct mlxsw_sp_fib_entry { enum mlxsw_sp_fib_entry_type type; struct list_head nexthop_group_node; struct mlxsw_sp_nexthop_group *nh_group; - bool offloaded; }; struct mlxsw_sp_fib4_entry { @@ -2139,8 +2138,6 @@ mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) { - fib_entry->offloaded = true; - switch (fib_entry->fib_node->fib->proto) { case MLXSW_SP_L3_PROTO_IPV4: mlxsw_sp_fib4_entry_offload_set(fib_entry); @@ -2160,8 +2157,6 @@ mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) case MLXSW_SP_L3_PROTO_IPV6: WARN_ON_ONCE(1); } - - fib_entry->offloaded = false; } static void @@ -2170,17 +2165,13 @@ mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry, { switch (op) { case MLXSW_REG_RALUE_OP_WRITE_DELETE: - if (!fib_entry->offloaded) - return; return mlxsw_sp_fib_entry_offload_unset(fib_entry); case MLXSW_REG_RALUE_OP_WRITE_WRITE: if (err) return; - if (mlxsw_sp_fib_entry_should_offload(fib_entry) && - !fib_entry->offloaded) + if (mlxsw_sp_fib_entry_should_offload(fib_entry)) mlxsw_sp_fib_entry_offload_set(fib_entry); - else if (!mlxsw_sp_fib_entry_should_offload(fib_entry) && - fib_entry->offloaded) + else if (!mlxsw_sp_fib_entry_should_offload(fib_entry)) mlxsw_sp_fib_entry_offload_unset(fib_entry); return; default: -- cgit v1.2.3 From 77d964e66cfad007db14077fc952ce13bdaa2733 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 2 Aug 2017 09:56:05 +0200 Subject: mlxsw: spectrum_router: Refresh offload indication upon group refresh Now that we provide offload indication using the nexthop's flags we must refresh the offload indication whenever the offload state within the group changes. This didn't matter until now, as offload indication was provided using the FIB info flags and multipath routes were marked as offloaded as long as one of the nexthops was offloaded. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Tested-by: David Ahern Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 40aecbc116ac..2f03c7e71584 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1652,6 +1652,24 @@ mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp, return 0; } +static void +mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op, int err); + +static void +mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp) +{ + enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE; + struct mlxsw_sp_fib_entry *fib_entry; + + list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) { + if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node, + fib_entry)) + continue; + mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0); + } +} + static void mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp) @@ -1739,6 +1757,10 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n"); goto set_trap; } + + /* Offload state within the group changed, so update the flags. */ + mlxsw_sp_nexthop_fib_entries_refresh(nh_grp); + return; set_trap: -- cgit v1.2.3 From fc81bab5eeb103711925d7510157cf5cd2b153f4 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 30 Jul 2017 09:02:19 +0100 Subject: rtlwifi: rtl_pci_probe: Fix fail path of _rtl_pci_find_adapter _rtl_pci_find_adapter fail path will jump to label fail3 for unsupported adapter types. However, on course for fail3 there will be call rtl_deinit_core before rtl_init_core. For the inclusion of checking pci_iounmap this fail can be moved to fail2. Fixes [ 4.492963] BUG: unable to handle kernel NULL pointer dereference at (null) [ 4.493067] IP: rtl_deinit_core+0x31/0x90 [rtlwifi] Signed-off-by: Malcolm Priestley Cc: Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 032b6317690d..08dc8919ef60 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -2257,7 +2257,7 @@ int rtl_pci_probe(struct pci_dev *pdev, /* find adapter */ if (!_rtl_pci_find_adapter(pdev, hw)) { err = -ENODEV; - goto fail3; + goto fail2; } /* Init IO handler */ @@ -2318,10 +2318,10 @@ fail3: pci_set_drvdata(pdev, NULL); rtl_deinit_core(hw); +fail2: if (rtlpriv->io.pci_mem_start != 0) pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start); -fail2: pci_release_regions(pdev); complete(&rtlpriv->firmware_loading_complete); -- cgit v1.2.3 From 9ddb378b237f0321ef0c25c407672591e4c86254 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Mon, 31 Jul 2017 09:49:24 +0000 Subject: mwifiex: correct IE parse during association It is observed that some IEs get missed during association. This patch correct the old IE parse code. sme->ie will be store as wpa ie, wps ie, wapi ie and gen ie accordingly. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 115 +++++++++++------------ 1 file changed, 55 insertions(+), 60 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 43ecd621d1ef..a6077ab3efc3 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -811,8 +811,8 @@ int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode) * is checked to determine WPA version. If buffer length is zero, the existing * WPA IE is reset. */ -static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, - u8 *ie_data_ptr, u16 ie_len) +static int mwifiex_set_wpa_ie(struct mwifiex_private *priv, + u8 *ie_data_ptr, u16 ie_len) { if (ie_len) { if (ie_len > sizeof(priv->wpa_ie)) { @@ -1351,101 +1351,96 @@ static int mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, u16 ie_len) { - int ret = 0; struct ieee_types_vendor_header *pvendor_ie; const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; - u16 unparsed_len = ie_len; - int find_wpa_ie = 0; + u16 unparsed_len = ie_len, cur_ie_len; /* If the passed length is zero, reset the buffer */ if (!ie_len) { priv->gen_ie_buf_len = 0; priv->wps.session_enable = false; - return 0; - } else if (!ie_data_ptr) { + } else if (!ie_data_ptr || + ie_len <= sizeof(struct ieee_types_header)) { return -1; } pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; while (pvendor_ie) { + cur_ie_len = pvendor_ie->len + sizeof(struct ieee_types_header); + + if (pvendor_ie->element_id == WLAN_EID_RSN) { + /* IE is a WPA/WPA2 IE so call set_wpa function */ + mwifiex_set_wpa_ie(priv, (u8 *)pvendor_ie, cur_ie_len); + priv->wps.session_enable = false; + goto next_ie; + } + + if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { + /* IE is a WAPI IE so call set_wapi function */ + mwifiex_set_wapi_ie(priv, (u8 *)pvendor_ie, + cur_ie_len); + goto next_ie; + } + if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) { - /* Test to see if it is a WPA IE, if not, then it is a - * gen IE + /* Test to see if it is a WPA IE, if not, then + * it is a gen IE */ if (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui))) { - find_wpa_ie = 1; - break; + /* IE is a WPA/WPA2 IE so call set_wpa function + */ + mwifiex_set_wpa_ie(priv, (u8 *)pvendor_ie, + cur_ie_len); + priv->wps.session_enable = false; + goto next_ie; } - /* Test to see if it is a WPS IE, if so, enable - * wps session flag - */ if (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui))) { + /* Test to see if it is a WPS IE, + * if so, enable wps session flag + */ priv->wps.session_enable = true; mwifiex_dbg(priv->adapter, MSG, - "info: WPS Session Enabled.\n"); - ret = mwifiex_set_wps_ie(priv, - (u8 *)pvendor_ie, - unparsed_len); + "WPS Session Enabled.\n"); + mwifiex_set_wps_ie(priv, (u8 *)pvendor_ie, + cur_ie_len); + goto next_ie; } } - if (pvendor_ie->element_id == WLAN_EID_RSN) { - find_wpa_ie = 1; - break; - } + /* Saved in gen_ie, such as P2P IE.etc.*/ - if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { - /* IE is a WAPI IE so call set_wapi function */ - ret = mwifiex_set_wapi_ie(priv, (u8 *)pvendor_ie, - unparsed_len); - return ret; + /* Verify that the passed length is not larger than the + * available space remaining in the buffer + */ + if (cur_ie_len < + (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { + /* Append the passed data to the end + * of the genIeBuffer + */ + memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, + (u8 *)pvendor_ie, cur_ie_len); + /* Increment the stored buffer length by the + * size passed + */ + priv->gen_ie_buf_len += cur_ie_len; } - unparsed_len -= (pvendor_ie->len + - sizeof(struct ieee_types_header)); +next_ie: + unparsed_len -= cur_ie_len; if (unparsed_len <= sizeof(struct ieee_types_header)) pvendor_ie = NULL; else pvendor_ie = (struct ieee_types_vendor_header *) - (((u8 *)pvendor_ie) + pvendor_ie->len + - sizeof(struct ieee_types_header)); - } - - if (find_wpa_ie) { - /* IE is a WPA/WPA2 IE so call set_wpa function */ - ret = mwifiex_set_wpa_ie_helper(priv, (u8 *)pvendor_ie, - unparsed_len); - priv->wps.session_enable = false; - return ret; + (((u8 *)pvendor_ie) + cur_ie_len); } - /* - * Verify that the passed length is not larger than the - * available space remaining in the buffer - */ - if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { - - /* Append the passed data to the end of the - genIeBuffer */ - memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, - ie_len); - /* Increment the stored buffer length by the - size passed */ - priv->gen_ie_buf_len += ie_len; - } else { - /* Passed data does not fit in the remaining - buffer space */ - ret = -1; - } - - /* Return 0, or -1 for error case */ - return ret; + return 0; } /* -- cgit v1.2.3 From 1d9b168d8ea9a0f51947d0e2f84856e77d2fe7ff Mon Sep 17 00:00:00 2001 From: Sven Joachim Date: Mon, 31 Jul 2017 18:10:45 +0200 Subject: rtlwifi: Fix fallback firmware loading Commit f70e4df2b384 ("rtlwifi: Add code to read new versions of firmware") added code to load an old firmware file if the new one is not available. Unfortunately that code is never reached because request_firmware_nowait() does not wait for the firmware to show up and returns 0 even if the file is not there. Use the existing fallback mechanism introduced by commit 62009b7f1279 ("rtlwifi: rtl8192cu: Add new firmware") instead. Fixes: f70e4df2b384 ("rtlwifi: Add code to read new versions of firmware") Cc: stable@vger.kernel.org Signed-off-by: Sven Joachim Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c | 17 +++++------------ drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c | 17 +++++------------ 2 files changed, 10 insertions(+), 24 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c index 56c05c4e1499..f47d839f388d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c @@ -187,18 +187,10 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); if (err) { - /* Failed to get firmware. Check if old version available */ - fw_name = "rtlwifi/rtl8723befw.bin"; - pr_info("Using firmware %s\n", fw_name); - err = request_firmware_nowait(THIS_MODULE, 1, fw_name, - rtlpriv->io.dev, GFP_KERNEL, hw, - rtl_fw_cb); - if (err) { - pr_err("Failed to request firmware!\n"); - vfree(rtlpriv->rtlhal.pfirmware); - rtlpriv->rtlhal.pfirmware = NULL; - return 1; - } + pr_err("Failed to request firmware!\n"); + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; + return 1; } return 0; } @@ -289,6 +281,7 @@ static const struct rtl_hal_cfg rtl8723be_hal_cfg = { .bar_id = 2, .write_readback = true, .name = "rtl8723be_pci", + .alt_fw_name = "rtlwifi/rtl8723befw.bin", .ops = &rtl8723be_hal_ops, .mod_params = &rtl8723be_mod_params, .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c index ec2d577ba85b..5925edf7877f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c @@ -216,18 +216,10 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); if (err) { - /* Failed to get firmware. Check if old version available */ - fw_name = "rtlwifi/rtl8821aefw.bin"; - pr_info("Using firmware %s\n", fw_name); - err = request_firmware_nowait(THIS_MODULE, 1, fw_name, - rtlpriv->io.dev, GFP_KERNEL, hw, - rtl_fw_cb); - if (err) { - pr_err("Failed to request normal firmware!\n"); - vfree(rtlpriv->rtlhal.wowlan_firmware); - vfree(rtlpriv->rtlhal.pfirmware); - return 1; - } + pr_err("Failed to request normal firmware!\n"); + vfree(rtlpriv->rtlhal.wowlan_firmware); + vfree(rtlpriv->rtlhal.pfirmware); + return 1; } /*load wowlan firmware*/ pr_info("Using firmware %s\n", wowlan_fw_name); @@ -331,6 +323,7 @@ static const struct rtl_hal_cfg rtl8821ae_hal_cfg = { .bar_id = 2, .write_readback = true, .name = "rtl8821ae_pci", + .alt_fw_name = "rtlwifi/rtl8821aefw.bin", .ops = &rtl8821ae_hal_ops, .mod_params = &rtl8821ae_mod_params, .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL, -- cgit v1.2.3 From 4dd07d2b58d5f3c343ee6e4c8ba63edc643cc972 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:43 +0300 Subject: qtnfmac: updates for regulatory support On startup driver obtains regulatory rules from firmware and enables them during wiphy registration. Later on regulatory domain change can be requested by host. In this case firmware is notified about the upcoming changes. If the change is valid, then firmware updates hardware channel configuration and host driver receives updated channel info for each band. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 81 ++----- drivers/net/wireless/quantenna/qtnfmac/commands.c | 278 ++++++++++++++++++---- drivers/net/wireless/quantenna/qtnfmac/commands.h | 1 + drivers/net/wireless/quantenna/qtnfmac/core.c | 3 + drivers/net/wireless/quantenna/qtnfmac/core.h | 7 +- drivers/net/wireless/quantenna/qtnfmac/qlink.h | 126 +++++++++- 6 files changed, 388 insertions(+), 108 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index e3c090008125..7f70b0aae069 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -700,66 +700,43 @@ static struct cfg80211_ops qtn_cfg80211_ops = { .disconnect = qtnf_disconnect }; -static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy, +static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, struct regulatory_request *req) { - struct qtnf_wmac *mac = wiphy_priv(wiphy); - struct qtnf_bus *bus; - struct qtnf_vif *vif; - struct qtnf_wmac *chan_mac; - int i; + struct qtnf_wmac *mac = wiphy_priv(wiphy_in); + struct qtnf_bus *bus = mac->bus; + struct wiphy *wiphy; + unsigned int mac_idx; enum nl80211_band band; - - bus = mac->bus; + int ret; pr_debug("MAC%u: initiator=%d alpha=%c%c\n", mac->macid, req->initiator, req->alpha2[0], req->alpha2[1]); - vif = qtnf_mac_get_base_vif(mac); - if (!vif) { - pr_err("MAC%u: primary VIF is not configured\n", mac->macid); - return; - } - - /* ignore non-ISO3166 country codes */ - for (i = 0; i < sizeof(req->alpha2); i++) { - if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { - pr_err("MAC%u: not an ISO3166 code\n", mac->macid); - return; - } - } - if (!strncasecmp(req->alpha2, bus->hw_info.alpha2_code, - sizeof(req->alpha2))) { - pr_warn("MAC%u: unchanged country code\n", mac->macid); - return; - } - - if (qtnf_cmd_send_regulatory_config(mac, req->alpha2)) { - pr_err("MAC%u: failed to configure regulatory\n", mac->macid); + ret = qtnf_cmd_reg_notify(bus, req); + if (ret) { + if (ret != -EOPNOTSUPP && ret != -EALREADY) + pr_err("failed to update reg domain to %c%c\n", + req->alpha2[0], req->alpha2[1]); return; } - for (i = 0; i < bus->hw_info.num_mac; i++) { - chan_mac = bus->mac[i]; - - if (!chan_mac) + for (mac_idx = 0; mac_idx < QTNF_MAX_MAC; ++mac_idx) { + if (!(bus->hw_info.mac_bitmap & (1 << mac_idx))) continue; - if (!(bus->hw_info.mac_bitmap & BIT(i))) - continue; + mac = bus->mac[mac_idx]; + wiphy = priv_to_wiphy(mac); for (band = 0; band < NUM_NL80211_BANDS; ++band) { if (!wiphy->bands[band]) continue; - if (qtnf_cmd_get_mac_chan_info(chan_mac, - wiphy->bands[band])) { - pr_err("MAC%u: can't get channel info\n", - chan_mac->macid); - qtnf_core_detach(bus); - - return; - } + ret = qtnf_cmd_get_mac_chan_info(mac, + wiphy->bands[band]); + if (ret) + pr_err("failed to get chan info for mac %u band %u\n", + mac_idx, band); } } } @@ -844,10 +821,8 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) } iface_comb = kzalloc(sizeof(*iface_comb), GFP_KERNEL); - if (!iface_comb) { - ret = -ENOMEM; - goto out; - } + if (!iface_comb) + return -ENOMEM; ret = qtnf_wiphy_setup_if_comb(wiphy, iface_comb, &mac->macinfo); if (ret) @@ -889,21 +864,17 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) ether_addr_copy(wiphy->perm_addr, mac->macaddr); if (hw_info->hw_capab & QLINK_HW_SUPPORTS_REG_UPDATE) { - pr_debug("device supports REG_UPDATE\n"); + wiphy->regulatory_flags |= REGULATORY_STRICT_REG | + REGULATORY_CUSTOM_REG; wiphy->reg_notifier = qtnf_cfg80211_reg_notifier; - pr_debug("hint regulatory about EP region: %c%c\n", - hw_info->alpha2_code[0], - hw_info->alpha2_code[1]); - regulatory_hint(wiphy, hw_info->alpha2_code); + wiphy_apply_custom_regulatory(wiphy, hw_info->rd); } else { - pr_debug("device doesn't support REG_UPDATE\n"); wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; } ret = wiphy_register(wiphy); - out: - if (ret < 0) { + if (ret) { kfree(iface_comb); return ret; } diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index b39dbc3d3c1f..8be96f02888f 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -181,38 +181,6 @@ out: return ret; } -int qtnf_cmd_send_regulatory_config(struct qtnf_wmac *mac, const char *alpha2) -{ - struct sk_buff *cmd_skb; - u16 res_code = QLINK_CMD_RESULT_OK; - int ret; - - cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, - QLINK_CMD_REG_REGION, - sizeof(struct qlink_cmd)); - if (unlikely(!cmd_skb)) - return -ENOMEM; - - qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_COUNTRY, alpha2, - QTNF_MAX_ALPHA_LEN); - - ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); - - if (unlikely(ret)) - goto out; - - if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { - pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); - ret = -EFAULT; - goto out; - } - - memcpy(mac->bus->hw_info.alpha2_code, alpha2, - sizeof(mac->bus->hw_info.alpha2_code)); -out: - return ret; -} - int qtnf_cmd_send_config_ap(struct qtnf_vif *vif) { struct sk_buff *cmd_skb; @@ -848,25 +816,168 @@ out: return ret; } +static u32 qtnf_cmd_resp_reg_rule_flags_parse(u32 qflags) +{ + u32 flags = 0; + + if (qflags & QLINK_RRF_NO_OFDM) + flags |= NL80211_RRF_NO_OFDM; + + if (qflags & QLINK_RRF_NO_CCK) + flags |= NL80211_RRF_NO_CCK; + + if (qflags & QLINK_RRF_NO_INDOOR) + flags |= NL80211_RRF_NO_INDOOR; + + if (qflags & QLINK_RRF_NO_OUTDOOR) + flags |= NL80211_RRF_NO_OUTDOOR; + + if (qflags & QLINK_RRF_DFS) + flags |= NL80211_RRF_DFS; + + if (qflags & QLINK_RRF_PTP_ONLY) + flags |= NL80211_RRF_PTP_ONLY; + + if (qflags & QLINK_RRF_PTMP_ONLY) + flags |= NL80211_RRF_PTMP_ONLY; + + if (qflags & QLINK_RRF_NO_IR) + flags |= NL80211_RRF_NO_IR; + + if (qflags & QLINK_RRF_AUTO_BW) + flags |= NL80211_RRF_AUTO_BW; + + if (qflags & QLINK_RRF_IR_CONCURRENT) + flags |= NL80211_RRF_IR_CONCURRENT; + + if (qflags & QLINK_RRF_NO_HT40MINUS) + flags |= NL80211_RRF_NO_HT40MINUS; + + if (qflags & QLINK_RRF_NO_HT40PLUS) + flags |= NL80211_RRF_NO_HT40PLUS; + + if (qflags & QLINK_RRF_NO_80MHZ) + flags |= NL80211_RRF_NO_80MHZ; + + if (qflags & QLINK_RRF_NO_160MHZ) + flags |= NL80211_RRF_NO_160MHZ; + + return flags; +} + static int qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus, - const struct qlink_resp_get_hw_info *resp) + const struct qlink_resp_get_hw_info *resp, + size_t info_len) { struct qtnf_hw_info *hwinfo = &bus->hw_info; + const struct qlink_tlv_hdr *tlv; + const struct qlink_tlv_reg_rule *tlv_rule; + struct ieee80211_reg_rule *rule; + u16 tlv_type; + u16 tlv_value_len; + unsigned int rule_idx = 0; + + if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) + return -E2BIG; + + hwinfo->rd = kzalloc(sizeof(*hwinfo->rd) + + sizeof(struct ieee80211_reg_rule) + * resp->n_reg_rules, GFP_KERNEL); + + if (!hwinfo->rd) + return -ENOMEM; hwinfo->num_mac = resp->num_mac; hwinfo->mac_bitmap = resp->mac_bitmap; hwinfo->fw_ver = le32_to_cpu(resp->fw_ver); hwinfo->ql_proto_ver = le16_to_cpu(resp->ql_proto_ver); - memcpy(hwinfo->alpha2_code, resp->alpha2_code, - sizeof(hwinfo->alpha2_code)); hwinfo->total_tx_chain = resp->total_tx_chain; hwinfo->total_rx_chain = resp->total_rx_chain; hwinfo->hw_capab = le32_to_cpu(resp->hw_capab); + hwinfo->rd->n_reg_rules = resp->n_reg_rules; + hwinfo->rd->alpha2[0] = resp->alpha2[0]; + hwinfo->rd->alpha2[1] = resp->alpha2[1]; + + switch (resp->dfs_region) { + case QLINK_DFS_FCC: + hwinfo->rd->dfs_region = NL80211_DFS_FCC; + break; + case QLINK_DFS_ETSI: + hwinfo->rd->dfs_region = NL80211_DFS_ETSI; + break; + case QLINK_DFS_JP: + hwinfo->rd->dfs_region = NL80211_DFS_JP; + break; + case QLINK_DFS_UNSET: + default: + hwinfo->rd->dfs_region = NL80211_DFS_UNSET; + break; + } + + tlv = (const struct qlink_tlv_hdr *)resp->info; + + while (info_len >= sizeof(*tlv)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_value_len = le16_to_cpu(tlv->len); + + if (tlv_value_len + sizeof(*tlv) > info_len) { + pr_warn("malformed TLV 0x%.2X; LEN: %u\n", + tlv_type, tlv_value_len); + return -EINVAL; + } + + switch (tlv_type) { + case QTN_TLV_ID_REG_RULE: + if (rule_idx >= resp->n_reg_rules) { + pr_warn("unexpected number of rules: %u\n", + resp->n_reg_rules); + return -EINVAL; + } + + if (tlv_value_len != sizeof(*tlv_rule) - sizeof(*tlv)) { + pr_warn("malformed TLV 0x%.2X; LEN: %u\n", + tlv_type, tlv_value_len); + return -EINVAL; + } + + tlv_rule = (const struct qlink_tlv_reg_rule *)tlv; + rule = &hwinfo->rd->reg_rules[rule_idx++]; + + rule->freq_range.start_freq_khz = + le32_to_cpu(tlv_rule->start_freq_khz); + rule->freq_range.end_freq_khz = + le32_to_cpu(tlv_rule->end_freq_khz); + rule->freq_range.max_bandwidth_khz = + le32_to_cpu(tlv_rule->max_bandwidth_khz); + rule->power_rule.max_antenna_gain = + le32_to_cpu(tlv_rule->max_antenna_gain); + rule->power_rule.max_eirp = + le32_to_cpu(tlv_rule->max_eirp); + rule->dfs_cac_ms = + le32_to_cpu(tlv_rule->dfs_cac_ms); + rule->flags = qtnf_cmd_resp_reg_rule_flags_parse( + le32_to_cpu(tlv_rule->flags)); + break; + default: + break; + } + + info_len -= tlv_value_len + sizeof(*tlv); + tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); + } + + if (rule_idx != resp->n_reg_rules) { + pr_warn("unexpected number of rules: expected %u got %u\n", + resp->n_reg_rules, rule_idx); + kfree(hwinfo->rd); + hwinfo->rd = NULL; + return -EINVAL; + } pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u\n", hwinfo->fw_ver, hwinfo->mac_bitmap, - hwinfo->alpha2_code[0], hwinfo->alpha2_code[1], + hwinfo->rd->alpha2[0], hwinfo->rd->alpha2[1], hwinfo->total_tx_chain, hwinfo->total_rx_chain); return 0; @@ -1013,14 +1124,24 @@ qtnf_cmd_resp_fill_channels_info(struct ieee80211_supported_band *band, unsigned int chidx = 0; u32 qflags; - kfree(band->channels); - band->channels = NULL; + if (band->channels) { + if (band->n_channels == resp->num_chans) { + memset(band->channels, 0, + sizeof(*band->channels) * band->n_channels); + } else { + kfree(band->channels); + band->n_channels = 0; + band->channels = NULL; + } + } band->n_channels = resp->num_chans; if (band->n_channels == 0) return 0; - band->channels = kcalloc(band->n_channels, sizeof(*chan), GFP_KERNEL); + if (!band->channels) + band->channels = kcalloc(band->n_channels, sizeof(*chan), + GFP_KERNEL); if (!band->channels) { band->n_channels = 0; return -ENOMEM; @@ -1256,6 +1377,7 @@ int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) const struct qlink_resp_get_hw_info *resp; u16 res_code = QLINK_CMD_RESULT_OK; int ret = 0; + size_t info_len; cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, QLINK_CMD_GET_HW_INFO, @@ -1266,7 +1388,7 @@ int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) qtnf_bus_lock(bus); ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, &res_code, - sizeof(*resp), NULL); + sizeof(*resp), &info_len); if (unlikely(ret)) goto out; @@ -1278,7 +1400,7 @@ int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) } resp = (const struct qlink_resp_get_hw_info *)resp_skb->data; - ret = qtnf_cmd_resp_proc_hw_info(bus, resp); + ret = qtnf_cmd_resp_proc_hw_info(bus, resp, info_len); out: qtnf_bus_unlock(bus); @@ -1976,3 +2098,77 @@ out: qtnf_bus_unlock(vif->mac->bus); return ret; } + +int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req) +{ + struct sk_buff *cmd_skb; + int ret; + u16 res_code; + struct qlink_cmd_reg_notify *cmd; + + cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, + QLINK_CMD_REG_NOTIFY, + sizeof(*cmd)); + if (!cmd_skb) + return -ENOMEM; + + cmd = (struct qlink_cmd_reg_notify *)cmd_skb->data; + cmd->alpha2[0] = req->alpha2[0]; + cmd->alpha2[1] = req->alpha2[1]; + + switch (req->initiator) { + case NL80211_REGDOM_SET_BY_CORE: + cmd->initiator = QLINK_REGDOM_SET_BY_CORE; + break; + case NL80211_REGDOM_SET_BY_USER: + cmd->initiator = QLINK_REGDOM_SET_BY_USER; + break; + case NL80211_REGDOM_SET_BY_DRIVER: + cmd->initiator = QLINK_REGDOM_SET_BY_DRIVER; + break; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + cmd->initiator = QLINK_REGDOM_SET_BY_COUNTRY_IE; + break; + } + + switch (req->user_reg_hint_type) { + case NL80211_USER_REG_HINT_USER: + cmd->user_reg_hint_type = QLINK_USER_REG_HINT_USER; + break; + case NL80211_USER_REG_HINT_CELL_BASE: + cmd->user_reg_hint_type = QLINK_USER_REG_HINT_CELL_BASE; + break; + case NL80211_USER_REG_HINT_INDOOR: + cmd->user_reg_hint_type = QLINK_USER_REG_HINT_INDOOR; + break; + } + + qtnf_bus_lock(bus); + + ret = qtnf_cmd_send(bus, cmd_skb, &res_code); + if (ret) + goto out; + + switch (res_code) { + case QLINK_CMD_RESULT_ENOTSUPP: + pr_warn("reg update not supported\n"); + ret = -EOPNOTSUPP; + break; + case QLINK_CMD_RESULT_EALREADY: + pr_info("regulatory domain is already set to %c%c", + req->alpha2[0], req->alpha2[1]); + ret = -EALREADY; + break; + case QLINK_CMD_RESULT_OK: + ret = 0; + break; + default: + ret = -EFAULT; + break; + } + +out: + qtnf_bus_unlock(bus); + + return ret; +} diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h index 6c51854ef5e7..155b265d42bf 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.h +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h @@ -70,5 +70,6 @@ int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, u16 reason_code); int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up); +int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req); #endif /* QLINK_COMMANDS_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c index f053532c0e87..17d17e332a8b 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.c +++ b/drivers/net/wireless/quantenna/qtnfmac/core.c @@ -549,6 +549,9 @@ void qtnf_core_detach(struct qtnf_bus *bus) destroy_workqueue(bus->workqueue); } + kfree(bus->hw_info.rd); + bus->hw_info.rd = NULL; + qtnf_trans_free(bus); } EXPORT_SYMBOL_GPL(qtnf_core_detach); diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h index a616434281cf..31b7ec2bfd3e 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.h +++ b/drivers/net/wireless/quantenna/qtnfmac/core.h @@ -42,7 +42,6 @@ #define QTNF_MAX_SSID_LIST_LENGTH 2 #define QTNF_MAX_VSIE_LEN 255 -#define QTNF_MAX_ALPHA_LEN 2 #define QTNF_MAX_INTF 8 #define QTNF_MAX_EVENT_QUEUE_LEN 255 #define QTNF_DEFAULT_BG_SCAN_PERIOD 300 @@ -136,14 +135,14 @@ struct qtnf_wmac { }; struct qtnf_hw_info { + u16 ql_proto_ver; u8 num_mac; u8 mac_bitmap; - u8 alpha2_code[QTNF_MAX_ALPHA_LEN]; u32 fw_ver; - u16 ql_proto_ver; + u32 hw_capab; + struct ieee80211_regdomain *rd; u8 total_tx_chain; u8 total_rx_chain; - u32 hw_capab; }; struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac); diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index 6eafc15e0065..e27833b78940 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -19,7 +19,7 @@ #include -#define QLINK_PROTO_VER 3 +#define QLINK_PROTO_VER 4 #define QLINK_MACID_RSVD 0xFF #define QLINK_VIFID_RSVD 0xFF @@ -133,6 +133,9 @@ enum qlink_channel_width { * number of operational channels and information on each of the channel. * This command is generic to a specified MAC, interface index must be set * to QLINK_VIFID_RSVD in command header. + * @QLINK_CMD_REG_NOTIFY: notify device about regulatory domain change. This + * command is supported only if device reports QLINK_HW_SUPPORTS_REG_UPDATE + * capability. */ enum qlink_cmd_type { QLINK_CMD_FW_INIT = 0x0001, @@ -148,7 +151,7 @@ enum qlink_cmd_type { QLINK_CMD_DEL_INTF = 0x0016, QLINK_CMD_CHANGE_INTF = 0x0017, QLINK_CMD_UPDOWN_INTF = 0x0018, - QLINK_CMD_REG_REGION = 0x0019, + QLINK_CMD_REG_NOTIFY = 0x0019, QLINK_CMD_CHANS_INFO_GET = 0x001A, QLINK_CMD_CONFIG_AP = 0x0020, QLINK_CMD_START_AP = 0x0021, @@ -430,6 +433,44 @@ struct qlink_cmd_chans_info_get { u8 band; } __packed; +/** + * enum qlink_reg_initiator - Indicates the initiator of a reg domain request + * + * See &enum nl80211_reg_initiator for more info. + */ +enum qlink_reg_initiator { + QLINK_REGDOM_SET_BY_CORE, + QLINK_REGDOM_SET_BY_USER, + QLINK_REGDOM_SET_BY_DRIVER, + QLINK_REGDOM_SET_BY_COUNTRY_IE, +}; + +/** + * enum qlink_user_reg_hint_type - type of user regulatory hint + * + * See &enum nl80211_user_reg_hint_type for more info. + */ +enum qlink_user_reg_hint_type { + QLINK_USER_REG_HINT_USER = 0, + QLINK_USER_REG_HINT_CELL_BASE = 1, + QLINK_USER_REG_HINT_INDOOR = 2, +}; + +/** + * struct qlink_cmd_reg_notify - data for QLINK_CMD_REG_NOTIFY command + * + * @alpha2: the ISO / IEC 3166 alpha2 country code. + * @initiator: which entity sent the request, one of &enum qlink_reg_initiator. + * @user_reg_hint_type: type of hint for QLINK_REGDOM_SET_BY_USER request, one + * of &enum qlink_user_reg_hint_type. + */ +struct qlink_cmd_reg_notify { + struct qlink_cmd chdr; + u8 alpha2[2]; + u8 initiator; + u8 user_reg_hint_type; +} __packed; + /* QLINK Command Responses messages related definitions */ @@ -438,6 +479,7 @@ enum qlink_cmd_result { QLINK_CMD_RESULT_INVALID, QLINK_CMD_RESULT_ENOTSUPP, QLINK_CMD_RESULT_ENOTFOUND, + QLINK_CMD_RESULT_EALREADY, }; /** @@ -496,6 +538,18 @@ struct qlink_resp_get_mac_info { u8 var_info[0]; } __packed; +/** + * enum qlink_dfs_regions - regulatory DFS regions + * + * Corresponds to &enum nl80211_dfs_regions. + */ +enum qlink_dfs_regions { + QLINK_DFS_UNSET = 0, + QLINK_DFS_FCC = 1, + QLINK_DFS_ETSI = 2, + QLINK_DFS_JP = 3, +}; + /** * struct qlink_resp_get_hw_info - response for QLINK_CMD_GET_HW_INFO command * @@ -504,22 +558,29 @@ struct qlink_resp_get_mac_info { * @fw_ver: wireless hardware firmware version. * @hw_capab: Bitmap of capabilities supported by firmware. * @ql_proto_ver: Version of QLINK protocol used by firmware. - * @country_code: country code ID firmware is configured to. * @num_mac: Number of separate physical radio devices provided by hardware. * @mac_bitmap: Bitmap of MAC IDs that are active and can be used in firmware. * @total_tx_chains: total number of transmit chains used by device. * @total_rx_chains: total number of receive chains. + * @alpha2: country code ID firmware is configured to. + * @n_reg_rules: number of regulatory rules TLVs in variable portion of the + * message. + * @dfs_region: regulatory DFS region, one of @enum qlink_dfs_region. + * @info: variable-length HW info, can contain QTN_TLV_ID_REG_RULE. */ struct qlink_resp_get_hw_info { struct qlink_resp rhdr; __le32 fw_ver; __le32 hw_capab; __le16 ql_proto_ver; - u8 alpha2_code[2]; u8 num_mac; u8 mac_bitmap; u8 total_tx_chain; u8 total_rx_chain; + u8 alpha2[2]; + u8 n_reg_rules; + u8 dfs_region; + u8 info[0]; } __packed; /** @@ -741,6 +802,7 @@ enum qlink_tlv_id { QTN_TLV_ID_LRETRY_LIMIT = 0x0204, QTN_TLV_ID_BCN_PERIOD = 0x0205, QTN_TLV_ID_DTIM = 0x0206, + QTN_TLV_ID_REG_RULE = 0x0207, QTN_TLV_ID_CHANNEL = 0x020F, QTN_TLV_ID_COVERAGE_CLASS = 0x0213, QTN_TLV_ID_IFACE_LIMIT = 0x0214, @@ -844,12 +906,54 @@ struct qlink_tlv_cclass { u8 cclass; } __packed; -enum qlink_dfs_state { - QLINK_DFS_USABLE, - QLINK_DFS_UNAVAILABLE, - QLINK_DFS_AVAILABLE, +/** + * enum qlink_reg_rule_flags - regulatory rule flags + * + * See description of &enum nl80211_reg_rule_flags + */ +enum qlink_reg_rule_flags { + QLINK_RRF_NO_OFDM = BIT(0), + QLINK_RRF_NO_CCK = BIT(1), + QLINK_RRF_NO_INDOOR = BIT(2), + QLINK_RRF_NO_OUTDOOR = BIT(3), + QLINK_RRF_DFS = BIT(4), + QLINK_RRF_PTP_ONLY = BIT(5), + QLINK_RRF_PTMP_ONLY = BIT(6), + QLINK_RRF_NO_IR = BIT(7), + QLINK_RRF_AUTO_BW = BIT(8), + QLINK_RRF_IR_CONCURRENT = BIT(9), + QLINK_RRF_NO_HT40MINUS = BIT(10), + QLINK_RRF_NO_HT40PLUS = BIT(11), + QLINK_RRF_NO_80MHZ = BIT(12), + QLINK_RRF_NO_160MHZ = BIT(13), }; +/** + * struct qlink_tlv_reg_rule - data for QTN_TLV_ID_REG_RULE TLV + * + * Regulatory rule description. + * + * @start_freq_khz: start frequency of the range the rule is attributed to. + * @end_freq_khz: end frequency of the range the rule is attributed to. + * @max_bandwidth_khz: max bandwidth that channels in specified range can be + * configured to. + * @max_antenna_gain: max antenna gain that can be used in the specified + * frequency range, dBi. + * @max_eirp: maximum EIRP. + * @flags: regulatory rule flags in &enum qlink_reg_rule_flags. + * @dfs_cac_ms: DFS CAC period. + */ +struct qlink_tlv_reg_rule { + struct qlink_tlv_hdr hdr; + __le32 start_freq_khz; + __le32 end_freq_khz; + __le32 max_bandwidth_khz; + __le32 max_antenna_gain; + __le32 max_eirp; + __le32 flags; + __le32 dfs_cac_ms; +} __packed; + enum qlink_channel_flags { QLINK_CHAN_DISABLED = BIT(0), QLINK_CHAN_NO_IR = BIT(1), @@ -865,6 +969,12 @@ enum qlink_channel_flags { QLINK_CHAN_NO_10MHZ = BIT(12), }; +enum qlink_dfs_state { + QLINK_DFS_USABLE, + QLINK_DFS_UNAVAILABLE, + QLINK_DFS_AVAILABLE, +}; + struct qlink_tlv_channel { struct qlink_tlv_hdr hdr; __le16 hw_value; -- cgit v1.2.3 From ea19479f68496f729ddf3f532ec94c6688279eec Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:44 +0300 Subject: qtnfmac: regulatory configuration for self-managed setup Regdomain information needs to be registered with cfg80211 for devices with REGULATORY_WIPHY_SELF_MANAGED flag set. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index 7f70b0aae069..e288b1d4432a 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -873,6 +873,15 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) } ret = wiphy_register(wiphy); + if (ret < 0) + goto out; + + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + ret = regulatory_set_wiphy_regd(wiphy, hw_info->rd); + else if (isalpha(hw_info->rd->alpha2[0]) && + isalpha(hw_info->rd->alpha2[1])) + ret = regulatory_hint(wiphy, hw_info->rd->alpha2); + out: if (ret) { kfree(iface_comb); -- cgit v1.2.3 From 9ef7509505c880f80a99ccd4e593fa181836da2e Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:45 +0300 Subject: qtnfmac: add missing bus lock Add missing bus lock into get_mac_chan_info command. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/commands.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index 8be96f02888f..f5a294f3c2a4 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -1442,6 +1442,9 @@ int qtnf_cmd_get_mac_chan_info(struct qtnf_wmac *mac, cmd = (struct qlink_cmd_chans_info_get *)cmd_skb->data; cmd->band = qband; + + qtnf_bus_lock(mac->bus); + ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, sizeof(*resp), &info_len); @@ -1465,6 +1468,7 @@ int qtnf_cmd_get_mac_chan_info(struct qtnf_wmac *mac, ret = qtnf_cmd_resp_fill_channels_info(band, resp, info_len); out: + qtnf_bus_unlock(mac->bus); consume_skb(resp_skb); return ret; -- cgit v1.2.3 From 7c04b43984a57bd820d8e646d089d9ae9984c77a Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:46 +0300 Subject: qtnfmac: implement cfg80211 dump_survey handler This patch implements cfg80211 dump_survey handler enabling per-channel survey data reports. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 69 +++++++++++++- drivers/net/wireless/quantenna/qtnfmac/commands.c | 107 ++++++++++++++++++++++ drivers/net/wireless/quantenna/qtnfmac/commands.h | 2 + drivers/net/wireless/quantenna/qtnfmac/core.h | 9 ++ drivers/net/wireless/quantenna/qtnfmac/qlink.h | 31 +++++++ 5 files changed, 217 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index e288b1d4432a..23f180b7d43c 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -677,6 +677,72 @@ qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int +qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, + int idx, struct survey_info *survey) +{ + struct qtnf_wmac *mac = wiphy_priv(wiphy); + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + struct qtnf_chan_stats stats; + int ret; + + sband = wiphy->bands[NL80211_BAND_2GHZ]; + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } + + if (!sband) + sband = wiphy->bands[NL80211_BAND_5GHZ]; + + if (!sband || idx >= sband->n_channels) + return -ENOENT; + + chan = &sband->channels[idx]; + memset(&stats, 0, sizeof(stats)); + + survey->channel = chan; + survey->filled = 0x0; + + ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats); + switch (ret) { + case 0: + if (unlikely(stats.chan_num != chan->hw_value)) { + pr_err("received stats for channel %d instead of %d\n", + stats.chan_num, chan->hw_value); + ret = -EINVAL; + break; + } + + survey->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_SCAN | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX | + SURVEY_INFO_NOISE_DBM; + + survey->time_scan = stats.cca_try; + survey->time = stats.cca_try; + survey->time_tx = stats.cca_tx; + survey->time_rx = stats.cca_rx; + survey->time_busy = stats.cca_busy; + survey->noise = stats.chan_noise; + break; + case -ENOENT: + pr_debug("no stats for channel %u\n", chan->hw_value); + ret = 0; + break; + default: + pr_debug("failed to get chan(%d) stats from card\n", + chan->hw_value); + ret = -EINVAL; + break; + } + + return ret; +} + static struct cfg80211_ops qtn_cfg80211_ops = { .add_virtual_intf = qtnf_add_virtual_intf, .change_virtual_intf = qtnf_change_virtual_intf, @@ -697,7 +763,8 @@ static struct cfg80211_ops qtn_cfg80211_ops = { .set_default_mgmt_key = qtnf_set_default_mgmt_key, .scan = qtnf_scan, .connect = qtnf_connect, - .disconnect = qtnf_disconnect + .disconnect = qtnf_disconnect, + .dump_survey = qtnf_dump_survey }; static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index f5a294f3c2a4..a1ce12082e10 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -1333,6 +1333,62 @@ static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac, return 0; } +static int +qtnf_cmd_resp_proc_chan_stat_info(struct qtnf_chan_stats *stats, + const u8 *payload, size_t payload_len) +{ + struct qlink_chan_stats *qlink_stats; + const struct qlink_tlv_hdr *tlv; + size_t tlv_full_len; + u16 tlv_value_len; + u16 tlv_type; + + tlv = (struct qlink_tlv_hdr *)payload; + while (payload_len >= sizeof(struct qlink_tlv_hdr)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_value_len = le16_to_cpu(tlv->len); + tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); + if (tlv_full_len > payload_len) { + pr_warn("malformed TLV 0x%.2X; LEN: %u\n", + tlv_type, tlv_value_len); + return -EINVAL; + } + switch (tlv_type) { + case QTN_TLV_ID_CHANNEL_STATS: + if (unlikely(tlv_value_len != sizeof(*qlink_stats))) { + pr_err("invalid CHANNEL_STATS entry size\n"); + return -EINVAL; + } + + qlink_stats = (void *)tlv->val; + + stats->chan_num = le32_to_cpu(qlink_stats->chan_num); + stats->cca_tx = le32_to_cpu(qlink_stats->cca_tx); + stats->cca_rx = le32_to_cpu(qlink_stats->cca_rx); + stats->cca_busy = le32_to_cpu(qlink_stats->cca_busy); + stats->cca_try = le32_to_cpu(qlink_stats->cca_try); + stats->chan_noise = qlink_stats->chan_noise; + + pr_debug("chan(%u) try(%u) busy(%u) noise(%d)\n", + stats->chan_num, stats->cca_try, + stats->cca_busy, stats->chan_noise); + break; + default: + pr_warn("Unknown TLV type: %#x\n", + le16_to_cpu(tlv->type)); + } + payload_len -= tlv_full_len; + tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); + } + + if (payload_len) { + pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len); + return -EINVAL; + } + + return 0; +} + int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac) { struct sk_buff *cmd_skb, *resp_skb = NULL; @@ -2176,3 +2232,54 @@ out: return ret; } + +int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, + struct qtnf_chan_stats *stats) +{ + struct sk_buff *cmd_skb, *resp_skb = NULL; + struct qlink_cmd_get_chan_stats *cmd; + struct qlink_resp_get_chan_stats *resp; + size_t var_data_len; + u16 res_code = QLINK_CMD_RESULT_OK; + int ret = 0; + + cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, + QLINK_CMD_CHAN_STATS, + sizeof(*cmd)); + if (!cmd_skb) + return -ENOMEM; + + qtnf_bus_lock(mac->bus); + + cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data; + cmd->channel = cpu_to_le16(channel); + + ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, + sizeof(*resp), &var_data_len); + if (unlikely(ret)) { + qtnf_bus_unlock(mac->bus); + return ret; + } + + if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { + switch (res_code) { + case QLINK_CMD_RESULT_ENOTFOUND: + ret = -ENOENT; + break; + default: + pr_err("cmd exec failed: 0x%.4X\n", res_code); + ret = -EFAULT; + break; + } + goto out; + } + + resp = (struct qlink_resp_get_chan_stats *)resp_skb->data; + ret = qtnf_cmd_resp_proc_chan_stat_info(stats, resp->info, + var_data_len); + +out: + qtnf_bus_unlock(mac->bus); + consume_skb(resp_skb); + return ret; +} diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h index 155b265d42bf..41e2d50988b7 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.h +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h @@ -71,5 +71,7 @@ int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up); int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req); +int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, + struct qtnf_chan_stats *stats); #endif /* QLINK_COMMANDS_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h index 31b7ec2bfd3e..b2d050c4f1e2 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.h +++ b/drivers/net/wireless/quantenna/qtnfmac/core.h @@ -124,6 +124,15 @@ struct qtnf_mac_info { size_t n_limits; }; +struct qtnf_chan_stats { + u32 chan_num; + u32 cca_tx; + u32 cca_rx; + u32 cca_busy; + u32 cca_try; + s8 chan_noise; +}; + struct qtnf_wmac { u8 macid; u8 wiphy_registered; diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index e27833b78940..dd01c0b4f632 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -164,6 +164,7 @@ enum qlink_cmd_type { QLINK_CMD_CHANGE_STA = 0x0051, QLINK_CMD_DEL_STA = 0x0052, QLINK_CMD_SCAN = 0x0053, + QLINK_CMD_CHAN_STATS = 0x0054, QLINK_CMD_CONNECT = 0x0060, QLINK_CMD_DISCONNECT = 0x0061, }; @@ -433,6 +434,16 @@ struct qlink_cmd_chans_info_get { u8 band; } __packed; +/** + * struct qlink_cmd_get_chan_stats - data for QLINK_CMD_CHAN_STATS command + * + * @channel: channel number according to 802.11 17.3.8.3.2 and Annex J + */ +struct qlink_cmd_get_chan_stats { + struct qlink_cmd chdr; + __le16 channel; +} __packed; + /** * enum qlink_reg_initiator - Indicates the initiator of a reg domain request * @@ -635,6 +646,16 @@ struct qlink_resp_phy_params { u8 info[0]; } __packed; +/** + * struct qlink_resp_get_chan_stats - response for QLINK_CMD_CHAN_STATS cmd + * + * @info: variable-length channel info. + */ +struct qlink_resp_get_chan_stats { + struct qlink_cmd rhdr; + u8 info[0]; +} __packed; + /* QLINK Events messages related definitions */ @@ -807,6 +828,7 @@ enum qlink_tlv_id { QTN_TLV_ID_COVERAGE_CLASS = 0x0213, QTN_TLV_ID_IFACE_LIMIT = 0x0214, QTN_TLV_ID_NUM_IFACE_COMB = 0x0215, + QTN_TLV_ID_CHANNEL_STATS = 0x0216, QTN_TLV_ID_STA_BASIC_COUNTERS = 0x0300, QTN_TLV_ID_STA_GENERIC_INFO = 0x0301, QTN_TLV_ID_KEY = 0x0302, @@ -1008,4 +1030,13 @@ struct qlink_auth_encr { u8 control_port_no_encrypt; } __packed; +struct qlink_chan_stats { + __le32 chan_num; + __le32 cca_tx; + __le32 cca_rx; + __le32 cca_busy; + __le32 cca_try; + s8 chan_noise; +} __packed; + #endif /* _QTN_QLINK_H_ */ -- cgit v1.2.3 From 278944482b2970b925d1f5b837e6618764a36125 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:47 +0300 Subject: qtnfmac: implement reporting current channel Implement current channel reporting functionality. Current operating channel can be obtained either directly using cfg80211 get_channel callback or from stats reported by cfg80211 survey_dump callback. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 62 ++++++++++++++++++++++- drivers/net/wireless/quantenna/qtnfmac/commands.c | 2 +- drivers/net/wireless/quantenna/qtnfmac/qlink.h | 2 +- 3 files changed, 62 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index 23f180b7d43c..ef8089c163bc 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -593,6 +593,7 @@ qtnf_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); + struct cfg80211_chan_def chandef; struct qtnf_bss_config *bss_cfg; int ret; @@ -605,9 +606,20 @@ qtnf_connect(struct wiphy *wiphy, struct net_device *dev, bss_cfg = &vif->bss_cfg; memset(bss_cfg, 0, sizeof(*bss_cfg)); + if (sme->channel) { + /* FIXME: need to set proper nl80211_channel_type value */ + cfg80211_chandef_create(&chandef, sme->channel, + NL80211_CHAN_HT20); + /* fall-back to minimal safe chandef description */ + if (!cfg80211_chandef_valid(&chandef)) + cfg80211_chandef_create(&chandef, sme->channel, + NL80211_CHAN_HT20); + + memcpy(&bss_cfg->chandef, &chandef, sizeof(bss_cfg->chandef)); + } + bss_cfg->ssid_len = sme->ssid_len; memcpy(&bss_cfg->ssid, sme->ssid, bss_cfg->ssid_len); - bss_cfg->chandef.chan = sme->channel; bss_cfg->auth_type = sme->auth_type; bss_cfg->privacy = sme->privacy; bss_cfg->mfp = sme->mfp; @@ -683,10 +695,15 @@ qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, { struct qtnf_wmac *mac = wiphy_priv(wiphy); struct ieee80211_supported_band *sband; + struct cfg80211_chan_def *bss_chandef; struct ieee80211_channel *chan; struct qtnf_chan_stats stats; + struct qtnf_vif *vif; int ret; + vif = qtnf_netdev_get_priv(dev); + bss_chandef = &vif->bss_cfg.chandef; + sband = wiphy->bands[NL80211_BAND_2GHZ]; if (sband && idx >= sband->n_channels) { idx -= sband->n_channels; @@ -705,6 +722,10 @@ qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, survey->channel = chan; survey->filled = 0x0; + if (bss_chandef->chan) + if (chan->hw_value == bss_chandef->chan->hw_value) + survey->filled |= SURVEY_INFO_IN_USE; + ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats); switch (ret) { case 0: @@ -743,6 +764,42 @@ qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, return ret; } +static int +qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) +{ + struct net_device *ndev = wdev->netdev; + struct qtnf_bss_config *bss_cfg; + struct qtnf_vif *vif; + + if (!ndev) + return -ENODEV; + + vif = qtnf_netdev_get_priv(wdev->netdev); + bss_cfg = &vif->bss_cfg; + + switch (vif->wdev.iftype) { + case NL80211_IFTYPE_STATION: + if (vif->sta_state == QTNF_STA_DISCONNECTED) { + pr_warn("%s: STA disconnected\n", ndev->name); + return -ENODATA; + } + break; + case NL80211_IFTYPE_AP: + if (!(vif->bss_status & QTNF_STATE_AP_START)) { + pr_warn("%s: AP not started\n", ndev->name); + return -ENODATA; + } + break; + default: + pr_err("unsupported vif type (%d)\n", vif->wdev.iftype); + return -ENODATA; + } + + memcpy(chandef, &bss_cfg->chandef, sizeof(*chandef)); + return 0; +} + static struct cfg80211_ops qtn_cfg80211_ops = { .add_virtual_intf = qtnf_add_virtual_intf, .change_virtual_intf = qtnf_change_virtual_intf, @@ -764,7 +821,8 @@ static struct cfg80211_ops qtn_cfg80211_ops = { .scan = qtnf_scan, .connect = qtnf_connect, .disconnect = qtnf_disconnect, - .dump_survey = qtnf_dump_survey + .dump_survey = qtnf_dump_survey, + .get_channel = qtnf_get_channel }; static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index a1ce12082e10..c8c0685f81b3 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -2036,7 +2036,7 @@ int qtnf_cmd_send_connect(struct qtnf_vif *vif, ether_addr_copy(cmd->bssid, bss_cfg->bssid); if (bss_cfg->chandef.chan) - cmd->freq = cpu_to_le16(bss_cfg->chandef.chan->center_freq); + cmd->channel = cpu_to_le16(bss_cfg->chandef.chan->hw_value); cmd->bg_scan_period = cpu_to_le16(bss_cfg->bg_scan_period); diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index dd01c0b4f632..6c6940b503a4 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -384,7 +384,7 @@ enum qlink_sta_connect_flags { struct qlink_cmd_connect { struct qlink_cmd chdr; __le32 flags; - __le16 freq; + __le16 channel; __le16 bg_scan_period; u8 bssid[ETH_ALEN]; u8 payload[0]; -- cgit v1.2.3 From fd19ecebe03e7d7d468a246b8078d062b8e8db25 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:48 +0300 Subject: qtnfmac: fix station leave reason endianness Use proper endianness conversion for client station leave reason. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/event.c | 4 ++-- drivers/net/wireless/quantenna/qtnfmac/qlink.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c index 9b61e9a83670..00570de918e6 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/event.c +++ b/drivers/net/wireless/quantenna/qtnfmac/event.c @@ -211,8 +211,8 @@ qtnf_event_handle_bss_leave(struct qtnf_vif *vif, pr_debug("VIF%u.%u: disconnected\n", vif->mac->macid, vif->vifid); - cfg80211_disconnected(vif->netdev, leave_info->reason, NULL, 0, 0, - GFP_KERNEL); + cfg80211_disconnected(vif->netdev, le16_to_cpu(leave_info->reason), + NULL, 0, 0, GFP_KERNEL); vif->sta_state = QTNF_STA_DISCONNECTED; netif_carrier_off(vif->netdev); diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index 6c6940b503a4..5c2d8f0abd7f 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -733,7 +733,7 @@ struct qlink_event_bss_join { */ struct qlink_event_bss_leave { struct qlink_event ehdr; - u16 reason; + __le16 reason; } __packed; enum qlink_rxmgmt_flags { -- cgit v1.2.3 From 34f1145b2c5ee7eac353c048e964510e252773d0 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:49 +0300 Subject: qtnfmac: move current channel info from vif to mac Wireless cfg80211 core supplies channel settings in cfg80211_ap_settings structure for each BSS in multiple BSS configuration. On the other hand all the virtual interfaces on one radio are using the same PHY settings including channel. Move chandef structure from vif to mac structure in order to mantain the only instance of cfg80211_chan_def structure in qtnf_wmac rather than its multiple copies in qtnf_vif. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 38 +++++++++++++++-------- drivers/net/wireless/quantenna/qtnfmac/commands.c | 6 ++-- drivers/net/wireless/quantenna/qtnfmac/core.h | 2 +- 3 files changed, 29 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index ef8089c163bc..d47050934f00 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -266,11 +266,19 @@ static int qtnf_start_ap(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ap_settings *settings) { struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); + struct qtnf_wmac *mac = wiphy_priv(wiphy); struct qtnf_bss_config *bss_cfg; int ret; - bss_cfg = &vif->bss_cfg; + if (!cfg80211_chandef_identical(&mac->chandef, &settings->chandef)) { + memcpy(&mac->chandef, &settings->chandef, sizeof(mac->chandef)); + if (vif->vifid != 0) + pr_warn("%s: unexpected chan %u (%u MHz)\n", dev->name, + settings->chandef.chan->hw_value, + settings->chandef.chan->center_freq); + } + bss_cfg = &vif->bss_cfg; memset(bss_cfg, 0, sizeof(*bss_cfg)); bss_cfg->bcn_period = settings->beacon_interval; @@ -281,8 +289,6 @@ static int qtnf_start_ap(struct wiphy *wiphy, struct net_device *dev, bss_cfg->ssid_len = settings->ssid_len; memcpy(&bss_cfg->ssid, settings->ssid, bss_cfg->ssid_len); - memcpy(&bss_cfg->chandef, &settings->chandef, - sizeof(struct cfg80211_chan_def)); memcpy(&bss_cfg->crypto, &settings->crypto, sizeof(struct cfg80211_crypto_settings)); @@ -593,6 +599,7 @@ qtnf_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); + struct qtnf_wmac *mac = wiphy_priv(wiphy); struct cfg80211_chan_def chandef; struct qtnf_bss_config *bss_cfg; int ret; @@ -615,7 +622,7 @@ qtnf_connect(struct wiphy *wiphy, struct net_device *dev, cfg80211_chandef_create(&chandef, sme->channel, NL80211_CHAN_HT20); - memcpy(&bss_cfg->chandef, &chandef, sizeof(bss_cfg->chandef)); + memcpy(&mac->chandef, &chandef, sizeof(mac->chandef)); } bss_cfg->ssid_len = sme->ssid_len; @@ -695,14 +702,14 @@ qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, { struct qtnf_wmac *mac = wiphy_priv(wiphy); struct ieee80211_supported_band *sband; - struct cfg80211_chan_def *bss_chandef; + struct cfg80211_chan_def *chandef; struct ieee80211_channel *chan; struct qtnf_chan_stats stats; struct qtnf_vif *vif; int ret; vif = qtnf_netdev_get_priv(dev); - bss_chandef = &vif->bss_cfg.chandef; + chandef = &mac->chandef; sband = wiphy->bands[NL80211_BAND_2GHZ]; if (sband && idx >= sband->n_channels) { @@ -722,9 +729,10 @@ qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, survey->channel = chan; survey->filled = 0x0; - if (bss_chandef->chan) - if (chan->hw_value == bss_chandef->chan->hw_value) - survey->filled |= SURVEY_INFO_IN_USE; + if (chandef->chan) { + if (chan->hw_value == chandef->chan->hw_value) + survey->filled = SURVEY_INFO_IN_USE; + } ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats); switch (ret) { @@ -736,7 +744,7 @@ qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, break; } - survey->filled = SURVEY_INFO_TIME | + survey->filled |= SURVEY_INFO_TIME | SURVEY_INFO_TIME_SCAN | SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX | @@ -768,15 +776,14 @@ static int qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_chan_def *chandef) { + struct qtnf_wmac *mac = wiphy_priv(wiphy); struct net_device *ndev = wdev->netdev; - struct qtnf_bss_config *bss_cfg; struct qtnf_vif *vif; if (!ndev) return -ENODEV; vif = qtnf_netdev_get_priv(wdev->netdev); - bss_cfg = &vif->bss_cfg; switch (vif->wdev.iftype) { case NL80211_IFTYPE_STATION: @@ -796,7 +803,12 @@ qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, return -ENODATA; } - memcpy(chandef, &bss_cfg->chandef, sizeof(*chandef)); + if (!cfg80211_chandef_valid(&mac->chandef)) { + pr_err("invalid channel settings on %s\n", ndev->name); + return -ENODATA; + } + + memcpy(chandef, &mac->chandef, sizeof(*chandef)); return 0; } diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index c8c0685f81b3..a3c3dddb194c 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -185,7 +185,7 @@ int qtnf_cmd_send_config_ap(struct qtnf_vif *vif) { struct sk_buff *cmd_skb; struct qtnf_bss_config *bss_cfg = &vif->bss_cfg; - struct cfg80211_chan_def *chandef = &bss_cfg->chandef; + struct cfg80211_chan_def *chandef = &vif->mac->chandef; struct qlink_tlv_channel *qchan; struct qlink_auth_encr aen; u16 res_code = QLINK_CMD_RESULT_OK; @@ -2035,8 +2035,8 @@ int qtnf_cmd_send_connect(struct qtnf_vif *vif, ether_addr_copy(cmd->bssid, bss_cfg->bssid); - if (bss_cfg->chandef.chan) - cmd->channel = cpu_to_le16(bss_cfg->chandef.chan->hw_value); + if (vif->mac->chandef.chan) + cmd->channel = cpu_to_le16(vif->mac->chandef.chan->hw_value); cmd->bg_scan_period = cpu_to_le16(bss_cfg->bg_scan_period); diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h index b2d050c4f1e2..6830ff45976d 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.h +++ b/drivers/net/wireless/quantenna/qtnfmac/core.h @@ -67,7 +67,6 @@ struct qtnf_bss_config { u16 auth_type; bool privacy; enum nl80211_mfp mfp; - struct cfg80211_chan_def chandef; struct cfg80211_crypto_settings crypto; u16 bg_scan_period; u32 connect_flags; @@ -141,6 +140,7 @@ struct qtnf_wmac { struct qtnf_mac_info macinfo; struct qtnf_vif iflist[QTNF_MAX_INTF]; struct cfg80211_scan_request *scan_req; + struct cfg80211_chan_def chandef; }; struct qtnf_hw_info { -- cgit v1.2.3 From 97883695d596e296bf327b65e1a4db32fa302d16 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:50 +0300 Subject: qtnfmac: implement cfg80211 channel_switch handler This patch implements cfg80211 channel_switch handler enabling CSA channel-switch procedure. Driver performs only basic validation of the requested new channel and then sends command to firmware. Beacon IEs are not sent since beacon update is handled by firmware. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 60 +++++++++++++++++++++- drivers/net/wireless/quantenna/qtnfmac/commands.c | 55 ++++++++++++++++++++ drivers/net/wireless/quantenna/qtnfmac/commands.h | 2 + drivers/net/wireless/quantenna/qtnfmac/core.h | 6 +++ drivers/net/wireless/quantenna/qtnfmac/event.c | 61 +++++++++++++++++++++++ drivers/net/wireless/quantenna/qtnfmac/qlink.h | 28 +++++++++++ 6 files changed, 210 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index d47050934f00..ac8fdc1db482 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -812,6 +812,59 @@ qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, return 0; } +static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + struct qtnf_wmac *mac = wiphy_priv(wiphy); + struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); + int ret; + + pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name, + params->chandef.chan->hw_value, params->count, + params->radar_required, params->block_tx); + + switch (vif->wdev.iftype) { + case NL80211_IFTYPE_AP: + if (!(vif->bss_status & QTNF_STATE_AP_START)) { + pr_warn("AP not started on %s\n", dev->name); + return -ENOTCONN; + } + break; + default: + pr_err("unsupported vif type (%d) on %s\n", + vif->wdev.iftype, dev->name); + return -EOPNOTSUPP; + } + + if (vif->vifid != 0) { + if (!(mac->status & QTNF_MAC_CSA_ACTIVE)) + return -EOPNOTSUPP; + + if (!cfg80211_chandef_identical(¶ms->chandef, + &mac->csa_chandef)) + return -EINVAL; + + return 0; + } + + if (!cfg80211_chandef_valid(¶ms->chandef)) { + pr_err("%s: invalid channel\n", dev->name); + return -EINVAL; + } + + if (cfg80211_chandef_identical(¶ms->chandef, &mac->chandef)) { + pr_err("%s: switch request to the same channel\n", dev->name); + return -EALREADY; + } + + ret = qtnf_cmd_send_chan_switch(mac, params); + if (ret) + pr_warn("%s: failed to switch to channel (%u)\n", + dev->name, params->chandef.chan->hw_value); + + return ret; +} + static struct cfg80211_ops qtn_cfg80211_ops = { .add_virtual_intf = qtnf_add_virtual_intf, .change_virtual_intf = qtnf_change_virtual_intf, @@ -834,7 +887,8 @@ static struct cfg80211_ops qtn_cfg80211_ops = { .connect = qtnf_connect, .disconnect = qtnf_disconnect, .dump_survey = qtnf_dump_survey, - .get_channel = qtnf_get_channel + .get_channel = qtnf_get_channel, + .channel_switch = qtnf_channel_switch }; static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, @@ -981,6 +1035,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) wiphy->iface_combinations = iface_comb; wiphy->n_iface_combinations = 1; + wiphy->max_num_csa_counters = 2; /* Initialize cipher suits */ wiphy->cipher_suites = qtnf_cipher_suites; @@ -988,7 +1043,8 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | - WIPHY_FLAG_AP_UAPSD; + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_HAS_CHANNEL_SWITCH; wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index a3c3dddb194c..524269d2c30c 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -2283,3 +2283,58 @@ out: consume_skb(resp_skb); return ret; } + +int qtnf_cmd_send_chan_switch(struct qtnf_wmac *mac, + struct cfg80211_csa_settings *params) +{ + struct qlink_cmd_chan_switch *cmd; + struct sk_buff *cmd_skb; + u16 res_code = QLINK_CMD_RESULT_OK; + int ret; + + cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0x0, + QLINK_CMD_CHAN_SWITCH, + sizeof(*cmd)); + + if (unlikely(!cmd_skb)) + return -ENOMEM; + + qtnf_bus_lock(mac->bus); + + cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data; + cmd->channel = cpu_to_le16(params->chandef.chan->hw_value); + cmd->radar_required = params->radar_required; + cmd->block_tx = params->block_tx; + cmd->beacon_count = params->count; + + ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); + + if (unlikely(ret)) + goto out; + + switch (res_code) { + case QLINK_CMD_RESULT_OK: + memcpy(&mac->csa_chandef, ¶ms->chandef, + sizeof(mac->csa_chandef)); + mac->status |= QTNF_MAC_CSA_ACTIVE; + ret = 0; + break; + case QLINK_CMD_RESULT_ENOTFOUND: + ret = -ENOENT; + break; + case QLINK_CMD_RESULT_ENOTSUPP: + ret = -EOPNOTSUPP; + break; + case QLINK_CMD_RESULT_EALREADY: + ret = -EALREADY; + break; + case QLINK_CMD_RESULT_INVALID: + default: + ret = -EFAULT; + break; + } + +out: + qtnf_bus_unlock(mac->bus); + return ret; +} diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h index 41e2d50988b7..783b20364296 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.h +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h @@ -73,5 +73,7 @@ int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req); int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, struct qtnf_chan_stats *stats); +int qtnf_cmd_send_chan_switch(struct qtnf_wmac *mac, + struct cfg80211_csa_settings *params); #endif /* QLINK_COMMANDS_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h index 6830ff45976d..099aad76afeb 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.h +++ b/drivers/net/wireless/quantenna/qtnfmac/core.h @@ -88,6 +88,10 @@ enum qtnf_sta_state { QTNF_STA_CONNECTED }; +enum qtnf_mac_status { + QTNF_MAC_CSA_ACTIVE = BIT(0) +}; + struct qtnf_vif { struct wireless_dev wdev; u8 vifid; @@ -136,11 +140,13 @@ struct qtnf_wmac { u8 macid; u8 wiphy_registered; u8 macaddr[ETH_ALEN]; + u32 status; struct qtnf_bus *bus; struct qtnf_mac_info macinfo; struct qtnf_vif iflist[QTNF_MAX_INTF]; struct cfg80211_scan_request *scan_req; struct cfg80211_chan_def chandef; + struct cfg80211_chan_def csa_chandef; }; struct qtnf_hw_info { diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c index 00570de918e6..43d2e7fd6e02 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/event.c +++ b/drivers/net/wireless/quantenna/qtnfmac/event.c @@ -350,6 +350,63 @@ qtnf_event_handle_scan_complete(struct qtnf_wmac *mac, return 0; } +static int +qtnf_event_handle_freq_change(struct qtnf_wmac *mac, + const struct qlink_event_freq_change *data, + u16 len) +{ + struct wiphy *wiphy = priv_to_wiphy(mac); + struct cfg80211_chan_def chandef; + struct ieee80211_channel *chan; + struct qtnf_vif *vif; + int freq; + int i; + + if (len < sizeof(*data)) { + pr_err("payload is too short\n"); + return -EINVAL; + } + + freq = le32_to_cpu(data->freq); + chan = ieee80211_get_channel(wiphy, freq); + if (!chan) { + pr_err("channel at %d MHz not found\n", freq); + return -EINVAL; + } + + pr_debug("MAC%d switch to new channel %u MHz\n", mac->macid, freq); + + if (mac->status & QTNF_MAC_CSA_ACTIVE) { + mac->status &= ~QTNF_MAC_CSA_ACTIVE; + if (chan->hw_value != mac->csa_chandef.chan->hw_value) + pr_warn("unexpected switch to %u during CSA to %u\n", + chan->hw_value, + mac->csa_chandef.chan->hw_value); + } + + /* FIXME: need to figure out proper nl80211_channel_type value */ + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); + /* fall-back to minimal safe chandef description */ + if (!cfg80211_chandef_valid(&chandef)) + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); + + memcpy(&mac->chandef, &chandef, sizeof(mac->chandef)); + + for (i = 0; i < QTNF_MAX_INTF; i++) { + vif = &mac->iflist[i]; + if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) + continue; + + if (vif->netdev) { + mutex_lock(&vif->wdev.mtx); + cfg80211_ch_switch_notify(vif->netdev, &chandef); + mutex_unlock(&vif->wdev.mtx); + } + } + + return 0; +} + static int qtnf_event_parse(struct qtnf_wmac *mac, const struct sk_buff *event_skb) { @@ -400,6 +457,10 @@ static int qtnf_event_parse(struct qtnf_wmac *mac, ret = qtnf_event_handle_bss_leave(vif, (const void *)event, event_len); break; + case QLINK_EVENT_FREQ_CHANGE: + ret = qtnf_event_handle_freq_change(mac, (const void *)event, + event_len); + break; default: pr_warn("unknown event type: %x\n", event_id); break; diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index 5c2d8f0abd7f..c529cc1994b4 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -153,6 +153,7 @@ enum qlink_cmd_type { QLINK_CMD_UPDOWN_INTF = 0x0018, QLINK_CMD_REG_NOTIFY = 0x0019, QLINK_CMD_CHANS_INFO_GET = 0x001A, + QLINK_CMD_CHAN_SWITCH = 0x001B, QLINK_CMD_CONFIG_AP = 0x0020, QLINK_CMD_START_AP = 0x0021, QLINK_CMD_STOP_AP = 0x0022, @@ -482,6 +483,22 @@ struct qlink_cmd_reg_notify { u8 user_reg_hint_type; } __packed; +/** + * struct qlink_cmd_chan_switch - data for QLINK_CMD_CHAN_SWITCH command + * + * @channel: channel number according to 802.11 17.3.8.3.2 and Annex J + * @radar_required: whether radar detection is required on the new channel + * @block_tx: whether transmissions should be blocked while changing + * @beacon_count: number of beacons until switch + */ +struct qlink_cmd_chan_switch { + struct qlink_cmd chdr; + __le16 channel; + u8 radar_required; + u8 block_tx; + u8 beacon_count; +} __packed; + /* QLINK Command Responses messages related definitions */ @@ -667,6 +684,7 @@ enum qlink_event_type { QLINK_EVENT_SCAN_COMPLETE = 0x0025, QLINK_EVENT_BSS_JOIN = 0x0026, QLINK_EVENT_BSS_LEAVE = 0x0027, + QLINK_EVENT_FREQ_CHANGE = 0x0028, }; /** @@ -736,6 +754,16 @@ struct qlink_event_bss_leave { __le16 reason; } __packed; +/** + * struct qlink_event_freq_change - data for QLINK_EVENT_FREQ_CHANGE event + * + * @freq: new operating frequency in MHz + */ +struct qlink_event_freq_change { + struct qlink_event ehdr; + __le32 freq; +} __packed; + enum qlink_rxmgmt_flags { QLINK_RXMGMT_FLAG_ANSWERED = 1 << 0, }; -- cgit v1.2.3 From c7ead2abd26ab536a2e479af605a6d9529e3a694 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:51 +0300 Subject: qtnfmac: implement scan timeout Userspace tools may hang on scan in the case when scan completion event is not returned by firmware. This patch implements the scan timeout to avoid such situation. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 22 ++++++++++++++++++---- drivers/net/wireless/quantenna/qtnfmac/cfg80211.h | 4 ++++ drivers/net/wireless/quantenna/qtnfmac/core.c | 2 ++ drivers/net/wireless/quantenna/qtnfmac/core.h | 3 +++ drivers/net/wireless/quantenna/qtnfmac/event.c | 2 ++ 5 files changed, 29 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index ac8fdc1db482..856fa6e8327e 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -579,19 +579,33 @@ qtnf_del_station(struct wiphy *wiphy, struct net_device *dev, return ret; } +static void qtnf_scan_timeout(unsigned long data) +{ + struct qtnf_wmac *mac = (struct qtnf_wmac *)data; + + pr_warn("mac%d scan timed out\n", mac->macid); + qtnf_scan_done(mac, true); +} + static int qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct qtnf_wmac *mac = wiphy_priv(wiphy); - int ret; mac->scan_req = request; - ret = qtnf_cmd_send_scan(mac); - if (ret) + if (qtnf_cmd_send_scan(mac)) { pr_err("MAC%u: failed to start scan\n", mac->macid); + mac->scan_req = NULL; + return -EFAULT; + } - return ret; + mac->scan_timeout.data = (unsigned long)mac; + mac->scan_timeout.function = qtnf_scan_timeout; + mod_timer(&mac->scan_timeout, + jiffies + QTNF_SCAN_TIMEOUT_SEC * HZ); + + return 0; } static int diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.h b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.h index 5bd33124a7c8..6a4af52522b8 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.h +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.h @@ -34,10 +34,14 @@ static inline void qtnf_scan_done(struct qtnf_wmac *mac, bool aborted) .aborted = aborted, }; + mutex_lock(&mac->mac_lock); + if (mac->scan_req) { cfg80211_scan_done(mac->scan_req, &info); mac->scan_req = NULL; } + + mutex_unlock(&mac->mac_lock); } #endif /* _QTN_FMAC_CFG80211_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c index 17d17e332a8b..5e60180482d1 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.c +++ b/drivers/net/wireless/quantenna/qtnfmac/core.c @@ -288,6 +288,8 @@ static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus, mac->iflist[i].mac = mac; mac->iflist[i].vifid = i; qtnf_sta_list_init(&mac->iflist[i].sta_list); + mutex_init(&mac->mac_lock); + init_timer(&mac->scan_timeout); } qtnf_mac_init_primary_intf(mac); diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h index 099aad76afeb..066fcd1095a0 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.h +++ b/drivers/net/wireless/quantenna/qtnfmac/core.h @@ -46,6 +46,7 @@ #define QTNF_MAX_EVENT_QUEUE_LEN 255 #define QTNF_DEFAULT_BG_SCAN_PERIOD 300 #define QTNF_MAX_BG_SCAN_PERIOD 0xffff +#define QTNF_SCAN_TIMEOUT_SEC 15 #define QTNF_DEF_BSS_PRIORITY 0 #define QTNF_DEF_WDOG_TIMEOUT 5 @@ -147,6 +148,8 @@ struct qtnf_wmac { struct cfg80211_scan_request *scan_req; struct cfg80211_chan_def chandef; struct cfg80211_chan_def csa_chandef; + struct mutex mac_lock; /* lock during wmac speicific ops */ + struct timer_list scan_timeout; }; struct qtnf_hw_info { diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c index 43d2e7fd6e02..0fc2814eafad 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/event.c +++ b/drivers/net/wireless/quantenna/qtnfmac/event.c @@ -345,6 +345,8 @@ qtnf_event_handle_scan_complete(struct qtnf_wmac *mac, return -EINVAL; } + if (timer_pending(&mac->scan_timeout)) + del_timer_sync(&mac->scan_timeout); qtnf_scan_done(mac, le32_to_cpu(status->flags) & QLINK_SCAN_ABORTED); return 0; -- cgit v1.2.3 From 41c8fa0c62744469466dae072fcb93b716155c7a Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:52 +0300 Subject: qtnfmac: fix handling of iftype mask reported by firmware Firmware sends supported interface type rather than mask. As a result, types field of ieee80211_iface_limit structure may end up having multiple iftype bits set. This leads to WARN_ON from wiphy_verify_combinations. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/commands.c | 9 +++++---- drivers/net/wireless/quantenna/qtnfmac/qlink.h | 2 +- .../net/wireless/quantenna/qtnfmac/qlink_util.c | 23 ++++++++++++---------- .../net/wireless/quantenna/qtnfmac/qlink_util.h | 2 +- 4 files changed, 20 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index 524269d2c30c..94656106b496 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -989,7 +989,7 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, struct ieee80211_iface_limit *limits = NULL; const struct qlink_iface_limit *limit_record; size_t record_count = 0, rec = 0; - u16 tlv_type, tlv_value_len, mask; + u16 tlv_type, tlv_value_len; struct qlink_iface_comb_num *comb; size_t tlv_full_len; const struct qlink_tlv_hdr *tlv; @@ -1042,9 +1042,10 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, limit_record = (void *)tlv->val; limits[rec].max = le16_to_cpu(limit_record->max_num); - mask = le16_to_cpu(limit_record->type_mask); - limits[rec].types = qlink_iface_type_mask_to_nl(mask); - /* only AP and STA modes are supported */ + limits[rec].types = qlink_iface_type_to_nl_mask( + le16_to_cpu(limit_record->type)); + + /* supported modes: STA, AP */ limits[rec].types &= BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION); diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index c529cc1994b4..ec5126362494 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -873,7 +873,7 @@ struct qlink_tlv_hdr { struct qlink_iface_limit { __le16 max_num; - __le16 type_mask; + __le16 type; } __packed; struct qlink_iface_comb_num { diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c index 49ae652ad9a3..22fa631d692d 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c @@ -17,24 +17,27 @@ #include "qlink_util.h" -u16 qlink_iface_type_mask_to_nl(u16 qlink_mask) +u16 qlink_iface_type_to_nl_mask(u16 qlink_type) { u16 result = 0; - if (qlink_mask & QLINK_IFTYPE_AP) + switch (qlink_type) { + case QLINK_IFTYPE_AP: result |= BIT(NL80211_IFTYPE_AP); - - if (qlink_mask & QLINK_IFTYPE_STATION) + break; + case QLINK_IFTYPE_STATION: result |= BIT(NL80211_IFTYPE_STATION); - - if (qlink_mask & QLINK_IFTYPE_ADHOC) + break; + case QLINK_IFTYPE_ADHOC: result |= BIT(NL80211_IFTYPE_ADHOC); - - if (qlink_mask & QLINK_IFTYPE_MONITOR) + break; + case QLINK_IFTYPE_MONITOR: result |= BIT(NL80211_IFTYPE_MONITOR); - - if (qlink_mask & QLINK_IFTYPE_WDS) + break; + case QLINK_IFTYPE_WDS: result |= BIT(NL80211_IFTYPE_WDS); + break; + } return result; } diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h index 90d7d09a6c63..c9e882a3a991 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h @@ -68,7 +68,7 @@ static inline void qtnf_cmd_skb_put_tlv_u16(struct sk_buff *skb, memcpy(hdr->val, &tmp, sizeof(tmp)); } -u16 qlink_iface_type_mask_to_nl(u16 qlink_mask); +u16 qlink_iface_type_to_nl_mask(u16 qlink_type); u8 qlink_chan_width_mask_to_nl(u16 qlink_mask); #endif /* _QTN_FMAC_QLINK_UTIL_H_ */ -- cgit v1.2.3 From 03ddf59d786d6c9f67d2fc8ea7b54c12f1daa45b Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:53 +0300 Subject: qtnfmac: remove function qtnf_cmd_skb_put_action This function is not used anymore, so remove it. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/qlink_util.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h index c9e882a3a991..de06c1e20b5b 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h @@ -22,14 +22,6 @@ #include "qlink.h" -static inline void qtnf_cmd_skb_put_action(struct sk_buff *skb, u16 action) -{ - __le16 *buf_ptr; - - buf_ptr = skb_put(skb, sizeof(action)); - *buf_ptr = cpu_to_le16(action); -} - static inline void qtnf_cmd_skb_put_buffer(struct sk_buff *skb, const u8 *buf_src, size_t len) { -- cgit v1.2.3 From 805b28c05c8e0496f679f180e2d276cce8b949b0 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 28 Jul 2017 02:06:54 +0300 Subject: qtnfmac: prepare for AP_VLAN interface type support Modify qlink command structures and interface types handling to prepare adding AP_VLAN support to qtnfmac driver. Signed-off-by: Igor Mitsyanko Signed-off-by: Sergey Matyukevich Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/commands.c | 27 ++++++++++++++++++---- drivers/net/wireless/quantenna/qtnfmac/qlink.h | 13 ++++++++--- .../net/wireless/quantenna/qtnfmac/qlink_util.c | 3 +++ 3 files changed, 36 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index 94656106b496..4206886b110c 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -1047,6 +1047,7 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, /* supported modes: STA, AP */ limits[rec].types &= BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_AP_VLAN) | BIT(NL80211_IFTYPE_STATION); pr_debug("MAC%u: MAX: %u; TYPES: %.4X\n", mac->macid, @@ -1058,6 +1059,7 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, default: break; } + tlv_buf_size -= tlv_full_len; tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); } @@ -1859,10 +1861,27 @@ int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac, cmd = (struct qlink_cmd_change_sta *)cmd_skb->data; ether_addr_copy(cmd->sta_addr, mac); - cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( - params->sta_flags_mask)); - cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( - params->sta_flags_set)); + + switch (vif->wdev.iftype) { + case NL80211_IFTYPE_AP: + cmd->if_type = cpu_to_le16(QLINK_IFTYPE_AP); + cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( + params->sta_flags_mask)); + cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( + params->sta_flags_set)); + break; + case NL80211_IFTYPE_STATION: + cmd->if_type = cpu_to_le16(QLINK_IFTYPE_STATION); + cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( + params->sta_flags_mask)); + cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( + params->sta_flags_set)); + break; + default: + pr_err("unsupported iftype %d\n", vif->wdev.iftype); + ret = -EINVAL; + goto out; + } ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); if (unlikely(ret)) diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index ec5126362494..a8242f678496 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -19,7 +19,7 @@ #include -#define QLINK_PROTO_VER 4 +#define QLINK_PROTO_VER 5 #define QLINK_MACID_RSVD 0xFF #define QLINK_VIFID_RSVD 0xFF @@ -77,6 +77,7 @@ enum qlink_iface_type { QLINK_IFTYPE_ADHOC = 3, QLINK_IFTYPE_MONITOR = 4, QLINK_IFTYPE_WDS = 5, + QLINK_IFTYPE_AP_VLAN = 6, }; /** @@ -85,12 +86,12 @@ enum qlink_iface_type { * Data describing a single virtual interface. * * @if_type: Mode of interface operation, one of &enum qlink_iface_type - * @flags: interface flagsmap. + * @vlanid: VLAN ID for AP_VLAN interface type * @mac_addr: MAC address of virtual interface. */ struct qlink_intf_info { __le16 if_type; - __le16 flags; + __le16 vlanid; u8 mac_addr[ETH_ALEN]; u8 rsvd[2]; } __packed; @@ -292,6 +293,7 @@ struct qlink_cmd_get_sta_info { * @pairwise: whether to use pairwise key. * @addr: MAC address of a STA key is being installed to. * @cipher: cipher suite. + * @vlanid: VLAN ID for AP_VLAN interface type * @key_data: key data itself. */ struct qlink_cmd_add_key { @@ -300,6 +302,7 @@ struct qlink_cmd_add_key { u8 pairwise; u8 addr[ETH_ALEN]; __le32 cipher; + __le16 vlanid; u8 key_data[0]; } __packed; @@ -346,12 +349,16 @@ struct qlink_cmd_set_def_mgmt_key { * * @sta_flags_mask: STA flags mask, bitmap of &enum qlink_sta_flags * @sta_flags_set: STA flags values, bitmap of &enum qlink_sta_flags + * @if_type: Mode of interface operation, one of &enum qlink_iface_type + * @vlanid: VLAN ID to assign to specific STA * @sta_addr: address of the STA for which parameters are set. */ struct qlink_cmd_change_sta { struct qlink_cmd chdr; __le32 sta_flags_mask; __le32 sta_flags_set; + __le16 if_type; + __le16 vlanid; u8 sta_addr[ETH_ALEN]; } __packed; diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c index 22fa631d692d..cf024c995fd6 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c @@ -37,6 +37,9 @@ u16 qlink_iface_type_to_nl_mask(u16 qlink_type) case QLINK_IFTYPE_WDS: result |= BIT(NL80211_IFTYPE_WDS); break; + case QLINK_IFTYPE_AP_VLAN: + result |= BIT(NL80211_IFTYPE_AP_VLAN); + break; } return result; -- cgit v1.2.3 From 13cb8a5845ff1a1fe1644bbae9790b557ac69bdb Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 24 Jul 2017 18:41:30 +0100 Subject: qtnfmac: Tidy up DMA mask setting As the only caller of dma_supported() outside of DMA API internals, the qtfnmac driver stands out and invites scrutiny. Thankfully, it's not being used for evil, but it is entirely redundant, since it open-codes a check that the DMA mask setting functions are going to perform anyway. In fact, the whole qtnf_pcie_init_dma_mask() function is nothing more than a rather long-winded implementation of dma_set_mask_and_coherent(), so let's just use that directly. Signed-off-by: Robin Murphy Acked-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- .../net/wireless/quantenna/qtnfmac/pearl/pcie.c | 28 +--------------------- 1 file changed, 1 insertion(+), 27 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index 7fc4f0d6a9ad..2c065ffda070 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -274,32 +274,6 @@ static int qtnf_pcie_init_memory(struct qtnf_pcie_bus_priv *priv) return 0; } -static int -qtnf_pcie_init_dma_mask(struct qtnf_pcie_bus_priv *priv, u64 dma_mask) -{ - int ret; - - ret = dma_supported(&priv->pdev->dev, dma_mask); - if (!ret) { - pr_err("DMA mask %llu not supported\n", dma_mask); - return ret; - } - - ret = pci_set_dma_mask(priv->pdev, dma_mask); - if (ret) { - pr_err("failed to set DMA mask %llu\n", dma_mask); - return ret; - } - - ret = pci_set_consistent_dma_mask(priv->pdev, dma_mask); - if (ret) { - pr_err("failed to set consistent DMA mask %llu\n", dma_mask); - return ret; - } - - return ret; -} - static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv) { struct pci_dev *pdev = priv->pdev; @@ -1212,7 +1186,7 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_base; } - ret = qtnf_pcie_init_dma_mask(pcie_priv, DMA_BIT_MASK(32)); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { pr_err("PCIE DMA mask init failed\n"); goto err_base; -- cgit v1.2.3 From d86d8dbdebcc8c2ef763af79b5bc1db4846216ad Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:39:49 +0530 Subject: rtlwifi: rtl8192de: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 2833 945 12 3790 ece realtek/rtlwifi/rtl8192de/sw.o File size After adding 'const': text data bss dec hex filename 2929 849 12 3790 ece realtek/rtlwifi/rtl8192de/sw.o Signed-off-by: Arvind Yadav Acked-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c index e38d6f7370aa..a6549f5f6c59 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c @@ -349,7 +349,7 @@ static const struct rtl_hal_cfg rtl92de_hal_cfg = { .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; -static struct pci_device_id rtl92de_pci_ids[] = { +static const struct pci_device_id rtl92de_pci_ids[] = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8193, rtl92de_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x002B, rtl92de_hal_cfg)}, {}, -- cgit v1.2.3 From 67f512e6707f41911c788c6a36990fce1a7ae1c0 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:39:50 +0530 Subject: rtlwifi: rtl8192se: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 2817 1040 0 3857 f11 realtek/rtlwifi/rtl8192se/sw.o File size After adding 'const': text data bss dec hex filename 3009 848 0 3857 f11 realtek/rtlwifi/rtl8192se/sw.o Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c index 1ec20efb9ce1..d7945b9db493 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c @@ -398,7 +398,7 @@ static const struct rtl_hal_cfg rtl92se_hal_cfg = { .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; -static struct pci_device_id rtl92se_pci_ids[] = { +static const struct pci_device_id rtl92se_pci_ids[] = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8192, rtl92se_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8171, rtl92se_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8172, rtl92se_hal_cfg)}, -- cgit v1.2.3 From 468952f147147e306e376f24d2af93dac63bef58 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:39:51 +0530 Subject: rtlwifi: rtl8821ae: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 2491 960 0 3451 d7b realtek/rtlwifi/rtl8821ae/sw.o File size After adding 'const': text data bss dec hex filename 2587 864 0 3451 d7b realtek/rtlwifi/rtl8821ae/sw.o Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c index 5925edf7877f..0894ef48ab87 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c @@ -423,7 +423,7 @@ static const struct rtl_hal_cfg rtl8821ae_hal_cfg = { .maps[RTL_RC_VHT_RATE_2SS_MCS9] = DESC_RATEVHT2SS_MCS9, }; -static struct pci_device_id rtl8821ae_pci_ids[] = { +static const struct pci_device_id rtl8821ae_pci_ids[] = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8812, rtl8821ae_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8821, rtl8821ae_hal_cfg)}, {}, -- cgit v1.2.3 From e9a214def6cec7b99d12de92ced4f3441f9b8536 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:39:52 +0530 Subject: rtlwifi: rtl8723ae: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 2775 912 0 3687 e67 realtek/rtlwifi/rtl8723ae/sw.o File size After adding 'const': text data bss dec hex filename 2839 848 0 3687 e67 realtek/rtlwifi/rtl8723ae/sw.o Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c index aab86667a7f3..97b8bd294aa8 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c @@ -369,7 +369,7 @@ static const struct rtl_hal_cfg rtl8723e_hal_cfg = { .maps[RTL_RC_HT_RATEMCS15] = DESC92C_RATEMCS15, }; -static struct pci_device_id rtl8723e_pci_ids[] = { +static const struct pci_device_id rtl8723e_pci_ids[] = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8723, rtl8723e_hal_cfg)}, {}, }; -- cgit v1.2.3 From 8c6f0f5bf1e15019cfa531e940b8ba1bdd9f6e08 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:39:53 +0530 Subject: rtlwifi: rtl8723be: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 3032 912 0 3944 f68 realtek/rtlwifi/rtl8723be/sw.o File size After adding 'const': text data bss dec hex filename 3096 848 0 3944 f68 realtek/rtlwifi/rtl8723be/sw.o Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c index f47d839f388d..2b16a1467e78 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c @@ -375,7 +375,7 @@ static const struct rtl_hal_cfg rtl8723be_hal_cfg = { .maps[RTL_RC_HT_RATEMCS15] = DESC92C_RATEMCS15, }; -static struct pci_device_id rtl8723be_pci_ids[] = { +static const struct pci_device_id rtl8723be_pci_ids[] = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xB723, rtl8723be_hal_cfg)}, {}, }; -- cgit v1.2.3 From 4dc2efc132a761a76a0cb684adf293692a7a9842 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:39:54 +0530 Subject: rtlwifi: rtl8188ee: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 3090 912 0 4002 fa2 realtek/rtlwifi/rtl8188ee/sw.o File size After adding 'const': text data bss dec hex filename 3154 848 0 4002 fa2 realtek/rtlwifi/rtl8188ee/sw.o Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c index bddd5a5ebe52..57e5d5c1d24b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c @@ -378,7 +378,7 @@ static const struct rtl_hal_cfg rtl88ee_hal_cfg = { .maps[RTL_RC_HT_RATEMCS15] = DESC92C_RATEMCS15, }; -static struct pci_device_id rtl88ee_pci_ids[] = { +static const struct pci_device_id rtl88ee_pci_ids[] = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8179, rtl88ee_hal_cfg)}, {}, }; -- cgit v1.2.3 From d20d893d7a749662f21ff97894c3a496f9a5c991 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:39:55 +0530 Subject: rtlwifi: rtl8192ee: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 1899 928 0 2827 b0b realtek/rtlwifi/rtl8192ee/sw.o File size After adding 'const': text data bss dec hex filename 1963 864 0 2827 b0b realtek/rtlwifi/rtl8192ee/sw.o Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c index 745e9c32655c..a3490080d066 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c @@ -356,7 +356,7 @@ static const struct rtl_hal_cfg rtl92ee_hal_cfg = { .maps[RTL_RC_HT_RATEMCS15] = DESC92C_RATEMCS15, }; -static struct pci_device_id rtl92ee_pci_ids[] = { +static const struct pci_device_id rtl92ee_pci_ids[] = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x818B, rtl92ee_hal_cfg)}, {}, }; -- cgit v1.2.3 From 9ff067ff4c4a08d412e51307f11f7de3fb3045e7 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Thu, 27 Jul 2017 13:20:15 +0200 Subject: hostap: Fix outdated comment about dev->destructor After commit cf124db566e6 ("net: Fix inconsistent teardown and release of private netdev state."), setting 'dev->needs_free_netdev' ensures device data is released, and 'dev->destructor' is not used anymore. Fixes: cf124db566e6 ("net: Fix inconsistent teardown and release of private netdev state.") Signed-off-by: Stefano Brivio Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/hostap/hostap_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intersil/hostap/hostap_main.c b/drivers/net/wireless/intersil/hostap/hostap_main.c index a3c066f90afc..012930d35434 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_main.c +++ b/drivers/net/wireless/intersil/hostap/hostap_main.c @@ -125,8 +125,8 @@ void hostap_remove_interface(struct net_device *dev, int rtnl_locked, else unregister_netdev(dev); - /* dev->destructor = free_netdev() will free the device data, including - * private data, when removing the device */ + /* 'dev->needs_free_netdev = true' implies device data, including + * private data, will be freed when the device is removed */ } -- cgit v1.2.3 From cc5becd38e87518671f68f17670f13c98bd62be0 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 23:44:23 +0530 Subject: net: qtnfmac: constify pci_device_id. pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Reviewed-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index 2c065ffda070..ae8acc1bf291 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -1310,7 +1310,7 @@ static SIMPLE_DEV_PM_OPS(qtnf_pcie_pm_ops, qtnf_pcie_suspend, qtnf_pcie_resume); #endif -static struct pci_device_id qtnf_pcie_devid_table[] = { +static const struct pci_device_id qtnf_pcie_devid_table[] = { { PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QTN_PEARL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, -- cgit v1.2.3 From e37f6483dc282267ebf12cb94c40e8a0c8823722 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Tue, 1 Aug 2017 01:38:23 +0000 Subject: mwifiex: make addba request command clean uninitilized variable, such as .add_req_result might be magic stack value. Initialize the structure to make it clean. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/11n.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c index 16c77c27f1b6..725206914911 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.c +++ b/drivers/net/wireless/marvell/mwifiex/11n.c @@ -572,6 +572,8 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) mwifiex_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid); + memset(&add_ba_req, 0, sizeof(add_ba_req)); + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && priv->adapter->is_hw_11ac_capable && -- cgit v1.2.3 From e7ece050113dcfab7ea844a643fa1f3c3ac75835 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Tue, 1 Aug 2017 01:38:24 +0000 Subject: mwifiex: pcie: compatible with wifi-only image while extract wifi-part fw Sometimes, we might using wifi-only firmware with a combo firmware name, in this case, do not need to filter bluetooth part from header. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Reviewed-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 3da1eeb730eb..cd314946452c 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -1985,7 +1985,8 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, * (3) wifi image. * * This function bypass the header and bluetooth part, return - * the offset of tail wifi-only part. + * the offset of tail wifi-only part. If the image is already wifi-only, + * that is start with CMD1, return 0. */ static int mwifiex_extract_wifi_fw(struct mwifiex_adapter *adapter, @@ -1993,7 +1994,7 @@ static int mwifiex_extract_wifi_fw(struct mwifiex_adapter *adapter, const struct mwifiex_fw_data *fwdata; u32 offset = 0, data_len, dnld_cmd; int ret = 0; - bool cmd7_before = false; + bool cmd7_before = false, first_cmd = false; while (1) { /* Check for integer and buffer overflow */ @@ -2014,20 +2015,29 @@ static int mwifiex_extract_wifi_fw(struct mwifiex_adapter *adapter, switch (dnld_cmd) { case MWIFIEX_FW_DNLD_CMD_1: - if (!cmd7_before) { - mwifiex_dbg(adapter, ERROR, - "no cmd7 before cmd1!\n"); + if (offset + data_len < data_len) { + mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); ret = -1; goto done; } - if (offset + data_len < data_len) { - mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); + + /* Image start with cmd1, already wifi-only firmware */ + if (!first_cmd) { + mwifiex_dbg(adapter, MSG, + "input wifi-only firmware\n"); + return 0; + } + + if (!cmd7_before) { + mwifiex_dbg(adapter, ERROR, + "no cmd7 before cmd1!\n"); ret = -1; goto done; } offset += data_len; break; case MWIFIEX_FW_DNLD_CMD_5: + first_cmd = true; /* Check for integer overflow */ if (offset + data_len < data_len) { mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); @@ -2037,6 +2047,7 @@ static int mwifiex_extract_wifi_fw(struct mwifiex_adapter *adapter, offset += data_len; break; case MWIFIEX_FW_DNLD_CMD_6: + first_cmd = true; /* Check for integer overflow */ if (offset + data_len < data_len) { mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); @@ -2053,6 +2064,7 @@ static int mwifiex_extract_wifi_fw(struct mwifiex_adapter *adapter, } goto done; case MWIFIEX_FW_DNLD_CMD_7: + first_cmd = true; cmd7_before = true; break; default: -- cgit v1.2.3 From 9d546198705a79630cb29b1cc47a43e75b8afb89 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Wed, 2 Aug 2017 17:59:15 +0530 Subject: rtlwifi: Replace hardcode value with macro In _rtl_init_mac80211(), hardcoded value for hw->max_listen_interval and hw->max_rate_tries are replaced by macro and removed the comment. Signed-off-by: Souptick Joarder Acked-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/base.c | 5 ++--- drivers/net/wireless/realtek/rtlwifi/base.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index 8b833e21b5cb..ea18aa7afecb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -426,9 +426,8 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw) hw->extra_tx_headroom = RTL_TX_HEADER_SIZE; /* TODO: Correct this value for our hw */ - /* TODO: define these hard code value */ - hw->max_listen_interval = 10; - hw->max_rate_tries = 4; + hw->max_listen_interval = MAX_LISTEN_INTERVAL; + hw->max_rate_tries = MAX_RATE_TRIES; /* hw->max_rates = 1; */ hw->sta_data_size = sizeof(struct rtl_sta_info); diff --git a/drivers/net/wireless/realtek/rtlwifi/base.h b/drivers/net/wireless/realtek/rtlwifi/base.h index ab7d81904d25..b56d1b7f5567 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.h +++ b/drivers/net/wireless/realtek/rtlwifi/base.h @@ -65,6 +65,8 @@ enum ap_peer { #define FRAME_OFFSET_ADDRESS3 16 #define FRAME_OFFSET_SEQUENCE 22 #define FRAME_OFFSET_ADDRESS4 24 +#define MAX_LISTEN_INTERVAL 10 +#define MAX_RATE_TRIES 4 #define SET_80211_HDR_FRAME_CONTROL(_hdr, _val) \ WRITEEF2BYTE(_hdr, _val) -- cgit v1.2.3 From 47cc0ca91c9e4dde68d69f35e139360ef356267f Mon Sep 17 00:00:00 2001 From: Matthias Frei Date: Fri, 28 Jul 2017 15:15:36 +0300 Subject: ath10k: set a-mpdu receiver reference number Set the a-mpdu reference number in ath10k to make it accessible in the receivers radiotap header. Implemented as in ath9k. The reference number is needed for troubleshooting and research at the receivers site (e.g. to identify mpdu's that were aggregated in an a-mpdu) Signed-off-by: Matthias Frei [kvalo@qca.qualcomm.com: fix checkpatch warning, commit log cleanup] Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 2 ++ drivers/net/wireless/ath/ath10k/htt_rx.c | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 2b499af722ad..d74e8980b96e 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -993,6 +993,8 @@ struct ath10k { u32 reg_ack_cts_timeout_orig; } fw_coverage; + u32 ampdu_reference; + void *ce_priv; /* must be last */ diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 398dda978d6e..799fb7501eb5 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -890,16 +890,26 @@ static void ath10k_htt_rx_h_ppdu(struct ath10k *ar, status->nss = 0; status->encoding = RX_ENC_LEGACY; status->bw = RATE_INFO_BW_20; + status->flag &= ~RX_FLAG_MACTIME_END; status->flag |= RX_FLAG_NO_SIGNAL_VAL; + status->flag &= ~(RX_FLAG_AMPDU_IS_LAST); + status->flag |= RX_FLAG_AMPDU_DETAILS | RX_FLAG_AMPDU_LAST_KNOWN; + status->ampdu_reference = ar->ampdu_reference; + ath10k_htt_rx_h_signal(ar, status, rxd); ath10k_htt_rx_h_channel(ar, status, rxd, vdev_id); ath10k_htt_rx_h_rates(ar, status, rxd); } - if (is_last_ppdu) + if (is_last_ppdu) { ath10k_htt_rx_h_mactime(ar, status, rxd); + + /* set ampdu last segment flag */ + status->flag |= RX_FLAG_AMPDU_IS_LAST; + ar->ampdu_reference++; + } } static const char * const tid_to_ac[] = { -- cgit v1.2.3 From b00435e6cda6863c2c7f40c09c6fab21c205f455 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Fri, 28 Jul 2017 15:15:37 +0300 Subject: ath10k: various usb related definitions Definitions for USB based chipsets Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 1 + drivers/net/wireless/ath/ath10k/core.h | 3 +++ drivers/net/wireless/ath/ath10k/debug.h | 2 ++ 3 files changed, 6 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 8ff47458207c..2aa320a223af 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1454,6 +1454,7 @@ static void ath10k_core_get_fw_name(struct ath10k *ar, char *fw_name, { switch (ar->hif.bus) { case ATH10K_BUS_SDIO: + case ATH10K_BUS_USB: scnprintf(fw_name, fw_name_len, "%s-%s-%d.bin", ATH10K_FW_FILE_BASE, ath10k_bus_str(ar->hif.bus), fw_api); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index d74e8980b96e..34b713c5e022 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -92,6 +92,7 @@ enum ath10k_bus { ATH10K_BUS_PCI, ATH10K_BUS_AHB, ATH10K_BUS_SDIO, + ATH10K_BUS_USB, }; static inline const char *ath10k_bus_str(enum ath10k_bus bus) @@ -103,6 +104,8 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus) return "ahb"; case ATH10K_BUS_SDIO: return "sdio"; + case ATH10K_BUS_USB: + return "usb"; } return "unknown"; diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 257d10985c6e..548ad5483a4a 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -40,6 +40,8 @@ enum ath10k_debug_mask { ATH10K_DBG_AHB = 0x00008000, ATH10K_DBG_SDIO = 0x00010000, ATH10K_DBG_SDIO_DUMP = 0x00020000, + ATH10K_DBG_USB = 0x00040000, + ATH10K_DBG_USB_BULK = 0x00080000, ATH10K_DBG_ANY = 0xffffffff, }; -- cgit v1.2.3 From 4db66499df91b9398435e2dbee0e42cd6df0bc27 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Fri, 28 Jul 2017 15:15:39 +0300 Subject: ath10k: add initial USB support Chipsets like QCA9377 have support for USB so add initial USB bus support to ath10k. With this patch we have the low level HIF and HTC protocol working and it's possible to boot the firmware, but it's still not possible to connect or anything like. More changes are needed for full functionality. For that reason we print during initialisation: WARNING: ath10k USB support is incomplete, don't expect anything to work! Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/Kconfig | 7 + drivers/net/wireless/ath/ath10k/Makefile | 3 + drivers/net/wireless/ath/ath10k/usb.c | 1106 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/usb.h | 128 ++++ 4 files changed, 1244 insertions(+) create mode 100644 drivers/net/wireless/ath/ath10k/usb.c create mode 100644 drivers/net/wireless/ath/ath10k/usb.h (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 412eb1380dcc..87f56d0e17a6 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -29,6 +29,13 @@ config ATH10K_SDIO This module adds experimental support for SDIO/MMC bus. Currently work in progress and will not fully work. +config ATH10K_USB + tristate "Atheros ath10k USB support (EXPERIMENTAL)" + depends on ATH10K && USB + ---help--- + This module adds experimental support for USB bus. Currently + work in progress and will not fully work. + config ATH10K_DEBUG bool "Atheros ath10k debugging" depends on ATH10K diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index b0b19a7eb98b..899b9b79f4ce 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -30,5 +30,8 @@ ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o obj-$(CONFIG_ATH10K_SDIO) += ath10k_sdio.o ath10k_sdio-y += sdio.o +obj-$(CONFIG_ATH10K_USB) += ath10k_usb.o +ath10k_usb-y += usb.o + # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c new file mode 100644 index 000000000000..d4803ff5a78a --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/usb.c @@ -0,0 +1,1106 @@ +/* + * Copyright (c) 2007-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc. + * Copyright (c) 2016-2017 Erik Stromdahl + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "debug.h" +#include "core.h" +#include "bmi.h" +#include "hif.h" +#include "htc.h" +#include "usb.h" + +static void ath10k_usb_post_recv_transfers(struct ath10k *ar, + struct ath10k_usb_pipe *recv_pipe); + +/* inlined helper functions */ + +static inline enum ath10k_htc_ep_id +eid_from_htc_hdr(struct ath10k_htc_hdr *htc_hdr) +{ + return (enum ath10k_htc_ep_id)htc_hdr->eid; +} + +static inline bool is_trailer_only_msg(struct ath10k_htc_hdr *htc_hdr) +{ + return __le16_to_cpu(htc_hdr->len) == htc_hdr->trailer_len; +} + +/* pipe/urb operations */ +static struct ath10k_urb_context * +ath10k_usb_alloc_urb_from_pipe(struct ath10k_usb_pipe *pipe) +{ + struct ath10k_urb_context *urb_context = NULL; + unsigned long flags; + + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); + if (!list_empty(&pipe->urb_list_head)) { + urb_context = list_first_entry(&pipe->urb_list_head, + struct ath10k_urb_context, link); + list_del(&urb_context->link); + pipe->urb_cnt--; + } + spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); + + return urb_context; +} + +static void ath10k_usb_free_urb_to_pipe(struct ath10k_usb_pipe *pipe, + struct ath10k_urb_context *urb_context) +{ + unsigned long flags; + + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); + + pipe->urb_cnt++; + list_add(&urb_context->link, &pipe->urb_list_head); + + spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); +} + +static void ath10k_usb_cleanup_recv_urb(struct ath10k_urb_context *urb_context) +{ + dev_kfree_skb(urb_context->skb); + urb_context->skb = NULL; + + ath10k_usb_free_urb_to_pipe(urb_context->pipe, urb_context); +} + +static void ath10k_usb_free_pipe_resources(struct ath10k *ar, + struct ath10k_usb_pipe *pipe) +{ + struct ath10k_urb_context *urb_context; + + if (!pipe->ar_usb) { + /* nothing allocated for this pipe */ + return; + } + + ath10k_dbg(ar, ATH10K_DBG_USB, + "usb free resources lpipe %d hpipe 0x%x urbs %d avail %d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc, pipe->urb_cnt); + + if (pipe->urb_alloc != pipe->urb_cnt) { + ath10k_dbg(ar, ATH10K_DBG_USB, + "usb urb leak lpipe %d hpipe 0x%x urbs %d avail %d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc, pipe->urb_cnt); + } + + for (;;) { + urb_context = ath10k_usb_alloc_urb_from_pipe(pipe); + + if (!urb_context) + break; + + kfree(urb_context); + } +} + +static void ath10k_usb_cleanup_pipe_resources(struct ath10k *ar) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + int i; + + for (i = 0; i < ATH10K_USB_PIPE_MAX; i++) + ath10k_usb_free_pipe_resources(ar, &ar_usb->pipes[i]); +} + +/* hif usb rx/tx completion functions */ + +static void ath10k_usb_recv_complete(struct urb *urb) +{ + struct ath10k_urb_context *urb_context = urb->context; + struct ath10k_usb_pipe *pipe = urb_context->pipe; + struct ath10k *ar = pipe->ar_usb->ar; + struct sk_buff *skb; + int status = 0; + + ath10k_dbg(ar, ATH10K_DBG_USB_BULK, + "usb recv pipe %d stat %d len %d urb 0x%pK\n", + pipe->logical_pipe_num, urb->status, urb->actual_length, + urb); + + if (urb->status != 0) { + status = -EIO; + switch (urb->status) { + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* no need to spew these errors when device + * removed or urb killed due to driver shutdown + */ + status = -ECANCELED; + break; + default: + ath10k_dbg(ar, ATH10K_DBG_USB_BULK, + "usb recv pipe %d ep 0x%2.2x failed: %d\n", + pipe->logical_pipe_num, + pipe->ep_address, urb->status); + break; + } + goto cleanup_recv_urb; + } + + if (urb->actual_length == 0) + goto cleanup_recv_urb; + + skb = urb_context->skb; + + /* we are going to pass it up */ + urb_context->skb = NULL; + skb_put(skb, urb->actual_length); + + /* note: queue implements a lock */ + skb_queue_tail(&pipe->io_comp_queue, skb); + schedule_work(&pipe->io_complete_work); + +cleanup_recv_urb: + ath10k_usb_cleanup_recv_urb(urb_context); + + if (status == 0 && + pipe->urb_cnt >= pipe->urb_cnt_thresh) { + /* our free urbs are piling up, post more transfers */ + ath10k_usb_post_recv_transfers(ar, pipe); + } +} + +static void ath10k_usb_transmit_complete(struct urb *urb) +{ + struct ath10k_urb_context *urb_context = urb->context; + struct ath10k_usb_pipe *pipe = urb_context->pipe; + struct ath10k *ar = pipe->ar_usb->ar; + struct sk_buff *skb; + + if (urb->status != 0) { + ath10k_dbg(ar, ATH10K_DBG_USB_BULK, + "pipe: %d, failed:%d\n", + pipe->logical_pipe_num, urb->status); + } + + skb = urb_context->skb; + urb_context->skb = NULL; + ath10k_usb_free_urb_to_pipe(urb_context->pipe, urb_context); + + /* note: queue implements a lock */ + skb_queue_tail(&pipe->io_comp_queue, skb); + schedule_work(&pipe->io_complete_work); +} + +/* pipe operations */ +static void ath10k_usb_post_recv_transfers(struct ath10k *ar, + struct ath10k_usb_pipe *recv_pipe) +{ + struct ath10k_urb_context *urb_context; + struct urb *urb; + int usb_status; + + for (;;) { + urb_context = ath10k_usb_alloc_urb_from_pipe(recv_pipe); + if (!urb_context) + break; + + urb_context->skb = dev_alloc_skb(ATH10K_USB_RX_BUFFER_SIZE); + if (!urb_context->skb) + goto err; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + goto err; + + usb_fill_bulk_urb(urb, + recv_pipe->ar_usb->udev, + recv_pipe->usb_pipe_handle, + urb_context->skb->data, + ATH10K_USB_RX_BUFFER_SIZE, + ath10k_usb_recv_complete, urb_context); + + ath10k_dbg(ar, ATH10K_DBG_USB_BULK, + "usb bulk recv submit %d 0x%x ep 0x%2.2x len %d buf 0x%pK\n", + recv_pipe->logical_pipe_num, + recv_pipe->usb_pipe_handle, recv_pipe->ep_address, + ATH10K_USB_RX_BUFFER_SIZE, urb_context->skb); + + usb_anchor_urb(urb, &recv_pipe->urb_submitted); + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + + if (usb_status) { + ath10k_dbg(ar, ATH10K_DBG_USB_BULK, + "usb bulk recv failed: %d\n", + usb_status); + usb_unanchor_urb(urb); + usb_free_urb(urb); + goto err; + } + usb_free_urb(urb); + } + + return; + +err: + ath10k_usb_cleanup_recv_urb(urb_context); +} + +static void ath10k_usb_flush_all(struct ath10k *ar) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + int i; + + for (i = 0; i < ATH10K_USB_PIPE_MAX; i++) { + if (ar_usb->pipes[i].ar_usb) { + usb_kill_anchored_urbs(&ar_usb->pipes[i].urb_submitted); + cancel_work_sync(&ar_usb->pipes[i].io_complete_work); + } + } +} + +static void ath10k_usb_start_recv_pipes(struct ath10k *ar) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + + ar_usb->pipes[ATH10K_USB_PIPE_RX_DATA].urb_cnt_thresh = 1; + + ath10k_usb_post_recv_transfers(ar, + &ar_usb->pipes[ATH10K_USB_PIPE_RX_DATA]); +} + +static void ath10k_usb_tx_complete(struct ath10k *ar, struct sk_buff *skb) +{ + struct ath10k_htc_hdr *htc_hdr; + struct ath10k_htc_ep *ep; + + htc_hdr = (struct ath10k_htc_hdr *)skb->data; + ep = &ar->htc.endpoint[htc_hdr->eid]; + ath10k_htc_notify_tx_completion(ep, skb); + /* The TX complete handler now owns the skb... */ +} + +static void ath10k_usb_rx_complete(struct ath10k *ar, struct sk_buff *skb) +{ + struct ath10k_htc *htc = &ar->htc; + struct ath10k_htc_hdr *htc_hdr; + enum ath10k_htc_ep_id eid; + struct ath10k_htc_ep *ep; + u16 payload_len; + u8 *trailer; + int ret; + + htc_hdr = (struct ath10k_htc_hdr *)skb->data; + eid = eid_from_htc_hdr(htc_hdr); + ep = &ar->htc.endpoint[eid]; + + if (ep->service_id == 0) { + ath10k_warn(ar, "ep %d is not connected\n", eid); + goto out_free_skb; + } + + payload_len = le16_to_cpu(htc_hdr->len); + if (!payload_len) { + ath10k_warn(ar, "zero length frame received, firmware crashed?\n"); + goto out_free_skb; + } + + if (payload_len < htc_hdr->trailer_len) { + ath10k_warn(ar, "malformed frame received, firmware crashed?\n"); + goto out_free_skb; + } + + if (htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT) { + trailer = skb->data + sizeof(*htc_hdr) + payload_len - + htc_hdr->trailer_len; + + ret = ath10k_htc_process_trailer(htc, + trailer, + htc_hdr->trailer_len, + eid, + NULL, + NULL); + if (ret) + goto out_free_skb; + + if (is_trailer_only_msg(htc_hdr)) + goto out_free_skb; + + /* strip off the trailer from the skb since it should not + * be passed on to upper layers + */ + skb_trim(skb, skb->len - htc_hdr->trailer_len); + } + + skb_pull(skb, sizeof(*htc_hdr)); + ep->ep_ops.ep_rx_complete(ar, skb); + /* The RX complete handler now owns the skb... */ + + return; + +out_free_skb: + dev_kfree_skb(skb); +} + +static void ath10k_usb_io_comp_work(struct work_struct *work) +{ + struct ath10k_usb_pipe *pipe = container_of(work, + struct ath10k_usb_pipe, + io_complete_work); + struct ath10k *ar = pipe->ar_usb->ar; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&pipe->io_comp_queue))) { + if (pipe->flags & ATH10K_USB_PIPE_FLAG_TX) + ath10k_usb_tx_complete(ar, skb); + else + ath10k_usb_rx_complete(ar, skb); + } +} + +#define ATH10K_USB_MAX_DIAG_CMD (sizeof(struct ath10k_usb_ctrl_diag_cmd_write)) +#define ATH10K_USB_MAX_DIAG_RESP (sizeof(struct ath10k_usb_ctrl_diag_resp_read)) + +static void ath10k_usb_destroy(struct ath10k *ar) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + + ath10k_usb_flush_all(ar); + ath10k_usb_cleanup_pipe_resources(ar); + usb_set_intfdata(ar_usb->interface, NULL); + + kfree(ar_usb->diag_cmd_buffer); + kfree(ar_usb->diag_resp_buffer); +} + +static int ath10k_usb_hif_start(struct ath10k *ar) +{ + int i; + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + + ath10k_usb_start_recv_pipes(ar); + + /* set the TX resource avail threshold for each TX pipe */ + for (i = ATH10K_USB_PIPE_TX_CTRL; + i <= ATH10K_USB_PIPE_TX_DATA_HP; i++) { + ar_usb->pipes[i].urb_cnt_thresh = + ar_usb->pipes[i].urb_alloc / 2; + } + + return 0; +} + +static int ath10k_usb_hif_tx_sg(struct ath10k *ar, u8 pipe_id, + struct ath10k_hif_sg_item *items, int n_items) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + struct ath10k_usb_pipe *pipe = &ar_usb->pipes[pipe_id]; + struct ath10k_urb_context *urb_context; + struct sk_buff *skb; + struct urb *urb; + int ret, i; + + for (i = 0; i < n_items; i++) { + urb_context = ath10k_usb_alloc_urb_from_pipe(pipe); + if (!urb_context) { + ret = -ENOMEM; + goto err; + } + + skb = items[i].transfer_context; + urb_context->skb = skb; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + ret = -ENOMEM; + goto err_free_urb_to_pipe; + } + + usb_fill_bulk_urb(urb, + ar_usb->udev, + pipe->usb_pipe_handle, + skb->data, + skb->len, + ath10k_usb_transmit_complete, urb_context); + + if (!(skb->len % pipe->max_packet_size)) { + /* hit a max packet boundary on this pipe */ + urb->transfer_flags |= URB_ZERO_PACKET; + } + + usb_anchor_urb(urb, &pipe->urb_submitted); + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + ath10k_dbg(ar, ATH10K_DBG_USB_BULK, + "usb bulk transmit failed: %d\n", ret); + usb_unanchor_urb(urb); + ret = -EINVAL; + goto err_free_urb_to_pipe; + } + + usb_free_urb(urb); + } + + return 0; + +err_free_urb_to_pipe: + ath10k_usb_free_urb_to_pipe(urb_context->pipe, urb_context); +err: + return ret; +} + +static void ath10k_usb_hif_stop(struct ath10k *ar) +{ + ath10k_usb_flush_all(ar); +} + +static u16 ath10k_usb_hif_get_free_queue_number(struct ath10k *ar, u8 pipe_id) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + + return ar_usb->pipes[pipe_id].urb_cnt; +} + +static int ath10k_usb_submit_ctrl_out(struct ath10k *ar, + u8 req, u16 value, u16 index, void *data, + u32 size) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + u8 *buf = NULL; + int ret; + + if (size > 0) { + buf = kmemdup(data, size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + /* note: if successful returns number of bytes transferred */ + ret = usb_control_msg(ar_usb->udev, + usb_sndctrlpipe(ar_usb->udev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, buf, + size, 1000); + + if (ret < 0) { + ath10k_warn(ar, "Failed to submit usb control message: %d\n", + ret); + kfree(buf); + return ret; + } + + kfree(buf); + + return 0; +} + +static int ath10k_usb_submit_ctrl_in(struct ath10k *ar, + u8 req, u16 value, u16 index, void *data, + u32 size) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + u8 *buf = NULL; + int ret; + + if (size > 0) { + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + /* note: if successful returns number of bytes transferred */ + ret = usb_control_msg(ar_usb->udev, + usb_rcvctrlpipe(ar_usb->udev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, buf, + size, 2 * HZ); + + if (ret < 0) { + ath10k_warn(ar, "Failed to read usb control message: %d\n", + ret); + kfree(buf); + return ret; + } + + memcpy((u8 *)data, buf, size); + + kfree(buf); + + return 0; +} + +static int ath10k_usb_ctrl_msg_exchange(struct ath10k *ar, + u8 req_val, u8 *req_buf, u32 req_len, + u8 resp_val, u8 *resp_buf, + u32 *resp_len) +{ + int ret; + + /* send command */ + ret = ath10k_usb_submit_ctrl_out(ar, req_val, 0, 0, + req_buf, req_len); + if (ret) + goto err; + + /* get response */ + if (resp_buf) { + ret = ath10k_usb_submit_ctrl_in(ar, resp_val, 0, 0, + resp_buf, *resp_len); + if (ret) + goto err; + } + + return 0; +err: + return ret; +} + +static int ath10k_usb_hif_diag_read(struct ath10k *ar, u32 address, void *buf, + size_t buf_len) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + struct ath10k_usb_ctrl_diag_cmd_read *cmd; + u32 resp_len; + int ret; + + if (buf_len < sizeof(struct ath10k_usb_ctrl_diag_resp_read)) + return -EINVAL; + + cmd = (struct ath10k_usb_ctrl_diag_cmd_read *)ar_usb->diag_cmd_buffer; + memset(cmd, 0, sizeof(*cmd)); + cmd->cmd = ATH10K_USB_CTRL_DIAG_CC_READ; + cmd->address = cpu_to_le32(address); + resp_len = sizeof(struct ath10k_usb_ctrl_diag_resp_read); + + ret = ath10k_usb_ctrl_msg_exchange(ar, + ATH10K_USB_CONTROL_REQ_DIAG_CMD, + (u8 *)cmd, + sizeof(*cmd), + ATH10K_USB_CONTROL_REQ_DIAG_RESP, + ar_usb->diag_resp_buffer, &resp_len); + if (ret) + return ret; + + if (resp_len != sizeof(struct ath10k_usb_ctrl_diag_resp_read)) + return -EMSGSIZE; + + memcpy(buf, ar_usb->diag_resp_buffer, + sizeof(struct ath10k_usb_ctrl_diag_resp_read)); + + return 0; +} + +static int ath10k_usb_hif_diag_write(struct ath10k *ar, u32 address, + const void *data, int nbytes) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + struct ath10k_usb_ctrl_diag_cmd_write *cmd; + int ret; + + if (nbytes != sizeof(cmd->value)) + return -EINVAL; + + cmd = (struct ath10k_usb_ctrl_diag_cmd_write *)ar_usb->diag_cmd_buffer; + memset(cmd, 0, sizeof(*cmd)); + cmd->cmd = cpu_to_le32(ATH10K_USB_CTRL_DIAG_CC_WRITE); + cmd->address = cpu_to_le32(address); + memcpy(&cmd->value, data, nbytes); + + ret = ath10k_usb_ctrl_msg_exchange(ar, + ATH10K_USB_CONTROL_REQ_DIAG_CMD, + (u8 *)cmd, + sizeof(*cmd), + 0, NULL, NULL); + if (ret) + return ret; + + return 0; +} + +static int ath10k_usb_bmi_exchange_msg(struct ath10k *ar, + void *req, u32 req_len, + void *resp, u32 *resp_len) +{ + int ret; + + if (req) { + ret = ath10k_usb_submit_ctrl_out(ar, + ATH10K_USB_CONTROL_REQ_SEND_BMI_CMD, + 0, 0, req, req_len); + if (ret) { + ath10k_warn(ar, + "unable to send the bmi data to the device: %d\n", + ret); + return ret; + } + } + + if (resp) { + ret = ath10k_usb_submit_ctrl_in(ar, + ATH10K_USB_CONTROL_REQ_RECV_BMI_RESP, + 0, 0, resp, *resp_len); + if (ret) { + ath10k_warn(ar, + "Unable to read the bmi data from the device: %d\n", + ret); + return ret; + } + } + + return 0; +} + +static void ath10k_usb_hif_get_default_pipe(struct ath10k *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + *ul_pipe = ATH10K_USB_PIPE_TX_CTRL; + *dl_pipe = ATH10K_USB_PIPE_RX_CTRL; +} + +static int ath10k_usb_hif_map_service_to_pipe(struct ath10k *ar, u16 svc_id, + u8 *ul_pipe, u8 *dl_pipe) +{ + switch (svc_id) { + case ATH10K_HTC_SVC_ID_RSVD_CTRL: + case ATH10K_HTC_SVC_ID_WMI_CONTROL: + *ul_pipe = ATH10K_USB_PIPE_TX_CTRL; + /* due to large control packets, shift to data pipe */ + *dl_pipe = ATH10K_USB_PIPE_RX_DATA; + break; + case ATH10K_HTC_SVC_ID_HTT_DATA_MSG: + *ul_pipe = ATH10K_USB_PIPE_TX_DATA_LP; + /* Disable rxdata2 directly, it will be enabled + * if FW enable rxdata2 + */ + *dl_pipe = ATH10K_USB_PIPE_RX_DATA; + break; + default: + return -EPERM; + } + + return 0; +} + +/* This op is currently only used by htc_wait_target if the HTC ready + * message times out. It is not applicable for USB since there is nothing + * we can do if the HTC ready message does not arrive in time. + * TODO: Make this op non mandatory by introducing a NULL check in the + * hif op wrapper. + */ +static void ath10k_usb_hif_send_complete_check(struct ath10k *ar, + u8 pipe, int force) +{ +} + +static int ath10k_usb_hif_power_up(struct ath10k *ar) +{ + return 0; +} + +static void ath10k_usb_hif_power_down(struct ath10k *ar) +{ + ath10k_usb_flush_all(ar); +} + +#ifdef CONFIG_PM + +static int ath10k_usb_hif_suspend(struct ath10k *ar) +{ + return -EOPNOTSUPP; +} + +static int ath10k_usb_hif_resume(struct ath10k *ar) +{ + return -EOPNOTSUPP; +} +#endif + +static const struct ath10k_hif_ops ath10k_usb_hif_ops = { + .tx_sg = ath10k_usb_hif_tx_sg, + .diag_read = ath10k_usb_hif_diag_read, + .diag_write = ath10k_usb_hif_diag_write, + .exchange_bmi_msg = ath10k_usb_bmi_exchange_msg, + .start = ath10k_usb_hif_start, + .stop = ath10k_usb_hif_stop, + .map_service_to_pipe = ath10k_usb_hif_map_service_to_pipe, + .get_default_pipe = ath10k_usb_hif_get_default_pipe, + .send_complete_check = ath10k_usb_hif_send_complete_check, + .get_free_queue_number = ath10k_usb_hif_get_free_queue_number, + .power_up = ath10k_usb_hif_power_up, + .power_down = ath10k_usb_hif_power_down, +#ifdef CONFIG_PM + .suspend = ath10k_usb_hif_suspend, + .resume = ath10k_usb_hif_resume, +#endif +}; + +static u8 ath10k_usb_get_logical_pipe_num(u8 ep_address, int *urb_count) +{ + u8 pipe_num = ATH10K_USB_PIPE_INVALID; + + switch (ep_address) { + case ATH10K_USB_EP_ADDR_APP_CTRL_IN: + pipe_num = ATH10K_USB_PIPE_RX_CTRL; + *urb_count = RX_URB_COUNT; + break; + case ATH10K_USB_EP_ADDR_APP_DATA_IN: + pipe_num = ATH10K_USB_PIPE_RX_DATA; + *urb_count = RX_URB_COUNT; + break; + case ATH10K_USB_EP_ADDR_APP_INT_IN: + pipe_num = ATH10K_USB_PIPE_RX_INT; + *urb_count = RX_URB_COUNT; + break; + case ATH10K_USB_EP_ADDR_APP_DATA2_IN: + pipe_num = ATH10K_USB_PIPE_RX_DATA2; + *urb_count = RX_URB_COUNT; + break; + case ATH10K_USB_EP_ADDR_APP_CTRL_OUT: + pipe_num = ATH10K_USB_PIPE_TX_CTRL; + *urb_count = TX_URB_COUNT; + break; + case ATH10K_USB_EP_ADDR_APP_DATA_LP_OUT: + pipe_num = ATH10K_USB_PIPE_TX_DATA_LP; + *urb_count = TX_URB_COUNT; + break; + case ATH10K_USB_EP_ADDR_APP_DATA_MP_OUT: + pipe_num = ATH10K_USB_PIPE_TX_DATA_MP; + *urb_count = TX_URB_COUNT; + break; + case ATH10K_USB_EP_ADDR_APP_DATA_HP_OUT: + pipe_num = ATH10K_USB_PIPE_TX_DATA_HP; + *urb_count = TX_URB_COUNT; + break; + default: + /* note: there may be endpoints not currently used */ + break; + } + + return pipe_num; +} + +static int ath10k_usb_alloc_pipe_resources(struct ath10k *ar, + struct ath10k_usb_pipe *pipe, + int urb_cnt) +{ + struct ath10k_urb_context *urb_context; + int i; + + INIT_LIST_HEAD(&pipe->urb_list_head); + init_usb_anchor(&pipe->urb_submitted); + + for (i = 0; i < urb_cnt; i++) { + urb_context = kzalloc(sizeof(*urb_context), GFP_KERNEL); + if (!urb_context) + return -ENOMEM; + + urb_context->pipe = pipe; + + /* we are only allocate the urb contexts here, the actual URB + * is allocated from the kernel as needed to do a transaction + */ + pipe->urb_alloc++; + ath10k_usb_free_urb_to_pipe(pipe, urb_context); + } + + ath10k_dbg(ar, ATH10K_DBG_USB, + "usb alloc resources lpipe %d hpipe 0x%x urbs %d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc); + + return 0; +} + +static int ath10k_usb_setup_pipe_resources(struct ath10k *ar, + struct usb_interface *interface) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + struct usb_host_interface *iface_desc = interface->cur_altsetting; + struct usb_endpoint_descriptor *endpoint; + struct ath10k_usb_pipe *pipe; + int ret, i, urbcount; + u8 pipe_num; + + ath10k_dbg(ar, ATH10K_DBG_USB, "usb setting up pipes using interface\n"); + + /* walk decriptors and setup pipes */ + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (ATH10K_USB_IS_BULK_EP(endpoint->bmAttributes)) { + ath10k_dbg(ar, ATH10K_DBG_USB, + "usb %s bulk ep 0x%2.2x maxpktsz %d\n", + ATH10K_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "rx" : "tx", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize)); + } else if (ATH10K_USB_IS_INT_EP(endpoint->bmAttributes)) { + ath10k_dbg(ar, ATH10K_DBG_USB, + "usb %s int ep 0x%2.2x maxpktsz %d interval %d\n", + ATH10K_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "rx" : "tx", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize), + endpoint->bInterval); + } else if (ATH10K_USB_IS_ISOC_EP(endpoint->bmAttributes)) { + /* TODO for ISO */ + ath10k_dbg(ar, ATH10K_DBG_USB, + "usb %s isoc ep 0x%2.2x maxpktsz %d interval %d\n", + ATH10K_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "rx" : "tx", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize), + endpoint->bInterval); + } + urbcount = 0; + + pipe_num = + ath10k_usb_get_logical_pipe_num(endpoint->bEndpointAddress, + &urbcount); + if (pipe_num == ATH10K_USB_PIPE_INVALID) + continue; + + pipe = &ar_usb->pipes[pipe_num]; + if (pipe->ar_usb) + /* hmmm..pipe was already setup */ + continue; + + pipe->ar_usb = ar_usb; + pipe->logical_pipe_num = pipe_num; + pipe->ep_address = endpoint->bEndpointAddress; + pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize); + + if (ATH10K_USB_IS_BULK_EP(endpoint->bmAttributes)) { + if (ATH10K_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvbulkpipe(ar_usb->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndbulkpipe(ar_usb->udev, + pipe->ep_address); + } + } else if (ATH10K_USB_IS_INT_EP(endpoint->bmAttributes)) { + if (ATH10K_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvintpipe(ar_usb->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndintpipe(ar_usb->udev, + pipe->ep_address); + } + } else if (ATH10K_USB_IS_ISOC_EP(endpoint->bmAttributes)) { + /* TODO for ISO */ + if (ATH10K_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvisocpipe(ar_usb->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndisocpipe(ar_usb->udev, + pipe->ep_address); + } + } + + pipe->ep_desc = endpoint; + + if (!ATH10K_USB_IS_DIR_IN(pipe->ep_address)) + pipe->flags |= ATH10K_USB_PIPE_FLAG_TX; + + ret = ath10k_usb_alloc_pipe_resources(ar, pipe, urbcount); + if (ret) + return ret; + } + + return 0; +} + +static int ath10k_usb_create(struct ath10k *ar, + struct usb_interface *interface) +{ + struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); + struct usb_device *dev = interface_to_usbdev(interface); + struct ath10k_usb_pipe *pipe; + int ret, i; + + usb_set_intfdata(interface, ar_usb); + spin_lock_init(&ar_usb->cs_lock); + ar_usb->udev = dev; + ar_usb->interface = interface; + + for (i = 0; i < ATH10K_USB_PIPE_MAX; i++) { + pipe = &ar_usb->pipes[i]; + INIT_WORK(&pipe->io_complete_work, + ath10k_usb_io_comp_work); + skb_queue_head_init(&pipe->io_comp_queue); + } + + ar_usb->diag_cmd_buffer = kzalloc(ATH10K_USB_MAX_DIAG_CMD, GFP_KERNEL); + if (!ar_usb->diag_cmd_buffer) { + ret = -ENOMEM; + goto err; + } + + ar_usb->diag_resp_buffer = kzalloc(ATH10K_USB_MAX_DIAG_RESP, + GFP_KERNEL); + if (!ar_usb->diag_resp_buffer) { + ret = -ENOMEM; + goto err; + } + + ret = ath10k_usb_setup_pipe_resources(ar, interface); + if (ret) + goto err; + + return 0; + +err: + ath10k_usb_destroy(ar); + return ret; +} + +/* ath10k usb driver registered functions */ +static int ath10k_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct ath10k *ar; + struct ath10k_usb *ar_usb; + struct usb_device *dev = interface_to_usbdev(interface); + int ret, vendor_id, product_id; + enum ath10k_hw_rev hw_rev; + u32 chip_id; + + /* Assumption: All USB based chipsets (so far) are QCA9377 based. + * If there will be newer chipsets that does not use the hw reg + * setup as defined in qca6174_regs and qca6174_values, this + * assumption is no longer valid and hw_rev must be setup differently + * depending on chipset. + */ + hw_rev = ATH10K_HW_QCA9377; + + ar = ath10k_core_create(sizeof(*ar_usb), &dev->dev, ATH10K_BUS_USB, + hw_rev, &ath10k_usb_hif_ops); + if (!ar) { + dev_err(&dev->dev, "failed to allocate core\n"); + return -ENOMEM; + } + + usb_get_dev(dev); + vendor_id = le16_to_cpu(dev->descriptor.idVendor); + product_id = le16_to_cpu(dev->descriptor.idProduct); + + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "usb new func vendor 0x%04x product 0x%04x\n", + vendor_id, product_id); + + ar_usb = ath10k_usb_priv(ar); + ret = ath10k_usb_create(ar, interface); + ar_usb->ar = ar; + + ar->dev_id = product_id; + ar->id.vendor = vendor_id; + ar->id.device = product_id; + + /* TODO: don't know yet how to get chip_id with USB */ + chip_id = 0; + ret = ath10k_core_register(ar, chip_id); + if (ret) { + ath10k_warn(ar, "failed to register driver core: %d\n", ret); + goto err; + } + + /* TODO: remove this once USB support is fully implemented */ + ath10k_warn(ar, "WARNING: ath10k USB support is incomplete, don't expect anything to work!\n"); + + return 0; + +err: + ath10k_core_destroy(ar); + + usb_put_dev(dev); + + return ret; +} + +static void ath10k_usb_remove(struct usb_interface *interface) +{ + struct ath10k_usb *ar_usb; + + ar_usb = usb_get_intfdata(interface); + if (!ar_usb) + return; + + ath10k_core_unregister(ar_usb->ar); + ath10k_usb_destroy(ar_usb->ar); + usb_put_dev(interface_to_usbdev(interface)); + ath10k_core_destroy(ar_usb->ar); +} + +#ifdef CONFIG_PM + +static int ath10k_usb_pm_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct ath10k_usb *ar_usb = usb_get_intfdata(interface); + + ath10k_usb_flush_all(ar_usb->ar); + return 0; +} + +static int ath10k_usb_pm_resume(struct usb_interface *interface) +{ + struct ath10k_usb *ar_usb = usb_get_intfdata(interface); + struct ath10k *ar = ar_usb->ar; + + ath10k_usb_post_recv_transfers(ar, + &ar_usb->pipes[ATH10K_USB_PIPE_RX_DATA]); + + return 0; +} + +#else + +#define ath10k_usb_pm_suspend NULL +#define ath10k_usb_pm_resume NULL + +#endif + +/* table of devices that work with this driver */ +static struct usb_device_id ath10k_usb_ids[] = { + {USB_DEVICE(0x13b1, 0x0042)}, /* Linksys WUSB6100M */ + { /* Terminating entry */ }, +}; + +MODULE_DEVICE_TABLE(usb, ath10k_usb_ids); + +static struct usb_driver ath10k_usb_driver = { + .name = "ath10k_usb", + .probe = ath10k_usb_probe, + .suspend = ath10k_usb_pm_suspend, + .resume = ath10k_usb_pm_resume, + .disconnect = ath10k_usb_remove, + .id_table = ath10k_usb_ids, + .supports_autosuspend = true, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(ath10k_usb_driver); + +MODULE_AUTHOR("Atheros Communications, Inc."); +MODULE_DESCRIPTION("Driver support for Qualcomm Atheros 802.11ac WLAN USB devices"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath10k/usb.h b/drivers/net/wireless/ath/ath10k/usb.h new file mode 100644 index 000000000000..f60a3cc7d712 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/usb.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. + * Copyright (c) 2016-2017 Erik Stromdahl + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _USB_H_ +#define _USB_H_ + +/* constants */ +#define TX_URB_COUNT 32 +#define RX_URB_COUNT 32 +#define ATH10K_USB_RX_BUFFER_SIZE 4096 + +#define ATH10K_USB_PIPE_INVALID ATH10K_USB_PIPE_MAX + +/* USB endpoint definitions */ +#define ATH10K_USB_EP_ADDR_APP_CTRL_IN 0x81 +#define ATH10K_USB_EP_ADDR_APP_DATA_IN 0x82 +#define ATH10K_USB_EP_ADDR_APP_DATA2_IN 0x83 +#define ATH10K_USB_EP_ADDR_APP_INT_IN 0x84 + +#define ATH10K_USB_EP_ADDR_APP_CTRL_OUT 0x01 +#define ATH10K_USB_EP_ADDR_APP_DATA_LP_OUT 0x02 +#define ATH10K_USB_EP_ADDR_APP_DATA_MP_OUT 0x03 +#define ATH10K_USB_EP_ADDR_APP_DATA_HP_OUT 0x04 + +/* diagnostic command defnitions */ +#define ATH10K_USB_CONTROL_REQ_SEND_BMI_CMD 1 +#define ATH10K_USB_CONTROL_REQ_RECV_BMI_RESP 2 +#define ATH10K_USB_CONTROL_REQ_DIAG_CMD 3 +#define ATH10K_USB_CONTROL_REQ_DIAG_RESP 4 + +#define ATH10K_USB_CTRL_DIAG_CC_READ 0 +#define ATH10K_USB_CTRL_DIAG_CC_WRITE 1 + +#define ATH10K_USB_IS_BULK_EP(attr) (((attr) & 3) == 0x02) +#define ATH10K_USB_IS_INT_EP(attr) (((attr) & 3) == 0x03) +#define ATH10K_USB_IS_ISOC_EP(attr) (((attr) & 3) == 0x01) +#define ATH10K_USB_IS_DIR_IN(addr) ((addr) & 0x80) + +struct ath10k_usb_ctrl_diag_cmd_write { + __le32 cmd; + __le32 address; + __le32 value; + __le32 padding; +} __packed; + +struct ath10k_usb_ctrl_diag_cmd_read { + __le32 cmd; + __le32 address; +} __packed; + +struct ath10k_usb_ctrl_diag_resp_read { + u8 value[4]; +} __packed; + +/* tx/rx pipes for usb */ +enum ath10k_usb_pipe_id { + ATH10K_USB_PIPE_TX_CTRL = 0, + ATH10K_USB_PIPE_TX_DATA_LP, + ATH10K_USB_PIPE_TX_DATA_MP, + ATH10K_USB_PIPE_TX_DATA_HP, + ATH10K_USB_PIPE_RX_CTRL, + ATH10K_USB_PIPE_RX_DATA, + ATH10K_USB_PIPE_RX_DATA2, + ATH10K_USB_PIPE_RX_INT, + ATH10K_USB_PIPE_MAX +}; + +struct ath10k_usb_pipe { + struct list_head urb_list_head; + struct usb_anchor urb_submitted; + u32 urb_alloc; + u32 urb_cnt; + u32 urb_cnt_thresh; + unsigned int usb_pipe_handle; + u32 flags; + u8 ep_address; + u8 logical_pipe_num; + struct ath10k_usb *ar_usb; + u16 max_packet_size; + struct work_struct io_complete_work; + struct sk_buff_head io_comp_queue; + struct usb_endpoint_descriptor *ep_desc; +}; + +#define ATH10K_USB_PIPE_FLAG_TX BIT(0) + +/* usb device object */ +struct ath10k_usb { + /* protects pipe->urb_list_head and pipe->urb_cnt */ + spinlock_t cs_lock; + + struct usb_device *udev; + struct usb_interface *interface; + struct ath10k_usb_pipe pipes[ATH10K_USB_PIPE_MAX]; + u8 *diag_cmd_buffer; + u8 *diag_resp_buffer; + struct ath10k *ar; +}; + +/* usb urb object */ +struct ath10k_urb_context { + struct list_head link; + struct ath10k_usb_pipe *pipe; + struct sk_buff *skb; + struct ath10k *ar; +}; + +static inline struct ath10k_usb *ath10k_usb_priv(struct ath10k *ar) +{ + return (struct ath10k_usb *)ar->drv_priv; +} + +#endif -- cgit v1.2.3 From 433ef1b226788d1a5e6fbdc6697ff2c06ba6a9b0 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Fri, 28 Jul 2017 15:15:40 +0300 Subject: ath10k: sdio: fix compile warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As suggested by Arnd Bergmann, replace "while (time_before_...) {}" with "do {} while (time_before_...)" This fixes the following warnings detected by gcc 4.1.2: drivers/net/wireless/ath/ath10k/sdio.c: In function ‘ath10k_sdio_mbox_rxmsg_pending_handler’: drivers/net/wireless/ath/ath10k/sdio.c:676: warning: ‘ret’ may be used uninitialized in this function ... drivers/net/wireless/ath/ath10k/sdio.c: In function ‘ath10k_sdio_irq_handler’: drivers/net/wireless/ath/ath10k/sdio.c:1331: warning: ‘ret’ may be used uninitialized in this function Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/sdio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 859ed870bd97..48268f02bc07 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -683,7 +683,7 @@ static int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar, lookaheads[0] = msg_lookahead; timeout = jiffies + SDIO_MBOX_PROCESSING_TIMEOUT_HZ; - while (time_before(jiffies, timeout)) { + do { /* Try to allocate as many HTC RX packets indicated by * n_lookaheads. */ @@ -719,7 +719,7 @@ static int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar, * performance in high throughput situations. */ *done = false; - } + } while (time_before(jiffies, timeout)); if (ret && (ret != -ECANCELED)) ath10k_warn(ar, "failed to get pending recv messages: %d\n", @@ -1336,11 +1336,11 @@ static void ath10k_sdio_irq_handler(struct sdio_func *func) sdio_release_host(ar_sdio->func); timeout = jiffies + ATH10K_SDIO_HIF_COMMUNICATION_TIMEOUT_HZ; - while (time_before(jiffies, timeout) && !done) { + do { ret = ath10k_sdio_mbox_proc_pending_irqs(ar, &done); if (ret) break; - } + } while (time_before(jiffies, timeout) && !done); sdio_claim_host(ar_sdio->func); -- cgit v1.2.3 From 810fe818d6a19b5dd822600003750c2ec6543f59 Mon Sep 17 00:00:00 2001 From: Manikanta Pubbisetty Date: Fri, 28 Jul 2017 15:15:41 +0300 Subject: ath10k: extend wmi service map to accommodate new services Though there is room to accommodate 512 services in wmi service ready event, target uses only first 4-bits of each 32-bit word for advertising wmi services thereby limiting max wmi services to 64. TDLS implementation for 10.4 firmwares introduces new wmi services by making use of remaining unused bits of each 32-bit word, therefore the wmi service mapping in host needs to be extended. This patch adds the logic to extend the wmi SVCMAP to accommodate new wmi services. Signed-off-by: Manikanta Pubbisetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index baa38c8f847c..3a4cbbcd815e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -420,9 +420,20 @@ static inline char *wmi_service_name(int service_id) __le32_to_cpu((wmi_svc_bmap)[(svc_id) / (sizeof(u32))]) & \ BIT((svc_id) % (sizeof(u32)))) +/* This extension is required to accommodate new services, current limit + * for wmi_services is 64 as target is using only 4-bits of each 32-bit + * wmi_service word. Extending this to make use of remaining unused bits + * for new services. + */ +#define WMI_EXT_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \ + ((svc_id) >= (len) && \ + __le32_to_cpu((wmi_svc_bmap)[((svc_id) - (len)) / 28]) & \ + BIT(((((svc_id) - (len)) % 28) & 0x1f) + 4)) + #define SVCMAP(x, y, len) \ do { \ - if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \ + if ((WMI_SERVICE_IS_ENABLED((in), (x), (len))) || \ + (WMI_EXT_SERVICE_IS_ENABLED((in), (x), (len)))) \ __set_bit(y, out); \ } while (0) -- cgit v1.2.3 From add6cd8d5ab7aea383f13ea5a7dd573257ef9c24 Mon Sep 17 00:00:00 2001 From: Manikanta Pubbisetty Date: Fri, 28 Jul 2017 15:15:42 +0300 Subject: ath10k: add tdls support for 10.4 firmwares This patch adds the support of TDLS feature for 10.4 firmware versions. A new WMI service is added to advertise the support of TDLS for 10.4 firmwares. Signed-off-by: Manikanta Pubbisetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 9 ++ drivers/net/wireless/ath/ath10k/hw.h | 5 + drivers/net/wireless/ath/ath10k/mac.c | 5 +- drivers/net/wireless/ath/ath10k/wmi.c | 163 ++++++++++++++++++++- drivers/net/wireless/ath/ath10k/wmi.h | 257 +++++++++++++++++++++++++++++++++ 5 files changed, 436 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 2aa320a223af..3602aa462662 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1886,6 +1886,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) ar->fw_stats_req_mask = WMI_10_4_STAT_PEER | WMI_10_4_STAT_PEER_EXTD; ar->max_spatial_stream = ar->hw_params.max_spatial_stream; + ar->max_num_tdls_vdevs = TARGET_10_4_NUM_TDLS_VDEVS; if (test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL, fw_file->fw_features)) @@ -2124,6 +2125,14 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, ar->running_fw->fw_file.fw_features)) val |= WMI_10_4_COEX_GPIO_SUPPORT; + if (test_bit(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, + ar->wmi.svc_map)) + val |= WMI_10_4_TDLS_EXPLICIT_MODE_ONLY; + + if (test_bit(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, + ar->wmi.svc_map)) + val |= WMI_10_4_TDLS_UAPSD_BUFFER_STA; + status = ath10k_mac_ext_resource_config(ar, val); if (status) { ath10k_err(ar, diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 19e43512af50..0c089f6dd3d9 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -720,6 +720,11 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, #define TARGET_10_4_IPHDR_PAD_CONFIG 1 #define TARGET_10_4_QWRAP_CONFIG 0 +/* TDLS config */ +#define TARGET_10_4_NUM_TDLS_VDEVS 1 +#define TARGET_10_4_NUM_TDLS_BUFFER_STA 1 +#define TARGET_10_4_NUM_TDLS_SLEEP_STA 1 + /* Maximum number of Copy Engine's supported */ #define CE_COUNT_MAX 12 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 55c808f03a84..523a5490dece 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8197,8 +8197,11 @@ int ath10k_mac_register(struct ath10k *ar) NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; } - if (test_bit(WMI_SERVICE_TDLS, ar->wmi.svc_map)) + if (test_bit(WMI_SERVICE_TDLS, ar->wmi.svc_map) || + test_bit(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, ar->wmi.svc_map)) { ar->hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; + ieee80211_hw_set(ar->hw, TDLS_WIDER_BW); + } ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 96cd1ebd6a7e..a09e6bcfb52f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -651,8 +651,6 @@ static struct wmi_cmd_map wmi_10_4_cmd_map = { .gpio_output_cmdid = WMI_10_4_GPIO_OUTPUT_CMDID, .pdev_get_temperature_cmdid = WMI_10_4_PDEV_GET_TEMPERATURE_CMDID, .vdev_set_wmm_params_cmdid = WMI_CMD_UNSUPPORTED, - .tdls_set_state_cmdid = WMI_CMD_UNSUPPORTED, - .tdls_peer_update_cmdid = WMI_CMD_UNSUPPORTED, .adaptive_qcs_cmdid = WMI_CMD_UNSUPPORTED, .scan_update_request_cmdid = WMI_10_4_SCAN_UPDATE_REQUEST_CMDID, .vdev_standby_response_cmdid = WMI_10_4_VDEV_STANDBY_RESPONSE_CMDID, @@ -711,6 +709,33 @@ static struct wmi_cmd_map wmi_10_4_cmd_map = { .pdev_bss_chan_info_request_cmdid = WMI_10_4_PDEV_BSS_CHAN_INFO_REQUEST_CMDID, .ext_resource_cfg_cmdid = WMI_10_4_EXT_RESOURCE_CFG_CMDID, + .vdev_set_ie_cmdid = WMI_10_4_VDEV_SET_IE_CMDID, + .set_lteu_config_cmdid = WMI_10_4_SET_LTEU_CONFIG_CMDID, + .atf_ssid_grouping_request_cmdid = + WMI_10_4_ATF_SSID_GROUPING_REQUEST_CMDID, + .peer_atf_ext_request_cmdid = WMI_10_4_PEER_ATF_EXT_REQUEST_CMDID, + .set_periodic_channel_stats_cfg_cmdid = + WMI_10_4_SET_PERIODIC_CHANNEL_STATS_CONFIG, + .peer_bwf_request_cmdid = WMI_10_4_PEER_BWF_REQUEST_CMDID, + .btcoex_cfg_cmdid = WMI_10_4_BTCOEX_CFG_CMDID, + .peer_tx_mu_txmit_count_cmdid = WMI_10_4_PEER_TX_MU_TXMIT_COUNT_CMDID, + .peer_tx_mu_txmit_rstcnt_cmdid = WMI_10_4_PEER_TX_MU_TXMIT_RSTCNT_CMDID, + .peer_gid_userpos_list_cmdid = WMI_10_4_PEER_GID_USERPOS_LIST_CMDID, + .pdev_check_cal_version_cmdid = WMI_10_4_PDEV_CHECK_CAL_VERSION_CMDID, + .coex_version_cfg_cmid = WMI_10_4_COEX_VERSION_CFG_CMID, + .pdev_get_rx_filter_cmdid = WMI_10_4_PDEV_GET_RX_FILTER_CMDID, + .pdev_extended_nss_cfg_cmdid = WMI_10_4_PDEV_EXTENDED_NSS_CFG_CMDID, + .vdev_set_scan_nac_rssi_cmdid = WMI_10_4_VDEV_SET_SCAN_NAC_RSSI_CMDID, + .prog_gpio_band_select_cmdid = WMI_10_4_PROG_GPIO_BAND_SELECT_CMDID, + .config_smart_logging_cmdid = WMI_10_4_CONFIG_SMART_LOGGING_CMDID, + .debug_fatal_condition_cmdid = WMI_10_4_DEBUG_FATAL_CONDITION_CMDID, + .get_tsf_timer_cmdid = WMI_10_4_GET_TSF_TIMER_CMDID, + .pdev_get_tpc_table_cmdid = WMI_10_4_PDEV_GET_TPC_TABLE_CMDID, + .vdev_sifs_trigger_time_cmdid = WMI_10_4_VDEV_SIFS_TRIGGER_TIME_CMDID, + .pdev_wds_entry_list_cmdid = WMI_10_4_PDEV_WDS_ENTRY_LIST_CMDID, + .tdls_set_state_cmdid = WMI_10_4_TDLS_SET_STATE_CMDID, + .tdls_peer_update_cmdid = WMI_10_4_TDLS_PEER_UPDATE_CMDID, + .tdls_set_offchan_mode_cmdid = WMI_10_4_TDLS_SET_OFFCHAN_MODE_CMDID, }; /* MAIN WMI VDEV param map */ @@ -7803,14 +7828,28 @@ ath10k_wmi_10_4_ext_resource_config(struct ath10k *ar, { struct wmi_ext_resource_config_10_4_cmd *cmd; struct sk_buff *skb; + u32 num_tdls_sleep_sta = 0; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return ERR_PTR(-ENOMEM); + if (test_bit(WMI_SERVICE_TDLS_UAPSD_SLEEP_STA, ar->wmi.svc_map)) + num_tdls_sleep_sta = TARGET_10_4_NUM_TDLS_SLEEP_STA; + cmd = (struct wmi_ext_resource_config_10_4_cmd *)skb->data; cmd->host_platform_config = __cpu_to_le32(type); cmd->fw_feature_bitmap = __cpu_to_le32(fw_feature_bitmap); + cmd->wlan_gpio_priority = __cpu_to_le32(-1); + cmd->coex_version = __cpu_to_le32(WMI_NO_COEX_VERSION_SUPPORT); + cmd->coex_gpio_pin1 = __cpu_to_le32(-1); + cmd->coex_gpio_pin2 = __cpu_to_le32(-1); + cmd->coex_gpio_pin3 = __cpu_to_le32(-1); + cmd->num_tdls_vdevs = __cpu_to_le32(TARGET_10_4_NUM_TDLS_VDEVS); + cmd->num_tdls_conn_table_entries = __cpu_to_le32(20); + cmd->max_tdls_concurrent_sleep_sta = __cpu_to_le32(num_tdls_sleep_sta); + cmd->max_tdls_concurrent_buffer_sta = + __cpu_to_le32(TARGET_10_4_NUM_TDLS_BUFFER_STA); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi ext resource config host type %d firmware feature bitmap %08x\n", @@ -7818,6 +7857,124 @@ ath10k_wmi_10_4_ext_resource_config(struct ath10k *ar, return skb; } +static struct sk_buff * +ath10k_wmi_10_4_gen_update_fw_tdls_state(struct ath10k *ar, u32 vdev_id, + enum wmi_tdls_state state) +{ + struct wmi_10_4_tdls_set_state_cmd *cmd; + struct sk_buff *skb; + u32 options = 0; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + if (test_bit(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, ar->wmi.svc_map)) + state = WMI_TDLS_ENABLE_PASSIVE; + + if (test_bit(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, ar->wmi.svc_map)) + options |= WMI_TDLS_BUFFER_STA_EN; + + cmd = (struct wmi_10_4_tdls_set_state_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->state = __cpu_to_le32(state); + cmd->notification_interval_ms = __cpu_to_le32(5000); + cmd->tx_discovery_threshold = __cpu_to_le32(100); + cmd->tx_teardown_threshold = __cpu_to_le32(5); + cmd->rssi_teardown_threshold = __cpu_to_le32(-75); + cmd->rssi_delta = __cpu_to_le32(-20); + cmd->tdls_options = __cpu_to_le32(options); + cmd->tdls_peer_traffic_ind_window = __cpu_to_le32(2); + cmd->tdls_peer_traffic_response_timeout_ms = __cpu_to_le32(5000); + cmd->tdls_puapsd_mask = __cpu_to_le32(0xf); + cmd->tdls_puapsd_inactivity_time_ms = __cpu_to_le32(0); + cmd->tdls_puapsd_rx_frame_threshold = __cpu_to_le32(10); + cmd->teardown_notification_ms = __cpu_to_le32(10); + cmd->tdls_peer_kickout_threshold = __cpu_to_le32(96); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi update fw tdls state %d for vdev %i\n", + state, vdev_id); + return skb; +} + +static u32 ath10k_wmi_prepare_peer_qos(u8 uapsd_queues, u8 sp) +{ + u32 peer_qos = 0; + + if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + peer_qos |= WMI_TDLS_PEER_QOS_AC_VO; + if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) + peer_qos |= WMI_TDLS_PEER_QOS_AC_VI; + if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + peer_qos |= WMI_TDLS_PEER_QOS_AC_BK; + if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) + peer_qos |= WMI_TDLS_PEER_QOS_AC_BE; + + peer_qos |= SM(sp, WMI_TDLS_PEER_SP); + + return peer_qos; +} + +static struct sk_buff * +ath10k_wmi_10_4_gen_tdls_peer_update(struct ath10k *ar, + const struct wmi_tdls_peer_update_cmd_arg *arg, + const struct wmi_tdls_peer_capab_arg *cap, + const struct wmi_channel_arg *chan_arg) +{ + struct wmi_10_4_tdls_peer_update_cmd *cmd; + struct wmi_tdls_peer_capabilities *peer_cap; + struct wmi_channel *chan; + struct sk_buff *skb; + u32 peer_qos; + int len, chan_len; + int i; + + /* tdls peer update cmd has place holder for one channel*/ + chan_len = cap->peer_chan_len ? (cap->peer_chan_len - 1) : 0; + + len = sizeof(*cmd) + chan_len * sizeof(*chan); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + memset(skb->data, 0, sizeof(*cmd)); + + cmd = (struct wmi_10_4_tdls_peer_update_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + ether_addr_copy(cmd->peer_macaddr.addr, arg->addr); + cmd->peer_state = __cpu_to_le32(arg->peer_state); + + peer_qos = ath10k_wmi_prepare_peer_qos(cap->peer_uapsd_queues, + cap->peer_max_sp); + + peer_cap = &cmd->peer_capab; + peer_cap->peer_qos = __cpu_to_le32(peer_qos); + peer_cap->buff_sta_support = __cpu_to_le32(cap->buff_sta_support); + peer_cap->off_chan_support = __cpu_to_le32(cap->off_chan_support); + peer_cap->peer_curr_operclass = __cpu_to_le32(cap->peer_curr_operclass); + peer_cap->self_curr_operclass = __cpu_to_le32(cap->self_curr_operclass); + peer_cap->peer_chan_len = __cpu_to_le32(cap->peer_chan_len); + peer_cap->peer_operclass_len = __cpu_to_le32(cap->peer_operclass_len); + + for (i = 0; i < WMI_TDLS_MAX_SUPP_OPER_CLASSES; i++) + peer_cap->peer_operclass[i] = cap->peer_operclass[i]; + + peer_cap->is_peer_responder = __cpu_to_le32(cap->is_peer_responder); + peer_cap->pref_offchan_num = __cpu_to_le32(cap->pref_offchan_num); + peer_cap->pref_offchan_bw = __cpu_to_le32(cap->pref_offchan_bw); + + for (i = 0; i < cap->peer_chan_len; i++) { + chan = (struct wmi_channel *)&peer_cap->peer_chan_list[i]; + ath10k_wmi_put_wmi_channel(chan, &chan_arg[i]); + } + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tdls peer update vdev %i state %d n_chans %u\n", + arg->vdev_id, arg->peer_state, cap->peer_chan_len); + return skb; +} + static struct sk_buff * ath10k_wmi_op_gen_echo(struct ath10k *ar, u32 value) { @@ -8197,6 +8354,8 @@ static const struct wmi_ops wmi_10_4_ops = { .gen_delba_send = ath10k_wmi_op_gen_delba_send, .fw_stats_fill = ath10k_wmi_10_4_op_fw_stats_fill, .ext_resource_config = ath10k_wmi_10_4_ext_resource_config, + .gen_update_fw_tdls_state = ath10k_wmi_10_4_gen_update_fw_tdls_state, + .gen_tdls_peer_update = ath10k_wmi_10_4_gen_tdls_peer_update, /* shared with 10.2 */ .pull_echo_ev = ath10k_wmi_op_pull_echo_ev, diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 3a4cbbcd815e..60d2bb045042 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -184,6 +184,17 @@ enum wmi_service { WMI_SERVICE_TX_MODE_PUSH_ONLY, WMI_SERVICE_TX_MODE_PUSH_PULL, WMI_SERVICE_TX_MODE_DYNAMIC, + WMI_SERVICE_VDEV_RX_FILTER, + WMI_SERVICE_BTCOEX, + WMI_SERVICE_CHECK_CAL_VERSION, + WMI_SERVICE_DBGLOG_WARN2, + WMI_SERVICE_BTCOEX_DUTY_CYCLE, + WMI_SERVICE_4_WIRE_COEX_SUPPORT, + WMI_SERVICE_EXTENDED_NSS_SUPPORT, + WMI_SERVICE_PROG_GPIO_BAND_SELECT, + WMI_SERVICE_SMART_LOGGING_SUPPORT, + WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE, + WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, /* keep last */ WMI_SERVICE_MAX, @@ -310,6 +321,21 @@ enum wmi_10_4_service { WMI_10_4_SERVICE_TX_MODE_PUSH_ONLY, WMI_10_4_SERVICE_TX_MODE_PUSH_PULL, WMI_10_4_SERVICE_TX_MODE_DYNAMIC, + WMI_10_4_SERVICE_VDEV_RX_FILTER, + WMI_10_4_SERVICE_BTCOEX, + WMI_10_4_SERVICE_CHECK_CAL_VERSION, + WMI_10_4_SERVICE_DBGLOG_WARN2, + WMI_10_4_SERVICE_BTCOEX_DUTY_CYCLE, + WMI_10_4_SERVICE_4_WIRE_COEX_SUPPORT, + WMI_10_4_SERVICE_EXTENDED_NSS_SUPPORT, + WMI_10_4_SERVICE_PROG_GPIO_BAND_SELECT, + WMI_10_4_SERVICE_SMART_LOGGING_SUPPORT, + WMI_10_4_SERVICE_TDLS, + WMI_10_4_SERVICE_TDLS_OFFCHAN, + WMI_10_4_SERVICE_TDLS_UAPSD_BUFFER_STA, + WMI_10_4_SERVICE_TDLS_UAPSD_SLEEP_STA, + WMI_10_4_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE, + WMI_10_4_SERVICE_TDLS_EXPLICIT_MODE_ONLY, }; static inline char *wmi_service_name(int service_id) @@ -408,6 +434,16 @@ static inline char *wmi_service_name(int service_id) SVCSTR(WMI_SERVICE_TX_MODE_PUSH_ONLY); SVCSTR(WMI_SERVICE_TX_MODE_PUSH_PULL); SVCSTR(WMI_SERVICE_TX_MODE_DYNAMIC); + SVCSTR(WMI_SERVICE_VDEV_RX_FILTER); + SVCSTR(WMI_SERVICE_CHECK_CAL_VERSION); + SVCSTR(WMI_SERVICE_DBGLOG_WARN2); + SVCSTR(WMI_SERVICE_BTCOEX_DUTY_CYCLE); + SVCSTR(WMI_SERVICE_4_WIRE_COEX_SUPPORT); + SVCSTR(WMI_SERVICE_EXTENDED_NSS_SUPPORT); + SVCSTR(WMI_SERVICE_PROG_GPIO_BAND_SELECT); + SVCSTR(WMI_SERVICE_SMART_LOGGING_SUPPORT); + SVCSTR(WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE); + SVCSTR(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY); default: return NULL; } @@ -674,6 +710,36 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out, WMI_SERVICE_TX_MODE_PUSH_PULL, len); SVCMAP(WMI_10_4_SERVICE_TX_MODE_DYNAMIC, WMI_SERVICE_TX_MODE_DYNAMIC, len); + SVCMAP(WMI_10_4_SERVICE_VDEV_RX_FILTER, + WMI_SERVICE_VDEV_RX_FILTER, len); + SVCMAP(WMI_10_4_SERVICE_BTCOEX, + WMI_SERVICE_BTCOEX, len); + SVCMAP(WMI_10_4_SERVICE_CHECK_CAL_VERSION, + WMI_SERVICE_CHECK_CAL_VERSION, len); + SVCMAP(WMI_10_4_SERVICE_DBGLOG_WARN2, + WMI_SERVICE_DBGLOG_WARN2, len); + SVCMAP(WMI_10_4_SERVICE_BTCOEX_DUTY_CYCLE, + WMI_SERVICE_BTCOEX_DUTY_CYCLE, len); + SVCMAP(WMI_10_4_SERVICE_4_WIRE_COEX_SUPPORT, + WMI_SERVICE_4_WIRE_COEX_SUPPORT, len); + SVCMAP(WMI_10_4_SERVICE_EXTENDED_NSS_SUPPORT, + WMI_SERVICE_EXTENDED_NSS_SUPPORT, len); + SVCMAP(WMI_10_4_SERVICE_PROG_GPIO_BAND_SELECT, + WMI_SERVICE_PROG_GPIO_BAND_SELECT, len); + SVCMAP(WMI_10_4_SERVICE_SMART_LOGGING_SUPPORT, + WMI_SERVICE_SMART_LOGGING_SUPPORT, len); + SVCMAP(WMI_10_4_SERVICE_TDLS, + WMI_SERVICE_TDLS, len); + SVCMAP(WMI_10_4_SERVICE_TDLS_OFFCHAN, + WMI_SERVICE_TDLS_OFFCHAN, len); + SVCMAP(WMI_10_4_SERVICE_TDLS_UAPSD_BUFFER_STA, + WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, len); + SVCMAP(WMI_10_4_SERVICE_TDLS_UAPSD_SLEEP_STA, + WMI_SERVICE_TDLS_UAPSD_SLEEP_STA, len); + SVCMAP(WMI_10_4_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE, + WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE, len); + SVCMAP(WMI_10_4_SERVICE_TDLS_EXPLICIT_MODE_ONLY, + WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, len); } #undef SVCMAP @@ -848,6 +914,29 @@ struct wmi_cmd_map { u32 pdev_bss_chan_info_request_cmdid; u32 pdev_enable_adaptive_cca_cmdid; u32 ext_resource_cfg_cmdid; + u32 vdev_set_ie_cmdid; + u32 set_lteu_config_cmdid; + u32 atf_ssid_grouping_request_cmdid; + u32 peer_atf_ext_request_cmdid; + u32 set_periodic_channel_stats_cfg_cmdid; + u32 peer_bwf_request_cmdid; + u32 btcoex_cfg_cmdid; + u32 peer_tx_mu_txmit_count_cmdid; + u32 peer_tx_mu_txmit_rstcnt_cmdid; + u32 peer_gid_userpos_list_cmdid; + u32 pdev_check_cal_version_cmdid; + u32 coex_version_cfg_cmid; + u32 pdev_get_rx_filter_cmdid; + u32 pdev_extended_nss_cfg_cmdid; + u32 vdev_set_scan_nac_rssi_cmdid; + u32 prog_gpio_band_select_cmdid; + u32 config_smart_logging_cmdid; + u32 debug_fatal_condition_cmdid; + u32 get_tsf_timer_cmdid; + u32 pdev_get_tpc_table_cmdid; + u32 vdev_sifs_trigger_time_cmdid; + u32 pdev_wds_entry_list_cmdid; + u32 tdls_set_offchan_mode_cmdid; }; /* @@ -1658,6 +1747,29 @@ enum wmi_10_4_cmd_id { WMI_10_4_EXT_RESOURCE_CFG_CMDID, WMI_10_4_VDEV_SET_IE_CMDID, WMI_10_4_SET_LTEU_CONFIG_CMDID, + WMI_10_4_ATF_SSID_GROUPING_REQUEST_CMDID, + WMI_10_4_PEER_ATF_EXT_REQUEST_CMDID, + WMI_10_4_SET_PERIODIC_CHANNEL_STATS_CONFIG, + WMI_10_4_PEER_BWF_REQUEST_CMDID, + WMI_10_4_BTCOEX_CFG_CMDID, + WMI_10_4_PEER_TX_MU_TXMIT_COUNT_CMDID, + WMI_10_4_PEER_TX_MU_TXMIT_RSTCNT_CMDID, + WMI_10_4_PEER_GID_USERPOS_LIST_CMDID, + WMI_10_4_PDEV_CHECK_CAL_VERSION_CMDID, + WMI_10_4_COEX_VERSION_CFG_CMID, + WMI_10_4_PDEV_GET_RX_FILTER_CMDID, + WMI_10_4_PDEV_EXTENDED_NSS_CFG_CMDID, + WMI_10_4_VDEV_SET_SCAN_NAC_RSSI_CMDID, + WMI_10_4_PROG_GPIO_BAND_SELECT_CMDID, + WMI_10_4_CONFIG_SMART_LOGGING_CMDID, + WMI_10_4_DEBUG_FATAL_CONDITION_CMDID, + WMI_10_4_GET_TSF_TIMER_CMDID, + WMI_10_4_PDEV_GET_TPC_TABLE_CMDID, + WMI_10_4_VDEV_SIFS_TRIGGER_TIME_CMDID, + WMI_10_4_PDEV_WDS_ENTRY_LIST_CMDID, + WMI_10_4_TDLS_SET_STATE_CMDID, + WMI_10_4_TDLS_PEER_UPDATE_CMDID, + WMI_10_4_TDLS_SET_OFFCHAN_MODE_CMDID, WMI_10_4_PDEV_UTF_CMDID = WMI_10_4_END_CMDID - 1, }; @@ -1721,6 +1833,18 @@ enum wmi_10_4_event_id { WMI_10_4_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENTID, WMI_10_4_PDEV_BSS_CHAN_INFO_EVENTID, WMI_10_4_MU_REPORT_EVENTID, + WMI_10_4_TX_DATA_TRAFFIC_CTRL_EVENTID, + WMI_10_4_PEER_TX_MU_TXMIT_COUNT_EVENTID, + WMI_10_4_PEER_GID_USERPOS_LIST_EVENTID, + WMI_10_4_PDEV_CHECK_CAL_VERSION_EVENTID, + WMI_10_4_ATF_PEER_STATS_EVENTID, + WMI_10_4_PDEV_GET_RX_FILTER_EVENTID, + WMI_10_4_NAC_RSSI_EVENTID, + WMI_10_4_DEBUG_FATAL_CONDITION_EVENTID, + WMI_10_4_GET_TSF_TIMER_RESP_EVENTID, + WMI_10_4_PDEV_TPC_TABLE_EVENTID, + WMI_10_4_PDEV_WDS_ENTRY_LIST_EVENTID, + WMI_10_4_TDLS_PEER_EVENTID, WMI_10_4_PDEV_UTF_EVENTID = WMI_10_4_END_EVENTID - 1, }; @@ -2729,6 +2853,18 @@ struct wmi_resource_config_10_4 { __le32 qwrap_config; } __packed; +enum wmi_coex_version { + WMI_NO_COEX_VERSION_SUPPORT = 0, + /* 3 wire coex support*/ + WMI_COEX_VERSION_1 = 1, + /* 2.5 wire coex support*/ + WMI_COEX_VERSION_2 = 2, + /* 2.5 wire coex with duty cycle support */ + WMI_COEX_VERSION_3 = 3, + /* 4 wire coex support*/ + WMI_COEX_VERSION_4 = 4, +}; + /** * enum wmi_10_4_feature_mask - WMI 10.4 feature enable/disable flags * @WMI_10_4_LTEU_SUPPORT: LTEU config @@ -2737,6 +2873,14 @@ struct wmi_resource_config_10_4 { * @WMI_10_4_AUX_RADIO_CHAN_LOAD_INTF: AUX Radio Enhancement for chan load scan * @WMI_10_4_BSS_CHANNEL_INFO_64: BSS channel info stats * @WMI_10_4_PEER_STATS: Per station stats + * @WMI_10_4_VDEV_STATS: Per vdev stats + * @WMI_10_4_TDLS: Implicit TDLS support in firmware enable/disable + * @WMI_10_4_TDLS_OFFCHAN: TDLS offchannel support enable/disable + * @WMI_10_4_TDLS_UAPSD_BUFFER_STA: TDLS buffer sta support enable/disable + * @WMI_10_4_TDLS_UAPSD_SLEEP_STA: TDLS sleep sta support enable/disable + * @WMI_10_4_TDLS_CONN_TRACKER_IN_HOST_MODE: TDLS connection tracker in host + * enable/disable + * @WMI_10_4_TDLS_EXPLICIT_MODE_ONLY:Explicit TDLS mode enable/disable */ enum wmi_10_4_feature_mask { WMI_10_4_LTEU_SUPPORT = BIT(0), @@ -2745,6 +2889,14 @@ enum wmi_10_4_feature_mask { WMI_10_4_AUX_RADIO_CHAN_LOAD_INTF = BIT(3), WMI_10_4_BSS_CHANNEL_INFO_64 = BIT(4), WMI_10_4_PEER_STATS = BIT(5), + WMI_10_4_VDEV_STATS = BIT(6), + WMI_10_4_TDLS = BIT(7), + WMI_10_4_TDLS_OFFCHAN = BIT(8), + WMI_10_4_TDLS_UAPSD_BUFFER_STA = BIT(9), + WMI_10_4_TDLS_UAPSD_SLEEP_STA = BIT(10), + WMI_10_4_TDLS_CONN_TRACKER_IN_HOST_MODE = BIT(11), + WMI_10_4_TDLS_EXPLICIT_MODE_ONLY = BIT(12), + }; struct wmi_ext_resource_config_10_4_cmd { @@ -2752,6 +2904,22 @@ struct wmi_ext_resource_config_10_4_cmd { __le32 host_platform_config; /* see enum wmi_10_4_feature_mask */ __le32 fw_feature_bitmap; + /* WLAN priority GPIO number */ + __le32 wlan_gpio_priority; + /* see enum wmi_coex_version */ + __le32 coex_version; + /* COEX GPIO config */ + __le32 coex_gpio_pin1; + __le32 coex_gpio_pin2; + __le32 coex_gpio_pin3; + /* number of vdevs allowed to perform tdls */ + __le32 num_tdls_vdevs; + /* number of peers to track per TDLS vdev */ + __le32 num_tdls_conn_table_entries; + /* number of tdls sleep sta supported */ + __le32 max_tdls_concurrent_sleep_sta; + /* number of tdls buffer sta supported */ + __le32 max_tdls_concurrent_buffer_sta; }; /* strucutre describing host memory chunk. */ @@ -6567,6 +6735,22 @@ struct wmi_tdls_peer_update_cmd_arg { #define WMI_TDLS_MAX_SUPP_OPER_CLASSES 32 +#define WMI_TDLS_PEER_SP_MASK 0x60 +#define WMI_TDLS_PEER_SP_LSB 5 + +enum wmi_tdls_options { + WMI_TDLS_OFFCHAN_EN = BIT(0), + WMI_TDLS_BUFFER_STA_EN = BIT(1), + WMI_TDLS_SLEEP_STA_EN = BIT(2), +}; + +enum { + WMI_TDLS_PEER_QOS_AC_VO = BIT(0), + WMI_TDLS_PEER_QOS_AC_VI = BIT(1), + WMI_TDLS_PEER_QOS_AC_BK = BIT(2), + WMI_TDLS_PEER_QOS_AC_BE = BIT(3), +}; + struct wmi_tdls_peer_capab_arg { u8 peer_uapsd_queues; u8 peer_max_sp; @@ -6582,6 +6766,79 @@ struct wmi_tdls_peer_capab_arg { u32 pref_offchan_bw; }; +struct wmi_10_4_tdls_set_state_cmd { + __le32 vdev_id; + __le32 state; + __le32 notification_interval_ms; + __le32 tx_discovery_threshold; + __le32 tx_teardown_threshold; + __le32 rssi_teardown_threshold; + __le32 rssi_delta; + __le32 tdls_options; + __le32 tdls_peer_traffic_ind_window; + __le32 tdls_peer_traffic_response_timeout_ms; + __le32 tdls_puapsd_mask; + __le32 tdls_puapsd_inactivity_time_ms; + __le32 tdls_puapsd_rx_frame_threshold; + __le32 teardown_notification_ms; + __le32 tdls_peer_kickout_threshold; +} __packed; + +struct wmi_tdls_peer_capabilities { + __le32 peer_qos; + __le32 buff_sta_support; + __le32 off_chan_support; + __le32 peer_curr_operclass; + __le32 self_curr_operclass; + __le32 peer_chan_len; + __le32 peer_operclass_len; + u8 peer_operclass[WMI_TDLS_MAX_SUPP_OPER_CLASSES]; + __le32 is_peer_responder; + __le32 pref_offchan_num; + __le32 pref_offchan_bw; + struct wmi_channel peer_chan_list[1]; +} __packed; + +struct wmi_10_4_tdls_peer_update_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 peer_state; + __le32 reserved[4]; + struct wmi_tdls_peer_capabilities peer_capab; +} __packed; + +enum wmi_tdls_peer_reason { + WMI_TDLS_TEARDOWN_REASON_TX, + WMI_TDLS_TEARDOWN_REASON_RSSI, + WMI_TDLS_TEARDOWN_REASON_SCAN, + WMI_TDLS_DISCONNECTED_REASON_PEER_DELETE, + WMI_TDLS_TEARDOWN_REASON_PTR_TIMEOUT, + WMI_TDLS_TEARDOWN_REASON_BAD_PTR, + WMI_TDLS_TEARDOWN_REASON_NO_RESPONSE, + WMI_TDLS_ENTER_BUF_STA, + WMI_TDLS_EXIT_BUF_STA, + WMI_TDLS_ENTER_BT_BUSY_MODE, + WMI_TDLS_EXIT_BT_BUSY_MODE, + WMI_TDLS_SCAN_STARTED_EVENT, + WMI_TDLS_SCAN_COMPLETED_EVENT, +}; + +enum wmi_tdls_peer_notification { + WMI_TDLS_SHOULD_DISCOVER, + WMI_TDLS_SHOULD_TEARDOWN, + WMI_TDLS_PEER_DISCONNECTED, + WMI_TDLS_CONNECTION_TRACKER_NOTIFICATION, +}; + +struct wmi_tdls_peer_event { + struct wmi_mac_addr peer_macaddr; + /* see enum wmi_tdls_peer_notification*/ + __le32 peer_status; + /* see enum wmi_tdls_peer_reason */ + __le32 peer_reason; + __le32 vdev_id; +} __packed; + enum wmi_txbf_conf { WMI_TXBF_CONF_UNSUPPORTED, WMI_TXBF_CONF_BEFORE_ASSOC, -- cgit v1.2.3 From be5b4f4002a6c1c37279c69e093567109be982f4 Mon Sep 17 00:00:00 2001 From: Manikanta Pubbisetty Date: Fri, 28 Jul 2017 15:15:43 +0300 Subject: ath10k: push peer type to target for TDLS peers WMI interface for all the firmwares(except QCA6174) does not include the type of peer(default/bss/tdls) requested during peer creation, therefore target creates a default peer. TDLS implementation on 10.4 firmware requires host to configure the peer type(tdls) for TDLS peers. This patch adds peer type parameter to the existing WMI interface for peer creation to accommodate this requirement. Tested this change on QCA9888(10.4-3.5.1-00018) and QCA988x(10.2.4.70.9-2) with ping tests for AP/STA modes. Signed-off-by: Manikanta Pubbisetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 1 + drivers/net/wireless/ath/ath10k/wmi.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index a09e6bcfb52f..38a97086708b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -6498,6 +6498,7 @@ ath10k_wmi_op_gen_peer_create(struct ath10k *ar, u32 vdev_id, cmd = (struct wmi_peer_create_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + cmd->peer_type = __cpu_to_le32(peer_type); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer create vdev_id %d peer_addr %pM\n", diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 60d2bb045042..7a3606dde227 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -5877,6 +5877,7 @@ struct wmi_tbtt_offset_event { struct wmi_peer_create_cmd { __le32 vdev_id; struct wmi_mac_addr peer_macaddr; + __le32 peer_type; } __packed; enum wmi_peer_type { -- cgit v1.2.3 From a764284f34f9b0b07f34907947d38d6632f61a1b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 28 Jul 2017 15:15:44 +0300 Subject: ath10k: explicitly request exclusive reset control Commit a53e35db70d1 ("reset: Ensure drivers are explicit when requesting reset lines") started to transition the reset control request API calls to explicitly state whether the driver needs exclusive or shared reset control behavior. Convert all drivers requesting exclusive resets to the explicit API call so the temporary transition helpers can be removed. No functional changes. Cc: Kalle Valo Cc: ath10k@lists.infradead.org Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Philipp Zabel Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ahb.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index b36dd792fbb2..ff6815e95684 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -197,35 +197,40 @@ static int ath10k_ahb_rst_ctrl_init(struct ath10k *ar) dev = &ar_ahb->pdev->dev; - ar_ahb->core_cold_rst = devm_reset_control_get(dev, "wifi_core_cold"); + ar_ahb->core_cold_rst = devm_reset_control_get_exclusive(dev, + "wifi_core_cold"); if (IS_ERR(ar_ahb->core_cold_rst)) { ath10k_err(ar, "failed to get core cold rst ctrl: %ld\n", PTR_ERR(ar_ahb->core_cold_rst)); return PTR_ERR(ar_ahb->core_cold_rst); } - ar_ahb->radio_cold_rst = devm_reset_control_get(dev, "wifi_radio_cold"); + ar_ahb->radio_cold_rst = devm_reset_control_get_exclusive(dev, + "wifi_radio_cold"); if (IS_ERR(ar_ahb->radio_cold_rst)) { ath10k_err(ar, "failed to get radio cold rst ctrl: %ld\n", PTR_ERR(ar_ahb->radio_cold_rst)); return PTR_ERR(ar_ahb->radio_cold_rst); } - ar_ahb->radio_warm_rst = devm_reset_control_get(dev, "wifi_radio_warm"); + ar_ahb->radio_warm_rst = devm_reset_control_get_exclusive(dev, + "wifi_radio_warm"); if (IS_ERR(ar_ahb->radio_warm_rst)) { ath10k_err(ar, "failed to get radio warm rst ctrl: %ld\n", PTR_ERR(ar_ahb->radio_warm_rst)); return PTR_ERR(ar_ahb->radio_warm_rst); } - ar_ahb->radio_srif_rst = devm_reset_control_get(dev, "wifi_radio_srif"); + ar_ahb->radio_srif_rst = devm_reset_control_get_exclusive(dev, + "wifi_radio_srif"); if (IS_ERR(ar_ahb->radio_srif_rst)) { ath10k_err(ar, "failed to get radio srif rst ctrl: %ld\n", PTR_ERR(ar_ahb->radio_srif_rst)); return PTR_ERR(ar_ahb->radio_srif_rst); } - ar_ahb->cpu_init_rst = devm_reset_control_get(dev, "wifi_cpu_init"); + ar_ahb->cpu_init_rst = devm_reset_control_get_exclusive(dev, + "wifi_cpu_init"); if (IS_ERR(ar_ahb->cpu_init_rst)) { ath10k_err(ar, "failed to get cpu init rst ctrl: %ld\n", PTR_ERR(ar_ahb->cpu_init_rst)); -- cgit v1.2.3 From f085c105479f05bf47cd8f1f9786be2a73de29cb Mon Sep 17 00:00:00 2001 From: Christoph Fritz Date: Fri, 28 Jul 2017 15:15:46 +0300 Subject: ath9k: fix debugfs file permission This patch fixes a trivial debugfs file permission issue. Debugfs file ack_to has no write function, so S_IWUSR gets purged. Signed-off-by: Christoph Fritz Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 2e64977a8ab6..01fa30117288 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1452,7 +1452,7 @@ int ath9k_init_debug(struct ath_hw *ah) #endif #ifdef CONFIG_ATH9K_DYNACK - debugfs_create_file("ack_to", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, + debugfs_create_file("ack_to", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_ackto); #endif debugfs_create_file("tpc", S_IRUSR | S_IWUSR, -- cgit v1.2.3 From 129e12a9eb42fbccf4dd5bcc2ad3d4645e14e788 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 28 Jul 2017 15:15:47 +0300 Subject: ath9k: Add Dell Wireless 1802 with wowlan capability Add the Dell Wireless 1802 card as an AR9462 in the ath9k pci list. Note that the wowlan feature is supported and has been tested successfully. Signed-off by: Rosen Penev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/pci.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 7b7627f85d3a..223606311261 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -388,6 +388,11 @@ static const struct pci_device_id ath_pci_id_table[] = { PCI_VENDOR_ID_DELL, 0x020B), .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_DELL, + 0x0300), + .driver_data = ATH9K_PCI_WOW }, /* Killer Wireless (2x2) */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, -- cgit v1.2.3 From 4165cf7ba52a381c95d1cce0d42a48aac2c96ea3 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 3 Jul 2017 09:38:45 -0400 Subject: wcn36xx: check dma_mapping_error() Fixes splat: wcn36xx a204000.wcnss:smd-edge:wcnss:wifi: DMA-API: device driver failed to check map error[device address=0x00000000b45ba000] [size=3872 bytes] [mapped as single] ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at ../lib/dma-debug.c:1167 check_unmap+0x474/0x8d0 Modules linked in: bnep(E) arc4(E) wcn36xx(E) mac80211(E) btqcomsmd(E) btqca(E) bluetooth(E) cfg80211(E) ecdh_generic(E) rfkill(E) vfat(E) fat(E) wcnss_ctrl qcom_wcnss_pil(E) mdt_loader(E) qcom_common(E) remoteproc(E) crc32_ce(E) virtio_ring(E) snd_soc_lpass_apq8016(E) snd_soc_lpass_cpu(E) virtio(E) snd_soc_lpass_platform(E) leds_gpio(E) snd_soc_hdmi_codec(E) snd_soc_apq8016_sbc(E) snd_soc_msm8916_digital(E) snd_soc_core(E) qcom_spmi_temp_alarm(E) ac97_bus(E) snd_pcm_dmaengine(E) snd_seq(E) snd_seq_device(E) snd_pcm(E) spi_qup(E) nvmem_qfprom(E) snd_timer(E) snd(E) soundcore(E) msm_rng(E) qcom_tsens(E) nvmem_core(E) uas(E) usb_storage(E) dm9601(E) cdc_ether(E) usbnet(E) mii(E) mmc_block(E) sdhci_msm(E) sdhci_pltfm(E) qcom_spmi_vadc(E) qcom_vadc_common(PE) clk_smd_rpm(E) industrialio(E) qcom_smd_regulator(E) pinctrl_spmi_mpp(E) pinctrl_spmi_gpio(E) rtc_pm8xxx(E) adv7511(E) smd_rpm(E) qcom_spmi_pmic(E) regmap_spmi(E) phy_msm_usb(E) usb3503(E) extcon_usb_gpio(E) ci_hdrc_msm(E) ci_hdrc(E) qcom_hwspinlock(E) udc_core(E) extcon_core(E) ehci_msm(E) i2c_qup(E) sdhci(E) msm(E) mmc_core(E) drm_kms_helper(E) syscopyarea(E) sysfillrect(E) sysimgblt(E) fb_sys_fops(E) spmi_pmic_arb(E) drm(E) spmi(E) qcom_smd(E) rpmsg_core smsm(E) gpio_keys(E) smp2p(E) smem(E) hwspinlock_core(E) sunrpc(E) scsi_transport_iscsi(E) CPU: 0 PID: 0 Comm: swapper/0 Tainted: P E 4.12.0-rc7+ #1476 Hardware name: qualcomm dragonboard410c/dragonboard410c, BIOS 2017.07-rc1-00234-g22fa70a-dirty 06/26/2017 task: ffff000009049780 task.stack: ffff000009030000 PC is at check_unmap+0x474/0x8d0 LR is at check_unmap+0x474/0x8d0 ... Mapped at: dma_entry_alloc+0x68/0xa8 debug_dma_map_page+0x94/0x148 wcn36xx_dxe_fill_skb.isra.1+0xbc/0xf8 [wcn36xx] wcn36xx_dxe_init+0x244/0x398 [wcn36xx] wcn36xx_start+0xf4/0x298 [wcn36xx] Signed-off-by: Rob Clark Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/dxe.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index 87dfdaf9044c..d5c810a8cc52 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -289,6 +289,11 @@ static int wcn36xx_dxe_fill_skb(struct device *dev, struct wcn36xx_dxe_ctl *ctl) skb_tail_pointer(skb), WCN36XX_PKT_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, dxe->dst_addr_l)) { + dev_err(dev, "unable to map skb\n"); + kfree_skb(skb); + return -ENOMEM; + } ctl->skb = skb; return 0; -- cgit v1.2.3 From b8c17f7088310e7ee34ca61929f737045adfd449 Mon Sep 17 00:00:00 2001 From: Lin Yun Sheng Date: Wed, 2 Aug 2017 17:57:37 +0800 Subject: net: hns: Add self-adaptive interrupt coalesce support in hns driver When deal with low and high throughput, it is hard to achiece both high performance and low latency. In order to achiece that, this patch calculates the rx rate, and adjust the interrupt coalesce parameter accordingly. Signed-off-by: Yunsheng Lin Tested-by: Weiwei Deng Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.c | 1 + drivers/net/ethernet/hisilicon/hns/hnae.h | 15 +++ drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 1 + drivers/net/ethernet/hisilicon/hns/hns_enet.c | 134 ++++++++++++++++++++-- drivers/net/ethernet/hisilicon/hns/hns_enet.h | 2 +- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 7 +- 6 files changed, 149 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c index 9d9b6e6dd988..a051e582d541 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.c +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -202,6 +202,7 @@ hnae_init_ring(struct hnae_queue *q, struct hnae_ring *ring, int flags) ring->q = q; ring->flags = flags; spin_lock_init(&ring->lock); + ring->coal_param = q->handle->coal_param; assert(!ring->desc && !ring->desc_cb && !ring->desc_dma_addr); /* not matter for tx or rx ring, the ntc and ntc start from 0 */ diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 7ba653af19cb..3e62692af011 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -89,6 +89,10 @@ do { \ #define RCB_RING_NAME_LEN 16 +#define HNAE_LOWEST_LATENCY_COAL_PARAM 30 +#define HNAE_LOW_LATENCY_COAL_PARAM 80 +#define HNAE_BULK_LATENCY_COAL_PARAM 150 + enum hnae_led_state { HNAE_LED_INACTIVE, HNAE_LED_ACTIVE, @@ -292,6 +296,12 @@ struct hnae_ring { int flags; /* ring attribute */ int irq_init_flag; + + /* total rx bytes after last rx rate calucated */ + u64 coal_last_rx_bytes; + unsigned long coal_last_jiffies; + u32 coal_param; + u32 coal_rx_rate; /* rx rate in MB */ }; #define ring_ptr_move_fw(ring, p) \ @@ -548,8 +558,13 @@ struct hnae_handle { u32 if_support; int q_num; int vf_id; + unsigned long coal_last_jiffies; + u32 coal_param; /* self adapt coalesce param */ + /* the ring index of last ring that set coal param */ + u32 coal_ring_idx; u32 eport_id; u32 dport_id; /* v2 tx bd should fill the dport_id */ + bool coal_adapt_en; enum hnae_port_type port_type; enum hnae_media_type media_type; struct list_head node; /* list to hnae_ae_dev->handle_list */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index a37166ee577b..bd68379d2bea 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -99,6 +99,7 @@ struct hnae_handle *hns_ae_get_handle(struct hnae_ae_dev *dev, ae_handle->owner_dev = dsaf_dev->dev; ae_handle->dev = dev; ae_handle->q_num = qnum_per_vf; + ae_handle->coal_param = HNAE_LOWEST_LATENCY_COAL_PARAM; /* find ring pair, and set vf id*/ for (ae_handle->vf_id = 0; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 3987699f8fe6..832f27792e3f 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -812,6 +812,112 @@ static int hns_desc_unused(struct hnae_ring *ring) return ((ntc >= ntu) ? 0 : ring->desc_num) + ntc - ntu; } +#define HNS_LOWEST_LATENCY_RATE 27 /* 27 MB/s */ +#define HNS_LOW_LATENCY_RATE 80 /* 80 MB/s */ + +#define HNS_COAL_BDNUM 3 + +static u32 hns_coal_rx_bdnum(struct hnae_ring *ring) +{ + bool coal_enable = ring->q->handle->coal_adapt_en; + + if (coal_enable && + ring->coal_last_rx_bytes > HNS_LOWEST_LATENCY_RATE) + return HNS_COAL_BDNUM; + else + return 0; +} + +static void hns_update_rx_rate(struct hnae_ring *ring) +{ + bool coal_enable = ring->q->handle->coal_adapt_en; + u32 time_passed_ms; + u64 total_bytes; + + if (!coal_enable || + time_before(jiffies, ring->coal_last_jiffies + (HZ >> 4))) + return; + + /* ring->stats.rx_bytes overflowed */ + if (ring->coal_last_rx_bytes > ring->stats.rx_bytes) { + ring->coal_last_rx_bytes = ring->stats.rx_bytes; + ring->coal_last_jiffies = jiffies; + return; + } + + total_bytes = ring->stats.rx_bytes - ring->coal_last_rx_bytes; + time_passed_ms = jiffies_to_msecs(jiffies - ring->coal_last_jiffies); + ring->coal_rx_rate = (total_bytes / time_passed_ms) >> 10; + + ring->coal_last_rx_bytes = ring->stats.rx_bytes; + ring->coal_last_jiffies = jiffies; +} + +/** + * smooth_alg - smoothing algrithm for adjusting coalesce parameter + **/ +static u32 smooth_alg(u32 new_param, u32 old_param) +{ + u32 gap = (new_param > old_param) ? new_param - old_param + : old_param - new_param; + + if (gap > 8) + gap >>= 3; + + if (new_param > old_param) + return old_param + gap; + else + return old_param - gap; +} + +/** + * hns_nic_adp_coalesce - self adapte coalesce according to rx rate + * @ring_data: pointer to hns_nic_ring_data + **/ +static void hns_nic_adpt_coalesce(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + struct hnae_handle *handle = ring->q->handle; + u32 new_coal_param, old_coal_param = ring->coal_param; + + if (ring->coal_rx_rate < HNS_LOWEST_LATENCY_RATE) + new_coal_param = HNAE_LOWEST_LATENCY_COAL_PARAM; + else if (ring->coal_rx_rate < HNS_LOW_LATENCY_RATE) + new_coal_param = HNAE_LOW_LATENCY_COAL_PARAM; + else + new_coal_param = HNAE_BULK_LATENCY_COAL_PARAM; + + if (new_coal_param == old_coal_param && + new_coal_param == handle->coal_param) + return; + + new_coal_param = smooth_alg(new_coal_param, old_coal_param); + ring->coal_param = new_coal_param; + + /** + * Because all ring in one port has one coalesce param, when one ring + * calculate its own coalesce param, it cannot write to hardware at + * once. There are three conditions as follows: + * 1. current ring's coalesce param is larger than the hardware. + * 2. or ring which adapt last time can change again. + * 3. timeout. + */ + if (new_coal_param == handle->coal_param) { + handle->coal_last_jiffies = jiffies; + handle->coal_ring_idx = ring_data->queue_index; + } else if (new_coal_param > handle->coal_param || + handle->coal_ring_idx == ring_data->queue_index || + time_after(jiffies, handle->coal_last_jiffies + (HZ >> 4))) { + handle->dev->ops->set_coalesce_usecs(handle, + new_coal_param); + handle->dev->ops->set_coalesce_frames(handle, + 1, new_coal_param); + handle->coal_param = new_coal_param; + handle->coal_ring_idx = ring_data->queue_index; + handle->coal_last_jiffies = jiffies; + } +} + static int hns_nic_rx_poll_one(struct hns_nic_ring_data *ring_data, int budget, void *v) { @@ -868,20 +974,27 @@ static bool hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data) { struct hnae_ring *ring = ring_data->ring; int num = 0; + bool rx_stopped; - ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0); + hns_update_rx_rate(ring); /* for hardware bug fixed */ + ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0); num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); - if (num > 0) { + if (num <= hns_coal_rx_bdnum(ring)) { + if (ring->q->handle->coal_adapt_en) + hns_nic_adpt_coalesce(ring_data); + + rx_stopped = true; + } else { ring_data->ring->q->handle->dev->ops->toggle_ring_irq( ring_data->ring, 1); - return false; - } else { - return true; + rx_stopped = false; } + + return rx_stopped; } static bool hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data) @@ -889,12 +1002,17 @@ static bool hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data) struct hnae_ring *ring = ring_data->ring; int num; + hns_update_rx_rate(ring); num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); - if (!num) + if (num <= hns_coal_rx_bdnum(ring)) { + if (ring->q->handle->coal_adapt_en) + hns_nic_adpt_coalesce(ring_data); + return true; - else - return false; + } + + return false; } static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h index 9cb4c7884201..26e9afcbdd50 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h @@ -38,7 +38,7 @@ struct hns_nic_ring_data { struct hnae_ring *ring; struct napi_struct napi; cpumask_t mask; /* affinity mask */ - int queue_index; + u32 queue_index; int (*poll_one)(struct hns_nic_ring_data *, int, void *); void (*ex_process)(struct hns_nic_ring_data *, struct sk_buff *); bool (*fini_process)(struct hns_nic_ring_data *); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 78cb20c67aa6..7ea7f8a4aa2a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -735,8 +735,8 @@ static int hns_get_coalesce(struct net_device *net_dev, ops = priv->ae_handle->dev->ops; - ec->use_adaptive_rx_coalesce = 1; - ec->use_adaptive_tx_coalesce = 1; + ec->use_adaptive_rx_coalesce = priv->ae_handle->coal_adapt_en; + ec->use_adaptive_tx_coalesce = priv->ae_handle->coal_adapt_en; if ((!ops->get_coalesce_usecs) || (!ops->get_max_coalesced_frames)) @@ -787,6 +787,9 @@ static int hns_set_coalesce(struct net_device *net_dev, (!ops->set_coalesce_frames)) return -ESRCH; + if (ec->use_adaptive_rx_coalesce != priv->ae_handle->coal_adapt_en) + priv->ae_handle->coal_adapt_en = ec->use_adaptive_rx_coalesce; + rc1 = ops->set_coalesce_usecs(priv->ae_handle, ec->rx_coalesce_usecs); -- cgit v1.2.3 From 2d29b39739b94f02196d99502126f269fd8ec2ea Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 2 Aug 2017 23:27:14 +0530 Subject: qlcnic: add const to bin_attribute structure Add const to bin_attribute structure as it is only passed to the functions sysfs_{remove/create}_bin_file. The corresponding arguments are of type const, so declare the structure to be const. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 73027a6c06c7..82fcb83ea3c8 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -1248,7 +1248,7 @@ static const struct bin_attribute bin_attr_pm_config = { .write = qlcnic_sysfs_write_pm_config, }; -static struct bin_attribute bin_attr_flash = { +static const struct bin_attribute bin_attr_flash = { .attr = {.name = "flash", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_83xx_sysfs_flash_read_handler, -- cgit v1.2.3 From 3a8f6f874fa1ac004157fb3df742a5034718a2ce Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Wed, 2 Aug 2017 15:48:25 -0400 Subject: net: dsa: bcm_sf2: dst in not an array It's been a while now since ds->dst is not an array anymore, but a simple pointer to a dsa_switch_tree. Fortunately, SF2 does not support multi-chip and thus ds->index is always 0. This patch substitutes 'ds->dst[ds->index].' with 'ds->dst->'. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 6bbfa6ea1efb..558667c814c9 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -788,7 +788,7 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->dst[ds->index].cpu_dp->netdev; + struct net_device *p = ds->dst->cpu_dp->netdev; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_wolinfo pwol; @@ -811,7 +811,7 @@ static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->dst[ds->index].cpu_dp->netdev; + struct net_device *p = ds->dst->cpu_dp->netdev; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); s8 cpu_port = ds->dst->cpu_dp->index; struct ethtool_wolinfo pwol; -- cgit v1.2.3 From 3d52b5949d0311c4b7bc4c223918fee0f5c0dc39 Mon Sep 17 00:00:00 2001 From: John Allen Date: Wed, 2 Aug 2017 16:44:14 -0500 Subject: ibmvnic: Implement per-queue statistics reporting Add counters to report number of packets, bytes, and dropped packets for each transmit queue and number of packets, bytes, and interrupts for each receive queue. Modify ethtool callbacks to report the new statistics. Signed-off-by: John Allen Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 86 +++++++++++++++++++++++++++++++++++++- drivers/net/ethernet/ibm/ibmvnic.h | 17 ++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 9d8af464dc44..b45ade6123a4 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -346,6 +346,31 @@ static void replenish_pools(struct ibmvnic_adapter *adapter) } } +static void release_stats_buffers(struct ibmvnic_adapter *adapter) +{ + kfree(adapter->tx_stats_buffers); + kfree(adapter->rx_stats_buffers); +} + +static int init_stats_buffers(struct ibmvnic_adapter *adapter) +{ + adapter->tx_stats_buffers = + kcalloc(adapter->req_tx_queues, + sizeof(struct ibmvnic_tx_queue_stats), + GFP_KERNEL); + if (!adapter->tx_stats_buffers) + return -ENOMEM; + + adapter->rx_stats_buffers = + kcalloc(adapter->req_rx_queues, + sizeof(struct ibmvnic_rx_queue_stats), + GFP_KERNEL); + if (!adapter->rx_stats_buffers) + return -ENOMEM; + + return 0; +} + static void release_stats_token(struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; @@ -686,6 +711,7 @@ static void release_resources(struct ibmvnic_adapter *adapter) release_rx_pools(adapter); release_stats_token(adapter); + release_stats_buffers(adapter); release_error_buffers(adapter); if (adapter->napi) { @@ -763,6 +789,10 @@ static int init_resources(struct ibmvnic_adapter *adapter) if (rc) return rc; + rc = init_stats_buffers(adapter); + if (rc) + return rc; + rc = init_stats_token(adapter); if (rc) return rc; @@ -1245,6 +1275,9 @@ out: netdev->stats.tx_packets += tx_packets; adapter->tx_send_failed += tx_send_failed; adapter->tx_map_failed += tx_map_failed; + adapter->tx_stats_buffers[queue_num].packets += tx_packets; + adapter->tx_stats_buffers[queue_num].bytes += tx_bytes; + adapter->tx_stats_buffers[queue_num].dropped_packets += tx_dropped; return ret; } @@ -1585,6 +1618,8 @@ restart_poll: napi_gro_receive(napi, skb); /* send it up */ netdev->stats.rx_packets++; netdev->stats.rx_bytes += length; + adapter->rx_stats_buffers[scrq_num].packets++; + adapter->rx_stats_buffers[scrq_num].bytes += length; frames_processed++; } @@ -1706,6 +1741,7 @@ static void ibmvnic_get_ringparam(struct net_device *netdev, static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) { + struct ibmvnic_adapter *adapter = netdev_priv(dev); int i; if (stringset != ETH_SS_STATS) @@ -1713,13 +1749,39 @@ static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++, data += ETH_GSTRING_LEN) memcpy(data, ibmvnic_stats[i].name, ETH_GSTRING_LEN); + + for (i = 0; i < adapter->req_tx_queues; i++) { + snprintf(data, ETH_GSTRING_LEN, "tx%d_packets", i); + data += ETH_GSTRING_LEN; + + snprintf(data, ETH_GSTRING_LEN, "tx%d_bytes", i); + data += ETH_GSTRING_LEN; + + snprintf(data, ETH_GSTRING_LEN, "tx%d_dropped_packets", i); + data += ETH_GSTRING_LEN; + } + + for (i = 0; i < adapter->req_rx_queues; i++) { + snprintf(data, ETH_GSTRING_LEN, "rx%d_packets", i); + data += ETH_GSTRING_LEN; + + snprintf(data, ETH_GSTRING_LEN, "rx%d_bytes", i); + data += ETH_GSTRING_LEN; + + snprintf(data, ETH_GSTRING_LEN, "rx%d_interrupts", i); + data += ETH_GSTRING_LEN; + } } static int ibmvnic_get_sset_count(struct net_device *dev, int sset) { + struct ibmvnic_adapter *adapter = netdev_priv(dev); + switch (sset) { case ETH_SS_STATS: - return ARRAY_SIZE(ibmvnic_stats); + return ARRAY_SIZE(ibmvnic_stats) + + adapter->req_tx_queues * NUM_TX_STATS + + adapter->req_rx_queues * NUM_RX_STATS; default: return -EOPNOTSUPP; } @@ -1730,7 +1792,7 @@ static void ibmvnic_get_ethtool_stats(struct net_device *dev, { struct ibmvnic_adapter *adapter = netdev_priv(dev); union ibmvnic_crq crq; - int i; + int i, j; memset(&crq, 0, sizeof(crq)); crq.request_statistics.first = IBMVNIC_CRQ_CMD; @@ -1746,6 +1808,24 @@ static void ibmvnic_get_ethtool_stats(struct net_device *dev, for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++) data[i] = IBMVNIC_GET_STAT(adapter, ibmvnic_stats[i].offset); + + for (j = 0; j < adapter->req_tx_queues; j++) { + data[i] = adapter->tx_stats_buffers[j].packets; + i++; + data[i] = adapter->tx_stats_buffers[j].bytes; + i++; + data[i] = adapter->tx_stats_buffers[j].dropped_packets; + i++; + } + + for (j = 0; j < adapter->req_rx_queues; j++) { + data[i] = adapter->rx_stats_buffers[j].packets; + i++; + data[i] = adapter->rx_stats_buffers[j].bytes; + i++; + data[i] = adapter->rx_stats_buffers[j].interrupts; + i++; + } } static const struct ethtool_ops ibmvnic_ethtool_ops = { @@ -2050,6 +2130,8 @@ static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance) struct ibmvnic_sub_crq_queue *scrq = instance; struct ibmvnic_adapter *adapter = scrq->adapter; + adapter->rx_stats_buffers[scrq->scrq_num].interrupts++; + if (napi_schedule_prep(&adapter->napi[scrq->scrq_num])) { disable_scrq_irq(adapter, scrq); __napi_schedule(&adapter->napi[scrq->scrq_num]); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 8eff6e15f4bb..d02257ccc377 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -166,6 +166,20 @@ struct ibmvnic_statistics { u8 reserved[72]; } __packed __aligned(8); +#define NUM_TX_STATS 3 +struct ibmvnic_tx_queue_stats { + u64 packets; + u64 bytes; + u64 dropped_packets; +}; + +#define NUM_RX_STATS 3 +struct ibmvnic_rx_queue_stats { + u64 packets; + u64 bytes; + u64 interrupts; +}; + struct ibmvnic_acl_buffer { __be32 len; __be32 version; @@ -956,6 +970,9 @@ struct ibmvnic_adapter { int tx_send_failed; int tx_map_failed; + struct ibmvnic_tx_queue_stats *tx_stats_buffers; + struct ibmvnic_rx_queue_stats *rx_stats_buffers; + int phys_link_state; int logical_link_state; -- cgit v1.2.3 From 52da5c114a884f8245dca5d3a69e1f45c759ba31 Mon Sep 17 00:00:00 2001 From: John Allen Date: Wed, 2 Aug 2017 16:45:28 -0500 Subject: ibmvnic: Convert vnic server reported statistics to cpu endian The vnic server reports the statistics buffer in big endian format and must be converted to cpu endian in order to be displayed correctly on little endian lpars. Signed-off-by: John Allen Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index b45ade6123a4..ea0ff2841a1f 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1807,7 +1807,8 @@ static void ibmvnic_get_ethtool_stats(struct net_device *dev, wait_for_completion(&adapter->stats_done); for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++) - data[i] = IBMVNIC_GET_STAT(adapter, ibmvnic_stats[i].offset); + data[i] = be64_to_cpu(IBMVNIC_GET_STAT(adapter, + ibmvnic_stats[i].offset)); for (j = 0; j < adapter->req_tx_queues; j++) { data[i] = adapter->tx_stats_buffers[j].packets; -- cgit v1.2.3 From bc131b3a4a92290d6142e930622d6f97d2504241 Mon Sep 17 00:00:00 2001 From: John Allen Date: Wed, 2 Aug 2017 16:46:30 -0500 Subject: ibmvnic: Implement .get_ringparam Implement .get_ringparam (ethtool -g) functionality Signed-off-by: John Allen Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index ea0ff2841a1f..c40456a399dd 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1729,12 +1729,14 @@ static u32 ibmvnic_get_link(struct net_device *netdev) static void ibmvnic_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { - ring->rx_max_pending = 0; - ring->tx_max_pending = 0; + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + + ring->rx_max_pending = adapter->max_rx_add_entries_per_subcrq; + ring->tx_max_pending = adapter->max_tx_entries_per_subcrq; ring->rx_mini_max_pending = 0; ring->rx_jumbo_max_pending = 0; - ring->rx_pending = 0; - ring->tx_pending = 0; + ring->rx_pending = adapter->req_rx_add_entries_per_subcrq; + ring->tx_pending = adapter->req_tx_entries_per_subcrq; ring->rx_mini_pending = 0; ring->rx_jumbo_pending = 0; } -- cgit v1.2.3 From c2dbeb671f44c526b7ba8808d4a8a617a36c7516 Mon Sep 17 00:00:00 2001 From: John Allen Date: Wed, 2 Aug 2017 16:47:17 -0500 Subject: ibmvnic: Implement .get_channels Implement .get_channels (ethtool -l) functionality Signed-off-by: John Allen Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index c40456a399dd..5932160eb815 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1741,6 +1741,21 @@ static void ibmvnic_get_ringparam(struct net_device *netdev, ring->rx_jumbo_pending = 0; } +static void ibmvnic_get_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + + channels->max_rx = adapter->max_rx_queues; + channels->max_tx = adapter->max_tx_queues; + channels->max_other = 0; + channels->max_combined = 0; + channels->rx_count = adapter->req_rx_queues; + channels->tx_count = adapter->req_tx_queues; + channels->other_count = 0; + channels->combined_count = 0; +} + static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) { struct ibmvnic_adapter *adapter = netdev_priv(dev); @@ -1837,6 +1852,7 @@ static const struct ethtool_ops ibmvnic_ethtool_ops = { .set_msglevel = ibmvnic_set_msglevel, .get_link = ibmvnic_get_link, .get_ringparam = ibmvnic_get_ringparam, + .get_channels = ibmvnic_get_channels, .get_strings = ibmvnic_get_strings, .get_sset_count = ibmvnic_get_sset_count, .get_ethtool_stats = ibmvnic_get_ethtool_stats, -- cgit v1.2.3 From 76ad4f0ee74758341e44a8871df6ca60906d2795 Mon Sep 17 00:00:00 2001 From: Salil Date: Wed, 2 Aug 2017 16:59:45 +0100 Subject: net: hns3: Add support of HNS3 Ethernet Driver for hip08 SoC This patch adds the support of Hisilicon Network Subsystem 3 Ethernet driver to hip08 family of SoCs. This driver includes basic Rx/Tx functionality. It also includes the client registration code with the HNAE3(Hisilicon Network Acceleration Engine 3) framework. This work provides the initial support to the hip08 SoC and would incrementally add features or enhancements. Signed-off-by: Daode Huang Signed-off-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: Yisen Zhuang Signed-off-by: Wei Hu (Xavier) Signed-off-by: David S. Miller --- .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 2848 ++++++++++++++++++++ .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h | 592 ++++ 2 files changed, 3440 insertions(+) create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c new file mode 100644 index 000000000000..ad9481c7ceae --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -0,0 +1,2848 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hnae3.h" +#include "hns3_enet.h" + +const char hns3_driver_name[] = "hns3"; +const char hns3_driver_version[] = VERMAGIC_STRING; +static const char hns3_driver_string[] = + "Hisilicon Ethernet Network Driver for Hip08 Family"; +static const char hns3_copyright[] = "Copyright (c) 2017 Huawei Corporation."; +static struct hnae3_client client; + +/* hns3_pci_tbl - PCI Device ID Table + * + * Last entry must be all 0s + * + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, + * Class, Class Mask, private data (not used) } + */ +static const struct pci_device_id hns3_pci_tbl[] = { + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), 0}, + /* required last entry */ + {0, } +}; +MODULE_DEVICE_TABLE(pci, hns3_pci_tbl); + +static irqreturn_t hns3_irq_handle(int irq, void *dev) +{ + struct hns3_enet_tqp_vector *tqp_vector = dev; + + napi_schedule(&tqp_vector->napi); + + return IRQ_HANDLED; +} + +static void hns3_nic_uninit_irq(struct hns3_nic_priv *priv) +{ + struct hns3_enet_tqp_vector *tqp_vectors; + unsigned int i; + + for (i = 0; i < priv->vector_num; i++) { + tqp_vectors = &priv->tqp_vector[i]; + + if (tqp_vectors->irq_init_flag != HNS3_VECTOR_INITED) + continue; + + /* release the irq resource */ + free_irq(tqp_vectors->vector_irq, tqp_vectors); + tqp_vectors->irq_init_flag = HNS3_VECTOR_NOT_INITED; + } +} + +static int hns3_nic_init_irq(struct hns3_nic_priv *priv) +{ + struct hns3_enet_tqp_vector *tqp_vectors; + int txrx_int_idx = 0; + int rx_int_idx = 0; + int tx_int_idx = 0; + unsigned int i; + int ret; + + for (i = 0; i < priv->vector_num; i++) { + tqp_vectors = &priv->tqp_vector[i]; + + if (tqp_vectors->irq_init_flag == HNS3_VECTOR_INITED) + continue; + + if (tqp_vectors->tx_group.ring && tqp_vectors->rx_group.ring) { + snprintf(tqp_vectors->name, HNAE3_INT_NAME_LEN - 1, + "%s-%s-%d", priv->netdev->name, "TxRx", + txrx_int_idx++); + txrx_int_idx++; + } else if (tqp_vectors->rx_group.ring) { + snprintf(tqp_vectors->name, HNAE3_INT_NAME_LEN - 1, + "%s-%s-%d", priv->netdev->name, "Rx", + rx_int_idx++); + } else if (tqp_vectors->tx_group.ring) { + snprintf(tqp_vectors->name, HNAE3_INT_NAME_LEN - 1, + "%s-%s-%d", priv->netdev->name, "Tx", + tx_int_idx++); + } else { + /* Skip this unused q_vector */ + continue; + } + + tqp_vectors->name[HNAE3_INT_NAME_LEN - 1] = '\0'; + + ret = request_irq(tqp_vectors->vector_irq, hns3_irq_handle, 0, + tqp_vectors->name, + tqp_vectors); + if (ret) { + netdev_err(priv->netdev, "request irq(%d) fail\n", + tqp_vectors->vector_irq); + return ret; + } + + tqp_vectors->irq_init_flag = HNS3_VECTOR_INITED; + } + + return 0; +} + +static void hns3_mask_vector_irq(struct hns3_enet_tqp_vector *tqp_vector, + u32 mask_en) +{ + writel(mask_en, tqp_vector->mask_addr); +} + +static void hns3_vector_enable(struct hns3_enet_tqp_vector *tqp_vector) +{ + napi_enable(&tqp_vector->napi); + + /* enable vector */ + hns3_mask_vector_irq(tqp_vector, 1); +} + +static void hns3_vector_disable(struct hns3_enet_tqp_vector *tqp_vector) +{ + /* disable vector */ + hns3_mask_vector_irq(tqp_vector, 0); + + disable_irq(tqp_vector->vector_irq); + napi_disable(&tqp_vector->napi); +} + +static void hns3_set_vector_coalesc_gl(struct hns3_enet_tqp_vector *tqp_vector, + u32 gl_value) +{ + /* this defines the configuration for GL (Interrupt Gap Limiter) + * GL defines inter interrupt gap. + * GL and RL(Rate Limiter) are 2 ways to acheive interrupt coalescing + */ + writel(gl_value, tqp_vector->mask_addr + HNS3_VECTOR_GL0_OFFSET); + writel(gl_value, tqp_vector->mask_addr + HNS3_VECTOR_GL1_OFFSET); + writel(gl_value, tqp_vector->mask_addr + HNS3_VECTOR_GL2_OFFSET); +} + +static void hns3_set_vector_coalesc_rl(struct hns3_enet_tqp_vector *tqp_vector, + u32 rl_value) +{ + /* this defines the configuration for RL (Interrupt Rate Limiter). + * Rl defines rate of interrupts i.e. number of interrupts-per-second + * GL and RL(Rate Limiter) are 2 ways to acheive interrupt coalescing + */ + writel(rl_value, tqp_vector->mask_addr + HNS3_VECTOR_RL_OFFSET); +} + +static void hns3_vector_gl_rl_init(struct hns3_enet_tqp_vector *tqp_vector) +{ + /* initialize the configuration for interrupt coalescing. + * 1. GL (Interrupt Gap Limiter) + * 2. RL (Interrupt Rate Limiter) + */ + + /* Default :enable interrupt coalesce */ + tqp_vector->rx_group.int_gl = HNS3_INT_GL_50K; + tqp_vector->tx_group.int_gl = HNS3_INT_GL_50K; + hns3_set_vector_coalesc_gl(tqp_vector, HNS3_INT_GL_50K); + /* for now we are disabling Interrupt RL - we + * will re-enable later + */ + hns3_set_vector_coalesc_rl(tqp_vector, 0); + tqp_vector->rx_group.flow_level = HNS3_FLOW_LOW; + tqp_vector->tx_group.flow_level = HNS3_FLOW_LOW; +} + +static int hns3_nic_net_up(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int i, j; + int ret; + + /* get irq resource for all vectors */ + ret = hns3_nic_init_irq(priv); + if (ret) { + netdev_err(netdev, "hns init irq failed! ret=%d\n", ret); + return ret; + } + + /* enable the vectors */ + for (i = 0; i < priv->vector_num; i++) + hns3_vector_enable(&priv->tqp_vector[i]); + + /* start the ae_dev */ + ret = h->ae_algo->ops->start ? h->ae_algo->ops->start(h) : 0; + if (ret) + goto out_start_err; + + return 0; + +out_start_err: + for (j = i - 1; j >= 0; j--) + hns3_vector_disable(&priv->tqp_vector[j]); + + hns3_nic_uninit_irq(priv); + + return ret; +} + +static int hns3_nic_net_open(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int ret; + + netif_carrier_off(netdev); + + ret = netif_set_real_num_tx_queues(netdev, h->kinfo.num_tqps); + if (ret) { + netdev_err(netdev, + "netif_set_real_num_tx_queues fail, ret=%d!\n", + ret); + return ret; + } + + ret = netif_set_real_num_rx_queues(netdev, h->kinfo.num_tqps); + if (ret) { + netdev_err(netdev, + "netif_set_real_num_rx_queues fail, ret=%d!\n", ret); + return ret; + } + + ret = hns3_nic_net_up(netdev); + if (ret) { + netdev_err(netdev, + "hns net up fail, ret=%d!\n", ret); + return ret; + } + + return 0; +} + +static void hns3_nic_net_down(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + const struct hnae3_ae_ops *ops; + int i; + + /* stop ae_dev */ + ops = priv->ae_handle->ae_algo->ops; + if (ops->stop) + ops->stop(priv->ae_handle); + + /* disable vectors */ + for (i = 0; i < priv->vector_num; i++) + hns3_vector_disable(&priv->tqp_vector[i]); + + /* free irq resources */ + hns3_nic_uninit_irq(priv); +} + +static int hns3_nic_net_stop(struct net_device *netdev) +{ + netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); + + hns3_nic_net_down(netdev); + + return 0; +} + +void hns3_set_multicast_list(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + struct netdev_hw_addr *ha = NULL; + + if (h->ae_algo->ops->set_mc_addr) { + netdev_for_each_mc_addr(ha, netdev) + if (h->ae_algo->ops->set_mc_addr(h, ha->addr)) + netdev_err(netdev, "set multicast fail\n"); + } +} + +static int hns3_nic_uc_sync(struct net_device *netdev, + const unsigned char *addr) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->add_uc_addr) + return h->ae_algo->ops->add_uc_addr(h, addr); + + return 0; +} + +static int hns3_nic_uc_unsync(struct net_device *netdev, + const unsigned char *addr) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->rm_uc_addr) + return h->ae_algo->ops->rm_uc_addr(h, addr); + + return 0; +} + +static int hns3_nic_mc_sync(struct net_device *netdev, + const unsigned char *addr) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->add_uc_addr) + return h->ae_algo->ops->add_mc_addr(h, addr); + + return 0; +} + +static int hns3_nic_mc_unsync(struct net_device *netdev, + const unsigned char *addr) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->rm_uc_addr) + return h->ae_algo->ops->rm_mc_addr(h, addr); + + return 0; +} + +void hns3_nic_set_rx_mode(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->set_promisc_mode) { + if (netdev->flags & IFF_PROMISC) + h->ae_algo->ops->set_promisc_mode(h, 1); + else + h->ae_algo->ops->set_promisc_mode(h, 0); + } + if (__dev_uc_sync(netdev, hns3_nic_uc_sync, hns3_nic_uc_unsync)) + netdev_err(netdev, "sync uc address fail\n"); + if (netdev->flags & IFF_MULTICAST) + if (__dev_mc_sync(netdev, hns3_nic_mc_sync, hns3_nic_mc_unsync)) + netdev_err(netdev, "sync mc address fail\n"); +} + +static int hns3_set_tso(struct sk_buff *skb, u32 *paylen, + u16 *mss, u32 *type_cs_vlan_tso) +{ + u32 l4_offset, hdr_len; + union l3_hdr_info l3; + union l4_hdr_info l4; + u32 l4_paylen; + int ret; + + if (!skb_is_gso(skb)) + return 0; + + ret = skb_cow_head(skb, 0); + if (ret) + return ret; + + l3.hdr = skb_network_header(skb); + l4.hdr = skb_transport_header(skb); + + /* Software should clear the IPv4's checksum field when tso is + * needed. + */ + if (l3.v4->version == 4) + l3.v4->check = 0; + + /* tunnel packet.*/ + if (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE | + SKB_GSO_GRE_CSUM | + SKB_GSO_UDP_TUNNEL | + SKB_GSO_UDP_TUNNEL_CSUM)) { + if ((!(skb_shinfo(skb)->gso_type & + SKB_GSO_PARTIAL)) && + (skb_shinfo(skb)->gso_type & + SKB_GSO_UDP_TUNNEL_CSUM)) { + /* Software should clear the udp's checksum + * field when tso is needed. + */ + l4.udp->check = 0; + } + /* reset l3&l4 pointers from outer to inner headers */ + l3.hdr = skb_inner_network_header(skb); + l4.hdr = skb_inner_transport_header(skb); + + /* Software should clear the IPv4's checksum field when + * tso is needed. + */ + if (l3.v4->version == 4) + l3.v4->check = 0; + } + + /* normal or tunnel packet*/ + l4_offset = l4.hdr - skb->data; + hdr_len = (l4.tcp->doff * 4) + l4_offset; + + /* remove payload length from inner pseudo checksum when tso*/ + l4_paylen = skb->len - l4_offset; + csum_replace_by_diff(&l4.tcp->check, + (__force __wsum)htonl(l4_paylen)); + + /* find the txbd field values */ + *paylen = skb->len - hdr_len; + hnae_set_bit(*type_cs_vlan_tso, + HNS3_TXD_TSO_B, 1); + + /* get MSS for TSO */ + *mss = skb_shinfo(skb)->gso_size; + + return 0; +} + +static void hns3_get_l4_protocol(struct sk_buff *skb, u8 *ol4_proto, + u8 *il4_proto) +{ + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } l3; + unsigned char *l4_hdr; + unsigned char *exthdr; + u8 l4_proto_tmp; + __be16 frag_off; + + /* find outer header point */ + l3.hdr = skb_network_header(skb); + l4_hdr = skb_inner_transport_header(skb); + + if (skb->protocol == htons(ETH_P_IPV6)) { + exthdr = l3.hdr + sizeof(*l3.v6); + l4_proto_tmp = l3.v6->nexthdr; + if (l4_hdr != exthdr) + ipv6_skip_exthdr(skb, exthdr - skb->data, + &l4_proto_tmp, &frag_off); + } else if (skb->protocol == htons(ETH_P_IP)) { + l4_proto_tmp = l3.v4->protocol; + } + + *ol4_proto = l4_proto_tmp; + + /* tunnel packet */ + if (!skb->encapsulation) { + *il4_proto = 0; + return; + } + + /* find inner header point */ + l3.hdr = skb_inner_network_header(skb); + l4_hdr = skb_inner_transport_header(skb); + + if (l3.v6->version == 6) { + exthdr = l3.hdr + sizeof(*l3.v6); + l4_proto_tmp = l3.v6->nexthdr; + if (l4_hdr != exthdr) + ipv6_skip_exthdr(skb, exthdr - skb->data, + &l4_proto_tmp, &frag_off); + } else if (l3.v4->version == 4) { + l4_proto_tmp = l3.v4->protocol; + } + + *il4_proto = l4_proto_tmp; +} + +static void hns3_set_l2l3l4_len(struct sk_buff *skb, u8 ol4_proto, + u8 il4_proto, u32 *type_cs_vlan_tso, + u32 *ol_type_vlan_len_msec) +{ + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } l3; + union { + struct tcphdr *tcp; + struct udphdr *udp; + struct gre_base_hdr *gre; + unsigned char *hdr; + } l4; + unsigned char *l2_hdr; + u8 l4_proto = ol4_proto; + u32 ol2_len; + u32 ol3_len; + u32 ol4_len; + u32 l2_len; + u32 l3_len; + + l3.hdr = skb_network_header(skb); + l4.hdr = skb_transport_header(skb); + + /* compute L2 header size for normal packet, defined in 2 Bytes */ + l2_len = l3.hdr - skb->data; + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L2LEN_M, + HNS3_TXD_L2LEN_S, l2_len >> 1); + + /* tunnel packet*/ + if (skb->encapsulation) { + /* compute OL2 header size, defined in 2 Bytes */ + ol2_len = l2_len; + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_L2LEN_M, + HNS3_TXD_L2LEN_S, ol2_len >> 1); + + /* compute OL3 header size, defined in 4 Bytes */ + ol3_len = l4.hdr - l3.hdr; + hnae_set_field(*ol_type_vlan_len_msec, HNS3_TXD_L3LEN_M, + HNS3_TXD_L3LEN_S, ol3_len >> 2); + + /* MAC in UDP, MAC in GRE (0x6558)*/ + if ((ol4_proto == IPPROTO_UDP) || (ol4_proto == IPPROTO_GRE)) { + /* switch MAC header ptr from outer to inner header.*/ + l2_hdr = skb_inner_mac_header(skb); + + /* compute OL4 header size, defined in 4 Bytes. */ + ol4_len = l2_hdr - l4.hdr; + hnae_set_field(*ol_type_vlan_len_msec, HNS3_TXD_L4LEN_M, + HNS3_TXD_L4LEN_S, ol4_len >> 2); + + /* switch IP header ptr from outer to inner header */ + l3.hdr = skb_inner_network_header(skb); + + /* compute inner l2 header size, defined in 2 Bytes. */ + l2_len = l3.hdr - l2_hdr; + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L2LEN_M, + HNS3_TXD_L2LEN_S, l2_len >> 1); + } else { + /* skb packet types not supported by hardware, + * txbd len fild doesn't be filled. + */ + return; + } + + /* switch L4 header pointer from outer to inner */ + l4.hdr = skb_inner_transport_header(skb); + + l4_proto = il4_proto; + } + + /* compute inner(/normal) L3 header size, defined in 4 Bytes */ + l3_len = l4.hdr - l3.hdr; + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L3LEN_M, + HNS3_TXD_L3LEN_S, l3_len >> 2); + + /* compute inner(/normal) L4 header size, defined in 4 Bytes */ + switch (l4_proto) { + case IPPROTO_TCP: + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L4LEN_M, + HNS3_TXD_L4LEN_S, l4.tcp->doff); + break; + case IPPROTO_SCTP: + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L4LEN_M, + HNS3_TXD_L4LEN_S, (sizeof(struct sctphdr) >> 2)); + break; + case IPPROTO_UDP: + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L4LEN_M, + HNS3_TXD_L4LEN_S, (sizeof(struct udphdr) >> 2)); + break; + default: + /* skb packet types not supported by hardware, + * txbd len fild doesn't be filled. + */ + return; + } +} + +static int hns3_set_l3l4_type_csum(struct sk_buff *skb, u8 ol4_proto, + u8 il4_proto, u32 *type_cs_vlan_tso, + u32 *ol_type_vlan_len_msec) +{ + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } l3; + u32 l4_proto = ol4_proto; + + l3.hdr = skb_network_header(skb); + + /* define OL3 type and tunnel type(OL4).*/ + if (skb->encapsulation) { + /* define outer network header type.*/ + if (skb->protocol == htons(ETH_P_IP)) { + if (skb_is_gso(skb)) + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_OL3T_M, HNS3_TXD_OL3T_S, + HNS3_OL3T_IPV4_CSUM); + else + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_OL3T_M, HNS3_TXD_OL3T_S, + HNS3_OL3T_IPV4_NO_CSUM); + + } else if (skb->protocol == htons(ETH_P_IPV6)) { + hnae_set_field(*ol_type_vlan_len_msec, HNS3_TXD_OL3T_M, + HNS3_TXD_OL3T_S, HNS3_OL3T_IPV6); + } + + /* define tunnel type(OL4).*/ + switch (l4_proto) { + case IPPROTO_UDP: + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_TUNTYPE_M, + HNS3_TXD_TUNTYPE_S, + HNS3_TUN_MAC_IN_UDP); + break; + case IPPROTO_GRE: + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_TUNTYPE_M, + HNS3_TXD_TUNTYPE_S, + HNS3_TUN_NVGRE); + break; + default: + /* drop the skb tunnel packet if hardware don't support, + * because hardware can't calculate csum when TSO. + */ + if (skb_is_gso(skb)) + return -EDOM; + + /* the stack computes the IP header already, + * driver calculate l4 checksum when not TSO. + */ + skb_checksum_help(skb); + return 0; + } + + l3.hdr = skb_inner_network_header(skb); + l4_proto = il4_proto; + } + + if (l3.v4->version == 4) { + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L3T_M, + HNS3_TXD_L3T_S, HNS3_L3T_IPV4); + + /* the stack computes the IP header already, the only time we + * need the hardware to recompute it is in the case of TSO. + */ + if (skb_is_gso(skb)) + hnae_set_bit(*type_cs_vlan_tso, HNS3_TXD_L3CS_B, 1); + + hnae_set_bit(*type_cs_vlan_tso, HNS3_TXD_L4CS_B, 1); + } else if (l3.v6->version == 6) { + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L3T_M, + HNS3_TXD_L3T_S, HNS3_L3T_IPV6); + hnae_set_bit(*type_cs_vlan_tso, HNS3_TXD_L4CS_B, 1); + } + + switch (l4_proto) { + case IPPROTO_TCP: + hnae_set_field(*type_cs_vlan_tso, + HNS3_TXD_L4T_M, + HNS3_TXD_L4T_S, + HNS3_L4T_TCP); + break; + case IPPROTO_UDP: + hnae_set_field(*type_cs_vlan_tso, + HNS3_TXD_L4T_M, + HNS3_TXD_L4T_S, + HNS3_L4T_UDP); + break; + case IPPROTO_SCTP: + hnae_set_field(*type_cs_vlan_tso, + HNS3_TXD_L4T_M, + HNS3_TXD_L4T_S, + HNS3_L4T_SCTP); + break; + default: + /* drop the skb tunnel packet if hardware don't support, + * because hardware can't calculate csum when TSO. + */ + if (skb_is_gso(skb)) + return -EDOM; + + /* the stack computes the IP header already, + * driver calculate l4 checksum when not TSO. + */ + skb_checksum_help(skb); + return 0; + } + + return 0; +} + +static void hns3_set_txbd_baseinfo(u16 *bdtp_fe_sc_vld_ra_ri, int frag_end) +{ + /* Config bd buffer end */ + hnae_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_BDTYPE_M, + HNS3_TXD_BDTYPE_M, 0); + hnae_set_bit(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_FE_B, !!frag_end); + hnae_set_bit(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_VLD_B, 1); + hnae_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_SC_M, HNS3_TXD_SC_S, 1); +} + +static int hns3_fill_desc(struct hns3_enet_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + enum hns_desc_type type) +{ + struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use]; + struct hns3_desc *desc = &ring->desc[ring->next_to_use]; + u32 ol_type_vlan_len_msec = 0; + u16 bdtp_fe_sc_vld_ra_ri = 0; + u32 type_cs_vlan_tso = 0; + struct sk_buff *skb; + u32 paylen = 0; + u16 mss = 0; + __be16 protocol; + u8 ol4_proto; + u8 il4_proto; + int ret; + + /* The txbd's baseinfo of DESC_TYPE_PAGE & DESC_TYPE_SKB */ + desc_cb->priv = priv; + desc_cb->length = size; + desc_cb->dma = dma; + desc_cb->type = type; + + /* now, fill the descriptor */ + desc->addr = cpu_to_le64(dma); + desc->tx.send_size = cpu_to_le16((u16)size); + hns3_set_txbd_baseinfo(&bdtp_fe_sc_vld_ra_ri, frag_end); + desc->tx.bdtp_fe_sc_vld_ra_ri = cpu_to_le16(bdtp_fe_sc_vld_ra_ri); + + if (type == DESC_TYPE_SKB) { + skb = (struct sk_buff *)priv; + paylen = cpu_to_le16(skb->len); + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + skb_reset_mac_len(skb); + protocol = skb->protocol; + + /* vlan packet*/ + if (protocol == htons(ETH_P_8021Q)) { + protocol = vlan_get_protocol(skb); + skb->protocol = protocol; + } + hns3_get_l4_protocol(skb, &ol4_proto, &il4_proto); + hns3_set_l2l3l4_len(skb, ol4_proto, il4_proto, + &type_cs_vlan_tso, + &ol_type_vlan_len_msec); + ret = hns3_set_l3l4_type_csum(skb, ol4_proto, il4_proto, + &type_cs_vlan_tso, + &ol_type_vlan_len_msec); + if (ret) + return ret; + + ret = hns3_set_tso(skb, &paylen, &mss, + &type_cs_vlan_tso); + if (ret) + return ret; + } + + /* Set txbd */ + desc->tx.ol_type_vlan_len_msec = + cpu_to_le32(ol_type_vlan_len_msec); + desc->tx.type_cs_vlan_tso_len = + cpu_to_le32(type_cs_vlan_tso); + desc->tx.paylen = cpu_to_le16(paylen); + desc->tx.mss = cpu_to_le16(mss); + } + + /* move ring pointer to next.*/ + ring_ptr_move_fw(ring, next_to_use); + + return 0; +} + +static int hns3_fill_desc_tso(struct hns3_enet_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + enum hns_desc_type type) +{ + unsigned int frag_buf_num; + unsigned int k; + int sizeoflast; + int ret; + + frag_buf_num = (size + HNS3_MAX_BD_SIZE - 1) / HNS3_MAX_BD_SIZE; + sizeoflast = size % HNS3_MAX_BD_SIZE; + sizeoflast = sizeoflast ? sizeoflast : HNS3_MAX_BD_SIZE; + + /* When the frag size is bigger than hardware, split this frag */ + for (k = 0; k < frag_buf_num; k++) { + ret = hns3_fill_desc(ring, priv, + (k == frag_buf_num - 1) ? + sizeoflast : HNS3_MAX_BD_SIZE, + dma + HNS3_MAX_BD_SIZE * k, + frag_end && (k == frag_buf_num - 1) ? 1 : 0, + (type == DESC_TYPE_SKB && !k) ? + DESC_TYPE_SKB : DESC_TYPE_PAGE); + if (ret) + return ret; + } + + return 0; +} + +static int hns3_nic_maybe_stop_tso(struct sk_buff **out_skb, int *bnum, + struct hns3_enet_ring *ring) +{ + struct sk_buff *skb = *out_skb; + struct skb_frag_struct *frag; + int bdnum_for_frag; + int frag_num; + int buf_num; + int size; + int i; + + size = skb_headlen(skb); + buf_num = (size + HNS3_MAX_BD_SIZE - 1) / HNS3_MAX_BD_SIZE; + + frag_num = skb_shinfo(skb)->nr_frags; + for (i = 0; i < frag_num; i++) { + frag = &skb_shinfo(skb)->frags[i]; + size = skb_frag_size(frag); + bdnum_for_frag = + (size + HNS3_MAX_BD_SIZE - 1) / HNS3_MAX_BD_SIZE; + if (bdnum_for_frag > HNS3_MAX_BD_PER_FRAG) + return -ENOMEM; + + buf_num += bdnum_for_frag; + } + + if (buf_num > ring_space(ring)) + return -EBUSY; + + *bnum = buf_num; + return 0; +} + +static int hns3_nic_maybe_stop_tx(struct sk_buff **out_skb, int *bnum, + struct hns3_enet_ring *ring) +{ + struct sk_buff *skb = *out_skb; + int buf_num; + + /* No. of segments (plus a header) */ + buf_num = skb_shinfo(skb)->nr_frags + 1; + + if (buf_num > ring_space(ring)) + return -EBUSY; + + *bnum = buf_num; + + return 0; +} + +static void hns_nic_dma_unmap(struct hns3_enet_ring *ring, int next_to_use_orig) +{ + struct device *dev = ring_to_dev(ring); + unsigned int i; + + for (i = 0; i < ring->desc_num; i++) { + /* check if this is where we started */ + if (ring->next_to_use == next_to_use_orig) + break; + + /* unmap the descriptor dma address */ + if (ring->desc_cb[ring->next_to_use].type == DESC_TYPE_SKB) + dma_unmap_single(dev, + ring->desc_cb[ring->next_to_use].dma, + ring->desc_cb[ring->next_to_use].length, + DMA_TO_DEVICE); + else + dma_unmap_page(dev, + ring->desc_cb[ring->next_to_use].dma, + ring->desc_cb[ring->next_to_use].length, + DMA_TO_DEVICE); + + /* rollback one */ + ring_ptr_move_bw(ring, next_to_use); + } +} + +static netdev_tx_t hns3_nic_net_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hns3_nic_ring_data *ring_data = + &tx_ring_data(priv, skb->queue_mapping); + struct hns3_enet_ring *ring = ring_data->ring; + struct device *dev = priv->dev; + struct netdev_queue *dev_queue; + struct skb_frag_struct *frag; + int next_to_use_head; + int next_to_use_frag; + dma_addr_t dma; + int buf_num; + int seg_num; + int size; + int ret; + int i; + + /* Prefetch the data used later */ + prefetch(skb->data); + + switch (priv->ops.maybe_stop_tx(&skb, &buf_num, ring)) { + case -EBUSY: + u64_stats_update_begin(&ring->syncp); + ring->stats.tx_busy++; + u64_stats_update_end(&ring->syncp); + + goto out_net_tx_busy; + case -ENOMEM: + u64_stats_update_begin(&ring->syncp); + ring->stats.sw_err_cnt++; + u64_stats_update_end(&ring->syncp); + netdev_err(netdev, "no memory to xmit!\n"); + + goto out_err_tx_ok; + default: + break; + } + + /* No. of segments (plus a header) */ + seg_num = skb_shinfo(skb)->nr_frags + 1; + /* Fill the first part */ + size = skb_headlen(skb); + + next_to_use_head = ring->next_to_use; + + dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(netdev, "TX head DMA map failed\n"); + ring->stats.sw_err_cnt++; + goto out_err_tx_ok; + } + + ret = priv->ops.fill_desc(ring, skb, size, dma, seg_num == 1 ? 1 : 0, + DESC_TYPE_SKB); + if (ret) + goto head_dma_map_err; + + next_to_use_frag = ring->next_to_use; + /* Fill the fragments */ + for (i = 1; i < seg_num; i++) { + frag = &skb_shinfo(skb)->frags[i - 1]; + size = skb_frag_size(frag); + dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(netdev, "TX frag(%d) DMA map failed\n", i); + ring->stats.sw_err_cnt++; + goto frag_dma_map_err; + } + ret = priv->ops.fill_desc(ring, skb_frag_page(frag), size, dma, + seg_num - 1 == i ? 1 : 0, + DESC_TYPE_PAGE); + + if (ret) + goto frag_dma_map_err; + } + + /* Complete translate all packets */ + dev_queue = netdev_get_tx_queue(netdev, ring_data->queue_index); + netdev_tx_sent_queue(dev_queue, skb->len); + + wmb(); /* Commit all data before submit */ + + hnae_queue_xmit(ring->tqp, buf_num); + + return NETDEV_TX_OK; + +frag_dma_map_err: + hns_nic_dma_unmap(ring, next_to_use_frag); + +head_dma_map_err: + hns_nic_dma_unmap(ring, next_to_use_head); + +out_err_tx_ok: + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + +out_net_tx_busy: + netif_stop_subqueue(netdev, ring_data->queue_index); + smp_mb(); /* Commit all data before submit */ + + return NETDEV_TX_BUSY; +} + +static int hns3_nic_net_set_mac_address(struct net_device *netdev, void *p) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + struct sockaddr *mac_addr = p; + int ret; + + if (!mac_addr || !is_valid_ether_addr((const u8 *)mac_addr->sa_data)) + return -EADDRNOTAVAIL; + + ret = h->ae_algo->ops->set_mac_addr(h, mac_addr->sa_data); + if (ret) { + netdev_err(netdev, "set_mac_address fail, ret=%d!\n", ret); + return ret; + } + + ether_addr_copy(netdev->dev_addr, mac_addr->sa_data); + + return 0; +} + +static int hns3_nic_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + + if (features & (NETIF_F_TSO | NETIF_F_TSO6)) { + priv->ops.fill_desc = hns3_fill_desc_tso; + priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tso; + } else { + priv->ops.fill_desc = hns3_fill_desc; + priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tx; + } + + netdev->features = features; + return 0; +} + +static void +hns3_nic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + int queue_num = priv->ae_handle->kinfo.num_tqps; + struct hns3_enet_ring *ring; + unsigned int start; + unsigned int idx; + u64 tx_bytes = 0; + u64 rx_bytes = 0; + u64 tx_pkts = 0; + u64 rx_pkts = 0; + + for (idx = 0; idx < queue_num; idx++) { + /* fetch the tx stats */ + ring = priv->ring_data[idx].ring; + do { + tx_bytes += ring->stats.tx_bytes; + tx_pkts += ring->stats.tx_pkts; + } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + + /* fetch the rx stats */ + ring = priv->ring_data[idx + queue_num].ring; + do { + rx_bytes += ring->stats.rx_bytes; + rx_pkts += ring->stats.rx_pkts; + } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } + + stats->tx_bytes = tx_bytes; + stats->tx_packets = tx_pkts; + stats->rx_bytes = rx_bytes; + stats->rx_packets = rx_pkts; + + stats->rx_errors = netdev->stats.rx_errors; + stats->multicast = netdev->stats.multicast; + stats->rx_length_errors = netdev->stats.rx_length_errors; + stats->rx_crc_errors = netdev->stats.rx_crc_errors; + stats->rx_missed_errors = netdev->stats.rx_missed_errors; + + stats->tx_errors = netdev->stats.tx_errors; + stats->rx_dropped = netdev->stats.rx_dropped; + stats->tx_dropped = netdev->stats.tx_dropped; + stats->collisions = netdev->stats.collisions; + stats->rx_over_errors = netdev->stats.rx_over_errors; + stats->rx_frame_errors = netdev->stats.rx_frame_errors; + stats->rx_fifo_errors = netdev->stats.rx_fifo_errors; + stats->tx_aborted_errors = netdev->stats.tx_aborted_errors; + stats->tx_carrier_errors = netdev->stats.tx_carrier_errors; + stats->tx_fifo_errors = netdev->stats.tx_fifo_errors; + stats->tx_heartbeat_errors = netdev->stats.tx_heartbeat_errors; + stats->tx_window_errors = netdev->stats.tx_window_errors; + stats->rx_compressed = netdev->stats.rx_compressed; + stats->tx_compressed = netdev->stats.tx_compressed; +} + +static void hns3_add_tunnel_port(struct net_device *netdev, u16 port, + enum hns3_udp_tnl_type type) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hns3_udp_tunnel *udp_tnl = &priv->udp_tnl[type]; + struct hnae3_handle *h = priv->ae_handle; + + if (udp_tnl->used && udp_tnl->dst_port == port) { + udp_tnl->used++; + return; + } + + if (udp_tnl->used) { + netdev_warn(netdev, + "UDP tunnel [%d], port [%d] offload\n", type, port); + return; + } + + udp_tnl->dst_port = port; + udp_tnl->used = 1; + /* TBD send command to hardware to add port */ + if (h->ae_algo->ops->add_tunnel_udp) + h->ae_algo->ops->add_tunnel_udp(h, port); +} + +static void hns3_del_tunnel_port(struct net_device *netdev, u16 port, + enum hns3_udp_tnl_type type) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hns3_udp_tunnel *udp_tnl = &priv->udp_tnl[type]; + struct hnae3_handle *h = priv->ae_handle; + + if (!udp_tnl->used || udp_tnl->dst_port != port) { + netdev_warn(netdev, + "Invalid UDP tunnel port %d\n", port); + return; + } + + udp_tnl->used--; + if (udp_tnl->used) + return; + + udp_tnl->dst_port = 0; + /* TBD send command to hardware to del port */ + if (h->ae_algo->ops->del_tunnel_udp) + h->ae_algo->ops->add_tunnel_udp(h, port); +} + +/* hns3_nic_udp_tunnel_add - Get notifiacetion about UDP tunnel ports + * @netdev: This physical ports's netdev + * @ti: Tunnel information + */ +static void hns3_nic_udp_tunnel_add(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + u16 port_n = ntohs(ti->port); + + switch (ti->type) { + case UDP_TUNNEL_TYPE_VXLAN: + hns3_add_tunnel_port(netdev, port_n, HNS3_UDP_TNL_VXLAN); + break; + case UDP_TUNNEL_TYPE_GENEVE: + hns3_add_tunnel_port(netdev, port_n, HNS3_UDP_TNL_GENEVE); + break; + default: + netdev_err(netdev, "unsupported tunnel type %d\n", ti->type); + break; + } +} + +static void hns3_nic_udp_tunnel_del(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + u16 port_n = ntohs(ti->port); + + switch (ti->type) { + case UDP_TUNNEL_TYPE_VXLAN: + hns3_del_tunnel_port(netdev, port_n, HNS3_UDP_TNL_VXLAN); + break; + case UDP_TUNNEL_TYPE_GENEVE: + hns3_del_tunnel_port(netdev, port_n, HNS3_UDP_TNL_GENEVE); + break; + default: + break; + } +} + +static int hns3_setup_tc(struct net_device *netdev, u8 tc) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + struct hnae3_knic_private_info *kinfo = &h->kinfo; + unsigned int i; + int ret; + + if (tc > HNAE3_MAX_TC) + return -EINVAL; + + if (kinfo->num_tc == tc) + return 0; + + if (!netdev) + return -EINVAL; + + if (!tc) { + netdev_reset_tc(netdev); + return 0; + } + + /* Set num_tc for netdev */ + ret = netdev_set_num_tc(netdev, tc); + if (ret) + return ret; + + /* Set per TC queues for the VSI */ + for (i = 0; i < HNAE3_MAX_TC; i++) { + if (kinfo->tc_info[i].enable) + netdev_set_tc_queue(netdev, + kinfo->tc_info[i].tc, + kinfo->tc_info[i].tqp_count, + kinfo->tc_info[i].tqp_offset); + } + + return 0; +} + +static int hns3_nic_setup_tc(struct net_device *dev, u32 handle, + u32 chain_index, __be16 protocol, + struct tc_to_netdev *tc) +{ + if (handle != TC_H_ROOT || tc->type != TC_SETUP_MQPRIO) + return -EINVAL; + + return hns3_setup_tc(dev, tc->mqprio->num_tc); +} + +static int hns3_vlan_rx_add_vid(struct net_device *netdev, + __be16 proto, u16 vid) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int ret = -EIO; + + if (h->ae_algo->ops->set_vlan_filter) + ret = h->ae_algo->ops->set_vlan_filter(h, proto, vid, false); + + return ret; +} + +static int hns3_vlan_rx_kill_vid(struct net_device *netdev, + __be16 proto, u16 vid) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int ret = -EIO; + + if (h->ae_algo->ops->set_vlan_filter) + ret = h->ae_algo->ops->set_vlan_filter(h, proto, vid, true); + + return ret; +} + +static int hns3_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, + u8 qos, __be16 vlan_proto) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int ret = -EIO; + + if (h->ae_algo->ops->set_vf_vlan_filter) + ret = h->ae_algo->ops->set_vf_vlan_filter(h, vf, vlan, + qos, vlan_proto); + + return ret; +} + +static const struct net_device_ops hns3_nic_netdev_ops = { + .ndo_open = hns3_nic_net_open, + .ndo_stop = hns3_nic_net_stop, + .ndo_start_xmit = hns3_nic_net_xmit, + .ndo_set_mac_address = hns3_nic_net_set_mac_address, + .ndo_set_features = hns3_nic_set_features, + .ndo_get_stats64 = hns3_nic_get_stats64, + .ndo_setup_tc = hns3_nic_setup_tc, + .ndo_set_rx_mode = hns3_nic_set_rx_mode, + .ndo_udp_tunnel_add = hns3_nic_udp_tunnel_add, + .ndo_udp_tunnel_del = hns3_nic_udp_tunnel_del, + .ndo_vlan_rx_add_vid = hns3_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = hns3_vlan_rx_kill_vid, + .ndo_set_vf_vlan = hns3_ndo_set_vf_vlan, +}; + +/* hns3_probe - Device initialization routine + * @pdev: PCI device information struct + * @ent: entry in hns3_pci_tbl + * + * hns3_probe initializes a PF identified by a pci_dev structure. + * The OS initialization, configuring of the PF private structure, + * and a hardware reset occur. + * + * Returns 0 on success, negative on failure + */ +static int hns3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hnae3_ae_dev *ae_dev; + int ret; + + ae_dev = devm_kzalloc(&pdev->dev, sizeof(*ae_dev), + GFP_KERNEL); + if (!ae_dev) { + ret = -ENOMEM; + return ret; + } + + ae_dev->pdev = pdev; + ae_dev->dev_type = HNAE3_DEV_KNIC; + pci_set_drvdata(pdev, ae_dev); + + return hnae3_register_ae_dev(ae_dev); +} + +/* hns3_remove - Device removal routine + * @pdev: PCI device information struct + */ +static void hns3_remove(struct pci_dev *pdev) +{ + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev); + + hnae3_unregister_ae_dev(ae_dev); + + devm_kfree(&pdev->dev, ae_dev); + + pci_set_drvdata(pdev, NULL); +} + +static struct pci_driver hns3_driver = { + .name = hns3_driver_name, + .id_table = hns3_pci_tbl, + .probe = hns3_probe, + .remove = hns3_remove, +}; + +/* set default feature to hns3 */ +static void hns3_set_default_feature(struct net_device *netdev) +{ + netdev->priv_flags |= IFF_UNICAST_FLT; + + netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + + netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID; + + netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM; + + netdev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + + netdev->vlan_features |= + NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | + NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO | + NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + + netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; +} + +static int hns3_alloc_buffer(struct hns3_enet_ring *ring, + struct hns3_desc_cb *cb) +{ + unsigned int order = hnae_page_order(ring); + struct page *p; + + p = dev_alloc_pages(order); + if (!p) + return -ENOMEM; + + cb->priv = p; + cb->page_offset = 0; + cb->reuse_flag = 0; + cb->buf = page_address(p); + cb->length = hnae_page_size(ring); + cb->type = DESC_TYPE_PAGE; + + memset(cb->buf, 0, cb->length); + + return 0; +} + +static void hns3_free_buffer(struct hns3_enet_ring *ring, + struct hns3_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dev_kfree_skb_any((struct sk_buff *)cb->priv); + else if (!HNAE3_IS_TX_RING(ring)) + put_page((struct page *)cb->priv); + memset(cb, 0, sizeof(*cb)); +} + +static int hns3_map_buffer(struct hns3_enet_ring *ring, struct hns3_desc_cb *cb) +{ + cb->dma = dma_map_page(ring_to_dev(ring), cb->priv, 0, + cb->length, ring_to_dma_dir(ring)); + + if (dma_mapping_error(ring_to_dev(ring), cb->dma)) + return -EIO; + + return 0; +} + +static void hns3_unmap_buffer(struct hns3_enet_ring *ring, + struct hns3_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dma_unmap_single(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); + else + dma_unmap_page(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); +} + +static void hns3_buffer_detach(struct hns3_enet_ring *ring, int i) +{ + hns3_unmap_buffer(ring, &ring->desc_cb[i]); + ring->desc[i].addr = 0; +} + +static void hns3_free_buffer_detach(struct hns3_enet_ring *ring, int i) +{ + struct hns3_desc_cb *cb = &ring->desc_cb[i]; + + if (!ring->desc_cb[i].dma) + return; + + hns3_buffer_detach(ring, i); + hns3_free_buffer(ring, cb); +} + +static void hns3_free_buffers(struct hns3_enet_ring *ring) +{ + int i; + + for (i = 0; i < ring->desc_num; i++) + hns3_free_buffer_detach(ring, i); +} + +/* free desc along with its attached buffer */ +static void hns3_free_desc(struct hns3_enet_ring *ring) +{ + hns3_free_buffers(ring); + + dma_unmap_single(ring_to_dev(ring), ring->desc_dma_addr, + ring->desc_num * sizeof(ring->desc[0]), + DMA_BIDIRECTIONAL); + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; +} + +static int hns3_alloc_desc(struct hns3_enet_ring *ring) +{ + int size = ring->desc_num * sizeof(ring->desc[0]); + + ring->desc = kzalloc(size, GFP_KERNEL); + if (!ring->desc) + return -ENOMEM; + + ring->desc_dma_addr = dma_map_single(ring_to_dev(ring), ring->desc, + size, DMA_BIDIRECTIONAL); + if (dma_mapping_error(ring_to_dev(ring), ring->desc_dma_addr)) { + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; + return -ENOMEM; + } + + return 0; +} + +static int hns3_reserve_buffer_map(struct hns3_enet_ring *ring, + struct hns3_desc_cb *cb) +{ + int ret; + + ret = hns3_alloc_buffer(ring, cb); + if (ret) + goto out; + + ret = hns3_map_buffer(ring, cb); + if (ret) + goto out_with_buf; + + return 0; + +out_with_buf: + hns3_free_buffers(ring); +out: + return ret; +} + +static int hns3_alloc_buffer_attach(struct hns3_enet_ring *ring, int i) +{ + int ret = hns3_reserve_buffer_map(ring, &ring->desc_cb[i]); + + if (ret) + return ret; + + ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma); + + return 0; +} + +/* Allocate memory for raw pkg, and map with dma */ +static int hns3_alloc_ring_buffers(struct hns3_enet_ring *ring) +{ + int i, j, ret; + + for (i = 0; i < ring->desc_num; i++) { + ret = hns3_alloc_buffer_attach(ring, i); + if (ret) + goto out_buffer_fail; + } + + return 0; + +out_buffer_fail: + for (j = i - 1; j >= 0; j--) + hns3_free_buffer_detach(ring, j); + return ret; +} + +/* detach a in-used buffer and replace with a reserved one */ +static void hns3_replace_buffer(struct hns3_enet_ring *ring, int i, + struct hns3_desc_cb *res_cb) +{ + hns3_map_buffer(ring, &ring->desc_cb[i]); + ring->desc_cb[i] = *res_cb; + ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma); +} + +static void hns3_reuse_buffer(struct hns3_enet_ring *ring, int i) +{ + ring->desc_cb[i].reuse_flag = 0; + ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma + + ring->desc_cb[i].page_offset); +} + +static void hns3_nic_reclaim_one_desc(struct hns3_enet_ring *ring, int *bytes, + int *pkts) +{ + struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_clean]; + + (*pkts) += (desc_cb->type == DESC_TYPE_SKB); + (*bytes) += desc_cb->length; + /* desc_cb will be cleaned, after hnae_free_buffer_detach*/ + hns3_free_buffer_detach(ring, ring->next_to_clean); + + ring_ptr_move_fw(ring, next_to_clean); +} + +static int is_valid_clean_head(struct hns3_enet_ring *ring, int h) +{ + int u = ring->next_to_use; + int c = ring->next_to_clean; + + if (unlikely(h > ring->desc_num)) + return 0; + + return u > c ? (h > c && h <= u) : (h > c || h <= u); +} + +int hns3_clean_tx_ring(struct hns3_enet_ring *ring, int budget) +{ + struct net_device *netdev = ring->tqp->handle->kinfo.netdev; + struct netdev_queue *dev_queue; + int bytes, pkts; + int head; + + head = readl_relaxed(ring->tqp->io_base + HNS3_RING_TX_RING_HEAD_REG); + rmb(); /* Make sure head is ready before touch any data */ + + if (is_ring_empty(ring) || head == ring->next_to_clean) + return 0; /* no data to poll */ + + if (!is_valid_clean_head(ring, head)) { + netdev_err(netdev, "wrong head (%d, %d-%d)\n", head, + ring->next_to_use, ring->next_to_clean); + + u64_stats_update_begin(&ring->syncp); + ring->stats.io_err_cnt++; + u64_stats_update_end(&ring->syncp); + return -EIO; + } + + bytes = 0; + pkts = 0; + while (head != ring->next_to_clean && budget) { + hns3_nic_reclaim_one_desc(ring, &bytes, &pkts); + /* Issue prefetch for next Tx descriptor */ + prefetch(&ring->desc_cb[ring->next_to_clean]); + budget--; + } + + ring->tqp_vector->tx_group.total_bytes += bytes; + ring->tqp_vector->tx_group.total_packets += pkts; + + u64_stats_update_begin(&ring->syncp); + ring->stats.tx_bytes += bytes; + ring->stats.tx_pkts += pkts; + u64_stats_update_end(&ring->syncp); + + dev_queue = netdev_get_tx_queue(netdev, ring->tqp->tqp_index); + netdev_tx_completed_queue(dev_queue, pkts, bytes); + + if (unlikely(pkts && netif_carrier_ok(netdev) && + (ring_space(ring) > HNS3_MAX_BD_PER_PKT))) { + /* Make sure that anybody stopping the queue after this + * sees the new next_to_clean. + */ + smp_mb(); + if (netif_tx_queue_stopped(dev_queue)) { + netif_tx_wake_queue(dev_queue); + ring->stats.restart_queue++; + } + } + + return !!budget; +} + +static int hns3_desc_unused(struct hns3_enet_ring *ring) +{ + int ntc = ring->next_to_clean; + int ntu = ring->next_to_use; + + return ((ntc >= ntu) ? 0 : ring->desc_num) + ntc - ntu; +} + +static void +hns3_nic_alloc_rx_buffers(struct hns3_enet_ring *ring, int cleand_count) +{ + struct hns3_desc_cb *desc_cb; + struct hns3_desc_cb res_cbs; + int i, ret; + + for (i = 0; i < cleand_count; i++) { + desc_cb = &ring->desc_cb[ring->next_to_use]; + if (desc_cb->reuse_flag) { + u64_stats_update_begin(&ring->syncp); + ring->stats.reuse_pg_cnt++; + u64_stats_update_end(&ring->syncp); + + hns3_reuse_buffer(ring, ring->next_to_use); + } else { + ret = hns3_reserve_buffer_map(ring, &res_cbs); + if (ret) { + u64_stats_update_begin(&ring->syncp); + ring->stats.sw_err_cnt++; + u64_stats_update_end(&ring->syncp); + + netdev_err(ring->tqp->handle->kinfo.netdev, + "hnae reserve buffer map failed.\n"); + break; + } + hns3_replace_buffer(ring, ring->next_to_use, &res_cbs); + } + + ring_ptr_move_fw(ring, next_to_use); + } + + wmb(); /* Make all data has been write before submit */ + writel_relaxed(i, ring->tqp->io_base + HNS3_RING_RX_RING_HEAD_REG); +} + +/* hns3_nic_get_headlen - determine size of header for LRO/GRO + * @data: pointer to the start of the headers + * @max: total length of section to find headers in + * + * This function is meant to determine the length of headers that will + * be recognized by hardware for LRO, GRO, and RSC offloads. The main + * motivation of doing this is to only perform one pull for IPv4 TCP + * packets so that we can do basic things like calculating the gso_size + * based on the average data per packet. + */ +static unsigned int hns3_nic_get_headlen(unsigned char *data, u32 flag, + unsigned int max_size) +{ + unsigned char *network; + u8 hlen; + + /* This should never happen, but better safe than sorry */ + if (max_size < ETH_HLEN) + return max_size; + + /* Initialize network frame pointer */ + network = data; + + /* Set first protocol and move network header forward */ + network += ETH_HLEN; + + /* Handle any vlan tag if present */ + if (hnae_get_field(flag, HNS3_RXD_VLAN_M, HNS3_RXD_VLAN_S) + == HNS3_RX_FLAG_VLAN_PRESENT) { + if ((typeof(max_size))(network - data) > (max_size - VLAN_HLEN)) + return max_size; + + network += VLAN_HLEN; + } + + /* Handle L3 protocols */ + if (hnae_get_field(flag, HNS3_RXD_L3ID_M, HNS3_RXD_L3ID_S) + == HNS3_RX_FLAG_L3ID_IPV4) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct iphdr))) + return max_size; + + /* Access ihl as a u8 to avoid unaligned access on ia64 */ + hlen = (network[0] & 0x0F) << 2; + + /* Verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct iphdr)) + return network - data; + + /* Record next protocol if header is present */ + } else if (hnae_get_field(flag, HNS3_RXD_L3ID_M, HNS3_RXD_L3ID_S) + == HNS3_RX_FLAG_L3ID_IPV6) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct ipv6hdr))) + return max_size; + + /* Record next protocol */ + hlen = sizeof(struct ipv6hdr); + } else { + return network - data; + } + + /* Relocate pointer to start of L4 header */ + network += hlen; + + /* Finally sort out TCP/UDP */ + if (hnae_get_field(flag, HNS3_RXD_L4ID_M, HNS3_RXD_L4ID_S) + == HNS3_RX_FLAG_L4ID_TCP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct tcphdr))) + return max_size; + + /* Access doff as a u8 to avoid unaligned access on ia64 */ + hlen = (network[12] & 0xF0) >> 2; + + /* Verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct tcphdr)) + return network - data; + + network += hlen; + } else if (hnae_get_field(flag, HNS3_RXD_L4ID_M, HNS3_RXD_L4ID_S) + == HNS3_RX_FLAG_L4ID_UDP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct udphdr))) + return max_size; + + network += sizeof(struct udphdr); + } + + /* If everything has gone correctly network should be the + * data section of the packet and will be the end of the header. + * If not then it probably represents the end of the last recognized + * header. + */ + if ((typeof(max_size))(network - data) < max_size) + return network - data; + else + return max_size; +} + +static void hns3_nic_reuse_page(struct sk_buff *skb, int i, + struct hns3_enet_ring *ring, int pull_len, + struct hns3_desc_cb *desc_cb) +{ + struct hns3_desc *desc; + int truesize, size; + int last_offset; + bool twobufs; + + twobufs = ((PAGE_SIZE < 8192) && + hnae_buf_size(ring) == HNS3_BUFFER_SIZE_2048); + + desc = &ring->desc[ring->next_to_clean]; + size = le16_to_cpu(desc->rx.size); + + if (twobufs) { + truesize = hnae_buf_size(ring); + } else { + truesize = ALIGN(size, L1_CACHE_BYTES); + last_offset = hnae_page_size(ring) - hnae_buf_size(ring); + } + + skb_add_rx_frag(skb, i, desc_cb->priv, desc_cb->page_offset + pull_len, + size - pull_len, truesize - pull_len); + + /* Avoid re-using remote pages,flag default unreuse */ + if (unlikely(page_to_nid(desc_cb->priv) != numa_node_id())) + return; + + if (twobufs) { + /* If we are only owner of page we can reuse it */ + if (likely(page_count(desc_cb->priv) == 1)) { + /* Flip page offset to other buffer */ + desc_cb->page_offset ^= truesize; + + desc_cb->reuse_flag = 1; + /* bump ref count on page before it is given*/ + get_page(desc_cb->priv); + } + return; + } + + /* Move offset up to the next cache line */ + desc_cb->page_offset += truesize; + + if (desc_cb->page_offset <= last_offset) { + desc_cb->reuse_flag = 1; + /* Bump ref count on page before it is given*/ + get_page(desc_cb->priv); + } +} + +static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb, + struct hns3_desc *desc) +{ + struct net_device *netdev = ring->tqp->handle->kinfo.netdev; + int l3_type, l4_type; + u32 bd_base_info; + int ol4_type; + u32 l234info; + + bd_base_info = le32_to_cpu(desc->rx.bd_base_info); + l234info = le32_to_cpu(desc->rx.l234_info); + + skb->ip_summed = CHECKSUM_NONE; + + skb_checksum_none_assert(skb); + + if (!(netdev->features & NETIF_F_RXCSUM)) + return; + + /* check if hardware has done checksum */ + if (!hnae_get_bit(bd_base_info, HNS3_RXD_L3L4P_B)) + return; + + if (unlikely(hnae_get_bit(l234info, HNS3_RXD_L3E_B) || + hnae_get_bit(l234info, HNS3_RXD_L4E_B) || + hnae_get_bit(l234info, HNS3_RXD_OL3E_B) || + hnae_get_bit(l234info, HNS3_RXD_OL4E_B))) { + netdev_err(netdev, "L3/L4 error pkt\n"); + u64_stats_update_begin(&ring->syncp); + ring->stats.l3l4_csum_err++; + u64_stats_update_end(&ring->syncp); + + return; + } + + l3_type = hnae_get_field(l234info, HNS3_RXD_L3ID_M, + HNS3_RXD_L3ID_S); + l4_type = hnae_get_field(l234info, HNS3_RXD_L4ID_M, + HNS3_RXD_L4ID_S); + + ol4_type = hnae_get_field(l234info, HNS3_RXD_OL4ID_M, HNS3_RXD_OL4ID_S); + switch (ol4_type) { + case HNS3_OL4_TYPE_MAC_IN_UDP: + case HNS3_OL4_TYPE_NVGRE: + skb->csum_level = 1; + case HNS3_OL4_TYPE_NO_TUN: + /* Can checksum ipv4 or ipv6 + UDP/TCP/SCTP packets */ + if (l3_type == HNS3_L3_TYPE_IPV4 || + (l3_type == HNS3_L3_TYPE_IPV6 && + (l4_type == HNS3_L4_TYPE_UDP || + l4_type == HNS3_L4_TYPE_TCP || + l4_type == HNS3_L4_TYPE_SCTP))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + break; + } +} + +static int hns3_handle_rx_bd(struct hns3_enet_ring *ring, + struct sk_buff **out_skb, int *out_bnum) +{ + struct net_device *netdev = ring->tqp->handle->kinfo.netdev; + struct hns3_desc_cb *desc_cb; + struct hns3_desc *desc; + struct sk_buff *skb; + unsigned char *va; + u32 bd_base_info; + int pull_len; + u32 l234info; + int length; + int bnum; + + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + + prefetch(desc); + + length = le16_to_cpu(desc->rx.pkt_len); + bd_base_info = le32_to_cpu(desc->rx.bd_base_info); + l234info = le32_to_cpu(desc->rx.l234_info); + + /* Check valid BD */ + if (!hnae_get_bit(bd_base_info, HNS3_RXD_VLD_B)) + return -EFAULT; + + va = (unsigned char *)desc_cb->buf + desc_cb->page_offset; + + /* Prefetch first cache line of first page + * Idea is to cache few bytes of the header of the packet. Our L1 Cache + * line size is 64B so need to prefetch twice to make it 128B. But in + * actual we can have greater size of caches with 128B Level 1 cache + * lines. In such a case, single fetch would suffice to cache in the + * relevant part of the header. + */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + + skb = *out_skb = napi_alloc_skb(&ring->tqp_vector->napi, + HNS3_RX_HEAD_SIZE); + if (unlikely(!skb)) { + netdev_err(netdev, "alloc rx skb fail\n"); + + u64_stats_update_begin(&ring->syncp); + ring->stats.sw_err_cnt++; + u64_stats_update_end(&ring->syncp); + + return -ENOMEM; + } + + prefetchw(skb->data); + + bnum = 1; + if (length <= HNS3_RX_HEAD_SIZE) { + memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long))); + + /* We can reuse buffer as-is, just make sure it is local */ + if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) + desc_cb->reuse_flag = 1; + else /* This page cannot be reused so discard it */ + put_page(desc_cb->priv); + + ring_ptr_move_fw(ring, next_to_clean); + } else { + u64_stats_update_begin(&ring->syncp); + ring->stats.seg_pkt_cnt++; + u64_stats_update_end(&ring->syncp); + + pull_len = hns3_nic_get_headlen(va, l234info, + HNS3_RX_HEAD_SIZE); + memcpy(__skb_put(skb, pull_len), va, + ALIGN(pull_len, sizeof(long))); + + hns3_nic_reuse_page(skb, 0, ring, pull_len, desc_cb); + ring_ptr_move_fw(ring, next_to_clean); + + while (!hnae_get_bit(bd_base_info, HNS3_RXD_FE_B)) { + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + bd_base_info = le32_to_cpu(desc->rx.bd_base_info); + hns3_nic_reuse_page(skb, bnum, ring, 0, desc_cb); + ring_ptr_move_fw(ring, next_to_clean); + bnum++; + } + } + + *out_bnum = bnum; + + if (unlikely(!hnae_get_bit(bd_base_info, HNS3_RXD_VLD_B))) { + netdev_err(netdev, "no valid bd,%016llx,%016llx\n", + ((u64 *)desc)[0], ((u64 *)desc)[1]); + u64_stats_update_begin(&ring->syncp); + ring->stats.non_vld_descs++; + u64_stats_update_end(&ring->syncp); + + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (unlikely((!desc->rx.pkt_len) || + hnae_get_bit(l234info, HNS3_RXD_TRUNCAT_B))) { + netdev_err(netdev, "truncated pkt\n"); + u64_stats_update_begin(&ring->syncp); + ring->stats.err_pkt_len++; + u64_stats_update_end(&ring->syncp); + + dev_kfree_skb_any(skb); + return -EFAULT; + } + + if (unlikely(hnae_get_bit(l234info, HNS3_RXD_L2E_B))) { + netdev_err(netdev, "L2 error pkt\n"); + u64_stats_update_begin(&ring->syncp); + ring->stats.l2_err++; + u64_stats_update_end(&ring->syncp); + + dev_kfree_skb_any(skb); + return -EFAULT; + } + + u64_stats_update_begin(&ring->syncp); + ring->stats.rx_pkts++; + ring->stats.rx_bytes += skb->len; + u64_stats_update_end(&ring->syncp); + + ring->tqp_vector->rx_group.total_bytes += skb->len; + + hns3_rx_checksum(ring, skb, desc); + return 0; +} + +static int hns3_clean_rx_ring(struct hns3_enet_ring *ring, int budget) +{ +#define RCB_NOF_ALLOC_RX_BUFF_ONCE 16 + struct net_device *netdev = ring->tqp->handle->kinfo.netdev; + int recv_pkts, recv_bds, clean_count, err; + int unused_count = hns3_desc_unused(ring); + struct sk_buff *skb = NULL; + int num, bnum = 0; + + num = readl_relaxed(ring->tqp->io_base + HNS3_RING_RX_RING_FBDNUM_REG); + rmb(); /* Make sure num taken effect before the other data is touched */ + + recv_pkts = 0, recv_bds = 0, clean_count = 0; + num -= unused_count; + + while (recv_pkts < budget && recv_bds < num) { + /* Reuse or realloc buffers */ + if (clean_count + unused_count >= RCB_NOF_ALLOC_RX_BUFF_ONCE) { + hns3_nic_alloc_rx_buffers(ring, + clean_count + unused_count); + clean_count = 0; + unused_count = hns3_desc_unused(ring); + } + + /* Poll one pkt */ + err = hns3_handle_rx_bd(ring, &skb, &bnum); + if (unlikely(!skb)) /* This fault cannot be repaired */ + goto out; + + recv_bds += bnum; + clean_count += bnum; + if (unlikely(err)) { /* Do jump the err */ + recv_pkts++; + continue; + } + + /* Do update ip stack process */ + skb->protocol = eth_type_trans(skb, netdev); + (void)napi_gro_receive(&ring->tqp_vector->napi, skb); + + recv_pkts++; + } + +out: + /* Make all data has been write before submit */ + if (clean_count + unused_count > 0) + hns3_nic_alloc_rx_buffers(ring, + clean_count + unused_count); + + return recv_pkts; +} + +static bool hns3_get_new_int_gl(struct hns3_enet_ring_group *ring_group) +{ +#define HNS3_RX_ULTRA_PACKET_RATE 40000 + enum hns3_flow_level_range new_flow_level; + struct hns3_enet_tqp_vector *tqp_vector; + int packets_per_secs; + int bytes_per_usecs; + u16 new_int_gl; + int usecs; + + if (!ring_group->int_gl) + return false; + + if (ring_group->total_packets == 0) { + ring_group->int_gl = HNS3_INT_GL_50K; + ring_group->flow_level = HNS3_FLOW_LOW; + return true; + } + + /* Simple throttlerate management + * 0-10MB/s lower (50000 ints/s) + * 10-20MB/s middle (20000 ints/s) + * 20-1249MB/s high (18000 ints/s) + * > 40000pps ultra (8000 ints/s) + */ + new_flow_level = ring_group->flow_level; + new_int_gl = ring_group->int_gl; + tqp_vector = ring_group->ring->tqp_vector; + usecs = (ring_group->int_gl << 1); + bytes_per_usecs = ring_group->total_bytes / usecs; + /* 1000000 microseconds */ + packets_per_secs = ring_group->total_packets * 1000000 / usecs; + + switch (new_flow_level) { + case HNS3_FLOW_LOW: + if (bytes_per_usecs > 10) + new_flow_level = HNS3_FLOW_MID; + break; + case HNS3_FLOW_MID: + if (bytes_per_usecs > 20) + new_flow_level = HNS3_FLOW_HIGH; + else if (bytes_per_usecs <= 10) + new_flow_level = HNS3_FLOW_LOW; + break; + case HNS3_FLOW_HIGH: + case HNS3_FLOW_ULTRA: + default: + if (bytes_per_usecs <= 20) + new_flow_level = HNS3_FLOW_MID; + break; + } + + if ((packets_per_secs > HNS3_RX_ULTRA_PACKET_RATE) && + (&tqp_vector->rx_group == ring_group)) + new_flow_level = HNS3_FLOW_ULTRA; + + switch (new_flow_level) { + case HNS3_FLOW_LOW: + new_int_gl = HNS3_INT_GL_50K; + break; + case HNS3_FLOW_MID: + new_int_gl = HNS3_INT_GL_20K; + break; + case HNS3_FLOW_HIGH: + new_int_gl = HNS3_INT_GL_18K; + break; + case HNS3_FLOW_ULTRA: + new_int_gl = HNS3_INT_GL_8K; + break; + default: + break; + } + + ring_group->total_bytes = 0; + ring_group->total_packets = 0; + ring_group->flow_level = new_flow_level; + if (new_int_gl != ring_group->int_gl) { + ring_group->int_gl = new_int_gl; + return true; + } + return false; +} + +static void hns3_update_new_int_gl(struct hns3_enet_tqp_vector *tqp_vector) +{ + u16 rx_int_gl, tx_int_gl; + bool rx, tx; + + rx = hns3_get_new_int_gl(&tqp_vector->rx_group); + tx = hns3_get_new_int_gl(&tqp_vector->tx_group); + rx_int_gl = tqp_vector->rx_group.int_gl; + tx_int_gl = tqp_vector->tx_group.int_gl; + if (rx && tx) { + if (rx_int_gl > tx_int_gl) { + tqp_vector->tx_group.int_gl = rx_int_gl; + tqp_vector->tx_group.flow_level = + tqp_vector->rx_group.flow_level; + hns3_set_vector_coalesc_gl(tqp_vector, rx_int_gl); + } else { + tqp_vector->rx_group.int_gl = tx_int_gl; + tqp_vector->rx_group.flow_level = + tqp_vector->tx_group.flow_level; + hns3_set_vector_coalesc_gl(tqp_vector, tx_int_gl); + } + } +} + +static int hns3_nic_common_poll(struct napi_struct *napi, int budget) +{ + struct hns3_enet_ring *ring; + int rx_pkt_total = 0; + + struct hns3_enet_tqp_vector *tqp_vector = + container_of(napi, struct hns3_enet_tqp_vector, napi); + bool clean_complete = true; + int rx_budget; + + /* Since the actual Tx work is minimal, we can give the Tx a larger + * budget and be more aggressive about cleaning up the Tx descriptors. + */ + hns3_for_each_ring(ring, tqp_vector->tx_group) { + if (!hns3_clean_tx_ring(ring, budget)) + clean_complete = false; + } + + /* make sure rx ring budget not smaller than 1 */ + rx_budget = max(budget / tqp_vector->num_tqps, 1); + + hns3_for_each_ring(ring, tqp_vector->rx_group) { + int rx_cleaned = hns3_clean_rx_ring(ring, rx_budget); + + if (rx_cleaned >= rx_budget) + clean_complete = false; + + rx_pkt_total += rx_cleaned; + } + + tqp_vector->rx_group.total_packets += rx_pkt_total; + + if (!clean_complete) + return budget; + + napi_complete(napi); + hns3_update_new_int_gl(tqp_vector); + hns3_mask_vector_irq(tqp_vector, 1); + + return rx_pkt_total; +} + +static int hns3_get_vector_ring_chain(struct hns3_enet_tqp_vector *tqp_vector, + struct hnae3_ring_chain_node *head) +{ + struct pci_dev *pdev = tqp_vector->handle->pdev; + struct hnae3_ring_chain_node *cur_chain = head; + struct hnae3_ring_chain_node *chain; + struct hns3_enet_ring *tx_ring; + struct hns3_enet_ring *rx_ring; + + tx_ring = tqp_vector->tx_group.ring; + if (tx_ring) { + cur_chain->tqp_index = tx_ring->tqp->tqp_index; + hnae_set_bit(cur_chain->flag, HNAE3_RING_TYPE_B, + HNAE3_RING_TYPE_TX); + + cur_chain->next = NULL; + + while (tx_ring->next) { + tx_ring = tx_ring->next; + + chain = devm_kzalloc(&pdev->dev, sizeof(*chain), + GFP_KERNEL); + if (!chain) + return -ENOMEM; + + cur_chain->next = chain; + chain->tqp_index = tx_ring->tqp->tqp_index; + hnae_set_bit(chain->flag, HNAE3_RING_TYPE_B, + HNAE3_RING_TYPE_TX); + + cur_chain = chain; + } + } + + rx_ring = tqp_vector->rx_group.ring; + if (!tx_ring && rx_ring) { + cur_chain->next = NULL; + cur_chain->tqp_index = rx_ring->tqp->tqp_index; + hnae_set_bit(cur_chain->flag, HNAE3_RING_TYPE_B, + HNAE3_RING_TYPE_RX); + + rx_ring = rx_ring->next; + } + + while (rx_ring) { + chain = devm_kzalloc(&pdev->dev, sizeof(*chain), GFP_KERNEL); + if (!chain) + return -ENOMEM; + + cur_chain->next = chain; + chain->tqp_index = rx_ring->tqp->tqp_index; + hnae_set_bit(chain->flag, HNAE3_RING_TYPE_B, + HNAE3_RING_TYPE_RX); + cur_chain = chain; + + rx_ring = rx_ring->next; + } + + return 0; +} + +static void hns3_free_vector_ring_chain(struct hns3_enet_tqp_vector *tqp_vector, + struct hnae3_ring_chain_node *head) +{ + struct pci_dev *pdev = tqp_vector->handle->pdev; + struct hnae3_ring_chain_node *chain_tmp, *chain; + + chain = head->next; + + while (chain) { + chain_tmp = chain->next; + devm_kfree(&pdev->dev, chain); + chain = chain_tmp; + } +} + +static void hns3_add_ring_to_group(struct hns3_enet_ring_group *group, + struct hns3_enet_ring *ring) +{ + ring->next = group->ring; + group->ring = ring; + + group->count++; +} + +static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv) +{ + struct hnae3_ring_chain_node vector_ring_chain; + struct hnae3_handle *h = priv->ae_handle; + struct hns3_enet_tqp_vector *tqp_vector; + struct hnae3_vector_info *vector; + struct pci_dev *pdev = h->pdev; + u16 tqp_num = h->kinfo.num_tqps; + u16 vector_num; + int ret = 0; + u16 i; + + /* RSS size, cpu online and vector_num should be the same */ + /* Should consider 2p/4p later */ + vector_num = min_t(u16, num_online_cpus(), tqp_num); + vector = devm_kcalloc(&pdev->dev, vector_num, sizeof(*vector), + GFP_KERNEL); + if (!vector) + return -ENOMEM; + + vector_num = h->ae_algo->ops->get_vector(h, vector_num, vector); + + priv->vector_num = vector_num; + priv->tqp_vector = (struct hns3_enet_tqp_vector *) + devm_kcalloc(&pdev->dev, vector_num, sizeof(*priv->tqp_vector), + GFP_KERNEL); + if (!priv->tqp_vector) + return -ENOMEM; + + for (i = 0; i < tqp_num; i++) { + u16 vector_i = i % vector_num; + + tqp_vector = &priv->tqp_vector[vector_i]; + + hns3_add_ring_to_group(&tqp_vector->tx_group, + priv->ring_data[i].ring); + + hns3_add_ring_to_group(&tqp_vector->rx_group, + priv->ring_data[i + tqp_num].ring); + + tqp_vector->idx = vector_i; + tqp_vector->mask_addr = vector[vector_i].io_addr; + tqp_vector->vector_irq = vector[vector_i].vector; + tqp_vector->num_tqps++; + + priv->ring_data[i].ring->tqp_vector = tqp_vector; + priv->ring_data[i + tqp_num].ring->tqp_vector = tqp_vector; + } + + for (i = 0; i < vector_num; i++) { + tqp_vector = &priv->tqp_vector[i]; + + tqp_vector->rx_group.total_bytes = 0; + tqp_vector->rx_group.total_packets = 0; + tqp_vector->tx_group.total_bytes = 0; + tqp_vector->tx_group.total_packets = 0; + hns3_vector_gl_rl_init(tqp_vector); + tqp_vector->handle = h; + + ret = hns3_get_vector_ring_chain(tqp_vector, + &vector_ring_chain); + if (ret) + goto out; + + ret = h->ae_algo->ops->map_ring_to_vector(h, + tqp_vector->vector_irq, &vector_ring_chain); + if (ret) + goto out; + + hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain); + + netif_napi_add(priv->netdev, &tqp_vector->napi, + hns3_nic_common_poll, NAPI_POLL_WEIGHT); + } + +out: + devm_kfree(&pdev->dev, vector); + return ret; +} + +static int hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv) +{ + struct hnae3_ring_chain_node vector_ring_chain; + struct hnae3_handle *h = priv->ae_handle; + struct hns3_enet_tqp_vector *tqp_vector; + struct pci_dev *pdev = h->pdev; + int i, ret; + + for (i = 0; i < priv->vector_num; i++) { + tqp_vector = &priv->tqp_vector[i]; + + ret = hns3_get_vector_ring_chain(tqp_vector, + &vector_ring_chain); + if (ret) + return ret; + + ret = h->ae_algo->ops->unmap_ring_from_vector(h, + tqp_vector->vector_irq, &vector_ring_chain); + if (ret) + return ret; + + hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain); + + if (priv->tqp_vector[i].irq_init_flag == HNS3_VECTOR_INITED) { + (void)irq_set_affinity_hint( + priv->tqp_vector[i].vector_irq, + NULL); + devm_free_irq(&pdev->dev, + priv->tqp_vector[i].vector_irq, + &priv->tqp_vector[i]); + } + + priv->ring_data[i].ring->irq_init_flag = HNS3_VECTOR_NOT_INITED; + + netif_napi_del(&priv->tqp_vector[i].napi); + } + + devm_kfree(&pdev->dev, priv->tqp_vector); + + return 0; +} + +static int hns3_ring_get_cfg(struct hnae3_queue *q, struct hns3_nic_priv *priv, + int ring_type) +{ + struct hns3_nic_ring_data *ring_data = priv->ring_data; + int queue_num = priv->ae_handle->kinfo.num_tqps; + struct pci_dev *pdev = priv->ae_handle->pdev; + struct hns3_enet_ring *ring; + + ring = devm_kzalloc(&pdev->dev, sizeof(*ring), GFP_KERNEL); + if (!ring) + return -ENOMEM; + + if (ring_type == HNAE3_RING_TYPE_TX) { + ring_data[q->tqp_index].ring = ring; + ring->io_base = (u8 __iomem *)q->io_base + HNS3_TX_REG_OFFSET; + } else { + ring_data[q->tqp_index + queue_num].ring = ring; + ring->io_base = q->io_base; + } + + hnae_set_bit(ring->flag, HNAE3_RING_TYPE_B, ring_type); + + ring_data[q->tqp_index].queue_index = q->tqp_index; + + ring->tqp = q; + ring->desc = NULL; + ring->desc_cb = NULL; + ring->dev = priv->dev; + ring->desc_dma_addr = 0; + ring->buf_size = q->buf_size; + ring->desc_num = q->desc_num; + ring->next_to_use = 0; + ring->next_to_clean = 0; + + return 0; +} + +static int hns3_queue_to_ring(struct hnae3_queue *tqp, + struct hns3_nic_priv *priv) +{ + int ret; + + ret = hns3_ring_get_cfg(tqp, priv, HNAE3_RING_TYPE_TX); + if (ret) + return ret; + + ret = hns3_ring_get_cfg(tqp, priv, HNAE3_RING_TYPE_RX); + if (ret) + return ret; + + return 0; +} + +static int hns3_get_ring_config(struct hns3_nic_priv *priv) +{ + struct hnae3_handle *h = priv->ae_handle; + struct pci_dev *pdev = h->pdev; + int i, ret; + + priv->ring_data = devm_kzalloc(&pdev->dev, h->kinfo.num_tqps * + sizeof(*priv->ring_data) * 2, + GFP_KERNEL); + if (!priv->ring_data) + return -ENOMEM; + + for (i = 0; i < h->kinfo.num_tqps; i++) { + ret = hns3_queue_to_ring(h->kinfo.tqp[i], priv); + if (ret) + goto err; + } + + return 0; +err: + devm_kfree(&pdev->dev, priv->ring_data); + return ret; +} + +static int hns3_alloc_ring_memory(struct hns3_enet_ring *ring) +{ + int ret; + + if (ring->desc_num <= 0 || ring->buf_size <= 0) + return -EINVAL; + + ring->desc_cb = kcalloc(ring->desc_num, sizeof(ring->desc_cb[0]), + GFP_KERNEL); + if (!ring->desc_cb) { + ret = -ENOMEM; + goto out; + } + + ret = hns3_alloc_desc(ring); + if (ret) + goto out_with_desc_cb; + + if (!HNAE3_IS_TX_RING(ring)) { + ret = hns3_alloc_ring_buffers(ring); + if (ret) + goto out_with_desc; + } + + return 0; + +out_with_desc: + hns3_free_desc(ring); +out_with_desc_cb: + kfree(ring->desc_cb); + ring->desc_cb = NULL; +out: + return ret; +} + +static void hns3_fini_ring(struct hns3_enet_ring *ring) +{ + hns3_free_desc(ring); + kfree(ring->desc_cb); + ring->desc_cb = NULL; + ring->next_to_clean = 0; + ring->next_to_use = 0; +} + +int hns3_buf_size2type(u32 buf_size) +{ + int bd_size_type; + + switch (buf_size) { + case 512: + bd_size_type = HNS3_BD_SIZE_512_TYPE; + break; + case 1024: + bd_size_type = HNS3_BD_SIZE_1024_TYPE; + break; + case 2048: + bd_size_type = HNS3_BD_SIZE_2048_TYPE; + break; + case 4096: + bd_size_type = HNS3_BD_SIZE_4096_TYPE; + break; + default: + bd_size_type = HNS3_BD_SIZE_2048_TYPE; + } + + return bd_size_type; +} + +static void hns3_init_ring_hw(struct hns3_enet_ring *ring) +{ + dma_addr_t dma = ring->desc_dma_addr; + struct hnae3_queue *q = ring->tqp; + + if (!HNAE3_IS_TX_RING(ring)) { + hns3_write_dev(q, HNS3_RING_RX_RING_BASEADDR_L_REG, + (u32)dma); + hns3_write_dev(q, HNS3_RING_RX_RING_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + + hns3_write_dev(q, HNS3_RING_RX_RING_BD_LEN_REG, + hns3_buf_size2type(ring->buf_size)); + hns3_write_dev(q, HNS3_RING_RX_RING_BD_NUM_REG, + ring->desc_num / 8 - 1); + + } else { + hns3_write_dev(q, HNS3_RING_TX_RING_BASEADDR_L_REG, + (u32)dma); + hns3_write_dev(q, HNS3_RING_TX_RING_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + + hns3_write_dev(q, HNS3_RING_TX_RING_BD_LEN_REG, + hns3_buf_size2type(ring->buf_size)); + hns3_write_dev(q, HNS3_RING_TX_RING_BD_NUM_REG, + ring->desc_num / 8 - 1); + } +} + +static int hns3_init_all_ring(struct hns3_nic_priv *priv) +{ + struct hnae3_handle *h = priv->ae_handle; + int ring_num = h->kinfo.num_tqps * 2; + int i, j; + int ret; + + for (i = 0; i < ring_num; i++) { + ret = hns3_alloc_ring_memory(priv->ring_data[i].ring); + if (ret) { + dev_err(priv->dev, + "Alloc ring memory fail! ret=%d\n", ret); + goto out_when_alloc_ring_memory; + } + + hns3_init_ring_hw(priv->ring_data[i].ring); + + u64_stats_init(&priv->ring_data[i].ring->syncp); + } + + return 0; + +out_when_alloc_ring_memory: + for (j = i - 1; j >= 0; j--) + hns3_fini_ring(priv->ring_data[i].ring); + + return -ENOMEM; +} + +static int hns3_uninit_all_ring(struct hns3_nic_priv *priv) +{ + struct hnae3_handle *h = priv->ae_handle; + int i; + + for (i = 0; i < h->kinfo.num_tqps; i++) { + if (h->ae_algo->ops->reset_queue) + h->ae_algo->ops->reset_queue(h, i); + + hns3_fini_ring(priv->ring_data[i].ring); + hns3_fini_ring(priv->ring_data[i + h->kinfo.num_tqps].ring); + } + + return 0; +} + +/* Set mac addr if it is configured. or leave it to the AE driver */ +static void hns3_init_mac_addr(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + u8 mac_addr_temp[ETH_ALEN]; + + if (h->ae_algo->ops->get_mac_addr) { + h->ae_algo->ops->get_mac_addr(h, mac_addr_temp); + ether_addr_copy(netdev->dev_addr, mac_addr_temp); + } + + /* Check if the MAC address is valid, if not get a random one */ + if (!is_valid_ether_addr(netdev->dev_addr)) { + eth_hw_addr_random(netdev); + dev_warn(priv->dev, "using random MAC address %pM\n", + netdev->dev_addr); + /* Also copy this new MAC address into hdev */ + if (h->ae_algo->ops->set_mac_addr) + h->ae_algo->ops->set_mac_addr(h, netdev->dev_addr); + } +} + +static void hns3_nic_set_priv_ops(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + + if ((netdev->features & NETIF_F_TSO) || + (netdev->features & NETIF_F_TSO6)) { + priv->ops.fill_desc = hns3_fill_desc_tso; + priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tso; + } else { + priv->ops.fill_desc = hns3_fill_desc; + priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tx; + } +} + +static int hns3_client_init(struct hnae3_handle *handle) +{ + struct pci_dev *pdev = handle->pdev; + struct hns3_nic_priv *priv; + struct net_device *netdev; + int ret; + + netdev = alloc_etherdev_mq(sizeof(struct hns3_nic_priv), + handle->kinfo.num_tqps); + if (!netdev) + return -ENOMEM; + + priv = netdev_priv(netdev); + priv->dev = &pdev->dev; + priv->netdev = netdev; + priv->ae_handle = handle; + + handle->kinfo.netdev = netdev; + handle->priv = (void *)priv; + + hns3_init_mac_addr(netdev); + + hns3_set_default_feature(netdev); + + netdev->watchdog_timeo = HNS3_TX_TIMEOUT; + netdev->priv_flags |= IFF_UNICAST_FLT; + netdev->netdev_ops = &hns3_nic_netdev_ops; + SET_NETDEV_DEV(netdev, &pdev->dev); + hns3_ethtool_set_ops(netdev); + hns3_nic_set_priv_ops(netdev); + + /* Carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + + ret = hns3_get_ring_config(priv); + if (ret) { + ret = -ENOMEM; + goto out_get_ring_cfg; + } + + ret = hns3_nic_init_vector_data(priv); + if (ret) { + ret = -ENOMEM; + goto out_init_vector_data; + } + + ret = hns3_init_all_ring(priv); + if (ret) { + ret = -ENOMEM; + goto out_init_ring_data; + } + + ret = register_netdev(netdev); + if (ret) { + dev_err(priv->dev, "probe register netdev fail!\n"); + goto out_reg_netdev_fail; + } + + return ret; + +out_reg_netdev_fail: +out_init_ring_data: + (void)hns3_nic_uninit_vector_data(priv); + priv->ring_data = NULL; +out_init_vector_data: +out_get_ring_cfg: + priv->ae_handle = NULL; + free_netdev(netdev); + return ret; +} + +static void hns3_client_uninit(struct hnae3_handle *handle, bool reset) +{ + struct net_device *netdev = handle->kinfo.netdev; + struct hns3_nic_priv *priv = netdev_priv(netdev); + int ret; + + if (netdev->reg_state != NETREG_UNINITIALIZED) + unregister_netdev(netdev); + + ret = hns3_nic_uninit_vector_data(priv); + if (ret) + netdev_err(netdev, "uninit vector error\n"); + + ret = hns3_uninit_all_ring(priv); + if (ret) + netdev_err(netdev, "uninit ring error\n"); + + priv->ring_data = NULL; + + free_netdev(netdev); +} + +static void hns3_link_status_change(struct hnae3_handle *handle, bool linkup) +{ + struct net_device *netdev = handle->kinfo.netdev; + + if (!netdev) + return; + + if (linkup) { + netif_carrier_on(netdev); + netif_tx_wake_all_queues(netdev); + netdev_info(netdev, "link up\n"); + } else { + netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); + netdev_info(netdev, "link down\n"); + } +} + +const struct hnae3_client_ops client_ops = { + .init_instance = hns3_client_init, + .uninit_instance = hns3_client_uninit, + .link_status_change = hns3_link_status_change, +}; + +/* hns3_init_module - Driver registration routine + * hns3_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + */ +static int __init hns3_init_module(void) +{ + int ret; + + pr_info("%s: %s - version\n", hns3_driver_name, hns3_driver_string); + pr_info("%s: %s\n", hns3_driver_name, hns3_copyright); + + client.type = HNAE3_CLIENT_KNIC; + snprintf(client.name, HNAE3_CLIENT_NAME_LENGTH - 1, "%s", + hns3_driver_name); + + client.ops = &client_ops; + + ret = hnae3_register_client(&client); + if (ret) + return ret; + + ret = pci_register_driver(&hns3_driver); + if (ret) + hnae3_unregister_client(&client); + + return ret; +} +module_init(hns3_init_module); + +/* hns3_exit_module - Driver exit cleanup routine + * hns3_exit_module is called just before the driver is removed + * from memory. + */ +static void __exit hns3_exit_module(void) +{ + pci_unregister_driver(&hns3_driver); + hnae3_unregister_client(&client); +} +module_exit(hns3_exit_module); + +MODULE_DESCRIPTION("HNS3: Hisilicon Ethernet Driver"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("pci:hns-nic"); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h new file mode 100644 index 000000000000..a6e8f15a4669 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2016 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNS3_ENET_H +#define __HNS3_ENET_H + +#include "hnae3.h" + +extern const char hns3_driver_version[]; + +enum hns3_nic_state { + HNS3_NIC_STATE_TESTING, + HNS3_NIC_STATE_RESETTING, + HNS3_NIC_STATE_REINITING, + HNS3_NIC_STATE_DOWN, + HNS3_NIC_STATE_DISABLED, + HNS3_NIC_STATE_REMOVING, + HNS3_NIC_STATE_SERVICE_INITED, + HNS3_NIC_STATE_SERVICE_SCHED, + HNS3_NIC_STATE2_RESET_REQUESTED, + HNS3_NIC_STATE_MAX +}; + +#define HNS3_RING_RX_RING_BASEADDR_L_REG 0x00000 +#define HNS3_RING_RX_RING_BASEADDR_H_REG 0x00004 +#define HNS3_RING_RX_RING_BD_NUM_REG 0x00008 +#define HNS3_RING_RX_RING_BD_LEN_REG 0x0000C +#define HNS3_RING_RX_RING_TAIL_REG 0x00018 +#define HNS3_RING_RX_RING_HEAD_REG 0x0001C +#define HNS3_RING_RX_RING_FBDNUM_REG 0x00020 +#define HNS3_RING_RX_RING_PKTNUM_RECORD_REG 0x0002C + +#define HNS3_RING_TX_RING_BASEADDR_L_REG 0x00040 +#define HNS3_RING_TX_RING_BASEADDR_H_REG 0x00044 +#define HNS3_RING_TX_RING_BD_NUM_REG 0x00048 +#define HNS3_RING_TX_RING_BD_LEN_REG 0x0004C +#define HNS3_RING_TX_RING_TAIL_REG 0x00058 +#define HNS3_RING_TX_RING_HEAD_REG 0x0005C +#define HNS3_RING_TX_RING_FBDNUM_REG 0x00060 +#define HNS3_RING_TX_RING_OFFSET_REG 0x00064 +#define HNS3_RING_TX_RING_PKTNUM_RECORD_REG 0x0006C + +#define HNS3_RING_PREFETCH_EN_REG 0x0007C +#define HNS3_RING_CFG_VF_NUM_REG 0x00080 +#define HNS3_RING_ASID_REG 0x0008C +#define HNS3_RING_RX_VM_REG 0x00090 +#define HNS3_RING_T0_BE_RST 0x00094 +#define HNS3_RING_COULD_BE_RST 0x00098 +#define HNS3_RING_WRR_WEIGHT_REG 0x0009c + +#define HNS3_RING_INTMSK_RXWL_REG 0x000A0 +#define HNS3_RING_INTSTS_RX_RING_REG 0x000A4 +#define HNS3_RX_RING_INT_STS_REG 0x000A8 +#define HNS3_RING_INTMSK_TXWL_REG 0x000AC +#define HNS3_RING_INTSTS_TX_RING_REG 0x000B0 +#define HNS3_TX_RING_INT_STS_REG 0x000B4 +#define HNS3_RING_INTMSK_RX_OVERTIME_REG 0x000B8 +#define HNS3_RING_INTSTS_RX_OVERTIME_REG 0x000BC +#define HNS3_RING_INTMSK_TX_OVERTIME_REG 0x000C4 +#define HNS3_RING_INTSTS_TX_OVERTIME_REG 0x000C8 + +#define HNS3_RING_MB_CTRL_REG 0x00100 +#define HNS3_RING_MB_DATA_BASE_REG 0x00200 + +#define HNS3_TX_REG_OFFSET 0x40 + +#define HNS3_RX_HEAD_SIZE 256 + +#define HNS3_TX_TIMEOUT (5 * HZ) +#define HNS3_RING_NAME_LEN 16 +#define HNS3_BUFFER_SIZE_2048 2048 +#define HNS3_RING_MAX_PENDING 32768 + +#define HNS3_BD_SIZE_512_TYPE 0 +#define HNS3_BD_SIZE_1024_TYPE 1 +#define HNS3_BD_SIZE_2048_TYPE 2 +#define HNS3_BD_SIZE_4096_TYPE 3 + +#define HNS3_RX_FLAG_VLAN_PRESENT 0x1 +#define HNS3_RX_FLAG_L3ID_IPV4 0x0 +#define HNS3_RX_FLAG_L3ID_IPV6 0x1 +#define HNS3_RX_FLAG_L4ID_UDP 0x0 +#define HNS3_RX_FLAG_L4ID_TCP 0x1 + +#define HNS3_RXD_DMAC_S 0 +#define HNS3_RXD_DMAC_M (0x3 << HNS3_RXD_DMAC_S) +#define HNS3_RXD_VLAN_S 2 +#define HNS3_RXD_VLAN_M (0x3 << HNS3_RXD_VLAN_S) +#define HNS3_RXD_L3ID_S 4 +#define HNS3_RXD_L3ID_M (0xf << HNS3_RXD_L3ID_S) +#define HNS3_RXD_L4ID_S 8 +#define HNS3_RXD_L4ID_M (0xf << HNS3_RXD_L4ID_S) +#define HNS3_RXD_FRAG_B 12 +#define HNS3_RXD_L2E_B 16 +#define HNS3_RXD_L3E_B 17 +#define HNS3_RXD_L4E_B 18 +#define HNS3_RXD_TRUNCAT_B 19 +#define HNS3_RXD_HOI_B 20 +#define HNS3_RXD_DOI_B 21 +#define HNS3_RXD_OL3E_B 22 +#define HNS3_RXD_OL4E_B 23 + +#define HNS3_RXD_ODMAC_S 0 +#define HNS3_RXD_ODMAC_M (0x3 << HNS3_RXD_ODMAC_S) +#define HNS3_RXD_OVLAN_S 2 +#define HNS3_RXD_OVLAN_M (0x3 << HNS3_RXD_OVLAN_S) +#define HNS3_RXD_OL3ID_S 4 +#define HNS3_RXD_OL3ID_M (0xf << HNS3_RXD_OL3ID_S) +#define HNS3_RXD_OL4ID_S 8 +#define HNS3_RXD_OL4ID_M (0xf << HNS3_RXD_OL4ID_S) +#define HNS3_RXD_FBHI_S 12 +#define HNS3_RXD_FBHI_M (0x3 << HNS3_RXD_FBHI_S) +#define HNS3_RXD_FBLI_S 14 +#define HNS3_RXD_FBLI_M (0x3 << HNS3_RXD_FBLI_S) + +#define HNS3_RXD_BDTYPE_S 0 +#define HNS3_RXD_BDTYPE_M (0xf << HNS3_RXD_BDTYPE_S) +#define HNS3_RXD_VLD_B 4 +#define HNS3_RXD_UDP0_B 5 +#define HNS3_RXD_EXTEND_B 7 +#define HNS3_RXD_FE_B 8 +#define HNS3_RXD_LUM_B 9 +#define HNS3_RXD_CRCP_B 10 +#define HNS3_RXD_L3L4P_B 11 +#define HNS3_RXD_TSIND_S 12 +#define HNS3_RXD_TSIND_M (0x7 << HNS3_RXD_TSIND_S) +#define HNS3_RXD_LKBK_B 15 +#define HNS3_RXD_HDL_S 16 +#define HNS3_RXD_HDL_M (0x7ff << HNS3_RXD_HDL_S) +#define HNS3_RXD_HSIND_B 31 + +#define HNS3_TXD_L3T_S 0 +#define HNS3_TXD_L3T_M (0x3 << HNS3_TXD_L3T_S) +#define HNS3_TXD_L4T_S 2 +#define HNS3_TXD_L4T_M (0x3 << HNS3_TXD_L4T_S) +#define HNS3_TXD_L3CS_B 4 +#define HNS3_TXD_L4CS_B 5 +#define HNS3_TXD_VLAN_B 6 +#define HNS3_TXD_TSO_B 7 + +#define HNS3_TXD_L2LEN_S 8 +#define HNS3_TXD_L2LEN_M (0xff << HNS3_TXD_L2LEN_S) +#define HNS3_TXD_L3LEN_S 16 +#define HNS3_TXD_L3LEN_M (0xff << HNS3_TXD_L3LEN_S) +#define HNS3_TXD_L4LEN_S 24 +#define HNS3_TXD_L4LEN_M (0xff << HNS3_TXD_L4LEN_S) + +#define HNS3_TXD_OL3T_S 0 +#define HNS3_TXD_OL3T_M (0x3 << HNS3_TXD_OL3T_S) +#define HNS3_TXD_OVLAN_B 2 +#define HNS3_TXD_MACSEC_B 3 +#define HNS3_TXD_TUNTYPE_S 4 +#define HNS3_TXD_TUNTYPE_M (0xf << HNS3_TXD_TUNTYPE_S) + +#define HNS3_TXD_BDTYPE_S 0 +#define HNS3_TXD_BDTYPE_M (0xf << HNS3_TXD_BDTYPE_S) +#define HNS3_TXD_FE_B 4 +#define HNS3_TXD_SC_S 5 +#define HNS3_TXD_SC_M (0x3 << HNS3_TXD_SC_S) +#define HNS3_TXD_EXTEND_B 7 +#define HNS3_TXD_VLD_B 8 +#define HNS3_TXD_RI_B 9 +#define HNS3_TXD_RA_B 10 +#define HNS3_TXD_TSYN_B 11 +#define HNS3_TXD_DECTTL_S 12 +#define HNS3_TXD_DECTTL_M (0xf << HNS3_TXD_DECTTL_S) + +#define HNS3_TXD_MSS_S 0 +#define HNS3_TXD_MSS_M (0x3fff << HNS3_TXD_MSS_S) + +#define HNS3_VECTOR_TX_IRQ BIT_ULL(0) +#define HNS3_VECTOR_RX_IRQ BIT_ULL(1) + +#define HNS3_VECTOR_NOT_INITED 0 +#define HNS3_VECTOR_INITED 1 + +#define HNS3_MAX_BD_SIZE 65535 +#define HNS3_MAX_BD_PER_FRAG 8 +#define HNS3_MAX_BD_PER_PKT MAX_SKB_FRAGS + +#define HNS3_VECTOR_GL0_OFFSET 0x100 +#define HNS3_VECTOR_GL1_OFFSET 0x200 +#define HNS3_VECTOR_GL2_OFFSET 0x300 +#define HNS3_VECTOR_RL_OFFSET 0x900 +#define HNS3_VECTOR_RL_EN_B 6 + +enum hns3_pkt_l3t_type { + HNS3_L3T_NONE, + HNS3_L3T_IPV6, + HNS3_L3T_IPV4, + HNS3_L3T_RESERVED +}; + +enum hns3_pkt_l4t_type { + HNS3_L4T_UNKNOWN, + HNS3_L4T_TCP, + HNS3_L4T_UDP, + HNS3_L4T_SCTP +}; + +enum hns3_pkt_ol3t_type { + HNS3_OL3T_NONE, + HNS3_OL3T_IPV6, + HNS3_OL3T_IPV4_NO_CSUM, + HNS3_OL3T_IPV4_CSUM +}; + +enum hns3_pkt_tun_type { + HNS3_TUN_NONE, + HNS3_TUN_MAC_IN_UDP, + HNS3_TUN_NVGRE, + HNS3_TUN_OTHER +}; + +/* hardware spec ring buffer format */ +struct __packed hns3_desc { + __le64 addr; + union { + struct { + __le16 vlan_tag; + __le16 send_size; + union { + __le32 type_cs_vlan_tso_len; + struct { + __u8 type_cs_vlan_tso; + __u8 l2_len; + __u8 l3_len; + __u8 l4_len; + }; + }; + __le16 outer_vlan_tag; + __le16 tv; + + union { + __le32 ol_type_vlan_len_msec; + struct { + __u8 ol_type_vlan_msec; + __u8 ol2_len; + __u8 ol3_len; + __u8 ol4_len; + }; + }; + + __le32 paylen; + __le16 bdtp_fe_sc_vld_ra_ri; + __le16 mss; + } tx; + + struct { + __le32 l234_info; + __le16 pkt_len; + __le16 size; + + __le32 rss_hash; + __le16 fd_id; + __le16 vlan_tag; + + union { + __le32 ol_info; + struct { + __le16 o_dm_vlan_id_fb; + __le16 ot_vlan_tag; + }; + }; + + __le32 bd_base_info; + } rx; + }; +}; + +struct hns3_desc_cb { + dma_addr_t dma; /* dma address of this desc */ + void *buf; /* cpu addr for a desc */ + + /* priv data for the desc, e.g. skb when use with ip stack*/ + void *priv; + u16 page_offset; + u16 reuse_flag; + + u16 length; /* length of the buffer */ + + /* desc type, used by the ring user to mark the type of the priv data */ + u16 type; +}; + +enum hns3_pkt_l3type { + HNS3_L3_TYPE_IPV4, + HNS3_L3_TYPE_IPV6, + HNS3_L3_TYPE_ARP, + HNS3_L3_TYPE_RARP, + HNS3_L3_TYPE_IPV4_OPT, + HNS3_L3_TYPE_IPV6_EXT, + HNS3_L3_TYPE_LLDP, + HNS3_L3_TYPE_BPDU, + HNS3_L3_TYPE_MAC_PAUSE, + HNS3_L3_TYPE_PFC_PAUSE,/* 0x9*/ + + /* reserved for 0xA~0xB*/ + + HNS3_L3_TYPE_CNM = 0xc, + + /* reserved for 0xD~0xE*/ + + HNS3_L3_TYPE_PARSE_FAIL = 0xf /* must be last */ +}; + +enum hns3_pkt_l4type { + HNS3_L4_TYPE_UDP, + HNS3_L4_TYPE_TCP, + HNS3_L4_TYPE_GRE, + HNS3_L4_TYPE_SCTP, + HNS3_L4_TYPE_IGMP, + HNS3_L4_TYPE_ICMP, + + /* reserved for 0x6~0xE */ + + HNS3_L4_TYPE_PARSE_FAIL = 0xf /* must be last */ +}; + +enum hns3_pkt_ol3type { + HNS3_OL3_TYPE_IPV4 = 0, + HNS3_OL3_TYPE_IPV6, + /* reserved for 0x2~0x3 */ + HNS3_OL3_TYPE_IPV4_OPT = 4, + HNS3_OL3_TYPE_IPV6_EXT, + + /* reserved for 0x6~0xE*/ + + HNS3_OL3_TYPE_PARSE_FAIL = 0xf /* must be last */ +}; + +enum hns3_pkt_ol4type { + HNS3_OL4_TYPE_NO_TUN, + HNS3_OL4_TYPE_MAC_IN_UDP, + HNS3_OL4_TYPE_NVGRE, + HNS3_OL4_TYPE_UNKNOWN +}; + +struct ring_stats { + u64 io_err_cnt; + u64 sw_err_cnt; + u64 seg_pkt_cnt; + union { + struct { + u64 tx_pkts; + u64 tx_bytes; + u64 tx_err_cnt; + u64 restart_queue; + u64 tx_busy; + }; + struct { + u64 rx_pkts; + u64 rx_bytes; + u64 rx_err_cnt; + u64 reuse_pg_cnt; + u64 err_pkt_len; + u64 non_vld_descs; + u64 err_bd_num; + u64 l2_err; + u64 l3l4_csum_err; + }; + }; +}; + +struct hns3_enet_ring { + u8 __iomem *io_base; /* base io address for the ring */ + struct hns3_desc *desc; /* dma map address space */ + struct hns3_desc_cb *desc_cb; + struct hns3_enet_ring *next; + struct hns3_enet_tqp_vector *tqp_vector; + struct hnae3_queue *tqp; + char ring_name[HNS3_RING_NAME_LEN]; + struct device *dev; /* will be used for DMA mapping of descriptors */ + + /* statistic */ + struct ring_stats stats; + struct u64_stats_sync syncp; + + dma_addr_t desc_dma_addr; + u32 buf_size; /* size for hnae_desc->addr, preset by AE */ + u16 desc_num; /* total number of desc */ + u16 max_desc_num_per_pkt; + u16 max_raw_data_sz_per_desc; + u16 max_pkt_size; + int next_to_use; /* idx of next spare desc */ + + /* idx of lastest sent desc, the ring is empty when equal to + * next_to_use + */ + int next_to_clean; + + u32 flag; /* ring attribute */ + int irq_init_flag; + + int numa_node; + cpumask_t affinity_mask; +}; + +struct hns_queue; + +struct hns3_nic_ring_data { + struct hns3_enet_ring *ring; + struct napi_struct napi; + int queue_index; + int (*poll_one)(struct hns3_nic_ring_data *, int, void *); + void (*ex_process)(struct hns3_nic_ring_data *, struct sk_buff *); + void (*fini_process)(struct hns3_nic_ring_data *); +}; + +struct hns3_nic_ops { + int (*fill_desc)(struct hns3_enet_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + enum hns_desc_type type); + int (*maybe_stop_tx)(struct sk_buff **out_skb, + int *bnum, struct hns3_enet_ring *ring); + void (*get_rxd_bnum)(u32 bnum_flag, int *out_bnum); +}; + +enum hns3_flow_level_range { + HNS3_FLOW_LOW = 0, + HNS3_FLOW_MID = 1, + HNS3_FLOW_HIGH = 2, + HNS3_FLOW_ULTRA = 3, +}; + +enum hns3_link_mode_bits { + HNS3_LM_FIBRE_BIT = BIT(0), + HNS3_LM_AUTONEG_BIT = BIT(1), + HNS3_LM_TP_BIT = BIT(2), + HNS3_LM_PAUSE_BIT = BIT(3), + HNS3_LM_BACKPLANE_BIT = BIT(4), + HNS3_LM_10BASET_HALF_BIT = BIT(5), + HNS3_LM_10BASET_FULL_BIT = BIT(6), + HNS3_LM_100BASET_HALF_BIT = BIT(7), + HNS3_LM_100BASET_FULL_BIT = BIT(8), + HNS3_LM_1000BASET_FULL_BIT = BIT(9), + HNS3_LM_10000BASEKR_FULL_BIT = BIT(10), + HNS3_LM_25000BASEKR_FULL_BIT = BIT(11), + HNS3_LM_40000BASELR4_FULL_BIT = BIT(12), + HNS3_LM_50000BASEKR2_FULL_BIT = BIT(13), + HNS3_LM_100000BASEKR4_FULL_BIT = BIT(14), + HNS3_LM_COUNT = 15 +}; + +#define HNS3_INT_GL_50K 0x000A +#define HNS3_INT_GL_20K 0x0019 +#define HNS3_INT_GL_18K 0x001B +#define HNS3_INT_GL_8K 0x003E + +struct hns3_enet_ring_group { + /* array of pointers to rings */ + struct hns3_enet_ring *ring; + u64 total_bytes; /* total bytes processed this group */ + u64 total_packets; /* total packets processed this group */ + u16 count; + enum hns3_flow_level_range flow_level; + u16 int_gl; +}; + +struct hns3_enet_tqp_vector { + struct hnae3_handle *handle; + u8 __iomem *mask_addr; + int vector_irq; + int irq_init_flag; + + u16 idx; /* index in the TQP vector array per handle. */ + + struct napi_struct napi; + + struct hns3_enet_ring_group rx_group; + struct hns3_enet_ring_group tx_group; + + u16 num_tqps; /* total number of tqps in TQP vector */ + + cpumask_t affinity_mask; + char name[HNAE3_INT_NAME_LEN]; + + /* when 0 should adjust interrupt coalesce parameter */ + u8 int_adapt_down; +} ____cacheline_internodealigned_in_smp; + +enum hns3_udp_tnl_type { + HNS3_UDP_TNL_VXLAN, + HNS3_UDP_TNL_GENEVE, + HNS3_UDP_TNL_MAX, +}; + +struct hns3_udp_tunnel { + u16 dst_port; + int used; +}; + +struct hns3_nic_priv { + struct hnae3_handle *ae_handle; + u32 enet_ver; + u32 port_id; + struct net_device *netdev; + struct device *dev; + struct hns3_nic_ops ops; + + /** + * the cb for nic to manage the ring buffer, the first half of the + * array is for tx_ring and vice versa for the second half + */ + struct hns3_nic_ring_data *ring_data; + struct hns3_enet_tqp_vector *tqp_vector; + u16 vector_num; + + /* The most recently read link state */ + int link; + u64 tx_timeout_count; + + unsigned long state; + + struct timer_list service_timer; + + struct work_struct service_task; + + struct notifier_block notifier_block; + /* Vxlan/Geneve information */ + struct hns3_udp_tunnel udp_tnl[HNS3_UDP_TNL_MAX]; +}; + +union l3_hdr_info { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; +}; + +union l4_hdr_info { + struct tcphdr *tcp; + struct udphdr *udp; + unsigned char *hdr; +}; + +/* the distance between [begin, end) in a ring buffer + * note: there is a unuse slot between the begin and the end + */ +static inline int ring_dist(struct hns3_enet_ring *ring, int begin, int end) +{ + return (end - begin + ring->desc_num) % ring->desc_num; +} + +static inline int ring_space(struct hns3_enet_ring *ring) +{ + return ring->desc_num - + ring_dist(ring, ring->next_to_clean, ring->next_to_use) - 1; +} + +static inline int is_ring_empty(struct hns3_enet_ring *ring) +{ + return ring->next_to_use == ring->next_to_clean; +} + +static inline void hns3_write_reg(void __iomem *base, u32 reg, u32 value) +{ + u8 __iomem *reg_addr = READ_ONCE(base); + + writel(value, reg_addr + reg); +} + +#define hns3_write_dev(a, reg, value) \ + hns3_write_reg((a)->io_base, (reg), (value)) + +#define hnae_queue_xmit(tqp, buf_num) writel_relaxed(buf_num, \ + (tqp)->io_base + HNS3_RING_TX_RING_TAIL_REG) + +#define ring_to_dev(ring) (&(ring)->tqp->handle->pdev->dev) + +#define ring_to_dma_dir(ring) (HNAE3_IS_TX_RING(ring) ? \ + DMA_TO_DEVICE : DMA_FROM_DEVICE) + +#define tx_ring_data(priv, idx) ((priv)->ring_data[idx]) + +#define hnae_buf_size(_ring) ((_ring)->buf_size) +#define hnae_page_order(_ring) (get_order(hnae_buf_size(_ring))) +#define hnae_page_size(_ring) (PAGE_SIZE << hnae_page_order(_ring)) + +/* iterator for handling rings in ring group */ +#define hns3_for_each_ring(pos, head) \ + for (pos = (head).ring; pos; pos = pos->next) + +void hns3_ethtool_set_ops(struct net_device *netdev); + +int hns3_clean_tx_ring(struct hns3_enet_ring *ring, int budget); +#endif -- cgit v1.2.3 From 38caee9d3ee8b32b36912f54e19438eddd126772 Mon Sep 17 00:00:00 2001 From: Salil Date: Wed, 2 Aug 2017 16:59:46 +0100 Subject: net: hns3: Add support of the HNAE3 framework This patch adds the support of the HNAE3 (Hisilicon Network Acceleration Engine 3) framework support to the HNS3 driver. Framework facilitates clients like ENET(HNS3 Ethernet Driver), RoCE and user-space Ethernet drivers (like ODP etc.) to register with HNAE3 devices and their associated operations. Signed-off-by: Daode Huang Signed-off-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: Yisen Zhuang Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hnae3.c | 300 +++++++++++++++++++ drivers/net/ethernet/hisilicon/hns3/hnae3.h | 444 ++++++++++++++++++++++++++++ 2 files changed, 744 insertions(+) create mode 100644 drivers/net/ethernet/hisilicon/hns3/hnae3.c create mode 100644 drivers/net/ethernet/hisilicon/hns3/hnae3.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.c b/drivers/net/ethernet/hisilicon/hns3/hnae3.c new file mode 100644 index 000000000000..59efbd605416 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2016-2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "hnae3.h" + +static LIST_HEAD(hnae3_ae_algo_list); +static LIST_HEAD(hnae3_client_list); +static LIST_HEAD(hnae3_ae_dev_list); + +/* we are keeping things simple and using single lock for all the + * list. This is a non-critical code so other updations, if happen + * in parallel, can wait. + */ +static DEFINE_MUTEX(hnae3_common_lock); + +static bool hnae3_client_match(enum hnae3_client_type client_type, + enum hnae3_dev_type dev_type) +{ + if ((dev_type == HNAE3_DEV_KNIC) && (client_type == HNAE3_CLIENT_KNIC || + client_type == HNAE3_CLIENT_ROCE)) + return true; + + if (dev_type == HNAE3_DEV_UNIC && client_type == HNAE3_CLIENT_UNIC) + return true; + + return false; +} + +static int hnae3_match_n_instantiate(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev, + bool is_reg, bool *matched) +{ + int ret; + + *matched = false; + + /* check if this client matches the type of ae_dev */ + if (!(hnae3_client_match(client->type, ae_dev->dev_type) && + hnae_get_bit(ae_dev->flag, HNAE3_DEV_INITED_B))) { + return 0; + } + /* there is a match of client and dev */ + *matched = true; + + /* now, (un-)instantiate client by calling lower layer */ + if (is_reg) { + ret = ae_dev->ops->init_client_instance(client, ae_dev); + if (ret) + dev_err(&ae_dev->pdev->dev, + "fail to instantiate client\n"); + return ret; + } + + ae_dev->ops->uninit_client_instance(client, ae_dev); + return 0; +} + +int hnae3_register_client(struct hnae3_client *client) +{ + struct hnae3_client *client_tmp; + struct hnae3_ae_dev *ae_dev; + bool matched; + int ret = 0; + + mutex_lock(&hnae3_common_lock); + /* one system should only have one client for every type */ + list_for_each_entry(client_tmp, &hnae3_client_list, node) { + if (client_tmp->type == client->type) + goto exit; + } + + list_add_tail(&client->node, &hnae3_client_list); + + /* initialize the client on every matched port */ + list_for_each_entry(ae_dev, &hnae3_ae_dev_list, node) { + /* if the client could not be initialized on current port, for + * any error reasons, move on to next available port + */ + ret = hnae3_match_n_instantiate(client, ae_dev, true, &matched); + if (ret) + dev_err(&ae_dev->pdev->dev, + "match and instantiation failed for port\n"); + } + +exit: + mutex_unlock(&hnae3_common_lock); + + return ret; +} +EXPORT_SYMBOL(hnae3_register_client); + +void hnae3_unregister_client(struct hnae3_client *client) +{ + struct hnae3_ae_dev *ae_dev; + bool matched; + + mutex_lock(&hnae3_common_lock); + /* un-initialize the client on every matched port */ + list_for_each_entry(ae_dev, &hnae3_ae_dev_list, node) { + hnae3_match_n_instantiate(client, ae_dev, false, &matched); + } + + list_del(&client->node); + mutex_unlock(&hnae3_common_lock); +} +EXPORT_SYMBOL(hnae3_unregister_client); + +/* hnae3_register_ae_algo - register a AE algorithm to hnae3 framework + * @ae_algo: AE algorithm + * NOTE: the duplicated name will not be checked + */ +int hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo) +{ + const struct pci_device_id *id; + struct hnae3_ae_dev *ae_dev; + struct hnae3_client *client; + bool matched; + int ret = 0; + + mutex_lock(&hnae3_common_lock); + + list_add_tail(&ae_algo->node, &hnae3_ae_algo_list); + + /* Check if this algo/ops matches the list of ae_devs */ + list_for_each_entry(ae_dev, &hnae3_ae_dev_list, node) { + id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev); + if (!id) + continue; + + /* ae_dev init should set flag */ + ae_dev->ops = ae_algo->ops; + ret = ae_algo->ops->init_ae_dev(ae_dev); + if (ret) { + dev_err(&ae_dev->pdev->dev, "init ae_dev error.\n"); + continue; + } + + hnae_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 1); + + /* check the client list for the match with this ae_dev type and + * initialize the figure out client instance + */ + list_for_each_entry(client, &hnae3_client_list, node) { + ret = hnae3_match_n_instantiate(client, ae_dev, true, + &matched); + if (ret) + dev_err(&ae_dev->pdev->dev, + "match and instantiation failed\n"); + if (matched) + break; + } + } + + mutex_unlock(&hnae3_common_lock); + + return ret; +} +EXPORT_SYMBOL(hnae3_register_ae_algo); + +/* hnae3_unregister_ae_algo - unregisters a AE algorithm + * @ae_algo: the AE algorithm to unregister + */ +void hnae3_unregister_ae_algo(struct hnae3_ae_algo *ae_algo) +{ + const struct pci_device_id *id; + struct hnae3_ae_dev *ae_dev; + struct hnae3_client *client; + bool matched; + + mutex_lock(&hnae3_common_lock); + /* Check if there are matched ae_dev */ + list_for_each_entry(ae_dev, &hnae3_ae_dev_list, node) { + id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev); + if (!id) + continue; + + /* check the client list for the match with this ae_dev type and + * un-initialize the figure out client instance + */ + list_for_each_entry(client, &hnae3_client_list, node) { + hnae3_match_n_instantiate(client, ae_dev, false, + &matched); + if (matched) + break; + } + + ae_algo->ops->uninit_ae_dev(ae_dev); + hnae_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 0); + } + + list_del(&ae_algo->node); + mutex_unlock(&hnae3_common_lock); +} +EXPORT_SYMBOL(hnae3_unregister_ae_algo); + +/* hnae3_register_ae_dev - registers a AE device to hnae3 framework + * @ae_dev: the AE device + * NOTE: the duplicated name will not be checked + */ +int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev) +{ + const struct pci_device_id *id; + struct hnae3_ae_algo *ae_algo; + struct hnae3_client *client; + bool matched; + int ret = 0; + + mutex_lock(&hnae3_common_lock); + list_add_tail(&ae_dev->node, &hnae3_ae_dev_list); + + /* Check if there are matched ae_algo */ + list_for_each_entry(ae_algo, &hnae3_ae_algo_list, node) { + id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev); + if (!id) + continue; + + ae_dev->ops = ae_algo->ops; + + if (!ae_dev->ops) { + dev_err(&ae_dev->pdev->dev, "ae_dev ops are null\n"); + goto out_err; + } + + /* ae_dev init should set flag */ + ret = ae_dev->ops->init_ae_dev(ae_dev); + if (ret) { + dev_err(&ae_dev->pdev->dev, "init ae_dev error\n"); + goto out_err; + } + + hnae_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 1); + break; + } + + /* check the client list for the match with this ae_dev type and + * initialize the figure out client instance + */ + list_for_each_entry(client, &hnae3_client_list, node) { + ret = hnae3_match_n_instantiate(client, ae_dev, true, + &matched); + if (ret) + dev_err(&ae_dev->pdev->dev, + "match and instantiation failed\n"); + if (matched) + break; + } + +out_err: + mutex_unlock(&hnae3_common_lock); + + return ret; +} +EXPORT_SYMBOL(hnae3_register_ae_dev); + +/* hnae3_unregister_ae_dev - unregisters a AE device + * @ae_dev: the AE device to unregister + */ +void hnae3_unregister_ae_dev(struct hnae3_ae_dev *ae_dev) +{ + const struct pci_device_id *id; + struct hnae3_ae_algo *ae_algo; + struct hnae3_client *client; + bool matched; + + mutex_lock(&hnae3_common_lock); + /* Check if there are matched ae_algo */ + list_for_each_entry(ae_algo, &hnae3_ae_algo_list, node) { + id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev); + if (!id) + continue; + + list_for_each_entry(client, &hnae3_client_list, node) { + hnae3_match_n_instantiate(client, ae_dev, false, + &matched); + if (matched) + break; + } + + ae_algo->ops->uninit_ae_dev(ae_dev); + hnae_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 0); + } + + list_del(&ae_dev->node); + mutex_unlock(&hnae3_common_lock); +} +EXPORT_SYMBOL(hnae3_unregister_ae_dev); + +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("HNAE3(Hisilicon Network Acceleration Engine) Framework"); diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h new file mode 100644 index 000000000000..b2f28ae81273 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2016-2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNAE3_H +#define __HNAE3_H + +/* Names used in this framework: + * ae handle (handle): + * a set of queues provided by AE + * ring buffer queue (rbq): + * the channel between upper layer and the AE, can do tx and rx + * ring: + * a tx or rx channel within a rbq + * ring description (desc): + * an element in the ring with packet information + * buffer: + * a memory region referred by desc with the full packet payload + * + * "num" means a static number set as a parameter, "count" mean a dynamic + * number set while running + * "cb" means control block + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Device IDs */ +#define HNAE3_DEV_ID_GE 0xA220 +#define HNAE3_DEV_ID_25GE 0xA221 +#define HNAE3_DEV_ID_25GE_RDMA 0xA222 +#define HNAE3_DEV_ID_25GE_RDMA_MACSEC 0xA223 +#define HNAE3_DEV_ID_50GE_RDMA 0xA224 +#define HNAE3_DEV_ID_50GE_RDMA_MACSEC 0xA225 +#define HNAE3_DEV_ID_100G_RDMA_MACSEC 0xA226 +#define HNAE3_DEV_ID_100G_VF 0xA22E +#define HNAE3_DEV_ID_100G_RDMA_DCB_PFC_VF 0xA22F + +#define HNAE3_CLASS_NAME_SIZE 16 + +#define HNAE3_DEV_INITED_B 0x0 +#define HNAE_DEV_SUPPORT_ROCE_B 0x1 + +#define ring_ptr_move_fw(ring, p) \ + ((ring)->p = ((ring)->p + 1) % (ring)->desc_num) +#define ring_ptr_move_bw(ring, p) \ + ((ring)->p = ((ring)->p - 1 + (ring)->desc_num) % (ring)->desc_num) + +enum hns_desc_type { + DESC_TYPE_SKB, + DESC_TYPE_PAGE, +}; + +struct hnae3_handle; + +struct hnae3_queue { + void __iomem *io_base; + struct hnae3_ae_algo *ae_algo; + struct hnae3_handle *handle; + int tqp_index; /* index in a handle */ + u32 buf_size; /* size for hnae_desc->addr, preset by AE */ + u16 desc_num; /* total number of desc */ +}; + +/*hnae3 loop mode*/ +enum hnae3_loop { + HNAE3_MAC_INTER_LOOP_MAC, + HNAE3_MAC_INTER_LOOP_SERDES, + HNAE3_MAC_INTER_LOOP_PHY, + HNAE3_MAC_LOOP_NONE, +}; + +enum hnae3_client_type { + HNAE3_CLIENT_KNIC, + HNAE3_CLIENT_UNIC, + HNAE3_CLIENT_ROCE, +}; + +enum hnae3_dev_type { + HNAE3_DEV_KNIC, + HNAE3_DEV_UNIC, +}; + +/* mac media type */ +enum hnae3_media_type { + HNAE3_MEDIA_TYPE_UNKNOWN, + HNAE3_MEDIA_TYPE_FIBER, + HNAE3_MEDIA_TYPE_COPPER, + HNAE3_MEDIA_TYPE_BACKPLANE, +}; + +struct hnae3_vector_info { + u8 __iomem *io_addr; + int vector; +}; + +#define HNAE3_RING_TYPE_B 0 +#define HNAE3_RING_TYPE_TX 0 +#define HNAE3_RING_TYPE_RX 1 + +struct hnae3_ring_chain_node { + struct hnae3_ring_chain_node *next; + u32 tqp_index; + u32 flag; +}; + +#define HNAE3_IS_TX_RING(node) \ + (((node)->flag & (1 << HNAE3_RING_TYPE_B)) == HNAE3_RING_TYPE_TX) + +struct hnae3_client_ops { + int (*init_instance)(struct hnae3_handle *handle); + void (*uninit_instance)(struct hnae3_handle *handle, bool reset); + void (*link_status_change)(struct hnae3_handle *handle, bool state); +}; + +#define HNAE3_CLIENT_NAME_LENGTH 16 +struct hnae3_client { + char name[HNAE3_CLIENT_NAME_LENGTH]; + u16 version; + unsigned long state; + enum hnae3_client_type type; + const struct hnae3_client_ops *ops; + struct list_head node; +}; + +struct hnae3_ae_dev { + struct pci_dev *pdev; + const struct hnae3_ae_ops *ops; + struct list_head node; + u32 flag; + enum hnae3_dev_type dev_type; + void *priv; +}; + +/* This struct defines the operation on the handle. + * + * init_ae_dev(): (mandatory) + * Get PF configure from pci_dev and initialize PF hardware + * uninit_ae_dev() + * Disable PF device and release PF resource + * register_client + * Register client to ae_dev + * unregister_client() + * Unregister client from ae_dev + * start() + * Enable the hardware + * stop() + * Disable the hardware + * get_status() + * Get the carrier state of the back channel of the handle, 1 for ok, 0 for + * non-ok + * get_ksettings_an_result() + * Get negotiation status,speed and duplex + * update_speed_duplex_h() + * Update hardware speed and duplex + * get_media_type() + * Get media type of MAC + * adjust_link() + * Adjust link status + * set_loopback() + * Set loopback + * set_promisc_mode + * Set promisc mode + * set_mtu() + * set mtu + * get_pauseparam() + * get tx and rx of pause frame use + * set_pauseparam() + * set tx and rx of pause frame use + * set_autoneg() + * set auto autonegotiation of pause frame use + * get_autoneg() + * get auto autonegotiation of pause frame use + * get_coalesce_usecs() + * get usecs to delay a TX interrupt after a packet is sent + * get_rx_max_coalesced_frames() + * get Maximum number of packets to be sent before a TX interrupt. + * set_coalesce_usecs() + * set usecs to delay a TX interrupt after a packet is sent + * set_coalesce_frames() + * set Maximum number of packets to be sent before a TX interrupt. + * get_mac_addr() + * get mac address + * set_mac_addr() + * set mac address + * add_uc_addr + * Add unicast addr to mac table + * rm_uc_addr + * Remove unicast addr from mac table + * set_mc_addr() + * Set multicast address + * add_mc_addr + * Add multicast address to mac table + * rm_mc_addr + * Remove multicast address from mac table + * update_stats() + * Update Old network device statistics + * get_ethtool_stats() + * Get ethtool network device statistics + * get_strings() + * Get a set of strings that describe the requested objects + * get_sset_count() + * Get number of strings that @get_strings will write + * update_led_status() + * Update the led status + * set_led_id() + * Set led id + * get_regs() + * Get regs dump + * get_regs_len() + * Get the len of the regs dump + * get_rss_key_size() + * Get rss key size + * get_rss_indir_size() + * Get rss indirection table size + * get_rss() + * Get rss table + * set_rss() + * Set rss table + * get_tc_size() + * Get tc size of handle + * get_vector() + * Get vector number and vector information + * map_ring_to_vector() + * Map rings to vector + * unmap_ring_from_vector() + * Unmap rings from vector + * add_tunnel_udp() + * Add tunnel information to hardware + * del_tunnel_udp() + * Delete tunnel information from hardware + * reset_queue() + * Reset queue + * get_fw_version() + * Get firmware version + * get_mdix_mode() + * Get media typr of phy + * set_vlan_filter() + * Set vlan filter config of Ports + * set_vf_vlan_filter() + * Set vlan filter config of vf + */ +struct hnae3_ae_ops { + int (*init_ae_dev)(struct hnae3_ae_dev *ae_dev); + void (*uninit_ae_dev)(struct hnae3_ae_dev *ae_dev); + + int (*init_client_instance)(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev); + void (*uninit_client_instance)(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev); + int (*start)(struct hnae3_handle *handle); + void (*stop)(struct hnae3_handle *handle); + int (*get_status)(struct hnae3_handle *handle); + void (*get_ksettings_an_result)(struct hnae3_handle *handle, + u8 *auto_neg, u32 *speed, u8 *duplex); + + int (*update_speed_duplex_h)(struct hnae3_handle *handle); + int (*cfg_mac_speed_dup_h)(struct hnae3_handle *handle, int speed, + u8 duplex); + + void (*get_media_type)(struct hnae3_handle *handle, u8 *media_type); + void (*adjust_link)(struct hnae3_handle *handle, int speed, int duplex); + int (*set_loopback)(struct hnae3_handle *handle, + enum hnae3_loop loop_mode, bool en); + + void (*set_promisc_mode)(struct hnae3_handle *handle, u32 en); + int (*set_mtu)(struct hnae3_handle *handle, int new_mtu); + + void (*get_pauseparam)(struct hnae3_handle *handle, + u32 *auto_neg, u32 *rx_en, u32 *tx_en); + int (*set_pauseparam)(struct hnae3_handle *handle, + u32 auto_neg, u32 rx_en, u32 tx_en); + + int (*set_autoneg)(struct hnae3_handle *handle, bool enable); + int (*get_autoneg)(struct hnae3_handle *handle); + + void (*get_coalesce_usecs)(struct hnae3_handle *handle, + u32 *tx_usecs, u32 *rx_usecs); + void (*get_rx_max_coalesced_frames)(struct hnae3_handle *handle, + u32 *tx_frames, u32 *rx_frames); + int (*set_coalesce_usecs)(struct hnae3_handle *handle, u32 timeout); + int (*set_coalesce_frames)(struct hnae3_handle *handle, + u32 coalesce_frames); + void (*get_coalesce_range)(struct hnae3_handle *handle, + u32 *tx_frames_low, u32 *rx_frames_low, + u32 *tx_frames_high, u32 *rx_frames_high, + u32 *tx_usecs_low, u32 *rx_usecs_low, + u32 *tx_usecs_high, u32 *rx_usecs_high); + + void (*get_mac_addr)(struct hnae3_handle *handle, u8 *p); + int (*set_mac_addr)(struct hnae3_handle *handle, void *p); + int (*add_uc_addr)(struct hnae3_handle *handle, + const unsigned char *addr); + int (*rm_uc_addr)(struct hnae3_handle *handle, + const unsigned char *addr); + int (*set_mc_addr)(struct hnae3_handle *handle, void *addr); + int (*add_mc_addr)(struct hnae3_handle *handle, + const unsigned char *addr); + int (*rm_mc_addr)(struct hnae3_handle *handle, + const unsigned char *addr); + + void (*set_tso_stats)(struct hnae3_handle *handle, int enable); + void (*update_stats)(struct hnae3_handle *handle, + struct net_device_stats *net_stats); + void (*get_stats)(struct hnae3_handle *handle, u64 *data); + + void (*get_strings)(struct hnae3_handle *handle, + u32 stringset, u8 *data); + int (*get_sset_count)(struct hnae3_handle *handle, int stringset); + + void (*get_regs)(struct hnae3_handle *handle, void *data); + int (*get_regs_len)(struct hnae3_handle *handle); + + u32 (*get_rss_key_size)(struct hnae3_handle *handle); + u32 (*get_rss_indir_size)(struct hnae3_handle *handle); + int (*get_rss)(struct hnae3_handle *handle, u32 *indir, u8 *key, + u8 *hfunc); + int (*set_rss)(struct hnae3_handle *handle, const u32 *indir, + const u8 *key, const u8 hfunc); + + int (*get_tc_size)(struct hnae3_handle *handle); + + int (*get_vector)(struct hnae3_handle *handle, u16 vector_num, + struct hnae3_vector_info *vector_info); + int (*map_ring_to_vector)(struct hnae3_handle *handle, + int vector_num, + struct hnae3_ring_chain_node *vr_chain); + int (*unmap_ring_from_vector)(struct hnae3_handle *handle, + int vector_num, + struct hnae3_ring_chain_node *vr_chain); + + int (*add_tunnel_udp)(struct hnae3_handle *handle, u16 port_num); + int (*del_tunnel_udp)(struct hnae3_handle *handle, u16 port_num); + + void (*reset_queue)(struct hnae3_handle *handle, u16 queue_id); + u32 (*get_fw_version)(struct hnae3_handle *handle); + void (*get_mdix_mode)(struct hnae3_handle *handle, + u8 *tp_mdix_ctrl, u8 *tp_mdix); + + int (*set_vlan_filter)(struct hnae3_handle *handle, __be16 proto, + u16 vlan_id, bool is_kill); + int (*set_vf_vlan_filter)(struct hnae3_handle *handle, int vfid, + u16 vlan, u8 qos, __be16 proto); +}; + +struct hnae3_ae_algo { + const struct hnae3_ae_ops *ops; + struct list_head node; + char name[HNAE3_CLASS_NAME_SIZE]; + const struct pci_device_id *pdev_id_table; +}; + +#define HNAE3_INT_NAME_LEN (IFNAMSIZ + 16) +#define HNAE3_ITR_COUNTDOWN_START 100 + +struct hnae3_tc_info { + u16 tqp_offset; /* TQP offset from base TQP */ + u16 tqp_count; /* Total TQPs */ + u8 up; /* user priority */ + u8 tc; /* TC index */ + bool enable; /* If this TC is enable or not */ +}; + +#define HNAE3_MAX_TC 8 +struct hnae3_knic_private_info { + struct net_device *netdev; /* Set by KNIC client when init instance */ + u16 rss_size; /* Allocated RSS queues */ + u16 rx_buf_len; + u16 num_desc; + + u8 num_tc; /* Total number of enabled TCs */ + struct hnae3_tc_info tc_info[HNAE3_MAX_TC]; /* Idx of array is HW TC */ + + u16 num_tqps; /* total number of TQPs in this handle */ + struct hnae3_queue **tqp; /* array base of all TQPs in this instance */ +}; + +struct hnae3_roce_private_info { + struct net_device *netdev; + void __iomem *roce_io_base; + int base_vector; + int num_vectors; +}; + +struct hnae3_unic_private_info { + struct net_device *netdev; + u16 rx_buf_len; + u16 num_desc; + u16 num_tqps; /* total number of tqps in this handle */ + struct hnae3_queue **tqp; /* array base of all TQPs of this instance */ +}; + +#define HNAE3_SUPPORT_MAC_LOOPBACK 1 +#define HNAE3_SUPPORT_PHY_LOOPBACK 2 +#define HNAE3_SUPPORT_SERDES_LOOPBACK 4 + +struct hnae3_handle { + struct hnae3_client *client; + struct pci_dev *pdev; + void *priv; + struct hnae3_ae_algo *ae_algo; /* the class who provides this handle */ + u64 flags; /* Indicate the capabilities for this handle*/ + + union { + struct net_device *netdev; /* first member */ + struct hnae3_knic_private_info kinfo; + struct hnae3_unic_private_info uinfo; + struct hnae3_roce_private_info rinfo; + }; + + u32 numa_node_mask; /* for multi-chip support */ +}; + +#define hnae_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~(mask)); \ + (origin) |= ((val) << (shift)) & (mask); \ + } while (0) +#define hnae_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift)) + +#define hnae_set_bit(origin, shift, val) \ + hnae_set_field((origin), (0x1 << (shift)), (shift), (val)) +#define hnae_get_bit(origin, shift) \ + hnae_get_field((origin), (0x1 << (shift)), (shift)) + +int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev); +void hnae3_unregister_ae_dev(struct hnae3_ae_dev *ae_dev); + +void hnae3_unregister_ae_algo(struct hnae3_ae_algo *ae_algo); +int hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo); + +void hnae3_unregister_client(struct hnae3_client *client); +int hnae3_register_client(struct hnae3_client *client); +#endif -- cgit v1.2.3 From 68c0a5c70614ce0adf8c6ff849534dd6d2c0ca43 Mon Sep 17 00:00:00 2001 From: Salil Date: Wed, 2 Aug 2017 16:59:47 +0100 Subject: net: hns3: Add HNS3 IMP(Integrated Mgmt Proc) Cmd Interface Support This patch adds the support of IMP (Integrated Management Processor) command interface to the HNS3 driver. Each PF/VF has support of CQP(Command Queue Pair) ring interface. Each CQP consis of send queue CSQ and receive queue CRQ. There are various commands a PF/VF may support, like for Flow Table manipulation, Device management, Packet buffer allocation, Forwarding, VLANs config, Tunneling/Overlays etc. This patch contains code to initialize the command queue, manage the command queue descriptors and Rx/Tx protocol with the command processor in the form of various commands/results and acknowledgements. Signed-off-by: Daode Huang Signed-off-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: Yisen Zhuang Signed-off-by: David S. Miller --- .../net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c | 356 ++++++++++ .../net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h | 740 +++++++++++++++++++++ 2 files changed, 1096 insertions(+) create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c new file mode 100644 index 000000000000..bc869842728f --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "hclge_cmd.h" +#include "hnae3.h" +#include "hclge_main.h" + +#define hclge_is_csq(ring) ((ring)->flag & HCLGE_TYPE_CSQ) +#define hclge_ring_to_dma_dir(ring) (hclge_is_csq(ring) ? \ + DMA_TO_DEVICE : DMA_FROM_DEVICE) +#define cmq_ring_to_dev(ring) (&(ring)->dev->pdev->dev) + +static int hclge_ring_space(struct hclge_cmq_ring *ring) +{ + int ntu = ring->next_to_use; + int ntc = ring->next_to_clean; + int used = (ntu - ntc + ring->desc_num) % ring->desc_num; + + return ring->desc_num - used - 1; +} + +static int hclge_alloc_cmd_desc(struct hclge_cmq_ring *ring) +{ + int size = ring->desc_num * sizeof(struct hclge_desc); + + ring->desc = kzalloc(size, GFP_KERNEL); + if (!ring->desc) + return -ENOMEM; + + ring->desc_dma_addr = dma_map_single(cmq_ring_to_dev(ring), ring->desc, + size, DMA_BIDIRECTIONAL); + if (dma_mapping_error(cmq_ring_to_dev(ring), ring->desc_dma_addr)) { + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; + return -ENOMEM; + } + + return 0; +} + +static void hclge_free_cmd_desc(struct hclge_cmq_ring *ring) +{ + dma_unmap_single(cmq_ring_to_dev(ring), ring->desc_dma_addr, + ring->desc_num * sizeof(ring->desc[0]), + DMA_BIDIRECTIONAL); + + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; +} + +static int hclge_init_cmd_queue(struct hclge_dev *hdev, int ring_type) +{ + struct hclge_hw *hw = &hdev->hw; + struct hclge_cmq_ring *ring = + (ring_type == HCLGE_TYPE_CSQ) ? &hw->cmq.csq : &hw->cmq.crq; + int ret; + + ring->flag = ring_type; + ring->dev = hdev; + + ret = hclge_alloc_cmd_desc(ring); + if (ret) { + dev_err(&hdev->pdev->dev, "descriptor %s alloc error %d\n", + (ring_type == HCLGE_TYPE_CSQ) ? "CSQ" : "CRQ", ret); + return ret; + } + + ring->next_to_clean = 0; + ring->next_to_use = 0; + + return 0; +} + +void hclge_cmd_setup_basic_desc(struct hclge_desc *desc, + enum hclge_opcode_type opcode, bool is_read) +{ + memset((void *)desc, 0, sizeof(struct hclge_desc)); + desc->opcode = cpu_to_le16(opcode); + desc->flag = cpu_to_le16(HCLGE_CMD_FLAG_NO_INTR | HCLGE_CMD_FLAG_IN); + + if (is_read) + desc->flag |= cpu_to_le16(HCLGE_CMD_FLAG_WR); + else + desc->flag &= cpu_to_le16(~HCLGE_CMD_FLAG_WR); +} + +static void hclge_cmd_config_regs(struct hclge_cmq_ring *ring) +{ + dma_addr_t dma = ring->desc_dma_addr; + struct hclge_dev *hdev = ring->dev; + struct hclge_hw *hw = &hdev->hw; + + if (ring->flag == HCLGE_TYPE_CSQ) { + hclge_write_dev(hw, HCLGE_NIC_CSQ_BASEADDR_L_REG, + (u32)dma); + hclge_write_dev(hw, HCLGE_NIC_CSQ_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + hclge_write_dev(hw, HCLGE_NIC_CSQ_DEPTH_REG, + (ring->desc_num >> HCLGE_NIC_CMQ_DESC_NUM_S) | + HCLGE_NIC_CMQ_ENABLE); + hclge_write_dev(hw, HCLGE_NIC_CSQ_TAIL_REG, 0); + hclge_write_dev(hw, HCLGE_NIC_CSQ_HEAD_REG, 0); + } else { + hclge_write_dev(hw, HCLGE_NIC_CRQ_BASEADDR_L_REG, + (u32)dma); + hclge_write_dev(hw, HCLGE_NIC_CRQ_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + hclge_write_dev(hw, HCLGE_NIC_CRQ_DEPTH_REG, + (ring->desc_num >> HCLGE_NIC_CMQ_DESC_NUM_S) | + HCLGE_NIC_CMQ_ENABLE); + hclge_write_dev(hw, HCLGE_NIC_CRQ_TAIL_REG, 0); + hclge_write_dev(hw, HCLGE_NIC_CRQ_HEAD_REG, 0); + } +} + +static void hclge_cmd_init_regs(struct hclge_hw *hw) +{ + hclge_cmd_config_regs(&hw->cmq.csq); + hclge_cmd_config_regs(&hw->cmq.crq); +} + +static int hclge_cmd_csq_clean(struct hclge_hw *hw) +{ + struct hclge_cmq_ring *csq = &hw->cmq.csq; + u16 ntc = csq->next_to_clean; + struct hclge_desc *desc; + int clean = 0; + u32 head; + + desc = &csq->desc[ntc]; + head = hclge_read_dev(hw, HCLGE_NIC_CSQ_HEAD_REG); + + while (head != ntc) { + memset(desc, 0, sizeof(*desc)); + ntc++; + if (ntc == csq->desc_num) + ntc = 0; + desc = &csq->desc[ntc]; + clean++; + } + csq->next_to_clean = ntc; + + return clean; +} + +static int hclge_cmd_csq_done(struct hclge_hw *hw) +{ + u32 head = hclge_read_dev(hw, HCLGE_NIC_CSQ_HEAD_REG); + return head == hw->cmq.csq.next_to_use; +} + +static bool hclge_is_special_opcode(u16 opcode) +{ + u16 spec_opcode[3] = {0x0030, 0x0031, 0x0032}; + int i; + + for (i = 0; i < ARRAY_SIZE(spec_opcode); i++) { + if (spec_opcode[i] == opcode) + return true; + } + + return false; +} + +/** + * hclge_cmd_send - send command to command queue + * @hw: pointer to the hw struct + * @desc: prefilled descriptor for describing the command + * @num : the number of descriptors to be sent + * + * This is the main send command for command queue, it + * sends the queue, cleans the queue, etc + **/ +int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num) +{ + struct hclge_dev *hdev = (struct hclge_dev *)hw->back; + struct hclge_desc *desc_to_use; + bool complete = false; + u32 timeout = 0; + int handle = 0; + int retval = 0; + u16 opcode, desc_ret; + int ntc; + + spin_lock_bh(&hw->cmq.csq.lock); + + if (num > hclge_ring_space(&hw->cmq.csq)) { + spin_unlock_bh(&hw->cmq.csq.lock); + return -EBUSY; + } + + /** + * Record the location of desc in the ring for this time + * which will be use for hardware to write back + */ + ntc = hw->cmq.csq.next_to_use; + opcode = desc[0].opcode; + while (handle < num) { + desc_to_use = &hw->cmq.csq.desc[hw->cmq.csq.next_to_use]; + *desc_to_use = desc[handle]; + (hw->cmq.csq.next_to_use)++; + if (hw->cmq.csq.next_to_use == hw->cmq.csq.desc_num) + hw->cmq.csq.next_to_use = 0; + handle++; + } + + /* Write to hardware */ + hclge_write_dev(hw, HCLGE_NIC_CSQ_TAIL_REG, hw->cmq.csq.next_to_use); + + /** + * If the command is sync, wait for the firmware to write back, + * if multi descriptors to be sent, use the first one to check + */ + if (HCLGE_SEND_SYNC(desc->flag)) { + do { + if (hclge_cmd_csq_done(hw)) + break; + udelay(1); + timeout++; + } while (timeout < hw->cmq.tx_timeout); + } + + if (hclge_cmd_csq_done(hw)) { + complete = true; + handle = 0; + while (handle < num) { + /* Get the result of hardware write back */ + desc_to_use = &hw->cmq.csq.desc[ntc]; + desc[handle] = *desc_to_use; + pr_debug("Get cmd desc:\n"); + + if (likely(!hclge_is_special_opcode(opcode))) + desc_ret = desc[handle].retval; + else + desc_ret = desc[0].retval; + + if ((enum hclge_cmd_return_status)desc_ret == + HCLGE_CMD_EXEC_SUCCESS) + retval = 0; + else + retval = -EIO; + hw->cmq.last_status = (enum hclge_cmd_status)desc_ret; + ntc++; + handle++; + if (ntc == hw->cmq.csq.desc_num) + ntc = 0; + } + } + + if (!complete) + retval = -EAGAIN; + + /* Clean the command send queue */ + handle = hclge_cmd_csq_clean(hw); + if (handle != num) { + dev_warn(&hdev->pdev->dev, + "cleaned %d, need to clean %d\n", handle, num); + } + + spin_unlock_bh(&hw->cmq.csq.lock); + + return retval; +} + +enum hclge_cmd_status hclge_cmd_query_firmware_version(struct hclge_hw *hw, + u32 *version) +{ + struct hclge_query_version *resp; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_FW_VER, 1); + resp = (struct hclge_query_version *)desc.data; + + ret = hclge_cmd_send(hw, &desc, 1); + if (!ret) + *version = le32_to_cpu(resp->firmware); + + return ret; +} + +int hclge_cmd_init(struct hclge_dev *hdev) +{ + u32 version; + int ret; + + /* Setup the queue entries for use cmd queue */ + hdev->hw.cmq.csq.desc_num = HCLGE_NIC_CMQ_DESC_NUM; + hdev->hw.cmq.crq.desc_num = HCLGE_NIC_CMQ_DESC_NUM; + + /* Setup the lock for command queue */ + spin_lock_init(&hdev->hw.cmq.csq.lock); + spin_lock_init(&hdev->hw.cmq.crq.lock); + + /* Setup Tx write back timeout */ + hdev->hw.cmq.tx_timeout = HCLGE_CMDQ_TX_TIMEOUT; + + /* Setup queue rings */ + ret = hclge_init_cmd_queue(hdev, HCLGE_TYPE_CSQ); + if (ret) { + dev_err(&hdev->pdev->dev, + "CSQ ring setup error %d\n", ret); + return ret; + } + + ret = hclge_init_cmd_queue(hdev, HCLGE_TYPE_CRQ); + if (ret) { + dev_err(&hdev->pdev->dev, + "CRQ ring setup error %d\n", ret); + goto err_csq; + } + + hclge_cmd_init_regs(&hdev->hw); + + ret = hclge_cmd_query_firmware_version(&hdev->hw, &version); + if (ret) { + dev_err(&hdev->pdev->dev, + "firmware version query failed %d\n", ret); + return ret; + } + hdev->fw_version = version; + + dev_info(&hdev->pdev->dev, "The firware version is %08x\n", version); + + return 0; +err_csq: + hclge_free_cmd_desc(&hdev->hw.cmq.csq); + return ret; +} + +static void hclge_destroy_queue(struct hclge_cmq_ring *ring) +{ + spin_lock_bh(&ring->lock); + hclge_free_cmd_desc(ring); + spin_unlock_bh(&ring->lock); +} + +void hclge_destroy_cmd_queue(struct hclge_hw *hw) +{ + hclge_destroy_queue(&hw->cmq.csq); + hclge_destroy_queue(&hw->cmq.crq); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h new file mode 100644 index 000000000000..91ae0135ee50 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HCLGE_CMD_H +#define __HCLGE_CMD_H +#include +#include + +#define HCLGE_CMDQ_TX_TIMEOUT 1000 + +struct hclge_dev; +struct hclge_desc { + __le16 opcode; + +#define HCLGE_CMDQ_RX_INVLD_B 0 +#define HCLGE_CMDQ_RX_OUTVLD_B 1 + + __le16 flag; + __le16 retval; + __le16 rsv; + __le32 data[6]; +}; + +struct hclge_desc_cb { + dma_addr_t dma; + void *va; + u32 length; +}; + +struct hclge_cmq_ring { + dma_addr_t desc_dma_addr; + struct hclge_desc *desc; + struct hclge_desc_cb *desc_cb; + struct hclge_dev *dev; + u32 head; + u32 tail; + + u16 buf_size; + u16 desc_num; + int next_to_use; + int next_to_clean; + u8 flag; + spinlock_t lock; /* Command queue lock */ +}; + +enum hclge_cmd_return_status { + HCLGE_CMD_EXEC_SUCCESS = 0, + HCLGE_CMD_NO_AUTH = 1, + HCLGE_CMD_NOT_EXEC = 2, + HCLGE_CMD_QUEUE_FULL = 3, +}; + +enum hclge_cmd_status { + HCLGE_STATUS_SUCCESS = 0, + HCLGE_ERR_CSQ_FULL = -1, + HCLGE_ERR_CSQ_TIMEOUT = -2, + HCLGE_ERR_CSQ_ERROR = -3, +}; + +struct hclge_cmq { + struct hclge_cmq_ring csq; + struct hclge_cmq_ring crq; + u16 tx_timeout; /* Tx timeout */ + enum hclge_cmd_status last_status; +}; + +#define HCLGE_CMD_FLAG_IN_VALID_SHIFT 0 +#define HCLGE_CMD_FLAG_OUT_VALID_SHIFT 1 +#define HCLGE_CMD_FLAG_NEXT_SHIFT 2 +#define HCLGE_CMD_FLAG_WR_OR_RD_SHIFT 3 +#define HCLGE_CMD_FLAG_NO_INTR_SHIFT 4 +#define HCLGE_CMD_FLAG_ERR_INTR_SHIFT 5 + +#define HCLGE_CMD_FLAG_IN BIT(HCLGE_CMD_FLAG_IN_VALID_SHIFT) +#define HCLGE_CMD_FLAG_OUT BIT(HCLGE_CMD_FLAG_OUT_VALID_SHIFT) +#define HCLGE_CMD_FLAG_NEXT BIT(HCLGE_CMD_FLAG_NEXT_SHIFT) +#define HCLGE_CMD_FLAG_WR BIT(HCLGE_CMD_FLAG_WR_OR_RD_SHIFT) +#define HCLGE_CMD_FLAG_NO_INTR BIT(HCLGE_CMD_FLAG_NO_INTR_SHIFT) +#define HCLGE_CMD_FLAG_ERR_INTR BIT(HCLGE_CMD_FLAG_ERR_INTR_SHIFT) + +enum hclge_opcode_type { + /* Generic command */ + HCLGE_OPC_QUERY_FW_VER = 0x0001, + HCLGE_OPC_CFG_RST_TRIGGER = 0x0020, + HCLGE_OPC_GBL_RST_STATUS = 0x0021, + HCLGE_OPC_QUERY_FUNC_STATUS = 0x0022, + HCLGE_OPC_QUERY_PF_RSRC = 0x0023, + HCLGE_OPC_QUERY_VF_RSRC = 0x0024, + HCLGE_OPC_GET_CFG_PARAM = 0x0025, + + HCLGE_OPC_STATS_64_BIT = 0x0030, + HCLGE_OPC_STATS_32_BIT = 0x0031, + HCLGE_OPC_STATS_MAC = 0x0032, + /* Device management command */ + + /* MAC commond */ + HCLGE_OPC_CONFIG_MAC_MODE = 0x0301, + HCLGE_OPC_CONFIG_AN_MODE = 0x0304, + HCLGE_OPC_QUERY_AN_RESULT = 0x0306, + HCLGE_OPC_QUERY_LINK_STATUS = 0x0307, + HCLGE_OPC_CONFIG_MAX_FRM_SIZE = 0x0308, + HCLGE_OPC_CONFIG_SPEED_DUP = 0x0309, + /* MACSEC command */ + + /* PFC/Pause CMD*/ + HCLGE_OPC_CFG_MAC_PAUSE_EN = 0x0701, + HCLGE_OPC_CFG_PFC_PAUSE_EN = 0x0702, + HCLGE_OPC_CFG_MAC_PARA = 0x0703, + HCLGE_OPC_CFG_PFC_PARA = 0x0704, + HCLGE_OPC_QUERY_MAC_TX_PKT_CNT = 0x0705, + HCLGE_OPC_QUERY_MAC_RX_PKT_CNT = 0x0706, + HCLGE_OPC_QUERY_PFC_TX_PKT_CNT = 0x0707, + HCLGE_OPC_QUERY_PFC_RX_PKT_CNT = 0x0708, + HCLGE_OPC_PRI_TO_TC_MAPPING = 0x0709, + HCLGE_OPC_QOS_MAP = 0x070A, + + /* ETS/scheduler commands */ + HCLGE_OPC_TM_PG_TO_PRI_LINK = 0x0804, + HCLGE_OPC_TM_QS_TO_PRI_LINK = 0x0805, + HCLGE_OPC_TM_NQ_TO_QS_LINK = 0x0806, + HCLGE_OPC_TM_RQ_TO_QS_LINK = 0x0807, + HCLGE_OPC_TM_PORT_WEIGHT = 0x0808, + HCLGE_OPC_TM_PG_WEIGHT = 0x0809, + HCLGE_OPC_TM_QS_WEIGHT = 0x080A, + HCLGE_OPC_TM_PRI_WEIGHT = 0x080B, + HCLGE_OPC_TM_PRI_C_SHAPPING = 0x080C, + HCLGE_OPC_TM_PRI_P_SHAPPING = 0x080D, + HCLGE_OPC_TM_PG_C_SHAPPING = 0x080E, + HCLGE_OPC_TM_PG_P_SHAPPING = 0x080F, + HCLGE_OPC_TM_PORT_SHAPPING = 0x0810, + HCLGE_OPC_TM_PG_SCH_MODE_CFG = 0x0812, + HCLGE_OPC_TM_PRI_SCH_MODE_CFG = 0x0813, + HCLGE_OPC_TM_QS_SCH_MODE_CFG = 0x0814, + HCLGE_OPC_TM_BP_TO_QSET_MAPPING = 0x0815, + + /* Packet buffer allocate command */ + HCLGE_OPC_TX_BUFF_ALLOC = 0x0901, + HCLGE_OPC_RX_PRIV_BUFF_ALLOC = 0x0902, + HCLGE_OPC_RX_PRIV_WL_ALLOC = 0x0903, + HCLGE_OPC_RX_COM_THRD_ALLOC = 0x0904, + HCLGE_OPC_RX_COM_WL_ALLOC = 0x0905, + HCLGE_OPC_RX_GBL_PKT_CNT = 0x0906, + + /* PTP command */ + /* TQP management command */ + HCLGE_OPC_SET_TQP_MAP = 0x0A01, + + /* TQP command */ + HCLGE_OPC_CFG_TX_QUEUE = 0x0B01, + HCLGE_OPC_QUERY_TX_POINTER = 0x0B02, + HCLGE_OPC_QUERY_TX_STATUS = 0x0B03, + HCLGE_OPC_CFG_RX_QUEUE = 0x0B11, + HCLGE_OPC_QUERY_RX_POINTER = 0x0B12, + HCLGE_OPC_QUERY_RX_STATUS = 0x0B13, + HCLGE_OPC_STASH_RX_QUEUE_LRO = 0x0B16, + HCLGE_OPC_CFG_RX_QUEUE_LRO = 0x0B17, + HCLGE_OPC_CFG_COM_TQP_QUEUE = 0x0B20, + HCLGE_OPC_RESET_TQP_QUEUE = 0x0B22, + + /* TSO cmd */ + HCLGE_OPC_TSO_GENERIC_CONFIG = 0x0C01, + + /* RSS cmd */ + HCLGE_OPC_RSS_GENERIC_CONFIG = 0x0D01, + HCLGE_OPC_RSS_INDIR_TABLE = 0x0D07, + HCLGE_OPC_RSS_TC_MODE = 0x0D08, + HCLGE_OPC_RSS_INPUT_TUPLE = 0x0D02, + + /* Promisuous mode command */ + HCLGE_OPC_CFG_PROMISC_MODE = 0x0E01, + + /* Interrupts cmd */ + HCLGE_OPC_ADD_RING_TO_VECTOR = 0x1503, + HCLGE_OPC_DEL_RING_TO_VECTOR = 0x1504, + + /* MAC command */ + HCLGE_OPC_MAC_VLAN_ADD = 0x1000, + HCLGE_OPC_MAC_VLAN_REMOVE = 0x1001, + HCLGE_OPC_MAC_VLAN_TYPE_ID = 0x1002, + HCLGE_OPC_MAC_VLAN_INSERT = 0x1003, + HCLGE_OPC_MAC_ETHTYPE_ADD = 0x1010, + HCLGE_OPC_MAC_ETHTYPE_REMOVE = 0x1011, + + /* Multicast linear table cmd */ + HCLGE_OPC_MTA_MAC_MODE_CFG = 0x1020, + HCLGE_OPC_MTA_MAC_FUNC_CFG = 0x1021, + HCLGE_OPC_MTA_TBL_ITEM_CFG = 0x1022, + HCLGE_OPC_MTA_TBL_ITEM_QUERY = 0x1023, + + /* VLAN command */ + HCLGE_OPC_VLAN_FILTER_CTRL = 0x1100, + HCLGE_OPC_VLAN_FILTER_PF_CFG = 0x1101, + HCLGE_OPC_VLAN_FILTER_VF_CFG = 0x1102, + + /* MDIO command */ + HCLGE_OPC_MDIO_CONFIG = 0x1900, + + /* QCN command */ + HCLGE_OPC_QCN_MOD_CFG = 0x1A01, + HCLGE_OPC_QCN_GRP_TMPLT_CFG = 0x1A02, + HCLGE_OPC_QCN_SHAPPING_IR_CFG = 0x1A03, + HCLGE_OPC_QCN_SHAPPING_BS_CFG = 0x1A04, + HCLGE_OPC_QCN_QSET_LINK_CFG = 0x1A05, + HCLGE_OPC_QCN_RP_STATUS_GET = 0x1A06, + HCLGE_OPC_QCN_AJUST_INIT = 0x1A07, + HCLGE_OPC_QCN_DFX_CNT_STATUS = 0x1A08, + + /* Mailbox cmd */ + HCLGEVF_OPC_MBX_PF_TO_VF = 0x2000, +}; + +#define HCLGE_TQP_REG_OFFSET 0x80000 +#define HCLGE_TQP_REG_SIZE 0x200 + +#define HCLGE_RCB_INIT_QUERY_TIMEOUT 10 +#define HCLGE_RCB_INIT_FLAG_EN_B 0 +#define HCLGE_RCB_INIT_FLAG_FINI_B 8 +struct hclge_config_rcb_init { + __le16 rcb_init_flag; + u8 rsv[22]; +}; + +struct hclge_tqp_map { + __le16 tqp_id; /* Absolute tqp id for in this pf */ + u8 tqp_vf; /* VF id */ +#define HCLGE_TQP_MAP_TYPE_PF 0 +#define HCLGE_TQP_MAP_TYPE_VF 1 +#define HCLGE_TQP_MAP_TYPE_B 0 +#define HCLGE_TQP_MAP_EN_B 1 + u8 tqp_flag; /* Indicate it's pf or vf tqp */ + __le16 tqp_vid; /* Virtual id in this pf/vf */ + u8 rsv[18]; +}; + +#define HCLGE_VECTOR_ELEMENTS_PER_CMD 11 + +enum hclge_int_type { + HCLGE_INT_TX, + HCLGE_INT_RX, + HCLGE_INT_EVENT, +}; + +struct hclge_ctrl_vector_chain { + u8 int_vector_id; + u8 int_cause_num; +#define HCLGE_INT_TYPE_S 0 +#define HCLGE_INT_TYPE_M 0x3 +#define HCLGE_TQP_ID_S 2 +#define HCLGE_TQP_ID_M (0x3fff << HCLGE_TQP_ID_S) + __le16 tqp_type_and_id[HCLGE_VECTOR_ELEMENTS_PER_CMD]; +}; + +#define HCLGE_TC_NUM 8 +#define HCLGE_TC0_PRI_BUF_EN_B 15 /* Bit 15 indicate enable or not */ +#define HCLGE_BUF_UNIT_S 7 /* Buf size is united by 128 bytes */ +struct hclge_tx_buff_alloc { + __le16 tx_pkt_buff[HCLGE_TC_NUM]; + u8 tx_buff_rsv[8]; +}; + +struct hclge_rx_priv_buff { + __le16 buf_num[HCLGE_TC_NUM]; + u8 rsv[8]; +}; + +struct hclge_query_version { + __le32 firmware; + __le32 firmware_rsv[5]; +}; + +#define HCLGE_RX_PRIV_EN_B 15 +#define HCLGE_TC_NUM_ONE_DESC 4 +struct hclge_priv_wl { + __le16 high; + __le16 low; +}; + +struct hclge_rx_priv_wl_buf { + struct hclge_priv_wl tc_wl[HCLGE_TC_NUM_ONE_DESC]; +}; + +struct hclge_rx_com_thrd { + struct hclge_priv_wl com_thrd[HCLGE_TC_NUM_ONE_DESC]; +}; + +struct hclge_rx_com_wl { + struct hclge_priv_wl com_wl; +}; + +struct hclge_waterline { + u32 low; + u32 high; +}; + +struct hclge_tc_thrd { + u32 low; + u32 high; +}; + +struct hclge_priv_buf { + struct hclge_waterline wl; /* Waterline for low and high*/ + u32 buf_size; /* TC private buffer size */ + u32 enable; /* Enable TC private buffer or not */ +}; + +#define HCLGE_MAX_TC_NUM 8 +struct hclge_shared_buf { + struct hclge_waterline self; + struct hclge_tc_thrd tc_thrd[HCLGE_MAX_TC_NUM]; + u32 buf_size; +}; + +#define HCLGE_RX_COM_WL_EN_B 15 +struct hclge_rx_com_wl_buf { + __le16 high_wl; + __le16 low_wl; + u8 rsv[20]; +}; + +#define HCLGE_RX_PKT_EN_B 15 +struct hclge_rx_pkt_buf { + __le16 high_pkt; + __le16 low_pkt; + u8 rsv[20]; +}; + +#define HCLGE_PF_STATE_DONE_B 0 +#define HCLGE_PF_STATE_MAIN_B 1 +#define HCLGE_PF_STATE_BOND_B 2 +#define HCLGE_PF_STATE_MAC_N_B 6 +#define HCLGE_PF_MAC_NUM_MASK 0x3 +#define HCLGE_PF_STATE_MAIN BIT(HCLGE_PF_STATE_MAIN_B) +#define HCLGE_PF_STATE_DONE BIT(HCLGE_PF_STATE_DONE_B) +struct hclge_func_status { + __le32 vf_rst_state[4]; + u8 pf_state; + u8 mac_id; + u8 rsv1; + u8 pf_cnt_in_mac; + u8 pf_num; + u8 vf_num; + u8 rsv[2]; +}; + +struct hclge_pf_res { + __le16 tqp_num; + __le16 buf_size; + __le16 msixcap_localid_ba_nic; + __le16 msixcap_localid_ba_rocee; +#define HCLGE_PF_VEC_NUM_S 0 +#define HCLGE_PF_VEC_NUM_M (0xff << HCLGE_PF_VEC_NUM_S) + __le16 pf_intr_vector_number; + __le16 pf_own_fun_number; + __le32 rsv[3]; +}; + +#define HCLGE_CFG_OFFSET_S 0 +#define HCLGE_CFG_OFFSET_M 0xfffff /* Byte (8-10.3) */ +#define HCLGE_CFG_RD_LEN_S 24 +#define HCLGE_CFG_RD_LEN_M (0xf << HCLGE_CFG_RD_LEN_S) +#define HCLGE_CFG_RD_LEN_BYTES 16 +#define HCLGE_CFG_RD_LEN_UNIT 4 + +#define HCLGE_CFG_VMDQ_S 0 +#define HCLGE_CFG_VMDQ_M (0xff << HCLGE_CFG_VMDQ_S) +#define HCLGE_CFG_TC_NUM_S 8 +#define HCLGE_CFG_TC_NUM_M (0xff << HCLGE_CFG_TC_NUM_S) +#define HCLGE_CFG_TQP_DESC_N_S 16 +#define HCLGE_CFG_TQP_DESC_N_M (0xffff << HCLGE_CFG_TQP_DESC_N_S) +#define HCLGE_CFG_PHY_ADDR_S 0 +#define HCLGE_CFG_PHY_ADDR_M (0x1f << HCLGE_CFG_PHY_ADDR_S) +#define HCLGE_CFG_MEDIA_TP_S 8 +#define HCLGE_CFG_MEDIA_TP_M (0xff << HCLGE_CFG_MEDIA_TP_S) +#define HCLGE_CFG_RX_BUF_LEN_S 16 +#define HCLGE_CFG_RX_BUF_LEN_M (0xffff << HCLGE_CFG_RX_BUF_LEN_S) +#define HCLGE_CFG_MAC_ADDR_H_S 0 +#define HCLGE_CFG_MAC_ADDR_H_M (0xffff << HCLGE_CFG_MAC_ADDR_H_S) +#define HCLGE_CFG_DEFAULT_SPEED_S 16 +#define HCLGE_CFG_DEFAULT_SPEED_M (0xff << HCLGE_CFG_DEFAULT_SPEED_S) + +struct hclge_cfg_param { + __le32 offset; + __le32 rsv; + __le32 param[4]; +}; + +#define HCLGE_MAC_MODE 0x0 +#define HCLGE_DESC_NUM 0x40 + +#define HCLGE_ALLOC_VALID_B 0 +struct hclge_vf_num { + u8 alloc_valid; + u8 rsv[23]; +}; + +#define HCLGE_RSS_DEFAULT_OUTPORT_B 4 +#define HCLGE_RSS_HASH_KEY_OFFSET_B 4 +#define HCLGE_RSS_HASH_KEY_NUM 16 +struct hclge_rss_config { + u8 hash_config; + u8 rsv[7]; + u8 hash_key[HCLGE_RSS_HASH_KEY_NUM]; +}; + +struct hclge_rss_input_tuple { + u8 ipv4_tcp_en; + u8 ipv4_udp_en; + u8 ipv4_sctp_en; + u8 ipv4_fragment_en; + u8 ipv6_tcp_en; + u8 ipv6_udp_en; + u8 ipv6_sctp_en; + u8 ipv6_fragment_en; + u8 rsv[16]; +}; + +#define HCLGE_RSS_CFG_TBL_SIZE 16 + +struct hclge_rss_indirection_table { + u16 start_table_index; + u16 rss_set_bitmap; + u8 rsv[4]; + u8 rss_result[HCLGE_RSS_CFG_TBL_SIZE]; +}; + +#define HCLGE_RSS_TC_OFFSET_S 0 +#define HCLGE_RSS_TC_OFFSET_M (0x3ff << HCLGE_RSS_TC_OFFSET_S) +#define HCLGE_RSS_TC_SIZE_S 12 +#define HCLGE_RSS_TC_SIZE_M (0x7 << HCLGE_RSS_TC_SIZE_S) +#define HCLGE_RSS_TC_VALID_B 15 +struct hclge_rss_tc_mode { + u16 rss_tc_mode[HCLGE_MAX_TC_NUM]; + u8 rsv[8]; +}; + +#define HCLGE_LINK_STS_B 0 +#define HCLGE_LINK_STATUS BIT(HCLGE_LINK_STS_B) +struct hclge_link_status { + u8 status; + u8 rsv[23]; +}; + +struct hclge_promisc_param { + u8 vf_id; + u8 enable; +}; + +#define HCLGE_PROMISC_EN_B 1 +#define HCLGE_PROMISC_EN_ALL 0x7 +#define HCLGE_PROMISC_EN_UC 0x1 +#define HCLGE_PROMISC_EN_MC 0x2 +#define HCLGE_PROMISC_EN_BC 0x4 +struct hclge_promisc_cfg { + u8 flag; + u8 vf_id; + __le16 rsv0; + u8 rsv1[20]; +}; + +enum hclge_promisc_type { + HCLGE_UNICAST = 1, + HCLGE_MULTICAST = 2, + HCLGE_BROADCAST = 3, +}; + +#define HCLGE_MAC_TX_EN_B 6 +#define HCLGE_MAC_RX_EN_B 7 +#define HCLGE_MAC_PAD_TX_B 11 +#define HCLGE_MAC_PAD_RX_B 12 +#define HCLGE_MAC_1588_TX_B 13 +#define HCLGE_MAC_1588_RX_B 14 +#define HCLGE_MAC_APP_LP_B 15 +#define HCLGE_MAC_LINE_LP_B 16 +#define HCLGE_MAC_FCS_TX_B 17 +#define HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B 18 +#define HCLGE_MAC_RX_FCS_STRIP_B 19 +#define HCLGE_MAC_RX_FCS_B 20 +#define HCLGE_MAC_TX_UNDER_MIN_ERR_B 21 +#define HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B 22 + +struct hclge_config_mac_mode { + __le32 txrx_pad_fcs_loop_en; + u8 rsv[20]; +}; + +#define HCLGE_CFG_SPEED_S 0 +#define HCLGE_CFG_SPEED_M (0x3f << HCLGE_CFG_SPEED_S) + +#define HCLGE_CFG_DUPLEX_B 7 +#define HCLGE_CFG_DUPLEX_M BIT(HCLGE_CFG_DUPLEX_B) + +struct hclge_config_mac_speed_dup { + u8 speed_dup; + +#define HCLGE_CFG_MAC_SPEED_CHANGE_EN_B 0 + u8 mac_change_fec_en; + u8 rsv[22]; +}; + +#define HCLGE_QUERY_SPEED_S 3 +#define HCLGE_QUERY_AN_B 0 +#define HCLGE_QUERY_DUPLEX_B 2 + +#define HCLGE_QUERY_SPEED_M (0x1f << HCLGE_QUERY_SPEED_S) +#define HCLGE_QUERY_AN_M BIT(HCLGE_QUERY_AN_B) +#define HCLGE_QUERY_DUPLEX_M BIT(HCLGE_QUERY_DUPLEX_B) + +struct hclge_query_an_speed_dup { + u8 an_syn_dup_speed; + u8 pause; + u8 rsv[23]; +}; + +#define HCLGE_RING_ID_MASK 0x3ff +#define HCLGE_TQP_ENABLE_B 0 + +#define HCLGE_MAC_CFG_AN_EN_B 0 +#define HCLGE_MAC_CFG_AN_INT_EN_B 1 +#define HCLGE_MAC_CFG_AN_INT_MSK_B 2 +#define HCLGE_MAC_CFG_AN_INT_CLR_B 3 +#define HCLGE_MAC_CFG_AN_RST_B 4 + +#define HCLGE_MAC_CFG_AN_EN BIT(HCLGE_MAC_CFG_AN_EN_B) + +struct hclge_config_auto_neg { + __le32 cfg_an_cmd_flag; + u8 rsv[20]; +}; + +#define HCLGE_MAC_MIN_MTU 64 +#define HCLGE_MAC_MAX_MTU 9728 +#define HCLGE_MAC_UPLINK_PORT 0x100 + +struct hclge_config_max_frm_size { + __le16 max_frm_size; + u8 rsv[22]; +}; + +enum hclge_mac_vlan_tbl_opcode { + HCLGE_MAC_VLAN_ADD, /* Add new or modify mac_vlan */ + HCLGE_MAC_VLAN_UPDATE, /* Modify other fields of this table */ + HCLGE_MAC_VLAN_REMOVE, /* Remove a entry through mac_vlan key */ + HCLGE_MAC_VLAN_LKUP, /* Lookup a entry through mac_vlan key */ +}; + +#define HCLGE_MAC_VLAN_BIT0_EN_B 0x0 +#define HCLGE_MAC_VLAN_BIT1_EN_B 0x1 +#define HCLGE_MAC_EPORT_SW_EN_B 0xc +#define HCLGE_MAC_EPORT_TYPE_B 0xb +#define HCLGE_MAC_EPORT_VFID_S 0x3 +#define HCLGE_MAC_EPORT_VFID_M (0xff << HCLGE_MAC_EPORT_VFID_S) +#define HCLGE_MAC_EPORT_PFID_S 0x0 +#define HCLGE_MAC_EPORT_PFID_M (0x7 << HCLGE_MAC_EPORT_PFID_S) +struct hclge_mac_vlan_tbl_entry { + u8 flags; + u8 resp_code; + __le16 vlan_tag; + __le32 mac_addr_hi32; + __le16 mac_addr_lo16; + __le16 rsv1; + u8 entry_type; + u8 mc_mac_en; + __le16 egress_port; + __le16 egress_queue; + u8 rsv2[6]; +}; + +#define HCLGE_CFG_MTA_MAC_SEL_S 0x0 +#define HCLGE_CFG_MTA_MAC_SEL_M (0x3 << HCLGE_CFG_MTA_MAC_SEL_S) +#define HCLGE_CFG_MTA_MAC_EN_B 0x7 +struct hclge_mta_filter_mode { + u8 dmac_sel_en; /* Use lowest 2 bit as sel_mode, bit 7 as enable */ + u8 rsv[23]; +}; + +#define HCLGE_CFG_FUNC_MTA_ACCEPT_B 0x0 +struct hclge_cfg_func_mta_filter { + u8 accept; /* Only used lowest 1 bit */ + u8 function_id; + u8 rsv[22]; +}; + +#define HCLGE_CFG_MTA_ITEM_ACCEPT_B 0x0 +#define HCLGE_CFG_MTA_ITEM_IDX_S 0x0 +#define HCLGE_CFG_MTA_ITEM_IDX_M (0xfff << HCLGE_CFG_MTA_ITEM_IDX_S) +struct hclge_cfg_func_mta_item { + u16 item_idx; /* Only used lowest 12 bit */ + u8 accept; /* Only used lowest 1 bit */ + u8 rsv[21]; +}; + +struct hclge_mac_vlan_add { + __le16 flags; + __le16 mac_addr_hi16; + __le32 mac_addr_lo32; + __le32 mac_addr_msk_hi32; + __le16 mac_addr_msk_lo16; + __le16 vlan_tag; + __le16 ingress_port; + __le16 egress_port; + u8 rsv[4]; +}; + +#define HNS3_MAC_VLAN_CFG_FLAG_BIT 0 +struct hclge_mac_vlan_remove { + __le16 flags; + __le16 mac_addr_hi16; + __le32 mac_addr_lo32; + __le32 mac_addr_msk_hi32; + __le16 mac_addr_msk_lo16; + __le16 vlan_tag; + __le16 ingress_port; + __le16 egress_port; + u8 rsv[4]; +}; + +struct hclge_vlan_filter_ctrl { + u8 vlan_type; + u8 vlan_fe; + u8 rsv[22]; +}; + +struct hclge_vlan_filter_pf_cfg { + u8 vlan_offset; + u8 vlan_cfg; + u8 rsv[2]; + u8 vlan_offset_bitmap[20]; +}; + +struct hclge_vlan_filter_vf_cfg { + u16 vlan_id; + u8 resp_code; + u8 rsv; + u8 vlan_cfg; + u8 rsv1[3]; + u8 vf_bitmap[16]; +}; + +struct hclge_cfg_com_tqp_queue { + __le16 tqp_id; + __le16 stream_id; + u8 enable; + u8 rsv[19]; +}; + +struct hclge_cfg_tx_queue_pointer { + __le16 tqp_id; + __le16 tx_tail; + __le16 tx_head; + __le16 fbd_num; + __le16 ring_offset; + u8 rsv[14]; +}; + +#define HCLGE_TSO_MSS_MIN_S 0 +#define HCLGE_TSO_MSS_MIN_M (0x3FFF << HCLGE_TSO_MSS_MIN_S) + +#define HCLGE_TSO_MSS_MAX_S 16 +#define HCLGE_TSO_MSS_MAX_M (0x3FFF << HCLGE_TSO_MSS_MAX_S) + +struct hclge_cfg_tso_status { + __le16 tso_mss_min; + __le16 tso_mss_max; + u8 rsv[20]; +}; + +#define HCLGE_TSO_MSS_MIN 256 +#define HCLGE_TSO_MSS_MAX 9668 + +#define HCLGE_TQP_RESET_B 0 +struct hclge_reset_tqp_queue { + __le16 tqp_id; + u8 reset_req; + u8 ready_to_reset; + u8 rsv[20]; +}; + +#define HCLGE_DEFAULT_TX_BUF 0x4000 /* 16k bytes */ +#define HCLGE_TOTAL_PKT_BUF 0x108000 /* 1.03125M bytes */ +#define HCLGE_DEFAULT_DV 0xA000 /* 40k byte */ + +#define HCLGE_TYPE_CRQ 0 +#define HCLGE_TYPE_CSQ 1 +#define HCLGE_NIC_CSQ_BASEADDR_L_REG 0x27000 +#define HCLGE_NIC_CSQ_BASEADDR_H_REG 0x27004 +#define HCLGE_NIC_CSQ_DEPTH_REG 0x27008 +#define HCLGE_NIC_CSQ_TAIL_REG 0x27010 +#define HCLGE_NIC_CSQ_HEAD_REG 0x27014 +#define HCLGE_NIC_CRQ_BASEADDR_L_REG 0x27018 +#define HCLGE_NIC_CRQ_BASEADDR_H_REG 0x2701c +#define HCLGE_NIC_CRQ_DEPTH_REG 0x27020 +#define HCLGE_NIC_CRQ_TAIL_REG 0x27024 +#define HCLGE_NIC_CRQ_HEAD_REG 0x27028 +#define HCLGE_NIC_CMQ_EN_B 16 +#define HCLGE_NIC_CMQ_ENABLE BIT(HCLGE_NIC_CMQ_EN_B) +#define HCLGE_NIC_CMQ_DESC_NUM 1024 +#define HCLGE_NIC_CMQ_DESC_NUM_S 3 + +int hclge_cmd_init(struct hclge_dev *hdev); +static inline void hclge_write_reg(void __iomem *base, u32 reg, u32 value) +{ + writel(value, base + reg); +} + +#define hclge_write_dev(a, reg, value) \ + hclge_write_reg((a)->io_base, (reg), (value)) +#define hclge_read_dev(a, reg) \ + hclge_read_reg((a)->io_base, (reg)) + +static inline u32 hclge_read_reg(u8 __iomem *base, u32 reg) +{ + u8 __iomem *reg_addr = READ_ONCE(base); + + return readl(reg_addr + reg); +} + +#define HCLGE_SEND_SYNC(flag) \ + ((flag) & HCLGE_CMD_FLAG_NO_INTR) + +struct hclge_hw; +int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num); +void hclge_cmd_setup_basic_desc(struct hclge_desc *desc, + enum hclge_opcode_type opcode, bool is_read); + +int hclge_cmd_set_promisc_mode(struct hclge_dev *hdev, + struct hclge_promisc_param *param); + +enum hclge_cmd_status hclge_cmd_mdio_write(struct hclge_hw *hw, + struct hclge_desc *desc); +enum hclge_cmd_status hclge_cmd_mdio_read(struct hclge_hw *hw, + struct hclge_desc *desc); + +void hclge_destroy_cmd_queue(struct hclge_hw *hw); +#endif -- cgit v1.2.3 From 46a3df9f9718541cf1f805be03146427264ef330 Mon Sep 17 00:00:00 2001 From: Salil Date: Wed, 2 Aug 2017 16:59:48 +0100 Subject: net: hns3: Add HNS3 Acceleration Engine & Compatibility Layer Support This patch adds the support of Hisilicon Network Subsystem Accceleration Engine and common operations to access it. This layer provides access to the hardware configuration, hardware statistics. This layer is also responsible for triggering the initialization of the PHY layer through the below MDIO layer. Signed-off-by: Daode Huang Signed-off-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: Yisen Zhuang Signed-off-by: Wei Hu (Xavier) Signed-off-by: David S. Miller --- .../ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 4267 ++++++++++++++++++++ .../ethernet/hisilicon/hns3/hns3pf/hclge_main.h | 519 +++ 2 files changed, 4786 insertions(+) create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c new file mode 100644 index 000000000000..3611991689bc --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -0,0 +1,4267 @@ +/* + * Copyright (c) 2016-2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hclge_cmd.h" +#include "hclge_main.h" +#include "hclge_mdio.h" +#include "hclge_tm.h" +#include "hnae3.h" + +#define HCLGE_NAME "hclge" +#define HCLGE_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset)))) +#define HCLGE_MAC_STATS_FIELD_OFF(f) (offsetof(struct hclge_mac_stats, f)) +#define HCLGE_64BIT_STATS_FIELD_OFF(f) (offsetof(struct hclge_64_bit_stats, f)) +#define HCLGE_32BIT_STATS_FIELD_OFF(f) (offsetof(struct hclge_32_bit_stats, f)) + +static int hclge_rss_init_hw(struct hclge_dev *hdev); +static int hclge_set_mta_filter_mode(struct hclge_dev *hdev, + enum hclge_mta_dmac_sel_type mta_mac_sel, + bool enable); +static int hclge_init_vlan_config(struct hclge_dev *hdev); + +static struct hnae3_ae_algo ae_algo; + +static const struct pci_device_id ae_algo_pci_tbl[] = { + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), 0}, + /* Required last entry */ + {0, } +}; + +static const struct pci_device_id roce_pci_tbl[] = { + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), 0}, + /* Required last entry */ + {0, } +}; + +static const char hns3_nic_test_strs[][ETH_GSTRING_LEN] = { + "Mac Loopback test", + "Serdes Loopback test", + "Phy Loopback test" +}; + +static const struct hclge_comm_stats_str g_all_64bit_stats_string[] = { + {"igu_rx_oversize_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_oversize_pkt)}, + {"igu_rx_undersize_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_undersize_pkt)}, + {"igu_rx_out_all_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_out_all_pkt)}, + {"igu_rx_uni_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_uni_pkt)}, + {"igu_rx_multi_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_multi_pkt)}, + {"igu_rx_broad_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_broad_pkt)}, + {"egu_tx_out_all_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(egu_tx_out_all_pkt)}, + {"egu_tx_uni_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(egu_tx_uni_pkt)}, + {"egu_tx_multi_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(egu_tx_multi_pkt)}, + {"egu_tx_broad_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(egu_tx_broad_pkt)}, + {"ssu_ppp_mac_key_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_ppp_mac_key_num)}, + {"ssu_ppp_host_key_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_ppp_host_key_num)}, + {"ppp_ssu_mac_rlt_num", + HCLGE_64BIT_STATS_FIELD_OFF(ppp_ssu_mac_rlt_num)}, + {"ppp_ssu_host_rlt_num", + HCLGE_64BIT_STATS_FIELD_OFF(ppp_ssu_host_rlt_num)}, + {"ssu_tx_in_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_tx_in_num)}, + {"ssu_tx_out_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_tx_out_num)}, + {"ssu_rx_in_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_rx_in_num)}, + {"ssu_rx_out_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_rx_out_num)} +}; + +static const struct hclge_comm_stats_str g_all_32bit_stats_string[] = { + {"igu_rx_err_pkt", + HCLGE_32BIT_STATS_FIELD_OFF(igu_rx_err_pkt)}, + {"igu_rx_no_eof_pkt", + HCLGE_32BIT_STATS_FIELD_OFF(igu_rx_no_eof_pkt)}, + {"igu_rx_no_sof_pkt", + HCLGE_32BIT_STATS_FIELD_OFF(igu_rx_no_sof_pkt)}, + {"egu_tx_1588_pkt", + HCLGE_32BIT_STATS_FIELD_OFF(egu_tx_1588_pkt)}, + {"ssu_full_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ssu_full_drop_num)}, + {"ssu_part_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ssu_part_drop_num)}, + {"ppp_key_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ppp_key_drop_num)}, + {"ppp_rlt_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ppp_rlt_drop_num)}, + {"ssu_key_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ssu_key_drop_num)}, + {"pkt_curr_buf_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_cnt)}, + {"qcn_fb_rcv_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(qcn_fb_rcv_cnt)}, + {"qcn_fb_drop_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(qcn_fb_drop_cnt)}, + {"qcn_fb_invaild_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(qcn_fb_invaild_cnt)}, + {"rx_packet_tc0_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc0_in_cnt)}, + {"rx_packet_tc1_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc1_in_cnt)}, + {"rx_packet_tc2_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc2_in_cnt)}, + {"rx_packet_tc3_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc3_in_cnt)}, + {"rx_packet_tc4_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc4_in_cnt)}, + {"rx_packet_tc5_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc5_in_cnt)}, + {"rx_packet_tc6_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc6_in_cnt)}, + {"rx_packet_tc7_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc7_in_cnt)}, + {"rx_packet_tc0_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc0_out_cnt)}, + {"rx_packet_tc1_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc1_out_cnt)}, + {"rx_packet_tc2_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc2_out_cnt)}, + {"rx_packet_tc3_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc3_out_cnt)}, + {"rx_packet_tc4_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc4_out_cnt)}, + {"rx_packet_tc5_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc5_out_cnt)}, + {"rx_packet_tc6_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc6_out_cnt)}, + {"rx_packet_tc7_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc7_out_cnt)}, + {"tx_packet_tc0_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc0_in_cnt)}, + {"tx_packet_tc1_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc1_in_cnt)}, + {"tx_packet_tc2_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc2_in_cnt)}, + {"tx_packet_tc3_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc3_in_cnt)}, + {"tx_packet_tc4_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc4_in_cnt)}, + {"tx_packet_tc5_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc5_in_cnt)}, + {"tx_packet_tc6_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc6_in_cnt)}, + {"tx_packet_tc7_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc7_in_cnt)}, + {"tx_packet_tc0_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc0_out_cnt)}, + {"tx_packet_tc1_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc1_out_cnt)}, + {"tx_packet_tc2_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc2_out_cnt)}, + {"tx_packet_tc3_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc3_out_cnt)}, + {"tx_packet_tc4_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc4_out_cnt)}, + {"tx_packet_tc5_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc5_out_cnt)}, + {"tx_packet_tc6_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc6_out_cnt)}, + {"tx_packet_tc7_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc7_out_cnt)}, + {"pkt_curr_buf_tc0_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc0_cnt)}, + {"pkt_curr_buf_tc1_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc1_cnt)}, + {"pkt_curr_buf_tc2_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc2_cnt)}, + {"pkt_curr_buf_tc3_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc3_cnt)}, + {"pkt_curr_buf_tc4_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc4_cnt)}, + {"pkt_curr_buf_tc5_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc5_cnt)}, + {"pkt_curr_buf_tc6_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc6_cnt)}, + {"pkt_curr_buf_tc7_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc7_cnt)}, + {"mb_uncopy_num", + HCLGE_32BIT_STATS_FIELD_OFF(mb_uncopy_num)}, + {"lo_pri_unicast_rlt_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(lo_pri_unicast_rlt_drop_num)}, + {"hi_pri_multicast_rlt_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(hi_pri_multicast_rlt_drop_num)}, + {"lo_pri_multicast_rlt_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(lo_pri_multicast_rlt_drop_num)}, + {"rx_oq_drop_pkt_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_oq_drop_pkt_cnt)}, + {"tx_oq_drop_pkt_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_oq_drop_pkt_cnt)}, + {"nic_l2_err_drop_pkt_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(nic_l2_err_drop_pkt_cnt)}, + {"roc_l2_err_drop_pkt_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(roc_l2_err_drop_pkt_cnt)} +}; + +static const struct hclge_comm_stats_str g_mac_stats_string[] = { + {"mac_tx_mac_pause_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_mac_pause_num)}, + {"mac_rx_mac_pause_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_mac_pause_num)}, + {"mac_tx_pfc_pri0_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri0_pkt_num)}, + {"mac_tx_pfc_pri1_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri1_pkt_num)}, + {"mac_tx_pfc_pri2_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri2_pkt_num)}, + {"mac_tx_pfc_pri3_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri3_pkt_num)}, + {"mac_tx_pfc_pri4_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri4_pkt_num)}, + {"mac_tx_pfc_pri5_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri5_pkt_num)}, + {"mac_tx_pfc_pri6_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri6_pkt_num)}, + {"mac_tx_pfc_pri7_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri7_pkt_num)}, + {"mac_rx_pfc_pri0_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri0_pkt_num)}, + {"mac_rx_pfc_pri1_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri1_pkt_num)}, + {"mac_rx_pfc_pri2_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri2_pkt_num)}, + {"mac_rx_pfc_pri3_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri3_pkt_num)}, + {"mac_rx_pfc_pri4_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri4_pkt_num)}, + {"mac_rx_pfc_pri5_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri5_pkt_num)}, + {"mac_rx_pfc_pri6_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri6_pkt_num)}, + {"mac_rx_pfc_pri7_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri7_pkt_num)}, + {"mac_tx_total_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_total_pkt_num)}, + {"mac_tx_total_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_total_oct_num)}, + {"mac_tx_good_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_good_pkt_num)}, + {"mac_tx_bad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_bad_pkt_num)}, + {"mac_tx_good_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_good_oct_num)}, + {"mac_tx_bad_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_bad_oct_num)}, + {"mac_tx_uni_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_uni_pkt_num)}, + {"mac_tx_multi_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_multi_pkt_num)}, + {"mac_tx_broad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_broad_pkt_num)}, + {"mac_tx_undersize_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_undersize_pkt_num)}, + {"mac_tx_overrsize_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_overrsize_pkt_num)}, + {"mac_tx_64_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_64_oct_pkt_num)}, + {"mac_tx_65_127_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_65_127_oct_pkt_num)}, + {"mac_tx_128_255_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_128_255_oct_pkt_num)}, + {"mac_tx_256_511_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_256_511_oct_pkt_num)}, + {"mac_tx_512_1023_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_512_1023_oct_pkt_num)}, + {"mac_tx_1024_1518_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_1024_1518_oct_pkt_num)}, + {"mac_tx_1519_max_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_1519_max_oct_pkt_num)}, + {"mac_rx_total_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_total_pkt_num)}, + {"mac_rx_total_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_total_oct_num)}, + {"mac_rx_good_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_good_pkt_num)}, + {"mac_rx_bad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_bad_pkt_num)}, + {"mac_rx_good_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_good_oct_num)}, + {"mac_rx_bad_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_bad_oct_num)}, + {"mac_rx_uni_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_uni_pkt_num)}, + {"mac_rx_multi_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_multi_pkt_num)}, + {"mac_rx_broad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_broad_pkt_num)}, + {"mac_rx_undersize_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_undersize_pkt_num)}, + {"mac_rx_overrsize_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_overrsize_pkt_num)}, + {"mac_rx_64_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_64_oct_pkt_num)}, + {"mac_rx_65_127_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_65_127_oct_pkt_num)}, + {"mac_rx_128_255_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_128_255_oct_pkt_num)}, + {"mac_rx_256_511_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_256_511_oct_pkt_num)}, + {"mac_rx_512_1023_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_512_1023_oct_pkt_num)}, + {"mac_rx_1024_1518_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_1024_1518_oct_pkt_num)}, + {"mac_rx_1519_max_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_1519_max_oct_pkt_num)}, + + {"mac_trans_fragment_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_fragment_pkt_num)}, + {"mac_trans_undermin_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_undermin_pkt_num)}, + {"mac_trans_jabber_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_jabber_pkt_num)}, + {"mac_trans_err_all_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_err_all_pkt_num)}, + {"mac_trans_from_app_good_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_from_app_good_pkt_num)}, + {"mac_trans_from_app_bad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_from_app_bad_pkt_num)}, + {"mac_rcv_fragment_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_fragment_pkt_num)}, + {"mac_rcv_undermin_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_undermin_pkt_num)}, + {"mac_rcv_jabber_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_jabber_pkt_num)}, + {"mac_rcv_fcs_err_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_fcs_err_pkt_num)}, + {"mac_rcv_send_app_good_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_send_app_good_pkt_num)}, + {"mac_rcv_send_app_bad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_send_app_bad_pkt_num)} +}; + +static int hclge_64_bit_update_stats(struct hclge_dev *hdev) +{ +#define HCLGE_64_BIT_CMD_NUM 5 +#define HCLGE_64_BIT_RTN_DATANUM 4 + u64 *data = (u64 *)(&hdev->hw_stats.all_64_bit_stats); + struct hclge_desc desc[HCLGE_64_BIT_CMD_NUM]; + u64 *desc_data; + int i, k, n; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_STATS_64_BIT, true); + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_64_BIT_CMD_NUM); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get 64 bit pkt stats fail, status = %d.\n", ret); + return ret; + } + + for (i = 0; i < HCLGE_64_BIT_CMD_NUM; i++) { + if (unlikely(i == 0)) { + desc_data = (u64 *)(&desc[i].data[0]); + n = HCLGE_64_BIT_RTN_DATANUM - 1; + } else { + desc_data = (u64 *)(&desc[i]); + n = HCLGE_64_BIT_RTN_DATANUM; + } + for (k = 0; k < n; k++) { + *data++ += cpu_to_le64(*desc_data); + desc_data++; + } + } + + return 0; +} + +static void hclge_reset_partial_32bit_counter(struct hclge_32_bit_stats *stats) +{ + stats->pkt_curr_buf_cnt = 0; + stats->pkt_curr_buf_tc0_cnt = 0; + stats->pkt_curr_buf_tc1_cnt = 0; + stats->pkt_curr_buf_tc2_cnt = 0; + stats->pkt_curr_buf_tc3_cnt = 0; + stats->pkt_curr_buf_tc4_cnt = 0; + stats->pkt_curr_buf_tc5_cnt = 0; + stats->pkt_curr_buf_tc6_cnt = 0; + stats->pkt_curr_buf_tc7_cnt = 0; +} + +static int hclge_32_bit_update_stats(struct hclge_dev *hdev) +{ +#define HCLGE_32_BIT_CMD_NUM 8 +#define HCLGE_32_BIT_RTN_DATANUM 8 + + struct hclge_desc desc[HCLGE_32_BIT_CMD_NUM]; + struct hclge_32_bit_stats *all_32_bit_stats; + u32 *desc_data; + int i, k, n; + u64 *data; + int ret; + + all_32_bit_stats = &hdev->hw_stats.all_32_bit_stats; + data = (u64 *)(&all_32_bit_stats->egu_tx_1588_pkt); + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_STATS_32_BIT, true); + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_32_BIT_CMD_NUM); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get 32 bit pkt stats fail, status = %d.\n", ret); + + return ret; + } + + hclge_reset_partial_32bit_counter(all_32_bit_stats); + for (i = 0; i < HCLGE_32_BIT_CMD_NUM; i++) { + if (unlikely(i == 0)) { + all_32_bit_stats->igu_rx_err_pkt += + cpu_to_le32(desc[i].data[0]); + all_32_bit_stats->igu_rx_no_eof_pkt += + cpu_to_le32(desc[i].data[1] & 0xffff); + all_32_bit_stats->igu_rx_no_sof_pkt += + cpu_to_le32((desc[i].data[1] >> 16) & 0xffff); + + desc_data = (u32 *)(&desc[i].data[2]); + n = HCLGE_32_BIT_RTN_DATANUM - 4; + } else { + desc_data = (u32 *)(&desc[i]); + n = HCLGE_32_BIT_RTN_DATANUM; + } + for (k = 0; k < n; k++) { + *data++ += cpu_to_le32(*desc_data); + desc_data++; + } + } + + return 0; +} + +static int hclge_mac_update_stats(struct hclge_dev *hdev) +{ +#define HCLGE_MAC_CMD_NUM 17 +#define HCLGE_RTN_DATA_NUM 4 + + u64 *data = (u64 *)(&hdev->hw_stats.mac_stats); + struct hclge_desc desc[HCLGE_MAC_CMD_NUM]; + u64 *desc_data; + int i, k, n; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_STATS_MAC, true); + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_MAC_CMD_NUM); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get MAC pkt stats fail, status = %d.\n", ret); + + return ret; + } + + for (i = 0; i < HCLGE_MAC_CMD_NUM; i++) { + if (unlikely(i == 0)) { + desc_data = (u64 *)(&desc[i].data[0]); + n = HCLGE_RTN_DATA_NUM - 2; + } else { + desc_data = (u64 *)(&desc[i]); + n = HCLGE_RTN_DATA_NUM; + } + for (k = 0; k < n; k++) { + *data++ += cpu_to_le64(*desc_data); + desc_data++; + } + } + + return 0; +} + +static int hclge_tqps_update_stats(struct hnae3_handle *handle) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hnae3_queue *queue; + struct hclge_desc desc[1]; + struct hclge_tqp *tqp; + int ret, i; + + for (i = 0; i < kinfo->num_tqps; i++) { + queue = handle->kinfo.tqp[i]; + tqp = container_of(queue, struct hclge_tqp, q); + /* command : HCLGE_OPC_QUERY_IGU_STAT */ + hclge_cmd_setup_basic_desc(&desc[0], + HCLGE_OPC_QUERY_RX_STATUS, + true); + + desc[0].data[0] = (tqp->index & 0x1ff); + ret = hclge_cmd_send(&hdev->hw, desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Query tqp stat fail, status = %d,queue = %d\n", + ret, i); + return ret; + } + tqp->tqp_stats.rcb_rx_ring_pktnum_rcd += + cpu_to_le32(desc[0].data[4]); + } + + for (i = 0; i < kinfo->num_tqps; i++) { + queue = handle->kinfo.tqp[i]; + tqp = container_of(queue, struct hclge_tqp, q); + /* command : HCLGE_OPC_QUERY_IGU_STAT */ + hclge_cmd_setup_basic_desc(&desc[0], + HCLGE_OPC_QUERY_TX_STATUS, + true); + + desc[0].data[0] = (tqp->index & 0x1ff); + ret = hclge_cmd_send(&hdev->hw, desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Query tqp stat fail, status = %d,queue = %d\n", + ret, i); + return ret; + } + tqp->tqp_stats.rcb_tx_ring_pktnum_rcd += + cpu_to_le32(desc[0].data[4]); + } + + return 0; +} + +static u64 *hclge_tqps_get_stats(struct hnae3_handle *handle, u64 *data) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + struct hclge_tqp *tqp; + u64 *buff = data; + int i; + + for (i = 0; i < kinfo->num_tqps; i++) { + tqp = container_of(kinfo->tqp[i], struct hclge_tqp, q); + *buff++ = cpu_to_le64(tqp->tqp_stats.rcb_tx_ring_pktnum_rcd); + } + + for (i = 0; i < kinfo->num_tqps; i++) { + tqp = container_of(kinfo->tqp[i], struct hclge_tqp, q); + *buff++ = cpu_to_le64(tqp->tqp_stats.rcb_rx_ring_pktnum_rcd); + } + + return buff; +} + +static int hclge_tqps_get_sset_count(struct hnae3_handle *handle, int stringset) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + + return kinfo->num_tqps * (2); +} + +static u8 *hclge_tqps_get_strings(struct hnae3_handle *handle, u8 *data) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + u8 *buff = data; + int i = 0; + + for (i = 0; i < kinfo->num_tqps; i++) { + struct hclge_tqp *tqp = container_of(handle->kinfo.tqp[i], + struct hclge_tqp, q); + snprintf(buff, ETH_GSTRING_LEN, "rcb_q%d_tx_pktnum_rcd", + tqp->index); + buff = buff + ETH_GSTRING_LEN; + } + + for (i = 0; i < kinfo->num_tqps; i++) { + struct hclge_tqp *tqp = container_of(kinfo->tqp[i], + struct hclge_tqp, q); + snprintf(buff, ETH_GSTRING_LEN, "rcb_q%d_rx_pktnum_rcd", + tqp->index); + buff = buff + ETH_GSTRING_LEN; + } + + return buff; +} + +static u64 *hclge_comm_get_stats(void *comm_stats, + const struct hclge_comm_stats_str strs[], + int size, u64 *data) +{ + u64 *buf = data; + u32 i; + + for (i = 0; i < size; i++) + buf[i] = HCLGE_STATS_READ(comm_stats, strs[i].offset); + + return buf + size; +} + +static u8 *hclge_comm_get_strings(u32 stringset, + const struct hclge_comm_stats_str strs[], + int size, u8 *data) +{ + char *buff = (char *)data; + u32 i; + + if (stringset != ETH_SS_STATS) + return buff; + + for (i = 0; i < size; i++) { + snprintf(buff, ETH_GSTRING_LEN, + strs[i].desc); + buff = buff + ETH_GSTRING_LEN; + } + + return (u8 *)buff; +} + +static void hclge_update_netstat(struct hclge_hw_stats *hw_stats, + struct net_device_stats *net_stats) +{ + net_stats->tx_dropped = 0; + net_stats->rx_dropped = hw_stats->all_32_bit_stats.ssu_full_drop_num; + net_stats->rx_dropped += hw_stats->all_32_bit_stats.ppp_key_drop_num; + net_stats->rx_dropped += hw_stats->all_32_bit_stats.ssu_key_drop_num; + + net_stats->rx_errors = hw_stats->mac_stats.mac_rx_overrsize_pkt_num; + net_stats->rx_errors += hw_stats->mac_stats.mac_rx_undersize_pkt_num; + net_stats->rx_errors += hw_stats->all_32_bit_stats.igu_rx_err_pkt; + net_stats->rx_errors += hw_stats->all_32_bit_stats.igu_rx_no_eof_pkt; + net_stats->rx_errors += hw_stats->all_32_bit_stats.igu_rx_no_sof_pkt; + net_stats->rx_errors += hw_stats->mac_stats.mac_rcv_fcs_err_pkt_num; + + net_stats->multicast = hw_stats->mac_stats.mac_tx_multi_pkt_num; + net_stats->multicast += hw_stats->mac_stats.mac_rx_multi_pkt_num; + + net_stats->rx_crc_errors = hw_stats->mac_stats.mac_rcv_fcs_err_pkt_num; + net_stats->rx_length_errors = + hw_stats->mac_stats.mac_rx_undersize_pkt_num; + net_stats->rx_length_errors += + hw_stats->mac_stats.mac_rx_overrsize_pkt_num; + net_stats->rx_over_errors = + hw_stats->mac_stats.mac_rx_overrsize_pkt_num; +} + +static void hclge_update_stats_for_all(struct hclge_dev *hdev) +{ + struct hnae3_handle *handle; + int status; + + handle = &hdev->vport[0].nic; + if (handle->client) { + status = hclge_tqps_update_stats(handle); + if (status) { + dev_err(&hdev->pdev->dev, + "Update TQPS stats fail, status = %d.\n", + status); + } + } + + status = hclge_mac_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update MAC stats fail, status = %d.\n", status); + + status = hclge_32_bit_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update 32 bit stats fail, status = %d.\n", + status); + + hclge_update_netstat(&hdev->hw_stats, &handle->kinfo.netdev->stats); +} + +static void hclge_update_stats(struct hnae3_handle *handle, + struct net_device_stats *net_stats) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_hw_stats *hw_stats = &hdev->hw_stats; + int status; + + status = hclge_mac_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update MAC stats fail, status = %d.\n", + status); + + status = hclge_32_bit_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update 32 bit stats fail, status = %d.\n", + status); + + status = hclge_64_bit_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update 64 bit stats fail, status = %d.\n", + status); + + status = hclge_tqps_update_stats(handle); + if (status) + dev_err(&hdev->pdev->dev, + "Update TQPS stats fail, status = %d.\n", + status); + + hclge_update_netstat(hw_stats, net_stats); +} + +static int hclge_get_sset_count(struct hnae3_handle *handle, int stringset) +{ +#define HCLGE_LOOPBACK_TEST_FLAGS 0x7 + + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int count = 0; + + /* Loopback test support rules: + * mac: only GE mode support + * serdes: all mac mode will support include GE/XGE/LGE/CGE + * phy: only support when phy device exist on board + */ + if (stringset == ETH_SS_TEST) { + /* clear loopback bit flags at first */ + handle->flags = (handle->flags & (~HCLGE_LOOPBACK_TEST_FLAGS)); + if (hdev->hw.mac.speed == HCLGE_MAC_SPEED_10M || + hdev->hw.mac.speed == HCLGE_MAC_SPEED_100M || + hdev->hw.mac.speed == HCLGE_MAC_SPEED_1G) { + count += 1; + handle->flags |= HNAE3_SUPPORT_MAC_LOOPBACK; + } else { + count = -EOPNOTSUPP; + } + } else if (stringset == ETH_SS_STATS) { + count = ARRAY_SIZE(g_mac_stats_string) + + ARRAY_SIZE(g_all_32bit_stats_string) + + ARRAY_SIZE(g_all_64bit_stats_string) + + hclge_tqps_get_sset_count(handle, stringset); + } + + return count; +} + +static void hclge_get_strings(struct hnae3_handle *handle, + u32 stringset, + u8 *data) +{ + u8 *p = (char *)data; + int size; + + if (stringset == ETH_SS_STATS) { + size = ARRAY_SIZE(g_mac_stats_string); + p = hclge_comm_get_strings(stringset, + g_mac_stats_string, + size, + p); + size = ARRAY_SIZE(g_all_32bit_stats_string); + p = hclge_comm_get_strings(stringset, + g_all_32bit_stats_string, + size, + p); + size = ARRAY_SIZE(g_all_64bit_stats_string); + p = hclge_comm_get_strings(stringset, + g_all_64bit_stats_string, + size, + p); + p = hclge_tqps_get_strings(handle, p); + } else if (stringset == ETH_SS_TEST) { + if (handle->flags & HNAE3_SUPPORT_MAC_LOOPBACK) { + memcpy(p, + hns3_nic_test_strs[HNAE3_MAC_INTER_LOOP_MAC], + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + if (handle->flags & HNAE3_SUPPORT_SERDES_LOOPBACK) { + memcpy(p, + hns3_nic_test_strs[HNAE3_MAC_INTER_LOOP_SERDES], + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + if (handle->flags & HNAE3_SUPPORT_PHY_LOOPBACK) { + memcpy(p, + hns3_nic_test_strs[HNAE3_MAC_INTER_LOOP_PHY], + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + } +} + +static void hclge_get_stats(struct hnae3_handle *handle, u64 *data) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + u64 *p; + + p = hclge_comm_get_stats(&hdev->hw_stats.mac_stats, + g_mac_stats_string, + ARRAY_SIZE(g_mac_stats_string), + data); + p = hclge_comm_get_stats(&hdev->hw_stats.all_32_bit_stats, + g_all_32bit_stats_string, + ARRAY_SIZE(g_all_32bit_stats_string), + p); + p = hclge_comm_get_stats(&hdev->hw_stats.all_64_bit_stats, + g_all_64bit_stats_string, + ARRAY_SIZE(g_all_64bit_stats_string), + p); + p = hclge_tqps_get_stats(handle, p); +} + +static int hclge_parse_func_status(struct hclge_dev *hdev, + struct hclge_func_status *status) +{ + if (!(status->pf_state & HCLGE_PF_STATE_DONE)) + return -EINVAL; + + /* Set the pf to main pf */ + if (status->pf_state & HCLGE_PF_STATE_MAIN) + hdev->flag |= HCLGE_FLAG_MAIN; + else + hdev->flag &= ~HCLGE_FLAG_MAIN; + + hdev->num_req_vfs = status->vf_num / status->pf_num; + return 0; +} + +static int hclge_query_function_status(struct hclge_dev *hdev) +{ + struct hclge_func_status *req; + struct hclge_desc desc; + int timeout = 0; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_FUNC_STATUS, true); + req = (struct hclge_func_status *)desc.data; + + do { + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "query function status failed %d.\n", + ret); + + return ret; + } + + /* Check pf reset is done */ + if (req->pf_state) + break; + usleep_range(1000, 2000); + } while (timeout++ < 5); + + ret = hclge_parse_func_status(hdev, req); + + return ret; +} + +static int hclge_query_pf_resource(struct hclge_dev *hdev) +{ + struct hclge_pf_res *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_PF_RSRC, true); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "query pf resource failed %d.\n", ret); + return ret; + } + + req = (struct hclge_pf_res *)desc.data; + hdev->num_tqps = __le16_to_cpu(req->tqp_num); + hdev->pkt_buf_size = __le16_to_cpu(req->buf_size) << HCLGE_BUF_UNIT_S; + + if (hnae_get_bit(hdev->ae_dev->flag, HNAE_DEV_SUPPORT_ROCE_B)) { + hdev->num_roce_msix = + hnae_get_field(__le16_to_cpu(req->pf_intr_vector_number), + HCLGE_PF_VEC_NUM_M, HCLGE_PF_VEC_NUM_S); + + /* PF should have NIC vectors and Roce vectors, + * NIC vectors are queued before Roce vectors. + */ + hdev->num_msi = hdev->num_roce_msix + HCLGE_ROCE_VECTOR_OFFSET; + } else { + hdev->num_msi = + hnae_get_field(__le16_to_cpu(req->pf_intr_vector_number), + HCLGE_PF_VEC_NUM_M, HCLGE_PF_VEC_NUM_S); + } + + return 0; +} + +static int hclge_parse_speed(int speed_cmd, int *speed) +{ + switch (speed_cmd) { + case 6: + *speed = HCLGE_MAC_SPEED_10M; + break; + case 7: + *speed = HCLGE_MAC_SPEED_100M; + break; + case 0: + *speed = HCLGE_MAC_SPEED_1G; + break; + case 1: + *speed = HCLGE_MAC_SPEED_10G; + break; + case 2: + *speed = HCLGE_MAC_SPEED_25G; + break; + case 3: + *speed = HCLGE_MAC_SPEED_40G; + break; + case 4: + *speed = HCLGE_MAC_SPEED_50G; + break; + case 5: + *speed = HCLGE_MAC_SPEED_100G; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void hclge_parse_cfg(struct hclge_cfg *cfg, struct hclge_desc *desc) +{ + struct hclge_cfg_param *req; + u64 mac_addr_tmp_high; + u64 mac_addr_tmp; + int i; + + req = (struct hclge_cfg_param *)desc[0].data; + + /* get the configuration */ + cfg->vmdq_vport_num = hnae_get_field(__le32_to_cpu(req->param[0]), + HCLGE_CFG_VMDQ_M, + HCLGE_CFG_VMDQ_S); + cfg->tc_num = hnae_get_field(__le32_to_cpu(req->param[0]), + HCLGE_CFG_TC_NUM_M, HCLGE_CFG_TC_NUM_S); + cfg->tqp_desc_num = hnae_get_field(__le32_to_cpu(req->param[0]), + HCLGE_CFG_TQP_DESC_N_M, + HCLGE_CFG_TQP_DESC_N_S); + + cfg->phy_addr = hnae_get_field(__le32_to_cpu(req->param[1]), + HCLGE_CFG_PHY_ADDR_M, + HCLGE_CFG_PHY_ADDR_S); + cfg->media_type = hnae_get_field(__le32_to_cpu(req->param[1]), + HCLGE_CFG_MEDIA_TP_M, + HCLGE_CFG_MEDIA_TP_S); + cfg->rx_buf_len = hnae_get_field(__le32_to_cpu(req->param[1]), + HCLGE_CFG_RX_BUF_LEN_M, + HCLGE_CFG_RX_BUF_LEN_S); + /* get mac_address */ + mac_addr_tmp = __le32_to_cpu(req->param[2]); + mac_addr_tmp_high = hnae_get_field(__le32_to_cpu(req->param[3]), + HCLGE_CFG_MAC_ADDR_H_M, + HCLGE_CFG_MAC_ADDR_H_S); + + mac_addr_tmp |= (mac_addr_tmp_high << 31) << 1; + + cfg->default_speed = hnae_get_field(__le32_to_cpu(req->param[3]), + HCLGE_CFG_DEFAULT_SPEED_M, + HCLGE_CFG_DEFAULT_SPEED_S); + for (i = 0; i < ETH_ALEN; i++) + cfg->mac_addr[i] = (mac_addr_tmp >> (8 * i)) & 0xff; + + req = (struct hclge_cfg_param *)desc[1].data; + cfg->numa_node_map = __le32_to_cpu(req->param[0]); +} + +/* hclge_get_cfg: query the static parameter from flash + * @hdev: pointer to struct hclge_dev + * @hcfg: the config structure to be getted + */ +static int hclge_get_cfg(struct hclge_dev *hdev, struct hclge_cfg *hcfg) +{ + struct hclge_desc desc[HCLGE_PF_CFG_DESC_NUM]; + struct hclge_cfg_param *req; + int i, ret; + + for (i = 0; i < HCLGE_PF_CFG_DESC_NUM; i++) { + req = (struct hclge_cfg_param *)desc[i].data; + hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_GET_CFG_PARAM, + true); + hnae_set_field(req->offset, HCLGE_CFG_OFFSET_M, + HCLGE_CFG_OFFSET_S, i * HCLGE_CFG_RD_LEN_BYTES); + /* Len should be united by 4 bytes when send to hardware */ + hnae_set_field(req->offset, HCLGE_CFG_RD_LEN_M, + HCLGE_CFG_RD_LEN_S, + HCLGE_CFG_RD_LEN_BYTES / HCLGE_CFG_RD_LEN_UNIT); + req->offset = cpu_to_le32(req->offset); + } + + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_PF_CFG_DESC_NUM); + if (ret) { + dev_err(&hdev->pdev->dev, + "get config failed %d.\n", ret); + return ret; + } + + hclge_parse_cfg(hcfg, desc); + return 0; +} + +static int hclge_get_cap(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_query_function_status(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "query function status error %d.\n", ret); + return ret; + } + + /* get pf resource */ + ret = hclge_query_pf_resource(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "query pf resource error %d.\n", ret); + return ret; + } + + return 0; +} + +static int hclge_configure(struct hclge_dev *hdev) +{ + struct hclge_cfg cfg; + int ret, i; + + ret = hclge_get_cfg(hdev, &cfg); + if (ret) { + dev_err(&hdev->pdev->dev, "get mac mode error %d.\n", ret); + return ret; + } + + hdev->num_vmdq_vport = cfg.vmdq_vport_num; + hdev->base_tqp_pid = 0; + hdev->rss_size_max = 1; + hdev->rx_buf_len = cfg.rx_buf_len; + for (i = 0; i < ETH_ALEN; i++) + hdev->hw.mac.mac_addr[i] = cfg.mac_addr[i]; + hdev->hw.mac.media_type = cfg.media_type; + hdev->num_desc = cfg.tqp_desc_num; + hdev->tm_info.num_pg = 1; + hdev->tm_info.num_tc = cfg.tc_num; + hdev->tm_info.hw_pfc_map = 0; + + ret = hclge_parse_speed(cfg.default_speed, &hdev->hw.mac.speed); + if (ret) { + dev_err(&hdev->pdev->dev, "Get wrong speed ret=%d.\n", ret); + return ret; + } + + if ((hdev->tm_info.num_tc > HNAE3_MAX_TC) || + (hdev->tm_info.num_tc < 1)) { + dev_warn(&hdev->pdev->dev, "TC num = %d.\n", + hdev->tm_info.num_tc); + hdev->tm_info.num_tc = 1; + } + + /* Currently not support uncontiuous tc */ + for (i = 0; i < cfg.tc_num; i++) + hnae_set_bit(hdev->hw_tc_map, i, 1); + + if (!hdev->num_vmdq_vport && !hdev->num_req_vfs) + hdev->tx_sch_mode = HCLGE_FLAG_TC_BASE_SCH_MODE; + else + hdev->tx_sch_mode = HCLGE_FLAG_VNET_BASE_SCH_MODE; + + return ret; +} + +static int hclge_config_tso(struct hclge_dev *hdev, int tso_mss_min, + int tso_mss_max) +{ + struct hclge_cfg_tso_status *req; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TSO_GENERIC_CONFIG, false); + + req = (struct hclge_cfg_tso_status *)desc.data; + hnae_set_field(req->tso_mss_min, HCLGE_TSO_MSS_MIN_M, + HCLGE_TSO_MSS_MIN_S, tso_mss_min); + hnae_set_field(req->tso_mss_max, HCLGE_TSO_MSS_MIN_M, + HCLGE_TSO_MSS_MIN_S, tso_mss_max); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_alloc_tqps(struct hclge_dev *hdev) +{ + struct hclge_tqp *tqp; + int i; + + hdev->htqp = devm_kcalloc(&hdev->pdev->dev, hdev->num_tqps, + sizeof(struct hclge_tqp), GFP_KERNEL); + if (!hdev->htqp) + return -ENOMEM; + + tqp = hdev->htqp; + + for (i = 0; i < hdev->num_tqps; i++) { + tqp->dev = &hdev->pdev->dev; + tqp->index = i; + + tqp->q.ae_algo = &ae_algo; + tqp->q.buf_size = hdev->rx_buf_len; + tqp->q.desc_num = hdev->num_desc; + tqp->q.io_base = hdev->hw.io_base + HCLGE_TQP_REG_OFFSET + + i * HCLGE_TQP_REG_SIZE; + + tqp++; + } + + return 0; +} + +static int hclge_map_tqps_to_func(struct hclge_dev *hdev, u16 func_id, + u16 tqp_pid, u16 tqp_vid, bool is_pf) +{ + struct hclge_tqp_map *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_SET_TQP_MAP, false); + + req = (struct hclge_tqp_map *)desc.data; + req->tqp_id = cpu_to_le16(tqp_pid); + req->tqp_vf = cpu_to_le16(func_id); + req->tqp_flag = !is_pf << HCLGE_TQP_MAP_TYPE_B | + 1 << HCLGE_TQP_MAP_EN_B; + req->tqp_vid = cpu_to_le16(tqp_vid); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "TQP map failed %d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_assign_tqp(struct hclge_vport *vport, + struct hnae3_queue **tqp, u16 num_tqps) +{ + struct hclge_dev *hdev = vport->back; + int i, alloced, func_id, ret; + bool is_pf; + + func_id = vport->vport_id; + is_pf = (vport->vport_id == 0) ? true : false; + + for (i = 0, alloced = 0; i < hdev->num_tqps && + alloced < num_tqps; i++) { + if (!hdev->htqp[i].alloced) { + hdev->htqp[i].q.handle = &vport->nic; + hdev->htqp[i].q.tqp_index = alloced; + tqp[alloced] = &hdev->htqp[i].q; + hdev->htqp[i].alloced = true; + ret = hclge_map_tqps_to_func(hdev, func_id, + hdev->htqp[i].index, + alloced, is_pf); + if (ret) + return ret; + + alloced++; + } + } + vport->alloc_tqps = num_tqps; + + return 0; +} + +static int hclge_knic_setup(struct hclge_vport *vport, u16 num_tqps) +{ + struct hnae3_handle *nic = &vport->nic; + struct hnae3_knic_private_info *kinfo = &nic->kinfo; + struct hclge_dev *hdev = vport->back; + int i, ret; + + kinfo->num_desc = hdev->num_desc; + kinfo->rx_buf_len = hdev->rx_buf_len; + kinfo->num_tc = min_t(u16, num_tqps, hdev->tm_info.num_tc); + kinfo->rss_size + = min_t(u16, hdev->rss_size_max, num_tqps / kinfo->num_tc); + kinfo->num_tqps = kinfo->rss_size * kinfo->num_tc; + + for (i = 0; i < HNAE3_MAX_TC; i++) { + if (hdev->hw_tc_map & BIT(i)) { + kinfo->tc_info[i].enable = true; + kinfo->tc_info[i].tqp_offset = i * kinfo->rss_size; + kinfo->tc_info[i].tqp_count = kinfo->rss_size; + kinfo->tc_info[i].tc = i; + } else { + /* Set to default queue if TC is disable */ + kinfo->tc_info[i].enable = false; + kinfo->tc_info[i].tqp_offset = 0; + kinfo->tc_info[i].tqp_count = 1; + kinfo->tc_info[i].tc = 0; + } + } + + kinfo->tqp = devm_kcalloc(&hdev->pdev->dev, kinfo->num_tqps, + sizeof(struct hnae3_queue *), GFP_KERNEL); + if (!kinfo->tqp) + return -ENOMEM; + + ret = hclge_assign_tqp(vport, kinfo->tqp, kinfo->num_tqps); + if (ret) { + dev_err(&hdev->pdev->dev, "fail to assign TQPs %d.\n", ret); + return -EINVAL; + } + + return 0; +} + +static void hclge_unic_setup(struct hclge_vport *vport, u16 num_tqps) +{ + /* this would be initialized later */ +} + +static int hclge_vport_setup(struct hclge_vport *vport, u16 num_tqps) +{ + struct hnae3_handle *nic = &vport->nic; + struct hclge_dev *hdev = vport->back; + int ret; + + nic->pdev = hdev->pdev; + nic->ae_algo = &ae_algo; + nic->numa_node_mask = hdev->numa_node_mask; + + if (hdev->ae_dev->dev_type == HNAE3_DEV_KNIC) { + ret = hclge_knic_setup(vport, num_tqps); + if (ret) { + dev_err(&hdev->pdev->dev, "knic setup failed %d\n", + ret); + return ret; + } + } else { + hclge_unic_setup(vport, num_tqps); + } + + return 0; +} + +static int hclge_alloc_vport(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + struct hclge_vport *vport; + u32 tqp_main_vport; + u32 tqp_per_vport; + int num_vport, i; + int ret; + + /* We need to alloc a vport for main NIC of PF */ + num_vport = hdev->num_vmdq_vport + hdev->num_req_vfs + 1; + + if (hdev->num_tqps < num_vport) + num_vport = hdev->num_tqps; + + /* Alloc the same number of TQPs for every vport */ + tqp_per_vport = hdev->num_tqps / num_vport; + tqp_main_vport = tqp_per_vport + hdev->num_tqps % num_vport; + + vport = devm_kcalloc(&pdev->dev, num_vport, sizeof(struct hclge_vport), + GFP_KERNEL); + if (!vport) + return -ENOMEM; + + hdev->vport = vport; + hdev->num_alloc_vport = num_vport; + +#ifdef CONFIG_PCI_IOV + /* Enable SRIOV */ + if (hdev->num_req_vfs) { + dev_info(&pdev->dev, "active VFs(%d) found, enabling SRIOV\n", + hdev->num_req_vfs); + ret = pci_enable_sriov(hdev->pdev, hdev->num_req_vfs); + if (ret) { + hdev->num_alloc_vfs = 0; + dev_err(&pdev->dev, "SRIOV enable failed %d\n", + ret); + return ret; + } + } + hdev->num_alloc_vfs = hdev->num_req_vfs; +#endif + + for (i = 0; i < num_vport; i++) { + vport->back = hdev; + vport->vport_id = i; + + if (i == 0) + ret = hclge_vport_setup(vport, tqp_main_vport); + else + ret = hclge_vport_setup(vport, tqp_per_vport); + if (ret) { + dev_err(&pdev->dev, + "vport setup failed for vport %d, %d\n", + i, ret); + return ret; + } + + vport++; + } + + return 0; +} + +static int hclge_cmd_alloc_tx_buff(struct hclge_dev *hdev, u16 buf_size) +{ +/* TX buffer size is unit by 128 byte */ +#define HCLGE_BUF_SIZE_UNIT_SHIFT 7 +#define HCLGE_BUF_SIZE_UPDATE_EN_MSK BIT(15) + struct hclge_tx_buff_alloc *req; + struct hclge_desc desc; + int ret; + u8 i; + + req = (struct hclge_tx_buff_alloc *)desc.data; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TX_BUFF_ALLOC, 0); + for (i = 0; i < HCLGE_TC_NUM; i++) + req->tx_pkt_buff[i] = + cpu_to_le16((buf_size >> HCLGE_BUF_SIZE_UNIT_SHIFT) | + HCLGE_BUF_SIZE_UPDATE_EN_MSK); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "tx buffer alloc cmd failed %d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_tx_buffer_alloc(struct hclge_dev *hdev, u32 buf_size) +{ + int ret = hclge_cmd_alloc_tx_buff(hdev, buf_size); + + if (ret) { + dev_err(&hdev->pdev->dev, + "tx buffer alloc failed %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_get_tc_num(struct hclge_dev *hdev) +{ + int i, cnt = 0; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) + if (hdev->hw_tc_map & BIT(i)) + cnt++; + return cnt; +} + +static int hclge_get_pfc_enalbe_num(struct hclge_dev *hdev) +{ + int i, cnt = 0; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) + if (hdev->hw_tc_map & BIT(i) && + hdev->tm_info.hw_pfc_map & BIT(i)) + cnt++; + return cnt; +} + +/* Get the number of pfc enabled TCs, which have private buffer */ +static int hclge_get_pfc_priv_num(struct hclge_dev *hdev) +{ + struct hclge_priv_buf *priv; + int i, cnt = 0; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + if ((hdev->tm_info.hw_pfc_map & BIT(i)) && + priv->enable) + cnt++; + } + + return cnt; +} + +/* Get the number of pfc disabled TCs, which have private buffer */ +static int hclge_get_no_pfc_priv_num(struct hclge_dev *hdev) +{ + struct hclge_priv_buf *priv; + int i, cnt = 0; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + if (hdev->hw_tc_map & BIT(i) && + !(hdev->tm_info.hw_pfc_map & BIT(i)) && + priv->enable) + cnt++; + } + + return cnt; +} + +static u32 hclge_get_rx_priv_buff_alloced(struct hclge_dev *hdev) +{ + struct hclge_priv_buf *priv; + u32 rx_priv = 0; + int i; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + if (priv->enable) + rx_priv += priv->buf_size; + } + return rx_priv; +} + +static bool hclge_is_rx_buf_ok(struct hclge_dev *hdev, u32 rx_all) +{ + u32 shared_buf_min, shared_buf_tc, shared_std; + int tc_num, pfc_enable_num; + u32 shared_buf; + u32 rx_priv; + int i; + + tc_num = hclge_get_tc_num(hdev); + pfc_enable_num = hclge_get_pfc_enalbe_num(hdev); + + shared_buf_min = 2 * hdev->mps + HCLGE_DEFAULT_DV; + shared_buf_tc = pfc_enable_num * hdev->mps + + (tc_num - pfc_enable_num) * hdev->mps / 2 + + hdev->mps; + shared_std = max_t(u32, shared_buf_min, shared_buf_tc); + + rx_priv = hclge_get_rx_priv_buff_alloced(hdev); + if (rx_all <= rx_priv + shared_std) + return false; + + shared_buf = rx_all - rx_priv; + hdev->s_buf.buf_size = shared_buf; + hdev->s_buf.self.high = shared_buf; + hdev->s_buf.self.low = 2 * hdev->mps; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + if ((hdev->hw_tc_map & BIT(i)) && + (hdev->tm_info.hw_pfc_map & BIT(i))) { + hdev->s_buf.tc_thrd[i].low = hdev->mps; + hdev->s_buf.tc_thrd[i].high = 2 * hdev->mps; + } else { + hdev->s_buf.tc_thrd[i].low = 0; + hdev->s_buf.tc_thrd[i].high = hdev->mps; + } + } + + return true; +} + +/* hclge_rx_buffer_calc: calculate the rx private buffer size for all TCs + * @hdev: pointer to struct hclge_dev + * @tx_size: the allocated tx buffer for all TCs + * @return: 0: calculate sucessful, negative: fail + */ +int hclge_rx_buffer_calc(struct hclge_dev *hdev, u32 tx_size) +{ + u32 rx_all = hdev->pkt_buf_size - tx_size; + int no_pfc_priv_num, pfc_priv_num; + struct hclge_priv_buf *priv; + int i; + + /* step 1, try to alloc private buffer for all enabled tc */ + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + if (hdev->hw_tc_map & BIT(i)) { + priv->enable = 1; + if (hdev->tm_info.hw_pfc_map & BIT(i)) { + priv->wl.low = hdev->mps; + priv->wl.high = priv->wl.low + hdev->mps; + priv->buf_size = priv->wl.high + + HCLGE_DEFAULT_DV; + } else { + priv->wl.low = 0; + priv->wl.high = 2 * hdev->mps; + priv->buf_size = priv->wl.high; + } + } + } + + if (hclge_is_rx_buf_ok(hdev, rx_all)) + return 0; + + /* step 2, try to decrease the buffer size of + * no pfc TC's private buffer + */ + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + + if (hdev->hw_tc_map & BIT(i)) + priv->enable = 1; + + if (hdev->tm_info.hw_pfc_map & BIT(i)) { + priv->wl.low = 128; + priv->wl.high = priv->wl.low + hdev->mps; + priv->buf_size = priv->wl.high + HCLGE_DEFAULT_DV; + } else { + priv->wl.low = 0; + priv->wl.high = hdev->mps; + priv->buf_size = priv->wl.high; + } + } + + if (hclge_is_rx_buf_ok(hdev, rx_all)) + return 0; + + /* step 3, try to reduce the number of pfc disabled TCs, + * which have private buffer + */ + /* get the total no pfc enable TC number, which have private buffer */ + no_pfc_priv_num = hclge_get_no_pfc_priv_num(hdev); + + /* let the last to be cleared first */ + for (i = HCLGE_MAX_TC_NUM - 1; i >= 0; i--) { + priv = &hdev->priv_buf[i]; + + if (hdev->hw_tc_map & BIT(i) && + !(hdev->tm_info.hw_pfc_map & BIT(i))) { + /* Clear the no pfc TC private buffer */ + priv->wl.low = 0; + priv->wl.high = 0; + priv->buf_size = 0; + priv->enable = 0; + no_pfc_priv_num--; + } + + if (hclge_is_rx_buf_ok(hdev, rx_all) || + no_pfc_priv_num == 0) + break; + } + + if (hclge_is_rx_buf_ok(hdev, rx_all)) + return 0; + + /* step 4, try to reduce the number of pfc enabled TCs + * which have private buffer. + */ + pfc_priv_num = hclge_get_pfc_priv_num(hdev); + + /* let the last to be cleared first */ + for (i = HCLGE_MAX_TC_NUM - 1; i >= 0; i--) { + priv = &hdev->priv_buf[i]; + + if (hdev->hw_tc_map & BIT(i) && + hdev->tm_info.hw_pfc_map & BIT(i)) { + /* Reduce the number of pfc TC with private buffer */ + priv->wl.low = 0; + priv->enable = 0; + priv->wl.high = 0; + priv->buf_size = 0; + pfc_priv_num--; + } + + if (hclge_is_rx_buf_ok(hdev, rx_all) || + pfc_priv_num == 0) + break; + } + if (hclge_is_rx_buf_ok(hdev, rx_all)) + return 0; + + return -ENOMEM; +} + +static int hclge_rx_priv_buf_alloc(struct hclge_dev *hdev) +{ + struct hclge_rx_priv_buff *req; + struct hclge_desc desc; + int ret; + int i; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RX_PRIV_BUFF_ALLOC, false); + req = (struct hclge_rx_priv_buff *)desc.data; + + /* Alloc private buffer TCs */ + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + struct hclge_priv_buf *priv = &hdev->priv_buf[i]; + + req->buf_num[i] = + cpu_to_le16(priv->buf_size >> HCLGE_BUF_UNIT_S); + req->buf_num[i] |= + cpu_to_le16(true << HCLGE_TC0_PRI_BUF_EN_B); + } + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "rx private buffer alloc cmd failed %d\n", ret); + return ret; + } + + return 0; +} + +#define HCLGE_PRIV_ENABLE(a) ((a) > 0 ? 1 : 0) + +static int hclge_rx_priv_wl_config(struct hclge_dev *hdev) +{ + struct hclge_rx_priv_wl_buf *req; + struct hclge_priv_buf *priv; + struct hclge_desc desc[2]; + int i, j; + int ret; + + for (i = 0; i < 2; i++) { + hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_RX_PRIV_WL_ALLOC, + false); + req = (struct hclge_rx_priv_wl_buf *)desc[i].data; + + /* The first descriptor set the NEXT bit to 1 */ + if (i == 0) + desc[i].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + else + desc[i].flag &= ~cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + + for (j = 0; j < HCLGE_TC_NUM_ONE_DESC; j++) { + priv = &hdev->priv_buf[i * HCLGE_TC_NUM_ONE_DESC + j]; + req->tc_wl[j].high = + cpu_to_le16(priv->wl.high >> HCLGE_BUF_UNIT_S); + req->tc_wl[j].high |= + cpu_to_le16(HCLGE_PRIV_ENABLE(priv->wl.high) << + HCLGE_RX_PRIV_EN_B); + req->tc_wl[j].low = + cpu_to_le16(priv->wl.low >> HCLGE_BUF_UNIT_S); + req->tc_wl[j].low |= + cpu_to_le16(HCLGE_PRIV_ENABLE(priv->wl.low) << + HCLGE_RX_PRIV_EN_B); + } + } + + /* Send 2 descriptor at one time */ + ret = hclge_cmd_send(&hdev->hw, desc, 2); + if (ret) { + dev_err(&hdev->pdev->dev, + "rx private waterline config cmd failed %d\n", + ret); + return ret; + } + return 0; +} + +static int hclge_common_thrd_config(struct hclge_dev *hdev) +{ + struct hclge_shared_buf *s_buf = &hdev->s_buf; + struct hclge_rx_com_thrd *req; + struct hclge_desc desc[2]; + struct hclge_tc_thrd *tc; + int i, j; + int ret; + + for (i = 0; i < 2; i++) { + hclge_cmd_setup_basic_desc(&desc[i], + HCLGE_OPC_RX_COM_THRD_ALLOC, false); + req = (struct hclge_rx_com_thrd *)&desc[i].data; + + /* The first descriptor set the NEXT bit to 1 */ + if (i == 0) + desc[i].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + else + desc[i].flag &= ~cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + + for (j = 0; j < HCLGE_TC_NUM_ONE_DESC; j++) { + tc = &s_buf->tc_thrd[i * HCLGE_TC_NUM_ONE_DESC + j]; + + req->com_thrd[j].high = + cpu_to_le16(tc->high >> HCLGE_BUF_UNIT_S); + req->com_thrd[j].high |= + cpu_to_le16(HCLGE_PRIV_ENABLE(tc->high) << + HCLGE_RX_PRIV_EN_B); + req->com_thrd[j].low = + cpu_to_le16(tc->low >> HCLGE_BUF_UNIT_S); + req->com_thrd[j].low |= + cpu_to_le16(HCLGE_PRIV_ENABLE(tc->low) << + HCLGE_RX_PRIV_EN_B); + } + } + + /* Send 2 descriptors at one time */ + ret = hclge_cmd_send(&hdev->hw, desc, 2); + if (ret) { + dev_err(&hdev->pdev->dev, + "common threshold config cmd failed %d\n", ret); + return ret; + } + return 0; +} + +static int hclge_common_wl_config(struct hclge_dev *hdev) +{ + struct hclge_shared_buf *buf = &hdev->s_buf; + struct hclge_rx_com_wl *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RX_COM_WL_ALLOC, false); + + req = (struct hclge_rx_com_wl *)desc.data; + req->com_wl.high = cpu_to_le16(buf->self.high >> HCLGE_BUF_UNIT_S); + req->com_wl.high |= + cpu_to_le16(HCLGE_PRIV_ENABLE(buf->self.high) << + HCLGE_RX_PRIV_EN_B); + + req->com_wl.low = cpu_to_le16(buf->self.low >> HCLGE_BUF_UNIT_S); + req->com_wl.low |= + cpu_to_le16(HCLGE_PRIV_ENABLE(buf->self.low) << + HCLGE_RX_PRIV_EN_B); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "common waterline config cmd failed %d\n", ret); + return ret; + } + + return 0; +} + +int hclge_buffer_alloc(struct hclge_dev *hdev) +{ + u32 tx_buf_size = HCLGE_DEFAULT_TX_BUF; + int ret; + + hdev->priv_buf = devm_kmalloc_array(&hdev->pdev->dev, HCLGE_MAX_TC_NUM, + sizeof(struct hclge_priv_buf), + GFP_KERNEL | __GFP_ZERO); + if (!hdev->priv_buf) + return -ENOMEM; + + ret = hclge_tx_buffer_alloc(hdev, tx_buf_size); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not alloc tx buffers %d\n", ret); + return ret; + } + + ret = hclge_rx_buffer_calc(hdev, tx_buf_size); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not calc rx priv buffer size for all TCs %d\n", + ret); + return ret; + } + + ret = hclge_rx_priv_buf_alloc(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, "could not alloc rx priv buffer %d\n", + ret); + return ret; + } + + ret = hclge_rx_priv_wl_config(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not configure rx private waterline %d\n", ret); + return ret; + } + + ret = hclge_common_thrd_config(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not configure common threshold %d\n", ret); + return ret; + } + + ret = hclge_common_wl_config(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not configure common waterline %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_init_roce_base_info(struct hclge_vport *vport) +{ + struct hnae3_handle *roce = &vport->roce; + struct hnae3_handle *nic = &vport->nic; + + roce->rinfo.num_vectors = vport->back->num_roce_msix; + + if (vport->back->num_msi_left < vport->roce.rinfo.num_vectors || + vport->back->num_msi_left == 0) + return -EINVAL; + + roce->rinfo.base_vector = vport->back->roce_base_vector; + + roce->rinfo.netdev = nic->kinfo.netdev; + roce->rinfo.roce_io_base = vport->back->hw.io_base; + + roce->pdev = nic->pdev; + roce->ae_algo = nic->ae_algo; + roce->numa_node_mask = nic->numa_node_mask; + + return 0; +} + +static int hclge_init_msix(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + int ret, i; + + hdev->msix_entries = devm_kcalloc(&pdev->dev, hdev->num_msi, + sizeof(struct msix_entry), + GFP_KERNEL); + if (!hdev->msix_entries) + return -ENOMEM; + + hdev->vector_status = devm_kcalloc(&pdev->dev, hdev->num_msi, + sizeof(u16), GFP_KERNEL); + if (!hdev->vector_status) + return -ENOMEM; + + for (i = 0; i < hdev->num_msi; i++) { + hdev->msix_entries[i].entry = i; + hdev->vector_status[i] = HCLGE_INVALID_VPORT; + } + + hdev->num_msi_left = hdev->num_msi; + hdev->base_msi_vector = hdev->pdev->irq; + hdev->roce_base_vector = hdev->base_msi_vector + + HCLGE_ROCE_VECTOR_OFFSET; + + ret = pci_enable_msix_range(hdev->pdev, hdev->msix_entries, + hdev->num_msi, hdev->num_msi); + if (ret < 0) { + dev_info(&hdev->pdev->dev, + "MSI-X vector alloc failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_init_msi(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + int vectors; + int i; + + hdev->vector_status = devm_kcalloc(&pdev->dev, hdev->num_msi, + sizeof(u16), GFP_KERNEL); + if (!hdev->vector_status) + return -ENOMEM; + + for (i = 0; i < hdev->num_msi; i++) + hdev->vector_status[i] = HCLGE_INVALID_VPORT; + + vectors = pci_alloc_irq_vectors(pdev, 1, hdev->num_msi, PCI_IRQ_MSI); + if (vectors < 0) { + dev_err(&pdev->dev, "MSI vectors enable failed %d\n", vectors); + return -EINVAL; + } + hdev->num_msi = vectors; + hdev->num_msi_left = vectors; + hdev->base_msi_vector = pdev->irq; + hdev->roce_base_vector = hdev->base_msi_vector + + HCLGE_ROCE_VECTOR_OFFSET; + + return 0; +} + +static void hclge_check_speed_dup(struct hclge_dev *hdev, int duplex, int speed) +{ + struct hclge_mac *mac = &hdev->hw.mac; + + if ((speed == HCLGE_MAC_SPEED_10M) || (speed == HCLGE_MAC_SPEED_100M)) + mac->duplex = (u8)duplex; + else + mac->duplex = HCLGE_MAC_FULL; + + mac->speed = speed; +} + +int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex) +{ + struct hclge_config_mac_speed_dup *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_config_mac_speed_dup *)desc.data; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_SPEED_DUP, false); + + hnae_set_bit(req->speed_dup, HCLGE_CFG_DUPLEX_B, !!duplex); + + switch (speed) { + case HCLGE_MAC_SPEED_10M: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 6); + break; + case HCLGE_MAC_SPEED_100M: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 7); + break; + case HCLGE_MAC_SPEED_1G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 0); + break; + case HCLGE_MAC_SPEED_10G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 1); + break; + case HCLGE_MAC_SPEED_25G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 2); + break; + case HCLGE_MAC_SPEED_40G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 3); + break; + case HCLGE_MAC_SPEED_50G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 4); + break; + case HCLGE_MAC_SPEED_100G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 5); + break; + default: + dev_err(&hdev->pdev->dev, "invald speed (%d)\n", speed); + return -EINVAL; + } + + hnae_set_bit(req->mac_change_fec_en, HCLGE_CFG_MAC_SPEED_CHANGE_EN_B, + 1); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "mac speed/duplex config cmd failed %d.\n", ret); + return ret; + } + + hclge_check_speed_dup(hdev, duplex, speed); + + return 0; +} + +static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed, + u8 duplex) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hclge_cfg_mac_speed_dup(hdev, speed, duplex); +} + +static int hclge_query_mac_an_speed_dup(struct hclge_dev *hdev, int *speed, + u8 *duplex) +{ + struct hclge_query_an_speed_dup *req; + struct hclge_desc desc; + int speed_tmp; + int ret; + + req = (struct hclge_query_an_speed_dup *)desc.data; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_AN_RESULT, true); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "mac speed/autoneg/duplex query cmd failed %d\n", + ret); + return ret; + } + + *duplex = hnae_get_bit(req->an_syn_dup_speed, HCLGE_QUERY_DUPLEX_B); + speed_tmp = hnae_get_field(req->an_syn_dup_speed, HCLGE_QUERY_SPEED_M, + HCLGE_QUERY_SPEED_S); + + ret = hclge_parse_speed(speed_tmp, speed); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not parse speed(=%d), %d\n", speed_tmp, ret); + return -EIO; + } + + return 0; +} + +static int hclge_query_autoneg_result(struct hclge_dev *hdev) +{ + struct hclge_mac *mac = &hdev->hw.mac; + struct hclge_query_an_speed_dup *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_query_an_speed_dup *)desc.data; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_AN_RESULT, true); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "autoneg result query cmd failed %d.\n", ret); + return ret; + } + + mac->autoneg = hnae_get_bit(req->an_syn_dup_speed, HCLGE_QUERY_AN_B); + + return 0; +} + +static int hclge_set_autoneg_en(struct hclge_dev *hdev, bool enable) +{ + struct hclge_config_auto_neg *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_AN_MODE, false); + + req = (struct hclge_config_auto_neg *)desc.data; + hnae_set_bit(req->cfg_an_cmd_flag, HCLGE_MAC_CFG_AN_EN_B, !!enable); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "auto neg set cmd failed %d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_set_autoneg(struct hnae3_handle *handle, bool enable) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hclge_set_autoneg_en(hdev, enable); +} + +static int hclge_get_autoneg(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + hclge_query_autoneg_result(hdev); + + return hdev->hw.mac.autoneg; +} + +static int hclge_mac_init(struct hclge_dev *hdev) +{ + struct hclge_mac *mac = &hdev->hw.mac; + int ret; + + ret = hclge_cfg_mac_speed_dup(hdev, hdev->hw.mac.speed, HCLGE_MAC_FULL); + if (ret) { + dev_err(&hdev->pdev->dev, + "Config mac speed dup fail ret=%d\n", ret); + return ret; + } + + mac->link = 0; + + ret = hclge_mac_mdio_config(hdev); + if (ret) { + dev_warn(&hdev->pdev->dev, + "mdio config fail ret=%d\n", ret); + return ret; + } + + /* Initialize the MTA table work mode */ + hdev->accept_mta_mc = true; + hdev->enable_mta = true; + hdev->mta_mac_sel_type = HCLGE_MAC_ADDR_47_36; + + ret = hclge_set_mta_filter_mode(hdev, + hdev->mta_mac_sel_type, + hdev->enable_mta); + if (ret) { + dev_err(&hdev->pdev->dev, "set mta filter mode failed %d\n", + ret); + return ret; + } + + return hclge_cfg_func_mta_filter(hdev, 0, hdev->accept_mta_mc); +} + +static void hclge_task_schedule(struct hclge_dev *hdev) +{ + if (!test_bit(HCLGE_STATE_DOWN, &hdev->state) && + !test_bit(HCLGE_STATE_REMOVING, &hdev->state) && + !test_and_set_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state)) + (void)schedule_work(&hdev->service_task); +} + +static int hclge_get_mac_link_status(struct hclge_dev *hdev) +{ + struct hclge_link_status *req; + struct hclge_desc desc; + int link_status; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_LINK_STATUS, true); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "get link status cmd failed %d\n", + ret); + return ret; + } + + req = (struct hclge_link_status *)desc.data; + link_status = req->status & HCLGE_LINK_STATUS; + + return !!link_status; +} + +static int hclge_get_mac_phy_link(struct hclge_dev *hdev) +{ + int mac_state; + int link_stat; + + mac_state = hclge_get_mac_link_status(hdev); + + if (hdev->hw.mac.phydev) { + if (!genphy_read_status(hdev->hw.mac.phydev)) + link_stat = mac_state & + hdev->hw.mac.phydev->link; + else + link_stat = 0; + + } else { + link_stat = mac_state; + } + + return !!link_stat; +} + +static void hclge_update_link_status(struct hclge_dev *hdev) +{ + struct hnae3_client *client = hdev->nic_client; + struct hnae3_handle *handle; + int state; + int i; + + if (!client) + return; + state = hclge_get_mac_phy_link(hdev); + if (state != hdev->hw.mac.link) { + for (i = 0; i < hdev->num_vmdq_vport + 1; i++) { + handle = &hdev->vport[i].nic; + client->ops->link_status_change(handle, state); + } + hdev->hw.mac.link = state; + } +} + +static int hclge_update_speed_duplex(struct hclge_dev *hdev) +{ + struct hclge_mac mac = hdev->hw.mac; + u8 duplex; + int speed; + int ret; + + /* get the speed and duplex as autoneg'result from mac cmd when phy + * doesn't exit. + */ + if (mac.phydev) + return 0; + + /* update mac->antoneg. */ + ret = hclge_query_autoneg_result(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "autoneg result query failed %d\n", ret); + return ret; + } + + if (!mac.autoneg) + return 0; + + ret = hclge_query_mac_an_speed_dup(hdev, &speed, &duplex); + if (ret) { + dev_err(&hdev->pdev->dev, + "mac autoneg/speed/duplex query failed %d\n", ret); + return ret; + } + + if ((mac.speed != speed) || (mac.duplex != duplex)) { + ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex); + if (ret) { + dev_err(&hdev->pdev->dev, + "mac speed/duplex config failed %d\n", ret); + return ret; + } + } + + return 0; +} + +static int hclge_update_speed_duplex_h(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hclge_update_speed_duplex(hdev); +} + +static int hclge_get_status(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + hclge_update_link_status(hdev); + + return hdev->hw.mac.link; +} + +static void hclge_service_timer(unsigned long data) +{ + struct hclge_dev *hdev = (struct hclge_dev *)data; + (void)mod_timer(&hdev->service_timer, jiffies + HZ); + + hclge_task_schedule(hdev); +} + +static void hclge_service_complete(struct hclge_dev *hdev) +{ + WARN_ON(!test_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state)); + + /* Flush memory before next watchdog */ + smp_mb__before_atomic(); + clear_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state); +} + +static void hclge_service_task(struct work_struct *work) +{ + struct hclge_dev *hdev = + container_of(work, struct hclge_dev, service_task); + + hclge_update_speed_duplex(hdev); + hclge_update_link_status(hdev); + hclge_update_stats_for_all(hdev); + hclge_service_complete(hdev); +} + +static void hclge_disable_sriov(struct hclge_dev *hdev) +{ +#ifdef CONFIG_PCI_IOV + /* If our VFs are assigned we cannot shut down SR-IOV + * without causing issues, so just leave the hardware + * available but disabled + */ + if (pci_vfs_assigned(hdev->pdev)) { + dev_warn(&hdev->pdev->dev, + "disabling driver while VFs are assigned\n"); + return; + } + + pci_disable_sriov(hdev->pdev); +#endif +} + +struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle) +{ + /* VF handle has no client */ + if (!handle->client) + return container_of(handle, struct hclge_vport, nic); + else if (handle->client->type == HNAE3_CLIENT_ROCE) + return container_of(handle, struct hclge_vport, roce); + else + return container_of(handle, struct hclge_vport, nic); +} + +static int hclge_get_vector(struct hnae3_handle *handle, u16 vector_num, + struct hnae3_vector_info *vector_info) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hnae3_vector_info *vector = vector_info; + struct hclge_dev *hdev = vport->back; + int alloc = 0; + int i, j; + + vector_num = min(hdev->num_msi_left, vector_num); + + for (j = 0; j < vector_num; j++) { + for (i = 1; i < hdev->num_msi; i++) { + if (hdev->vector_status[i] == HCLGE_INVALID_VPORT) { + vector->vector = pci_irq_vector(hdev->pdev, i); + vector->io_addr = hdev->hw.io_base + + HCLGE_VECTOR_REG_BASE + + (i - 1) * HCLGE_VECTOR_REG_OFFSET + + vport->vport_id * + HCLGE_VECTOR_VF_OFFSET; + hdev->vector_status[i] = vport->vport_id; + + vector++; + alloc++; + + break; + } + } + } + hdev->num_msi_left -= alloc; + hdev->num_msi_used += alloc; + + return alloc; +} + +static int hclge_get_vector_index(struct hclge_dev *hdev, int vector) +{ + int i; + + for (i = 0; i < hdev->num_msi; i++) { + if (hdev->msix_entries) { + if (vector == hdev->msix_entries[i].vector) + return i; + } else { + if (vector == (hdev->base_msi_vector + i)) + return i; + } + } + return -EINVAL; +} + +static u32 hclge_get_rss_key_size(struct hnae3_handle *handle) +{ + return HCLGE_RSS_KEY_SIZE; +} + +static u32 hclge_get_rss_indir_size(struct hnae3_handle *handle) +{ + return HCLGE_RSS_IND_TBL_SIZE; +} + +static int hclge_get_rss_algo(struct hclge_dev *hdev) +{ + struct hclge_rss_config *req; + struct hclge_desc desc; + int rss_hash_algo; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_GENERIC_CONFIG, true); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get link status error, status =%d\n", ret); + return ret; + } + + req = (struct hclge_rss_config *)desc.data; + rss_hash_algo = (req->hash_config & HCLGE_RSS_HASH_ALGO_MASK); + + if (rss_hash_algo == HCLGE_RSS_HASH_ALGO_TOEPLITZ) + return ETH_RSS_HASH_TOP; + + return -EINVAL; +} + +static int hclge_set_rss_algo_key(struct hclge_dev *hdev, + const u8 hfunc, const u8 *key) +{ + struct hclge_rss_config *req; + struct hclge_desc desc; + int key_offset; + int key_size; + int ret; + + req = (struct hclge_rss_config *)desc.data; + + for (key_offset = 0; key_offset < 3; key_offset++) { + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_GENERIC_CONFIG, + false); + + req->hash_config |= (hfunc & HCLGE_RSS_HASH_ALGO_MASK); + req->hash_config |= (key_offset << HCLGE_RSS_HASH_KEY_OFFSET_B); + + if (key_offset == 2) + key_size = + HCLGE_RSS_KEY_SIZE - HCLGE_RSS_HASH_KEY_NUM * 2; + else + key_size = HCLGE_RSS_HASH_KEY_NUM; + + memcpy(req->hash_key, + key + key_offset * HCLGE_RSS_HASH_KEY_NUM, key_size); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Configure RSS config fail, status = %d\n", + ret); + return ret; + } + } + return 0; +} + +static int hclge_set_rss_indir_table(struct hclge_dev *hdev, const u32 *indir) +{ + struct hclge_rss_indirection_table *req; + struct hclge_desc desc; + int i, j; + int ret; + + req = (struct hclge_rss_indirection_table *)desc.data; + + for (i = 0; i < HCLGE_RSS_CFG_TBL_NUM; i++) { + hclge_cmd_setup_basic_desc + (&desc, HCLGE_OPC_RSS_INDIR_TABLE, false); + + req->start_table_index = i * HCLGE_RSS_CFG_TBL_SIZE; + req->rss_set_bitmap = HCLGE_RSS_SET_BITMAP_MSK; + + for (j = 0; j < HCLGE_RSS_CFG_TBL_SIZE; j++) + req->rss_result[j] = + indir[i * HCLGE_RSS_CFG_TBL_SIZE + j]; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Configure rss indir table fail,status = %d\n", + ret); + return ret; + } + } + return 0; +} + +static int hclge_set_rss_tc_mode(struct hclge_dev *hdev, u16 *tc_valid, + u16 *tc_size, u16 *tc_offset) +{ + struct hclge_rss_tc_mode *req; + struct hclge_desc desc; + int ret; + int i; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_TC_MODE, false); + req = (struct hclge_rss_tc_mode *)desc.data; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + hnae_set_bit(req->rss_tc_mode[i], HCLGE_RSS_TC_VALID_B, + (tc_valid[i] & 0x1)); + hnae_set_field(req->rss_tc_mode[i], HCLGE_RSS_TC_SIZE_M, + HCLGE_RSS_TC_SIZE_S, tc_size[i]); + hnae_set_field(req->rss_tc_mode[i], HCLGE_RSS_TC_OFFSET_M, + HCLGE_RSS_TC_OFFSET_S, tc_offset[i]); + } + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Configure rss tc mode fail, status = %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_set_rss_input_tuple(struct hclge_dev *hdev) +{ +#define HCLGE_RSS_INPUT_TUPLE_OTHER 0xf +#define HCLGE_RSS_INPUT_TUPLE_SCTP 0x1f + struct hclge_rss_input_tuple *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_INPUT_TUPLE, false); + + req = (struct hclge_rss_input_tuple *)desc.data; + req->ipv4_tcp_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv4_udp_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv4_sctp_en = HCLGE_RSS_INPUT_TUPLE_SCTP; + req->ipv4_fragment_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv6_tcp_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv6_udp_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv6_sctp_en = HCLGE_RSS_INPUT_TUPLE_SCTP; + req->ipv6_fragment_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Configure rss input fail, status = %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_get_rss(struct hnae3_handle *handle, u32 *indir, + u8 *key, u8 *hfunc) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int i; + + /* Get hash algorithm */ + if (hfunc) + *hfunc = hclge_get_rss_algo(hdev); + + /* Get the RSS Key required by the user */ + if (key) + memcpy(key, vport->rss_hash_key, HCLGE_RSS_KEY_SIZE); + + /* Get indirect table */ + if (indir) + for (i = 0; i < HCLGE_RSS_IND_TBL_SIZE; i++) + indir[i] = vport->rss_indirection_tbl[i]; + + return 0; +} + +static int hclge_set_rss(struct hnae3_handle *handle, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + u8 hash_algo; + int ret, i; + + /* Set the RSS Hash Key if specififed by the user */ + if (key) { + /* Update the shadow RSS key with user specified qids */ + memcpy(vport->rss_hash_key, key, HCLGE_RSS_KEY_SIZE); + + if (hfunc == ETH_RSS_HASH_TOP || + hfunc == ETH_RSS_HASH_NO_CHANGE) + hash_algo = HCLGE_RSS_HASH_ALGO_TOEPLITZ; + else + return -EINVAL; + ret = hclge_set_rss_algo_key(hdev, hash_algo, key); + if (ret) + return ret; + } + + /* Update the shadow RSS table with user specified qids */ + for (i = 0; i < HCLGE_RSS_IND_TBL_SIZE; i++) + vport->rss_indirection_tbl[i] = indir[i]; + + /* Update the hardware */ + ret = hclge_set_rss_indir_table(hdev, indir); + return ret; +} + +static int hclge_get_tc_size(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hdev->rss_size_max; +} + +static int hclge_rss_init_hw(struct hclge_dev *hdev) +{ + const u8 hfunc = HCLGE_RSS_HASH_ALGO_TOEPLITZ; + struct hclge_vport *vport = hdev->vport; + u16 tc_offset[HCLGE_MAX_TC_NUM]; + u8 rss_key[HCLGE_RSS_KEY_SIZE]; + u16 tc_valid[HCLGE_MAX_TC_NUM]; + u16 tc_size[HCLGE_MAX_TC_NUM]; + u32 *rss_indir = NULL; + const u8 *key; + int i, ret, j; + + rss_indir = kcalloc(HCLGE_RSS_IND_TBL_SIZE, sizeof(u32), GFP_KERNEL); + if (!rss_indir) + return -ENOMEM; + + /* Get default RSS key */ + netdev_rss_key_fill(rss_key, HCLGE_RSS_KEY_SIZE); + + /* Initialize RSS indirect table for each vport */ + for (j = 0; j < hdev->num_vmdq_vport + 1; j++) { + for (i = 0; i < HCLGE_RSS_IND_TBL_SIZE; i++) { + vport[j].rss_indirection_tbl[i] = + i % hdev->rss_size_max; + rss_indir[i] = vport[j].rss_indirection_tbl[i]; + } + } + ret = hclge_set_rss_indir_table(hdev, rss_indir); + if (ret) + goto err; + + key = rss_key; + ret = hclge_set_rss_algo_key(hdev, hfunc, key); + if (ret) + goto err; + + ret = hclge_set_rss_input_tuple(hdev); + if (ret) + goto err; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + if (hdev->hw_tc_map & BIT(i)) + tc_valid[i] = 1; + else + tc_valid[i] = 0; + + switch (hdev->rss_size_max) { + case HCLGE_RSS_TC_SIZE_0: + tc_size[i] = 0; + break; + case HCLGE_RSS_TC_SIZE_1: + tc_size[i] = 1; + break; + case HCLGE_RSS_TC_SIZE_2: + tc_size[i] = 2; + break; + case HCLGE_RSS_TC_SIZE_3: + tc_size[i] = 3; + break; + case HCLGE_RSS_TC_SIZE_4: + tc_size[i] = 4; + break; + case HCLGE_RSS_TC_SIZE_5: + tc_size[i] = 5; + break; + case HCLGE_RSS_TC_SIZE_6: + tc_size[i] = 6; + break; + case HCLGE_RSS_TC_SIZE_7: + tc_size[i] = 7; + break; + default: + break; + } + tc_offset[i] = hdev->rss_size_max * i; + } + ret = hclge_set_rss_tc_mode(hdev, tc_valid, tc_size, tc_offset); + +err: + kfree(rss_indir); + + return ret; +} + +int hclge_map_vport_ring_to_vector(struct hclge_vport *vport, int vector_id, + struct hnae3_ring_chain_node *ring_chain) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_ctrl_vector_chain *req; + struct hnae3_ring_chain_node *node; + struct hclge_desc desc; + int ret; + int i; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_ADD_RING_TO_VECTOR, false); + + req = (struct hclge_ctrl_vector_chain *)desc.data; + req->int_vector_id = vector_id; + + i = 0; + for (node = ring_chain; node; node = node->next) { + hnae_set_field(req->tqp_type_and_id[i], HCLGE_INT_TYPE_M, + HCLGE_INT_TYPE_S, + hnae_get_bit(node->flag, HNAE3_RING_TYPE_B)); + hnae_set_field(req->tqp_type_and_id[i], HCLGE_TQP_ID_M, + HCLGE_TQP_ID_S, node->tqp_index); + req->tqp_type_and_id[i] = cpu_to_le16(req->tqp_type_and_id[i]); + + if (++i >= HCLGE_VECTOR_ELEMENTS_PER_CMD) { + req->int_cause_num = HCLGE_VECTOR_ELEMENTS_PER_CMD; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Map TQP fail, status is %d.\n", + ret); + return ret; + } + i = 0; + + hclge_cmd_setup_basic_desc(&desc, + HCLGE_OPC_ADD_RING_TO_VECTOR, + false); + req->int_vector_id = vector_id; + } + } + + if (i > 0) { + req->int_cause_num = i; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Map TQP fail, status is %d.\n", ret); + return ret; + } + } + + return 0; +} + +int hclge_map_handle_ring_to_vector(struct hnae3_handle *handle, + int vector, + struct hnae3_ring_chain_node *ring_chain) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int vector_id; + + vector_id = hclge_get_vector_index(hdev, vector); + if (vector_id < 0) { + dev_err(&hdev->pdev->dev, + "Get vector index fail. ret =%d\n", vector_id); + return vector_id; + } + + return hclge_map_vport_ring_to_vector(vport, vector_id, ring_chain); +} + +static int hclge_unmap_ring_from_vector( + struct hnae3_handle *handle, int vector, + struct hnae3_ring_chain_node *ring_chain) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_ctrl_vector_chain *req; + struct hnae3_ring_chain_node *node; + struct hclge_desc desc; + int i, vector_id; + int ret; + + vector_id = hclge_get_vector_index(hdev, vector); + if (vector_id < 0) { + dev_err(&handle->pdev->dev, + "Get vector index fail. ret =%d\n", vector_id); + return vector_id; + } + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_DEL_RING_TO_VECTOR, false); + + req = (struct hclge_ctrl_vector_chain *)desc.data; + req->int_vector_id = vector_id; + + i = 0; + for (node = ring_chain; node; node = node->next) { + hnae_set_field(req->tqp_type_and_id[i], HCLGE_INT_TYPE_M, + HCLGE_INT_TYPE_S, + hnae_get_bit(node->flag, HNAE3_RING_TYPE_B)); + hnae_set_field(req->tqp_type_and_id[i], HCLGE_TQP_ID_M, + HCLGE_TQP_ID_S, node->tqp_index); + + req->tqp_type_and_id[i] = cpu_to_le16(req->tqp_type_and_id[i]); + + if (++i >= HCLGE_VECTOR_ELEMENTS_PER_CMD) { + req->int_cause_num = HCLGE_VECTOR_ELEMENTS_PER_CMD; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Unmap TQP fail, status is %d.\n", + ret); + return ret; + } + i = 0; + hclge_cmd_setup_basic_desc(&desc, + HCLGE_OPC_ADD_RING_TO_VECTOR, + false); + req->int_vector_id = vector_id; + } + } + + if (i > 0) { + req->int_cause_num = i; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Unmap TQP fail, status is %d.\n", ret); + return ret; + } + } + + return 0; +} + +int hclge_cmd_set_promisc_mode(struct hclge_dev *hdev, + struct hclge_promisc_param *param) +{ + struct hclge_promisc_cfg *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_PROMISC_MODE, false); + + req = (struct hclge_promisc_cfg *)desc.data; + req->vf_id = param->vf_id; + req->flag = (param->enable << HCLGE_PROMISC_EN_B); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Set promisc mode fail, status is %d.\n", ret); + return ret; + } + return 0; +} + +void hclge_promisc_param_init(struct hclge_promisc_param *param, bool en_uc, + bool en_mc, bool en_bc, int vport_id) +{ + if (!param) + return; + + memset(param, 0, sizeof(struct hclge_promisc_param)); + if (en_uc) + param->enable = HCLGE_PROMISC_EN_UC; + if (en_mc) + param->enable |= HCLGE_PROMISC_EN_MC; + if (en_bc) + param->enable |= HCLGE_PROMISC_EN_BC; + param->vf_id = vport_id; +} + +static void hclge_set_promisc_mode(struct hnae3_handle *handle, u32 en) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_promisc_param param; + + hclge_promisc_param_init(¶m, en, en, true, vport->vport_id); + hclge_cmd_set_promisc_mode(hdev, ¶m); +} + +static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) +{ + struct hclge_desc desc; + struct hclge_config_mac_mode *req = + (struct hclge_config_mac_mode *)desc.data; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAC_MODE, false); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_TX_EN_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_RX_EN_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_PAD_TX_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_PAD_RX_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_1588_TX_B, 0); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_1588_RX_B, 0); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_APP_LP_B, 0); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_LINE_LP_B, 0); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_FCS_TX_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_RX_FCS_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, + HCLGE_MAC_RX_FCS_STRIP_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, + HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, + HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, + HCLGE_MAC_TX_UNDER_MIN_ERR_B, enable); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, + "mac enable fail, ret =%d.\n", ret); +} + +static int hclge_tqp_enable(struct hclge_dev *hdev, int tqp_id, + int stream_id, bool enable) +{ + struct hclge_desc desc; + struct hclge_cfg_com_tqp_queue *req = + (struct hclge_cfg_com_tqp_queue *)desc.data; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_COM_TQP_QUEUE, false); + req->tqp_id = cpu_to_le16(tqp_id & HCLGE_RING_ID_MASK); + req->stream_id = cpu_to_le16(stream_id); + req->enable |= enable << HCLGE_TQP_ENABLE_B; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, + "Tqp enable fail, status =%d.\n", ret); + return ret; +} + +static void hclge_reset_tqp_stats(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hnae3_queue *queue; + struct hclge_tqp *tqp; + int i; + + for (i = 0; i < vport->alloc_tqps; i++) { + queue = handle->kinfo.tqp[i]; + tqp = container_of(queue, struct hclge_tqp, q); + memset(&tqp->tqp_stats, 0, sizeof(tqp->tqp_stats)); + } +} + +static int hclge_ae_start(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int i, queue_id, ret; + + for (i = 0; i < vport->alloc_tqps; i++) { + /* todo clear interrupt */ + /* ring enable */ + queue_id = hclge_get_queue_id(handle->kinfo.tqp[i]); + if (queue_id < 0) { + dev_warn(&hdev->pdev->dev, + "Get invalid queue id, ignore it\n"); + continue; + } + + hclge_tqp_enable(hdev, queue_id, 0, true); + } + /* mac enable */ + hclge_cfg_mac_mode(hdev, true); + clear_bit(HCLGE_STATE_DOWN, &hdev->state); + (void)mod_timer(&hdev->service_timer, jiffies + HZ); + + ret = hclge_mac_start_phy(hdev); + if (ret) + return ret; + + /* reset tqp stats */ + hclge_reset_tqp_stats(handle); + + return 0; +} + +static void hclge_ae_stop(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int i, queue_id; + + for (i = 0; i < vport->alloc_tqps; i++) { + /* Ring disable */ + queue_id = hclge_get_queue_id(handle->kinfo.tqp[i]); + if (queue_id < 0) { + dev_warn(&hdev->pdev->dev, + "Get invalid queue id, ignore it\n"); + continue; + } + + hclge_tqp_enable(hdev, queue_id, 0, false); + } + /* Mac disable */ + hclge_cfg_mac_mode(hdev, false); + + hclge_mac_stop_phy(hdev); + + /* reset tqp stats */ + hclge_reset_tqp_stats(handle); +} + +static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport, + u16 cmdq_resp, u8 resp_code, + enum hclge_mac_vlan_tbl_opcode op) +{ + struct hclge_dev *hdev = vport->back; + int return_status = -EIO; + + if (cmdq_resp) { + dev_err(&hdev->pdev->dev, + "cmdq execute failed for get_mac_vlan_cmd_status,status=%d.\n", + cmdq_resp); + return -EIO; + } + + if (op == HCLGE_MAC_VLAN_ADD) { + if ((!resp_code) || (resp_code == 1)) { + return_status = 0; + } else if (resp_code == 2) { + return_status = -EIO; + dev_err(&hdev->pdev->dev, + "add mac addr failed for uc_overflow.\n"); + } else if (resp_code == 3) { + return_status = -EIO; + dev_err(&hdev->pdev->dev, + "add mac addr failed for mc_overflow.\n"); + } else { + dev_err(&hdev->pdev->dev, + "add mac addr failed for undefined, code=%d.\n", + resp_code); + } + } else if (op == HCLGE_MAC_VLAN_REMOVE) { + if (!resp_code) { + return_status = 0; + } else if (resp_code == 1) { + return_status = -EIO; + dev_dbg(&hdev->pdev->dev, + "remove mac addr failed for miss.\n"); + } else { + dev_err(&hdev->pdev->dev, + "remove mac addr failed for undefined, code=%d.\n", + resp_code); + } + } else if (op == HCLGE_MAC_VLAN_LKUP) { + if (!resp_code) { + return_status = 0; + } else if (resp_code == 1) { + return_status = -EIO; + dev_dbg(&hdev->pdev->dev, + "lookup mac addr failed for miss.\n"); + } else { + dev_err(&hdev->pdev->dev, + "lookup mac addr failed for undefined, code=%d.\n", + resp_code); + } + } else { + return_status = -EIO; + dev_err(&hdev->pdev->dev, + "unknown opcode for get_mac_vlan_cmd_status,opcode=%d.\n", + op); + } + + return return_status; +} + +static int hclge_update_desc_vfid(struct hclge_desc *desc, int vfid, bool clr) +{ + int word_num; + int bit_num; + + if (vfid > 255 || vfid < 0) + return -EIO; + + if (vfid >= 0 && vfid <= 191) { + word_num = vfid / 32; + bit_num = vfid % 32; + if (clr) + desc[1].data[word_num] &= ~(1 << bit_num); + else + desc[1].data[word_num] |= (1 << bit_num); + } else { + word_num = (vfid - 192) / 32; + bit_num = vfid % 32; + if (clr) + desc[2].data[word_num] &= ~(1 << bit_num); + else + desc[2].data[word_num] |= (1 << bit_num); + } + + return 0; +} + +static bool hclge_is_all_function_id_zero(struct hclge_desc *desc) +{ +#define HCLGE_DESC_NUMBER 3 +#define HCLGE_FUNC_NUMBER_PER_DESC 6 + int i, j; + + for (i = 0; i < HCLGE_DESC_NUMBER; i++) + for (j = 0; j < HCLGE_FUNC_NUMBER_PER_DESC; j++) + if (desc[i].data[j]) + return false; + + return true; +} + +static void hclge_prepare_mac_addr(struct hclge_mac_vlan_tbl_entry *new_req, + const u8 *addr) +{ + const unsigned char *mac_addr = addr; + u32 high_val = mac_addr[2] << 16 | (mac_addr[3] << 24) | + (mac_addr[0]) | (mac_addr[1] << 8); + u32 low_val = mac_addr[4] | (mac_addr[5] << 8); + + new_req->mac_addr_hi32 = cpu_to_le32(high_val); + new_req->mac_addr_lo16 = cpu_to_le16(low_val & 0xffff); +} + +u16 hclge_get_mac_addr_to_mta_index(struct hclge_vport *vport, + const u8 *addr) +{ + u16 high_val = addr[1] | (addr[0] << 8); + struct hclge_dev *hdev = vport->back; + u32 rsh = 4 - hdev->mta_mac_sel_type; + u16 ret_val = (high_val >> rsh) & 0xfff; + + return ret_val; +} + +static int hclge_set_mta_filter_mode(struct hclge_dev *hdev, + enum hclge_mta_dmac_sel_type mta_mac_sel, + bool enable) +{ + struct hclge_mta_filter_mode *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_mta_filter_mode *)desc.data; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MTA_MAC_MODE_CFG, false); + + hnae_set_bit(req->dmac_sel_en, HCLGE_CFG_MTA_MAC_EN_B, + enable); + hnae_set_field(req->dmac_sel_en, HCLGE_CFG_MTA_MAC_SEL_M, + HCLGE_CFG_MTA_MAC_SEL_S, mta_mac_sel); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Config mat filter mode failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + + return 0; +} + +int hclge_cfg_func_mta_filter(struct hclge_dev *hdev, + u8 func_id, + bool enable) +{ + struct hclge_cfg_func_mta_filter *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_cfg_func_mta_filter *)desc.data; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MTA_MAC_FUNC_CFG, false); + + hnae_set_bit(req->accept, HCLGE_CFG_FUNC_MTA_ACCEPT_B, + enable); + req->function_id = func_id; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Config func_id enable failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_set_mta_table_item(struct hclge_vport *vport, + u16 idx, + bool enable) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_cfg_func_mta_item *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_cfg_func_mta_item *)desc.data; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MTA_TBL_ITEM_CFG, false); + hnae_set_bit(req->accept, HCLGE_CFG_MTA_ITEM_ACCEPT_B, enable); + + hnae_set_field(req->item_idx, HCLGE_CFG_MTA_ITEM_IDX_M, + HCLGE_CFG_MTA_ITEM_IDX_S, idx); + req->item_idx = cpu_to_le16(req->item_idx); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Config mta table item failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_remove_mac_vlan_tbl(struct hclge_vport *vport, + struct hclge_mac_vlan_tbl_entry *req) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_desc desc; + u8 resp_code; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MAC_VLAN_REMOVE, false); + + memcpy(desc.data, req, sizeof(struct hclge_mac_vlan_tbl_entry)); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "del mac addr failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + resp_code = (desc.data[0] >> 8) & 0xff; + + return hclge_get_mac_vlan_cmd_status(vport, desc.retval, resp_code, + HCLGE_MAC_VLAN_REMOVE); +} + +static int hclge_lookup_mac_vlan_tbl(struct hclge_vport *vport, + struct hclge_mac_vlan_tbl_entry *req, + struct hclge_desc *desc, + bool is_mc) +{ + struct hclge_dev *hdev = vport->back; + u8 resp_code; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_MAC_VLAN_ADD, true); + if (is_mc) { + desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + memcpy(desc[0].data, + req, + sizeof(struct hclge_mac_vlan_tbl_entry)); + hclge_cmd_setup_basic_desc(&desc[1], + HCLGE_OPC_MAC_VLAN_ADD, + true); + desc[1].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + hclge_cmd_setup_basic_desc(&desc[2], + HCLGE_OPC_MAC_VLAN_ADD, + true); + ret = hclge_cmd_send(&hdev->hw, desc, 3); + } else { + memcpy(desc[0].data, + req, + sizeof(struct hclge_mac_vlan_tbl_entry)); + ret = hclge_cmd_send(&hdev->hw, desc, 1); + } + if (ret) { + dev_err(&hdev->pdev->dev, + "lookup mac addr failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + resp_code = (desc[0].data[0] >> 8) & 0xff; + + return hclge_get_mac_vlan_cmd_status(vport, desc[0].retval, resp_code, + HCLGE_MAC_VLAN_LKUP); +} + +static int hclge_add_mac_vlan_tbl(struct hclge_vport *vport, + struct hclge_mac_vlan_tbl_entry *req, + struct hclge_desc *mc_desc) +{ + struct hclge_dev *hdev = vport->back; + int cfg_status; + u8 resp_code; + int ret; + + if (!mc_desc) { + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, + HCLGE_OPC_MAC_VLAN_ADD, + false); + memcpy(desc.data, req, sizeof(struct hclge_mac_vlan_tbl_entry)); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + resp_code = (desc.data[0] >> 8) & 0xff; + cfg_status = hclge_get_mac_vlan_cmd_status(vport, desc.retval, + resp_code, + HCLGE_MAC_VLAN_ADD); + } else { + mc_desc[0].flag &= cpu_to_le16(~HCLGE_CMD_FLAG_WR); + mc_desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + mc_desc[1].flag &= cpu_to_le16(~HCLGE_CMD_FLAG_WR); + mc_desc[1].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + mc_desc[2].flag &= cpu_to_le16(~HCLGE_CMD_FLAG_WR); + mc_desc[2].flag &= cpu_to_le16(~HCLGE_CMD_FLAG_NEXT); + memcpy(mc_desc[0].data, req, + sizeof(struct hclge_mac_vlan_tbl_entry)); + ret = hclge_cmd_send(&hdev->hw, mc_desc, 3); + resp_code = (mc_desc[0].data[0] >> 8) & 0xff; + cfg_status = hclge_get_mac_vlan_cmd_status(vport, + mc_desc[0].retval, + resp_code, + HCLGE_MAC_VLAN_ADD); + } + + if (ret) { + dev_err(&hdev->pdev->dev, + "add mac addr failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + + return cfg_status; +} + +static int hclge_add_uc_addr(struct hnae3_handle *handle, + const unsigned char *addr) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + return hclge_add_uc_addr_common(vport, addr); +} + +int hclge_add_uc_addr_common(struct hclge_vport *vport, + const unsigned char *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_mac_vlan_tbl_entry req; + enum hclge_cmd_status status; + + /* mac addr check */ + if (is_zero_ether_addr(addr) || + is_broadcast_ether_addr(addr) || + is_multicast_ether_addr(addr)) { + dev_err(&hdev->pdev->dev, + "Set_uc mac err! invalid mac:%pM. is_zero:%d,is_br=%d,is_mul=%d\n", + addr, + is_zero_ether_addr(addr), + is_broadcast_ether_addr(addr), + is_multicast_ether_addr(addr)); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + hnae_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 0); + hnae_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hnae_set_bit(req.egress_port, + HCLGE_MAC_EPORT_SW_EN_B, 0); + hnae_set_bit(req.egress_port, + HCLGE_MAC_EPORT_TYPE_B, 0); + hnae_set_field(req.egress_port, HCLGE_MAC_EPORT_VFID_M, + HCLGE_MAC_EPORT_VFID_S, vport->vport_id); + hnae_set_field(req.egress_port, HCLGE_MAC_EPORT_PFID_M, + HCLGE_MAC_EPORT_PFID_S, 0); + req.egress_port = cpu_to_le16(req.egress_port); + + hclge_prepare_mac_addr(&req, addr); + + status = hclge_add_mac_vlan_tbl(vport, &req, NULL); + + return status; +} + +static int hclge_rm_uc_addr(struct hnae3_handle *handle, + const unsigned char *addr) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + return hclge_rm_uc_addr_common(vport, addr); +} + +int hclge_rm_uc_addr_common(struct hclge_vport *vport, + const unsigned char *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_mac_vlan_tbl_entry req; + enum hclge_cmd_status status; + + /* mac addr check */ + if (is_zero_ether_addr(addr) || + is_broadcast_ether_addr(addr) || + is_multicast_ether_addr(addr)) { + dev_dbg(&hdev->pdev->dev, + "Remove mac err! invalid mac:%pM.\n", + addr); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + hnae_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hclge_prepare_mac_addr(&req, addr); + status = hclge_remove_mac_vlan_tbl(vport, &req); + + return status; +} + +static int hclge_add_mc_addr(struct hnae3_handle *handle, + const unsigned char *addr) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + return hclge_add_mc_addr_common(vport, addr); +} + +int hclge_add_mc_addr_common(struct hclge_vport *vport, + const unsigned char *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_mac_vlan_tbl_entry req; + struct hclge_desc desc[3]; + u16 tbl_idx; + int status; + + /* mac addr check */ + if (!is_multicast_ether_addr(addr)) { + dev_err(&hdev->pdev->dev, + "Add mc mac err! invalid mac:%pM.\n", + addr); + return -EINVAL; + } + memset(&req, 0, sizeof(req)); + hnae_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1); + hnae_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hclge_prepare_mac_addr(&req, addr); + status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true); + if (!status) { + /* This mac addr exist, update VFID for it */ + hclge_update_desc_vfid(desc, vport->vport_id, false); + status = hclge_add_mac_vlan_tbl(vport, &req, desc); + } else { + /* This mac addr do not exist, add new entry for it */ + memset(desc[0].data, 0, sizeof(desc[0].data)); + memset(desc[1].data, 0, sizeof(desc[0].data)); + memset(desc[2].data, 0, sizeof(desc[0].data)); + hclge_update_desc_vfid(desc, vport->vport_id, false); + status = hclge_add_mac_vlan_tbl(vport, &req, desc); + } + + /* Set MTA table for this MAC address */ + tbl_idx = hclge_get_mac_addr_to_mta_index(vport, addr); + status = hclge_set_mta_table_item(vport, tbl_idx, true); + + return status; +} + +static int hclge_rm_mc_addr(struct hnae3_handle *handle, + const unsigned char *addr) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + return hclge_rm_mc_addr_common(vport, addr); +} + +int hclge_rm_mc_addr_common(struct hclge_vport *vport, + const unsigned char *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_mac_vlan_tbl_entry req; + enum hclge_cmd_status status; + struct hclge_desc desc[3]; + u16 tbl_idx; + + /* mac addr check */ + if (!is_multicast_ether_addr(addr)) { + dev_dbg(&hdev->pdev->dev, + "Remove mc mac err! invalid mac:%pM.\n", + addr); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + hnae_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1); + hnae_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hclge_prepare_mac_addr(&req, addr); + status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true); + if (!status) { + /* This mac addr exist, remove this handle's VFID for it */ + hclge_update_desc_vfid(desc, vport->vport_id, true); + + if (hclge_is_all_function_id_zero(desc)) + /* All the vfid is zero, so need to delete this entry */ + status = hclge_remove_mac_vlan_tbl(vport, &req); + else + /* Not all the vfid is zero, update the vfid */ + status = hclge_add_mac_vlan_tbl(vport, &req, desc); + + } else { + /* This mac addr do not exist, can't delete it */ + dev_err(&hdev->pdev->dev, + "Rm mutilcast mac addr failed, ret = %d.\n", + status); + return -EIO; + } + + /* Set MTB table for this MAC address */ + tbl_idx = hclge_get_mac_addr_to_mta_index(vport, addr); + status = hclge_set_mta_table_item(vport, tbl_idx, false); + + return status; +} + +static void hclge_get_mac_addr(struct hnae3_handle *handle, u8 *p) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + ether_addr_copy(p, hdev->hw.mac.mac_addr); +} + +static int hclge_set_mac_addr(struct hnae3_handle *handle, void *p) +{ + const unsigned char *new_addr = (const unsigned char *)p; + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + /* mac addr check */ + if (is_zero_ether_addr(new_addr) || + is_broadcast_ether_addr(new_addr) || + is_multicast_ether_addr(new_addr)) { + dev_err(&hdev->pdev->dev, + "Change uc mac err! invalid mac:%p.\n", + new_addr); + return -EINVAL; + } + + hclge_rm_uc_addr(handle, hdev->hw.mac.mac_addr); + + if (!hclge_add_uc_addr(handle, new_addr)) { + ether_addr_copy(hdev->hw.mac.mac_addr, new_addr); + return 0; + } + + return -EIO; +} + +static int hclge_set_vlan_filter_ctrl(struct hclge_dev *hdev, u8 vlan_type, + bool filter_en) +{ + struct hclge_vlan_filter_ctrl *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_FILTER_CTRL, false); + + req = (struct hclge_vlan_filter_ctrl *)desc.data; + req->vlan_type = vlan_type; + req->vlan_fe = filter_en; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "set vlan filter fail, ret =%d.\n", + ret); + return ret; + } + + return 0; +} + +int hclge_set_vf_vlan_common(struct hclge_dev *hdev, int vfid, + bool is_kill, u16 vlan, u8 qos, __be16 proto) +{ +#define HCLGE_MAX_VF_BYTES 16 + struct hclge_vlan_filter_vf_cfg *req0; + struct hclge_vlan_filter_vf_cfg *req1; + struct hclge_desc desc[2]; + u8 vf_byte_val; + u8 vf_byte_off; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], + HCLGE_OPC_VLAN_FILTER_VF_CFG, false); + hclge_cmd_setup_basic_desc(&desc[1], + HCLGE_OPC_VLAN_FILTER_VF_CFG, false); + + desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + + vf_byte_off = vfid / 8; + vf_byte_val = 1 << (vfid % 8); + + req0 = (struct hclge_vlan_filter_vf_cfg *)desc[0].data; + req1 = (struct hclge_vlan_filter_vf_cfg *)desc[1].data; + + req0->vlan_id = vlan; + req0->vlan_cfg = is_kill; + + if (vf_byte_off < HCLGE_MAX_VF_BYTES) + req0->vf_bitmap[vf_byte_off] = vf_byte_val; + else + req1->vf_bitmap[vf_byte_off - HCLGE_MAX_VF_BYTES] = vf_byte_val; + + ret = hclge_cmd_send(&hdev->hw, desc, 2); + if (ret) { + dev_err(&hdev->pdev->dev, + "Send vf vlan command fail, ret =%d.\n", + ret); + return ret; + } + + if (!is_kill) { + if (!req0->resp_code || req0->resp_code == 1) + return 0; + + dev_err(&hdev->pdev->dev, + "Add vf vlan filter fail, ret =%d.\n", + req0->resp_code); + } else { + if (!req0->resp_code) + return 0; + + dev_err(&hdev->pdev->dev, + "Kill vf vlan filter fail, ret =%d.\n", + req0->resp_code); + } + + return -EIO; +} + +static int hclge_set_port_vlan_filter(struct hnae3_handle *handle, + __be16 proto, u16 vlan_id, + bool is_kill) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_vlan_filter_pf_cfg *req; + struct hclge_desc desc; + u8 vlan_offset_byte_val; + u8 vlan_offset_byte; + u8 vlan_offset_160; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_FILTER_PF_CFG, false); + + vlan_offset_160 = vlan_id / 160; + vlan_offset_byte = (vlan_id % 160) / 8; + vlan_offset_byte_val = 1 << (vlan_id % 8); + + req = (struct hclge_vlan_filter_pf_cfg *)desc.data; + req->vlan_offset = vlan_offset_160; + req->vlan_cfg = is_kill; + req->vlan_offset_bitmap[vlan_offset_byte] = vlan_offset_byte_val; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "port vlan command, send fail, ret =%d.\n", + ret); + return ret; + } + + ret = hclge_set_vf_vlan_common(hdev, 0, is_kill, vlan_id, 0, proto); + if (ret) { + dev_err(&hdev->pdev->dev, + "Set pf vlan filter config fail, ret =%d.\n", + ret); + return -EIO; + } + + return 0; +} + +static int hclge_set_vf_vlan_filter(struct hnae3_handle *handle, int vfid, + u16 vlan, u8 qos, __be16 proto) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + if ((vfid >= hdev->num_alloc_vfs) || (vlan > 4095) || (qos > 7)) + return -EINVAL; + if (proto != htons(ETH_P_8021Q)) + return -EPROTONOSUPPORT; + + return hclge_set_vf_vlan_common(hdev, vfid, false, vlan, qos, proto); +} + +static int hclge_init_vlan_config(struct hclge_dev *hdev) +{ +#define HCLGE_VLAN_TYPE_VF_TABLE 0 +#define HCLGE_VLAN_TYPE_PORT_TABLE 1 + int ret; + + ret = hclge_set_vlan_filter_ctrl(hdev, HCLGE_VLAN_TYPE_VF_TABLE, + true); + if (ret) + return ret; + + ret = hclge_set_vlan_filter_ctrl(hdev, HCLGE_VLAN_TYPE_PORT_TABLE, + true); + + return ret; +} + +static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_config_max_frm_size *req; + struct hclge_dev *hdev = vport->back; + struct hclge_desc desc; + int ret; + + if ((new_mtu < HCLGE_MAC_MIN_MTU) || (new_mtu > HCLGE_MAC_MAX_MTU)) + return -EINVAL; + + hdev->mps = new_mtu; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAX_FRM_SIZE, false); + + req = (struct hclge_config_max_frm_size *)desc.data; + req->max_frm_size = cpu_to_le16(new_mtu); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "set mtu fail, ret =%d.\n", ret); + return ret; + } + + return 0; +} + +static int hclge_send_reset_tqp_cmd(struct hclge_dev *hdev, u16 queue_id, + bool enable) +{ + struct hclge_reset_tqp_queue *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RESET_TQP_QUEUE, false); + + req = (struct hclge_reset_tqp_queue *)desc.data; + req->tqp_id = cpu_to_le16(queue_id & HCLGE_RING_ID_MASK); + hnae_set_bit(req->reset_req, HCLGE_TQP_RESET_B, enable); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Send tqp reset cmd error, status =%d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_get_reset_status(struct hclge_dev *hdev, u16 queue_id) +{ + struct hclge_reset_tqp_queue *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RESET_TQP_QUEUE, true); + + req = (struct hclge_reset_tqp_queue *)desc.data; + req->tqp_id = cpu_to_le16(queue_id & HCLGE_RING_ID_MASK); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get reset status error, status =%d\n", ret); + return ret; + } + + return hnae_get_bit(req->ready_to_reset, HCLGE_TQP_RESET_B); +} + +static void hclge_reset_tqp(struct hnae3_handle *handle, u16 queue_id) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int reset_try_times = 0; + int reset_status; + int ret; + + ret = hclge_tqp_enable(hdev, queue_id, 0, false); + if (ret) { + dev_warn(&hdev->pdev->dev, "Disable tqp fail, ret = %d\n", ret); + return; + } + + ret = hclge_send_reset_tqp_cmd(hdev, queue_id, true); + if (ret) { + dev_warn(&hdev->pdev->dev, + "Send reset tqp cmd fail, ret = %d\n", ret); + return; + } + + reset_try_times = 0; + while (reset_try_times++ < HCLGE_TQP_RESET_TRY_TIMES) { + /* Wait for tqp hw reset */ + msleep(20); + reset_status = hclge_get_reset_status(hdev, queue_id); + if (reset_status) + break; + } + + if (reset_try_times >= HCLGE_TQP_RESET_TRY_TIMES) { + dev_warn(&hdev->pdev->dev, "Reset TQP fail\n"); + return; + } + + ret = hclge_send_reset_tqp_cmd(hdev, queue_id, false); + if (ret) { + dev_warn(&hdev->pdev->dev, + "Deassert the soft reset fail, ret = %d\n", ret); + return; + } +} + +static u32 hclge_get_fw_version(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hdev->fw_version; +} + +static void hclge_get_pauseparam(struct hnae3_handle *handle, u32 *auto_neg, + u32 *rx_en, u32 *tx_en) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + *auto_neg = hclge_get_autoneg(handle); + + if (hdev->tm_info.fc_mode == HCLGE_FC_PFC) { + *rx_en = 0; + *tx_en = 0; + return; + } + + if (hdev->tm_info.fc_mode == HCLGE_FC_RX_PAUSE) { + *rx_en = 1; + *tx_en = 0; + } else if (hdev->tm_info.fc_mode == HCLGE_FC_TX_PAUSE) { + *tx_en = 1; + *rx_en = 0; + } else if (hdev->tm_info.fc_mode == HCLGE_FC_FULL) { + *rx_en = 1; + *tx_en = 1; + } else { + *rx_en = 0; + *tx_en = 0; + } +} + +static void hclge_get_ksettings_an_result(struct hnae3_handle *handle, + u8 *auto_neg, u32 *speed, u8 *duplex) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + if (speed) + *speed = hdev->hw.mac.speed; + if (duplex) + *duplex = hdev->hw.mac.duplex; + if (auto_neg) + *auto_neg = hdev->hw.mac.autoneg; +} + +static void hclge_get_media_type(struct hnae3_handle *handle, u8 *media_type) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + if (media_type) + *media_type = hdev->hw.mac.media_type; +} + +static void hclge_get_mdix_mode(struct hnae3_handle *handle, + u8 *tp_mdix_ctrl, u8 *tp_mdix) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct phy_device *phydev = hdev->hw.mac.phydev; + int mdix_ctrl, mdix, retval, is_resolved; + + if (!phydev) { + *tp_mdix_ctrl = ETH_TP_MDI_INVALID; + *tp_mdix = ETH_TP_MDI_INVALID; + return; + } + + phy_write(phydev, HCLGE_PHY_PAGE_REG, HCLGE_PHY_PAGE_MDIX); + + retval = phy_read(phydev, HCLGE_PHY_CSC_REG); + mdix_ctrl = hnae_get_field(retval, HCLGE_PHY_MDIX_CTRL_M, + HCLGE_PHY_MDIX_CTRL_S); + + retval = phy_read(phydev, HCLGE_PHY_CSS_REG); + mdix = hnae_get_bit(retval, HCLGE_PHY_MDIX_STATUS_B); + is_resolved = hnae_get_bit(retval, HCLGE_PHY_SPEED_DUP_RESOLVE_B); + + phy_write(phydev, HCLGE_PHY_PAGE_REG, HCLGE_PHY_PAGE_COPPER); + + switch (mdix_ctrl) { + case 0x0: + *tp_mdix_ctrl = ETH_TP_MDI; + break; + case 0x1: + *tp_mdix_ctrl = ETH_TP_MDI_X; + break; + case 0x3: + *tp_mdix_ctrl = ETH_TP_MDI_AUTO; + break; + default: + *tp_mdix_ctrl = ETH_TP_MDI_INVALID; + break; + } + + if (!is_resolved) + *tp_mdix = ETH_TP_MDI_INVALID; + else if (mdix) + *tp_mdix = ETH_TP_MDI_X; + else + *tp_mdix = ETH_TP_MDI; +} + +static int hclge_init_client_instance(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev) +{ + struct hclge_dev *hdev = ae_dev->priv; + struct hclge_vport *vport; + int i, ret; + + for (i = 0; i < hdev->num_vmdq_vport + 1; i++) { + vport = &hdev->vport[i]; + + switch (client->type) { + case HNAE3_CLIENT_KNIC: + + hdev->nic_client = client; + vport->nic.client = client; + ret = client->ops->init_instance(&vport->nic); + if (ret) + goto err; + + if (hdev->roce_client && + hnae_get_bit(hdev->ae_dev->flag, + HNAE_DEV_SUPPORT_ROCE_B)) { + struct hnae3_client *rc = hdev->roce_client; + + ret = hclge_init_roce_base_info(vport); + if (ret) + goto err; + + ret = rc->ops->init_instance(&vport->roce); + if (ret) + goto err; + } + + break; + case HNAE3_CLIENT_UNIC: + hdev->nic_client = client; + vport->nic.client = client; + + ret = client->ops->init_instance(&vport->nic); + if (ret) + goto err; + + break; + case HNAE3_CLIENT_ROCE: + if (hnae_get_bit(hdev->ae_dev->flag, + HNAE_DEV_SUPPORT_ROCE_B)) { + hdev->roce_client = client; + vport->roce.client = client; + } + + if (hdev->roce_client) { + ret = hclge_init_roce_base_info(vport); + if (ret) + goto err; + + ret = client->ops->init_instance(&vport->roce); + if (ret) + goto err; + } + } + } + + return 0; +err: + return ret; +} + +static void hclge_uninit_client_instance(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev) +{ + struct hclge_dev *hdev = ae_dev->priv; + struct hclge_vport *vport; + int i; + + for (i = 0; i < hdev->num_vmdq_vport + 1; i++) { + vport = &hdev->vport[i]; + if (hdev->roce_client) + hdev->roce_client->ops->uninit_instance(&vport->roce, + 0); + if (client->type == HNAE3_CLIENT_ROCE) + return; + if (client->ops->uninit_instance) + client->ops->uninit_instance(&vport->nic, 0); + } +} + +static int hclge_pci_init(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + struct hclge_hw *hw; + int ret; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to enable PCI device\n"); + goto err_no_drvdata; + } + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, + "can't set consistent PCI DMA"); + goto err_disable_device; + } + dev_warn(&pdev->dev, "set DMA mask to 32 bits\n"); + } + + ret = pci_request_regions(pdev, HCLGE_DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, "PCI request regions failed %d\n", ret); + goto err_disable_device; + } + + pci_set_master(pdev); + hw = &hdev->hw; + hw->back = hdev; + hw->io_base = pcim_iomap(pdev, 2, 0); + if (!hw->io_base) { + dev_err(&pdev->dev, "Can't map configuration register space\n"); + ret = -ENOMEM; + goto err_clr_master; + } + + return 0; +err_clr_master: + pci_clear_master(pdev); + pci_release_regions(pdev); +err_disable_device: + pci_disable_device(pdev); +err_no_drvdata: + pci_set_drvdata(pdev, NULL); + + return ret; +} + +static void hclge_pci_uninit(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + + if (hdev->flag & HCLGE_FLAG_USE_MSIX) { + pci_disable_msix(pdev); + devm_kfree(&pdev->dev, hdev->msix_entries); + hdev->msix_entries = NULL; + } else { + pci_disable_msi(pdev); + } + + pci_clear_master(pdev); + pci_release_mem_regions(pdev); + pci_disable_device(pdev); +} + +static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) +{ + struct pci_dev *pdev = ae_dev->pdev; + const struct pci_device_id *id; + struct hclge_dev *hdev; + int ret; + + hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) { + ret = -ENOMEM; + goto err_hclge_dev; + } + + hdev->flag |= HCLGE_FLAG_USE_MSIX; + hdev->pdev = pdev; + hdev->ae_dev = ae_dev; + ae_dev->priv = hdev; + + id = pci_match_id(roce_pci_tbl, ae_dev->pdev); + if (id) + hnae_set_bit(ae_dev->flag, HNAE_DEV_SUPPORT_ROCE_B, 1); + + ret = hclge_pci_init(hdev); + if (ret) { + dev_err(&pdev->dev, "PCI init failed\n"); + goto err_pci_init; + } + + /* Command queue initialize */ + ret = hclge_cmd_init(hdev); + if (ret) + goto err_cmd_init; + + ret = hclge_get_cap(hdev); + if (ret) { + dev_err(&pdev->dev, "get hw capabilty error, ret = %d.\n", ret); + return ret; + } + + ret = hclge_configure(hdev); + if (ret) { + dev_err(&pdev->dev, "Configure dev error, ret = %d.\n", ret); + return ret; + } + + if (hdev->flag & HCLGE_FLAG_USE_MSIX) + ret = hclge_init_msix(hdev); + else + ret = hclge_init_msi(hdev); + if (ret) { + dev_err(&pdev->dev, "Init msix/msi error, ret = %d.\n", ret); + return ret; + } + + ret = hclge_alloc_tqps(hdev); + if (ret) { + dev_err(&pdev->dev, "Allocate TQPs error, ret = %d.\n", ret); + return ret; + } + + ret = hclge_alloc_vport(hdev); + if (ret) { + dev_err(&pdev->dev, "Allocate vport error, ret = %d.\n", ret); + return ret; + } + + ret = hclge_mac_init(hdev); + if (ret) { + dev_err(&pdev->dev, "Mac init error, ret = %d\n", ret); + return ret; + } + ret = hclge_buffer_alloc(hdev); + if (ret) { + dev_err(&pdev->dev, "Buffer allocate fail, ret =%d\n", ret); + return ret; + } + + ret = hclge_config_tso(hdev, HCLGE_TSO_MSS_MIN, HCLGE_TSO_MSS_MAX); + if (ret) { + dev_err(&pdev->dev, "Enable tso fail, ret =%d\n", ret); + return ret; + } + + ret = hclge_rss_init_hw(hdev); + if (ret) { + dev_err(&pdev->dev, "Rss init fail, ret =%d\n", ret); + return ret; + } + + ret = hclge_init_vlan_config(hdev); + if (ret) { + dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret); + return ret; + } + + ret = hclge_tm_schd_init(hdev); + if (ret) { + dev_err(&pdev->dev, "tm schd init fail, ret =%d\n", ret); + return ret; + } + + setup_timer(&hdev->service_timer, hclge_service_timer, + (unsigned long)hdev); + INIT_WORK(&hdev->service_task, hclge_service_task); + + set_bit(HCLGE_STATE_SERVICE_INITED, &hdev->state); + set_bit(HCLGE_STATE_DOWN, &hdev->state); + + pr_info("%s driver initialization finished.\n", HCLGE_DRIVER_NAME); + return 0; + +err_cmd_init: + pci_release_regions(pdev); +err_pci_init: + pci_set_drvdata(pdev, NULL); +err_hclge_dev: + return ret; +} + +static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev) +{ + struct hclge_dev *hdev = ae_dev->priv; + struct hclge_mac *mac = &hdev->hw.mac; + + set_bit(HCLGE_STATE_DOWN, &hdev->state); + +#ifdef CONFIG_PCI_IOV + hclge_disable_sriov(hdev); +#endif + + if (hdev->service_timer.data) + del_timer_sync(&hdev->service_timer); + if (hdev->service_task.func) + cancel_work_sync(&hdev->service_task); + + if (mac->phydev) + mdiobus_unregister(mac->mdio_bus); + + hclge_destroy_cmd_queue(&hdev->hw); + hclge_pci_uninit(hdev); + ae_dev->priv = NULL; +} + +static const struct hnae3_ae_ops hclge_ops = { + .init_ae_dev = hclge_init_ae_dev, + .uninit_ae_dev = hclge_uninit_ae_dev, + .init_client_instance = hclge_init_client_instance, + .uninit_client_instance = hclge_uninit_client_instance, + .map_ring_to_vector = hclge_map_handle_ring_to_vector, + .unmap_ring_from_vector = hclge_unmap_ring_from_vector, + .get_vector = hclge_get_vector, + .set_promisc_mode = hclge_set_promisc_mode, + .start = hclge_ae_start, + .stop = hclge_ae_stop, + .get_status = hclge_get_status, + .get_ksettings_an_result = hclge_get_ksettings_an_result, + .update_speed_duplex_h = hclge_update_speed_duplex_h, + .cfg_mac_speed_dup_h = hclge_cfg_mac_speed_dup_h, + .get_media_type = hclge_get_media_type, + .get_rss_key_size = hclge_get_rss_key_size, + .get_rss_indir_size = hclge_get_rss_indir_size, + .get_rss = hclge_get_rss, + .set_rss = hclge_set_rss, + .get_tc_size = hclge_get_tc_size, + .get_mac_addr = hclge_get_mac_addr, + .set_mac_addr = hclge_set_mac_addr, + .add_uc_addr = hclge_add_uc_addr, + .rm_uc_addr = hclge_rm_uc_addr, + .add_mc_addr = hclge_add_mc_addr, + .rm_mc_addr = hclge_rm_mc_addr, + .set_autoneg = hclge_set_autoneg, + .get_autoneg = hclge_get_autoneg, + .get_pauseparam = hclge_get_pauseparam, + .set_mtu = hclge_set_mtu, + .reset_queue = hclge_reset_tqp, + .get_stats = hclge_get_stats, + .update_stats = hclge_update_stats, + .get_strings = hclge_get_strings, + .get_sset_count = hclge_get_sset_count, + .get_fw_version = hclge_get_fw_version, + .get_mdix_mode = hclge_get_mdix_mode, + .set_vlan_filter = hclge_set_port_vlan_filter, + .set_vf_vlan_filter = hclge_set_vf_vlan_filter, +}; + +static struct hnae3_ae_algo ae_algo = { + .ops = &hclge_ops, + .name = HCLGE_NAME, + .pdev_id_table = ae_algo_pci_tbl, +}; + +static int hclge_init(void) +{ + pr_info("%s is initializing\n", HCLGE_NAME); + + return hnae3_register_ae_algo(&ae_algo); +} + +static void hclge_exit(void) +{ + hnae3_unregister_ae_algo(&ae_algo); +} +module_init(hclge_init); +module_exit(hclge_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_DESCRIPTION("HCLGE Driver"); +MODULE_VERSION(HCLGE_MOD_VERSION); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h new file mode 100644 index 000000000000..edb10ad075eb --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HCLGE_MAIN_H +#define __HCLGE_MAIN_H +#include +#include +#include +#include "hclge_cmd.h" +#include "hnae3.h" + +#define HCLGE_MOD_VERSION "v1.0" +#define HCLGE_DRIVER_NAME "hclge" + +#define HCLGE_INVALID_VPORT 0xffff + +#define HCLGE_ROCE_VECTOR_OFFSET 96 + +#define HCLGE_PF_CFG_BLOCK_SIZE 32 +#define HCLGE_PF_CFG_DESC_NUM \ + (HCLGE_PF_CFG_BLOCK_SIZE / HCLGE_CFG_RD_LEN_BYTES) + +#define HCLGE_VECTOR_REG_BASE 0x20000 + +#define HCLGE_VECTOR_REG_OFFSET 0x4 +#define HCLGE_VECTOR_VF_OFFSET 0x100000 + +#define HCLGE_RSS_IND_TBL_SIZE 512 +#define HCLGE_RSS_SET_BITMAP_MSK 0xffff +#define HCLGE_RSS_KEY_SIZE 40 +#define HCLGE_RSS_HASH_ALGO_TOEPLITZ 0 +#define HCLGE_RSS_HASH_ALGO_SIMPLE 1 +#define HCLGE_RSS_HASH_ALGO_SYMMETRIC 2 +#define HCLGE_RSS_HASH_ALGO_MASK 0xf +#define HCLGE_RSS_CFG_TBL_NUM \ + (HCLGE_RSS_IND_TBL_SIZE / HCLGE_RSS_CFG_TBL_SIZE) + +#define HCLGE_RSS_TC_SIZE_0 1 +#define HCLGE_RSS_TC_SIZE_1 2 +#define HCLGE_RSS_TC_SIZE_2 4 +#define HCLGE_RSS_TC_SIZE_3 8 +#define HCLGE_RSS_TC_SIZE_4 16 +#define HCLGE_RSS_TC_SIZE_5 32 +#define HCLGE_RSS_TC_SIZE_6 64 +#define HCLGE_RSS_TC_SIZE_7 128 + +#define HCLGE_TQP_RESET_TRY_TIMES 10 + +#define HCLGE_PHY_PAGE_MDIX 0 +#define HCLGE_PHY_PAGE_COPPER 0 + +/* Page Selection Reg. */ +#define HCLGE_PHY_PAGE_REG 22 + +/* Copper Specific Control Register */ +#define HCLGE_PHY_CSC_REG 16 + +/* Copper Specific Status Register */ +#define HCLGE_PHY_CSS_REG 17 + +#define HCLGE_PHY_MDIX_CTRL_S (5) +#define HCLGE_PHY_MDIX_CTRL_M (3 << HCLGE_PHY_MDIX_CTRL_S) + +#define HCLGE_PHY_MDIX_STATUS_B (6) +#define HCLGE_PHY_SPEED_DUP_RESOLVE_B (11) + +enum HCLGE_DEV_STATE { + HCLGE_STATE_REINITING, + HCLGE_STATE_DOWN, + HCLGE_STATE_DISABLED, + HCLGE_STATE_REMOVING, + HCLGE_STATE_SERVICE_INITED, + HCLGE_STATE_SERVICE_SCHED, + HCLGE_STATE_MBX_HANDLING, + HCLGE_STATE_MBX_IRQ, + HCLGE_STATE_MAX +}; + +#define HCLGE_MPF_ENBALE 1 +struct hclge_caps { + u16 num_tqp; + u16 num_buffer_cell; + u32 flag; + u16 vmdq; +}; + +enum HCLGE_MAC_SPEED { + HCLGE_MAC_SPEED_10M = 10, /* 10 Mbps */ + HCLGE_MAC_SPEED_100M = 100, /* 100 Mbps */ + HCLGE_MAC_SPEED_1G = 1000, /* 1000 Mbps = 1 Gbps */ + HCLGE_MAC_SPEED_10G = 10000, /* 10000 Mbps = 10 Gbps */ + HCLGE_MAC_SPEED_25G = 25000, /* 25000 Mbps = 25 Gbps */ + HCLGE_MAC_SPEED_40G = 40000, /* 40000 Mbps = 40 Gbps */ + HCLGE_MAC_SPEED_50G = 50000, /* 50000 Mbps = 50 Gbps */ + HCLGE_MAC_SPEED_100G = 100000 /* 100000 Mbps = 100 Gbps */ +}; + +enum HCLGE_MAC_DUPLEX { + HCLGE_MAC_HALF, + HCLGE_MAC_FULL +}; + +enum hclge_mta_dmac_sel_type { + HCLGE_MAC_ADDR_47_36, + HCLGE_MAC_ADDR_46_35, + HCLGE_MAC_ADDR_45_34, + HCLGE_MAC_ADDR_44_33, +}; + +struct hclge_mac { + u8 phy_addr; + u8 flag; + u8 media_type; + u8 mac_addr[ETH_ALEN]; + u8 autoneg; + u8 duplex; + u32 speed; + int link; /* store the link status of mac & phy (if phy exit)*/ + struct phy_device *phydev; + struct mii_bus *mdio_bus; + phy_interface_t phy_if; +}; + +struct hclge_hw { + void __iomem *io_base; + struct hclge_mac mac; + int num_vec; + struct hclge_cmq cmq; + struct hclge_caps caps; + void *back; +}; + +/* TQP stats */ +struct hlcge_tqp_stats { + /* query_tqp_tx_queue_statistics ,opcode id: 0x0B03 */ + u64 rcb_tx_ring_pktnum_rcd; /* 32bit */ + /* query_tqp_rx_queue_statistics ,opcode id: 0x0B13 */ + u64 rcb_rx_ring_pktnum_rcd; /* 32bit */ +}; + +struct hclge_tqp { + struct device *dev; /* Device for DMA mapping */ + struct hnae3_queue q; + struct hlcge_tqp_stats tqp_stats; + u16 index; /* Global index in a NIC controller */ + + bool alloced; +}; + +enum hclge_fc_mode { + HCLGE_FC_NONE, + HCLGE_FC_RX_PAUSE, + HCLGE_FC_TX_PAUSE, + HCLGE_FC_FULL, + HCLGE_FC_PFC, + HCLGE_FC_DEFAULT +}; + +#define HCLGE_PG_NUM 4 +#define HCLGE_SCH_MODE_SP 0 +#define HCLGE_SCH_MODE_DWRR 1 +struct hclge_pg_info { + u8 pg_id; + u8 pg_sch_mode; /* 0: sp; 1: dwrr */ + u8 tc_bit_map; + u32 bw_limit; + u8 tc_dwrr[HNAE3_MAX_TC]; +}; + +struct hclge_tc_info { + u8 tc_id; + u8 tc_sch_mode; /* 0: sp; 1: dwrr */ + u8 up; + u8 pgid; + u32 bw_limit; +}; + +struct hclge_cfg { + u8 vmdq_vport_num; + u8 tc_num; + u16 tqp_desc_num; + u16 rx_buf_len; + u8 phy_addr; + u8 media_type; + u8 mac_addr[ETH_ALEN]; + u8 default_speed; + u32 numa_node_map; +}; + +struct hclge_tm_info { + u8 num_tc; + u8 num_pg; /* It must be 1 if vNET-Base schd */ + u8 pg_dwrr[HCLGE_PG_NUM]; + struct hclge_pg_info pg_info[HCLGE_PG_NUM]; + struct hclge_tc_info tc_info[HNAE3_MAX_TC]; + enum hclge_fc_mode fc_mode; + u8 hw_pfc_map; /* Allow for packet drop or not on this TC */ +}; + +struct hclge_comm_stats_str { + char desc[ETH_GSTRING_LEN]; + unsigned long offset; +}; + +/* all 64bit stats, opcode id: 0x0030 */ +struct hclge_64_bit_stats { + /* query_igu_stat */ + u64 igu_rx_oversize_pkt; + u64 igu_rx_undersize_pkt; + u64 igu_rx_out_all_pkt; + u64 igu_rx_uni_pkt; + u64 igu_rx_multi_pkt; + u64 igu_rx_broad_pkt; + u64 rsv0; + + /* query_egu_stat */ + u64 egu_tx_out_all_pkt; + u64 egu_tx_uni_pkt; + u64 egu_tx_multi_pkt; + u64 egu_tx_broad_pkt; + + /* ssu_ppp packet stats */ + u64 ssu_ppp_mac_key_num; + u64 ssu_ppp_host_key_num; + u64 ppp_ssu_mac_rlt_num; + u64 ppp_ssu_host_rlt_num; + + /* ssu_tx_in_out_dfx_stats */ + u64 ssu_tx_in_num; + u64 ssu_tx_out_num; + /* ssu_rx_in_out_dfx_stats */ + u64 ssu_rx_in_num; + u64 ssu_rx_out_num; +}; + +/* all 32bit stats, opcode id: 0x0031 */ +struct hclge_32_bit_stats { + u64 igu_rx_err_pkt; + u64 igu_rx_no_eof_pkt; + u64 igu_rx_no_sof_pkt; + u64 egu_tx_1588_pkt; + u64 egu_tx_err_pkt; + u64 ssu_full_drop_num; + u64 ssu_part_drop_num; + u64 ppp_key_drop_num; + u64 ppp_rlt_drop_num; + u64 ssu_key_drop_num; + u64 pkt_curr_buf_cnt; + u64 qcn_fb_rcv_cnt; + u64 qcn_fb_drop_cnt; + u64 qcn_fb_invaild_cnt; + u64 rsv0; + u64 rx_packet_tc0_in_cnt; + u64 rx_packet_tc1_in_cnt; + u64 rx_packet_tc2_in_cnt; + u64 rx_packet_tc3_in_cnt; + u64 rx_packet_tc4_in_cnt; + u64 rx_packet_tc5_in_cnt; + u64 rx_packet_tc6_in_cnt; + u64 rx_packet_tc7_in_cnt; + u64 rx_packet_tc0_out_cnt; + u64 rx_packet_tc1_out_cnt; + u64 rx_packet_tc2_out_cnt; + u64 rx_packet_tc3_out_cnt; + u64 rx_packet_tc4_out_cnt; + u64 rx_packet_tc5_out_cnt; + u64 rx_packet_tc6_out_cnt; + u64 rx_packet_tc7_out_cnt; + + /* Tx packet level statistics */ + u64 tx_packet_tc0_in_cnt; + u64 tx_packet_tc1_in_cnt; + u64 tx_packet_tc2_in_cnt; + u64 tx_packet_tc3_in_cnt; + u64 tx_packet_tc4_in_cnt; + u64 tx_packet_tc5_in_cnt; + u64 tx_packet_tc6_in_cnt; + u64 tx_packet_tc7_in_cnt; + u64 tx_packet_tc0_out_cnt; + u64 tx_packet_tc1_out_cnt; + u64 tx_packet_tc2_out_cnt; + u64 tx_packet_tc3_out_cnt; + u64 tx_packet_tc4_out_cnt; + u64 tx_packet_tc5_out_cnt; + u64 tx_packet_tc6_out_cnt; + u64 tx_packet_tc7_out_cnt; + + /* packet buffer statistics */ + u64 pkt_curr_buf_tc0_cnt; + u64 pkt_curr_buf_tc1_cnt; + u64 pkt_curr_buf_tc2_cnt; + u64 pkt_curr_buf_tc3_cnt; + u64 pkt_curr_buf_tc4_cnt; + u64 pkt_curr_buf_tc5_cnt; + u64 pkt_curr_buf_tc6_cnt; + u64 pkt_curr_buf_tc7_cnt; + + u64 mb_uncopy_num; + u64 lo_pri_unicast_rlt_drop_num; + u64 hi_pri_multicast_rlt_drop_num; + u64 lo_pri_multicast_rlt_drop_num; + u64 rx_oq_drop_pkt_cnt; + u64 tx_oq_drop_pkt_cnt; + u64 nic_l2_err_drop_pkt_cnt; + u64 roc_l2_err_drop_pkt_cnt; +}; + +/* mac stats ,opcode id: 0x0032 */ +struct hclge_mac_stats { + u64 mac_tx_mac_pause_num; + u64 mac_rx_mac_pause_num; + u64 mac_tx_pfc_pri0_pkt_num; + u64 mac_tx_pfc_pri1_pkt_num; + u64 mac_tx_pfc_pri2_pkt_num; + u64 mac_tx_pfc_pri3_pkt_num; + u64 mac_tx_pfc_pri4_pkt_num; + u64 mac_tx_pfc_pri5_pkt_num; + u64 mac_tx_pfc_pri6_pkt_num; + u64 mac_tx_pfc_pri7_pkt_num; + u64 mac_rx_pfc_pri0_pkt_num; + u64 mac_rx_pfc_pri1_pkt_num; + u64 mac_rx_pfc_pri2_pkt_num; + u64 mac_rx_pfc_pri3_pkt_num; + u64 mac_rx_pfc_pri4_pkt_num; + u64 mac_rx_pfc_pri5_pkt_num; + u64 mac_rx_pfc_pri6_pkt_num; + u64 mac_rx_pfc_pri7_pkt_num; + u64 mac_tx_total_pkt_num; + u64 mac_tx_total_oct_num; + u64 mac_tx_good_pkt_num; + u64 mac_tx_bad_pkt_num; + u64 mac_tx_good_oct_num; + u64 mac_tx_bad_oct_num; + u64 mac_tx_uni_pkt_num; + u64 mac_tx_multi_pkt_num; + u64 mac_tx_broad_pkt_num; + u64 mac_tx_undersize_pkt_num; + u64 mac_tx_overrsize_pkt_num; + u64 mac_tx_64_oct_pkt_num; + u64 mac_tx_65_127_oct_pkt_num; + u64 mac_tx_128_255_oct_pkt_num; + u64 mac_tx_256_511_oct_pkt_num; + u64 mac_tx_512_1023_oct_pkt_num; + u64 mac_tx_1024_1518_oct_pkt_num; + u64 mac_tx_1519_max_oct_pkt_num; + u64 mac_rx_total_pkt_num; + u64 mac_rx_total_oct_num; + u64 mac_rx_good_pkt_num; + u64 mac_rx_bad_pkt_num; + u64 mac_rx_good_oct_num; + u64 mac_rx_bad_oct_num; + u64 mac_rx_uni_pkt_num; + u64 mac_rx_multi_pkt_num; + u64 mac_rx_broad_pkt_num; + u64 mac_rx_undersize_pkt_num; + u64 mac_rx_overrsize_pkt_num; + u64 mac_rx_64_oct_pkt_num; + u64 mac_rx_65_127_oct_pkt_num; + u64 mac_rx_128_255_oct_pkt_num; + u64 mac_rx_256_511_oct_pkt_num; + u64 mac_rx_512_1023_oct_pkt_num; + u64 mac_rx_1024_1518_oct_pkt_num; + u64 mac_rx_1519_max_oct_pkt_num; + + u64 mac_trans_fragment_pkt_num; + u64 mac_trans_undermin_pkt_num; + u64 mac_trans_jabber_pkt_num; + u64 mac_trans_err_all_pkt_num; + u64 mac_trans_from_app_good_pkt_num; + u64 mac_trans_from_app_bad_pkt_num; + u64 mac_rcv_fragment_pkt_num; + u64 mac_rcv_undermin_pkt_num; + u64 mac_rcv_jabber_pkt_num; + u64 mac_rcv_fcs_err_pkt_num; + u64 mac_rcv_send_app_good_pkt_num; + u64 mac_rcv_send_app_bad_pkt_num; +}; + +struct hclge_hw_stats { + struct hclge_mac_stats mac_stats; + struct hclge_64_bit_stats all_64_bit_stats; + struct hclge_32_bit_stats all_32_bit_stats; +}; + +struct hclge_dev { + struct pci_dev *pdev; + struct hnae3_ae_dev *ae_dev; + struct hclge_hw hw; + struct hclge_hw_stats hw_stats; + unsigned long state; + + u32 fw_version; + u16 num_vmdq_vport; /* Num vmdq vport this PF has set up */ + u16 num_tqps; /* Num task queue pairs of this PF */ + u16 num_req_vfs; /* Num VFs requested for this PF */ + + u16 num_roce_msix; /* Num of roce vectors for this PF */ + int roce_base_vector; + + /* Base task tqp physical id of this PF */ + u16 base_tqp_pid; + u16 alloc_rss_size; /* Allocated RSS task queue */ + u16 rss_size_max; /* HW defined max RSS task queue */ + + /* Num of guaranteed filters for this PF */ + u16 fdir_pf_filter_count; + u16 num_alloc_vport; /* Num vports this driver supports */ + u32 numa_node_mask; + u16 rx_buf_len; + u16 num_desc; + u8 hw_tc_map; + u8 tc_num_last_time; + enum hclge_fc_mode fc_mode_last_time; + +#define HCLGE_FLAG_TC_BASE_SCH_MODE 1 +#define HCLGE_FLAG_VNET_BASE_SCH_MODE 2 + u8 tx_sch_mode; + + u8 default_up; + struct hclge_tm_info tm_info; + + u16 num_msi; + u16 num_msi_left; + u16 num_msi_used; + u32 base_msi_vector; + struct msix_entry *msix_entries; + u16 *vector_status; + + u16 pending_udp_bitmap; + + u16 rx_itr_default; + u16 tx_itr_default; + + u16 adminq_work_limit; /* Num of admin receive queue desc to process */ + unsigned long service_timer_period; + unsigned long service_timer_previous; + struct timer_list service_timer; + struct work_struct service_task; + + bool cur_promisc; + int num_alloc_vfs; /* Actual number of VFs allocated */ + + struct hclge_tqp *htqp; + struct hclge_vport *vport; + + struct dentry *hclge_dbgfs; + + struct hnae3_client *nic_client; + struct hnae3_client *roce_client; + +#define HCLGE_FLAG_USE_MSI 0x00000001 +#define HCLGE_FLAG_USE_MSIX 0x00000002 +#define HCLGE_FLAG_MAIN 0x00000004 +#define HCLGE_FLAG_DCB_CAPABLE 0x00000008 +#define HCLGE_FLAG_DCB_ENABLE 0x00000010 + u32 flag; + + u32 pkt_buf_size; /* Total pf buf size for tx/rx */ + u32 mps; /* Max packet size */ + struct hclge_priv_buf *priv_buf; + struct hclge_shared_buf s_buf; + + enum hclge_mta_dmac_sel_type mta_mac_sel_type; + bool enable_mta; /* Mutilcast filter enable */ + bool accept_mta_mc; /* Whether accept mta filter multicast */ +}; + +struct hclge_vport { + u16 alloc_tqps; /* Allocated Tx/Rx queues */ + + u8 rss_hash_key[HCLGE_RSS_KEY_SIZE]; /* User configured hash keys */ + /* User configured lookup table entries */ + u8 rss_indirection_tbl[HCLGE_RSS_IND_TBL_SIZE]; + + u16 qs_offset; + u16 bw_limit; /* VSI BW Limit (0 = disabled) */ + u8 dwrr; + + int vport_id; + struct hclge_dev *back; /* Back reference to associated dev */ + struct hnae3_handle nic; + struct hnae3_handle roce; +}; + +void hclge_promisc_param_init(struct hclge_promisc_param *param, bool en_uc, + bool en_mc, bool en_bc, int vport_id); + +int hclge_add_uc_addr_common(struct hclge_vport *vport, + const unsigned char *addr); +int hclge_rm_uc_addr_common(struct hclge_vport *vport, + const unsigned char *addr); +int hclge_add_mc_addr_common(struct hclge_vport *vport, + const unsigned char *addr); +int hclge_rm_mc_addr_common(struct hclge_vport *vport, + const unsigned char *addr); + +int hclge_cfg_func_mta_filter(struct hclge_dev *hdev, + u8 func_id, + bool enable); +struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle); +int hclge_map_vport_ring_to_vector(struct hclge_vport *vport, int vector, + struct hnae3_ring_chain_node *ring_chain); +static inline int hclge_get_queue_id(struct hnae3_queue *queue) +{ + struct hclge_tqp *tqp = container_of(queue, struct hclge_tqp, q); + + return tqp->index; +} + +int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex); +int hclge_set_vf_vlan_common(struct hclge_dev *vport, int vfid, + bool is_kill, u16 vlan, u8 qos, __be16 proto); +#endif -- cgit v1.2.3 From 848440544b41fbe21f36072ee7dc7c3c59ce62e2 Mon Sep 17 00:00:00 2001 From: Salil Date: Wed, 2 Aug 2017 16:59:49 +0100 Subject: net: hns3: Add support of TX Scheduler & Shaper to HNS3 driver THis patch adds the support of the Scheduling and Shaping functionalities during the transmit leg. This also adds the support of Pause at MAC level. (Pause at per-priority level shall be added later along with the DCB feature). Hardware as such consists of two types of cofiguration of 6 level schedulers. Algorithms varies according to the level and type of scheduler being used. Current patch is used to initialize the mapping, algorithms(like SP, DWRR etc) and shaper(CIR, PIR etc) being used. Signed-off-by: Daode Huang Signed-off-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: Yisen Zhuang Signed-off-by: Wei Hu (Xavier) Signed-off-by: David S. Miller --- .../net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c | 1015 ++++++++++++++++++++ .../net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h | 106 ++ 2 files changed, 1121 insertions(+) create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c new file mode 100644 index 000000000000..1c577d268f00 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -0,0 +1,1015 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +#include "hclge_cmd.h" +#include "hclge_main.h" +#include "hclge_tm.h" + +enum hclge_shaper_level { + HCLGE_SHAPER_LVL_PRI = 0, + HCLGE_SHAPER_LVL_PG = 1, + HCLGE_SHAPER_LVL_PORT = 2, + HCLGE_SHAPER_LVL_QSET = 3, + HCLGE_SHAPER_LVL_CNT = 4, + HCLGE_SHAPER_LVL_VF = 0, + HCLGE_SHAPER_LVL_PF = 1, +}; + +#define HCLGE_SHAPER_BS_U_DEF 1 +#define HCLGE_SHAPER_BS_S_DEF 4 + +#define HCLGE_ETHER_MAX_RATE 100000 + +/* hclge_shaper_para_calc: calculate ir parameter for the shaper + * @ir: Rate to be config, its unit is Mbps + * @shaper_level: the shaper level. eg: port, pg, priority, queueset + * @ir_b: IR_B parameter of IR shaper + * @ir_u: IR_U parameter of IR shaper + * @ir_s: IR_S parameter of IR shaper + * + * the formula: + * + * IR_b * (2 ^ IR_u) * 8 + * IR(Mbps) = ------------------------- * CLOCK(1000Mbps) + * Tick * (2 ^ IR_s) + * + * @return: 0: calculate sucessful, negative: fail + */ +static int hclge_shaper_para_calc(u32 ir, u8 shaper_level, + u8 *ir_b, u8 *ir_u, u8 *ir_s) +{ + const u16 tick_array[HCLGE_SHAPER_LVL_CNT] = { + 6 * 256, /* Prioriy level */ + 6 * 32, /* Prioriy group level */ + 6 * 8, /* Port level */ + 6 * 256 /* Qset level */ + }; + u8 ir_u_calc = 0, ir_s_calc = 0; + u32 ir_calc; + u32 tick; + + /* Calc tick */ + if (shaper_level >= HCLGE_SHAPER_LVL_CNT) + return -EINVAL; + + tick = tick_array[shaper_level]; + + /** + * Calc the speed if ir_b = 126, ir_u = 0 and ir_s = 0 + * the formula is changed to: + * 126 * 1 * 8 + * ir_calc = ---------------- * 1000 + * tick * 1 + */ + ir_calc = (1008000 + (tick >> 1) - 1) / tick; + + if (ir_calc == ir) { + *ir_b = 126; + *ir_u = 0; + *ir_s = 0; + + return 0; + } else if (ir_calc > ir) { + /* Increasing the denominator to select ir_s value */ + while (ir_calc > ir) { + ir_s_calc++; + ir_calc = 1008000 / (tick * (1 << ir_s_calc)); + } + + if (ir_calc == ir) + *ir_b = 126; + else + *ir_b = (ir * tick * (1 << ir_s_calc) + 4000) / 8000; + } else { + /* Increasing the numerator to select ir_u value */ + u32 numerator; + + while (ir_calc < ir) { + ir_u_calc++; + numerator = 1008000 * (1 << ir_u_calc); + ir_calc = (numerator + (tick >> 1)) / tick; + } + + if (ir_calc == ir) { + *ir_b = 126; + } else { + u32 denominator = (8000 * (1 << --ir_u_calc)); + *ir_b = (ir * tick + (denominator >> 1)) / denominator; + } + } + + *ir_u = ir_u_calc; + *ir_s = ir_s_calc; + + return 0; +} + +static int hclge_mac_pause_en_cfg(struct hclge_dev *hdev, bool tx, bool rx) +{ + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_MAC_PAUSE_EN, false); + + desc.data[0] = cpu_to_le32((tx ? HCLGE_TX_MAC_PAUSE_EN_MSK : 0) | + (rx ? HCLGE_RX_MAC_PAUSE_EN_MSK : 0)); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_fill_pri_array(struct hclge_dev *hdev, u8 *pri, u8 pri_id) +{ + u8 tc; + + for (tc = 0; tc < hdev->tm_info.num_tc; tc++) + if (hdev->tm_info.tc_info[tc].up == pri_id) + break; + + if (tc >= hdev->tm_info.num_tc) + return -EINVAL; + + /** + * the register for priority has four bytes, the first bytes includes + * priority0 and priority1, the higher 4bit stands for priority1 + * while the lower 4bit stands for priority0, as below: + * first byte: | pri_1 | pri_0 | + * second byte: | pri_3 | pri_2 | + * third byte: | pri_5 | pri_4 | + * fourth byte: | pri_7 | pri_6 | + */ + pri[pri_id >> 1] |= tc << ((pri_id & 1) * 4); + + return 0; +} + +static int hclge_up_to_tc_map(struct hclge_dev *hdev) +{ + struct hclge_desc desc; + u8 *pri = (u8 *)desc.data; + u8 pri_id; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PRI_TO_TC_MAPPING, false); + + for (pri_id = 0; pri_id < hdev->tm_info.num_tc; pri_id++) { + ret = hclge_fill_pri_array(hdev, pri, pri_id); + if (ret) + return ret; + } + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pg_to_pri_map_cfg(struct hclge_dev *hdev, + u8 pg_id, u8 pri_bit_map) +{ + struct hclge_pg_to_pri_link_cmd *map; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PG_TO_PRI_LINK, false); + + map = (struct hclge_pg_to_pri_link_cmd *)desc.data; + + map->pg_id = pg_id; + map->pri_bit_map = pri_bit_map; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_qs_to_pri_map_cfg(struct hclge_dev *hdev, + u16 qs_id, u8 pri) +{ + struct hclge_qs_to_pri_link_cmd *map; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_QS_TO_PRI_LINK, false); + + map = (struct hclge_qs_to_pri_link_cmd *)desc.data; + + map->qs_id = cpu_to_le16(qs_id); + map->priority = pri; + map->link_vld = HCLGE_TM_QS_PRI_LINK_VLD_MSK; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_q_to_qs_map_cfg(struct hclge_dev *hdev, + u8 q_id, u16 qs_id) +{ + struct hclge_nq_to_qs_link_cmd *map; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_NQ_TO_QS_LINK, false); + + map = (struct hclge_nq_to_qs_link_cmd *)desc.data; + + map->nq_id = cpu_to_le16(q_id); + map->qset_id = cpu_to_le16(qs_id | HCLGE_TM_Q_QS_LINK_VLD_MSK); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pg_weight_cfg(struct hclge_dev *hdev, u8 pg_id, + u8 dwrr) +{ + struct hclge_pg_weight_cmd *weight; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PG_WEIGHT, false); + + weight = (struct hclge_pg_weight_cmd *)desc.data; + + weight->pg_id = pg_id; + weight->dwrr = dwrr; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pri_weight_cfg(struct hclge_dev *hdev, u8 pri_id, + u8 dwrr) +{ + struct hclge_priority_weight_cmd *weight; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PRI_WEIGHT, false); + + weight = (struct hclge_priority_weight_cmd *)desc.data; + + weight->pri_id = pri_id; + weight->dwrr = dwrr; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_qs_weight_cfg(struct hclge_dev *hdev, u16 qs_id, + u8 dwrr) +{ + struct hclge_qs_weight_cmd *weight; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_QS_WEIGHT, false); + + weight = (struct hclge_qs_weight_cmd *)desc.data; + + weight->qs_id = cpu_to_le16(qs_id); + weight->dwrr = dwrr; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pg_shapping_cfg(struct hclge_dev *hdev, + enum hclge_shap_bucket bucket, u8 pg_id, + u8 ir_b, u8 ir_u, u8 ir_s, u8 bs_b, u8 bs_s) +{ + struct hclge_pg_shapping_cmd *shap_cfg_cmd; + enum hclge_opcode_type opcode; + struct hclge_desc desc; + + opcode = bucket ? HCLGE_OPC_TM_PG_P_SHAPPING : + HCLGE_OPC_TM_PG_C_SHAPPING; + hclge_cmd_setup_basic_desc(&desc, opcode, false); + + shap_cfg_cmd = (struct hclge_pg_shapping_cmd *)desc.data; + + shap_cfg_cmd->pg_id = pg_id; + + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, IR_B, ir_b); + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, IR_U, ir_u); + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, IR_S, ir_s); + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, BS_B, bs_b); + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, BS_S, bs_s); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pri_shapping_cfg(struct hclge_dev *hdev, + enum hclge_shap_bucket bucket, u8 pri_id, + u8 ir_b, u8 ir_u, u8 ir_s, + u8 bs_b, u8 bs_s) +{ + struct hclge_pri_shapping_cmd *shap_cfg_cmd; + enum hclge_opcode_type opcode; + struct hclge_desc desc; + + opcode = bucket ? HCLGE_OPC_TM_PRI_P_SHAPPING : + HCLGE_OPC_TM_PRI_C_SHAPPING; + + hclge_cmd_setup_basic_desc(&desc, opcode, false); + + shap_cfg_cmd = (struct hclge_pri_shapping_cmd *)desc.data; + + shap_cfg_cmd->pri_id = pri_id; + + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, IR_B, ir_b); + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, IR_U, ir_u); + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, IR_S, ir_s); + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, BS_B, bs_b); + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, BS_S, bs_s); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pg_schd_mode_cfg(struct hclge_dev *hdev, u8 pg_id) +{ + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PG_SCH_MODE_CFG, false); + + if (hdev->tm_info.pg_info[pg_id].pg_sch_mode == HCLGE_SCH_MODE_DWRR) + desc.data[1] = cpu_to_le32(HCLGE_TM_TX_SCHD_DWRR_MSK); + else + desc.data[1] = 0; + + desc.data[0] = cpu_to_le32(pg_id); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pri_schd_mode_cfg(struct hclge_dev *hdev, u8 pri_id) +{ + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PRI_SCH_MODE_CFG, false); + + if (hdev->tm_info.tc_info[pri_id].tc_sch_mode == HCLGE_SCH_MODE_DWRR) + desc.data[1] = cpu_to_le32(HCLGE_TM_TX_SCHD_DWRR_MSK); + else + desc.data[1] = 0; + + desc.data[0] = cpu_to_le32(pri_id); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_qs_schd_mode_cfg(struct hclge_dev *hdev, u16 qs_id) +{ + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_QS_SCH_MODE_CFG, false); + + if (hdev->tm_info.tc_info[qs_id].tc_sch_mode == HCLGE_SCH_MODE_DWRR) + desc.data[1] = cpu_to_le32(HCLGE_TM_TX_SCHD_DWRR_MSK); + else + desc.data[1] = 0; + + desc.data[0] = cpu_to_le32(qs_id); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_qs_bp_cfg(struct hclge_dev *hdev, u8 tc) +{ + struct hclge_bp_to_qs_map_cmd *bp_to_qs_map_cmd; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_BP_TO_QSET_MAPPING, + false); + + bp_to_qs_map_cmd = (struct hclge_bp_to_qs_map_cmd *)desc.data; + + bp_to_qs_map_cmd->tc_id = tc; + + /* Qset and tc is one by one mapping */ + bp_to_qs_map_cmd->qs_bit_map = cpu_to_le32(1 << tc); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static void hclge_tm_vport_tc_info_update(struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hclge_dev *hdev = vport->back; + u8 i; + + kinfo = &vport->nic.kinfo; + vport->bw_limit = hdev->tm_info.pg_info[0].bw_limit; + kinfo->num_tc = + min_t(u16, kinfo->num_tqps, hdev->tm_info.num_tc); + kinfo->rss_size + = min_t(u16, hdev->rss_size_max, + kinfo->num_tqps / kinfo->num_tc); + vport->qs_offset = hdev->tm_info.num_tc * vport->vport_id; + vport->dwrr = 100; /* 100 percent as init */ + + for (i = 0; i < kinfo->num_tc; i++) { + if (hdev->hw_tc_map & BIT(i)) { + kinfo->tc_info[i].enable = true; + kinfo->tc_info[i].tqp_offset = i * kinfo->rss_size; + kinfo->tc_info[i].tqp_count = kinfo->rss_size; + kinfo->tc_info[i].tc = i; + kinfo->tc_info[i].up = hdev->tm_info.tc_info[i].up; + } else { + /* Set to default queue if TC is disable */ + kinfo->tc_info[i].enable = false; + kinfo->tc_info[i].tqp_offset = 0; + kinfo->tc_info[i].tqp_count = 1; + kinfo->tc_info[i].tc = 0; + kinfo->tc_info[i].up = 0; + } + } +} + +static void hclge_tm_vport_info_update(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + u32 i; + + for (i = 0; i < hdev->num_alloc_vport; i++) { + hclge_tm_vport_tc_info_update(vport); + + vport++; + } +} + +static void hclge_tm_tc_info_init(struct hclge_dev *hdev) +{ + u8 i; + + for (i = 0; i < hdev->tm_info.num_tc; i++) { + hdev->tm_info.tc_info[i].tc_id = i; + hdev->tm_info.tc_info[i].tc_sch_mode = HCLGE_SCH_MODE_DWRR; + hdev->tm_info.tc_info[i].up = i; + hdev->tm_info.tc_info[i].pgid = 0; + hdev->tm_info.tc_info[i].bw_limit = + hdev->tm_info.pg_info[0].bw_limit; + } + + hdev->flag &= ~HCLGE_FLAG_DCB_ENABLE; +} + +static void hclge_tm_pg_info_init(struct hclge_dev *hdev) +{ + u8 i; + + for (i = 0; i < hdev->tm_info.num_pg; i++) { + int k; + + hdev->tm_info.pg_dwrr[i] = i ? 0 : 100; + + hdev->tm_info.pg_info[i].pg_id = i; + hdev->tm_info.pg_info[i].pg_sch_mode = HCLGE_SCH_MODE_DWRR; + + hdev->tm_info.pg_info[i].bw_limit = HCLGE_ETHER_MAX_RATE; + + if (i != 0) + continue; + + hdev->tm_info.pg_info[i].tc_bit_map = hdev->hw_tc_map; + for (k = 0; k < hdev->tm_info.num_tc; k++) + hdev->tm_info.pg_info[i].tc_dwrr[k] = 100; + } +} + +static int hclge_tm_schd_info_init(struct hclge_dev *hdev) +{ + if ((hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) && + (hdev->tm_info.num_pg != 1)) + return -EINVAL; + + hclge_tm_pg_info_init(hdev); + + hclge_tm_tc_info_init(hdev); + + hclge_tm_vport_info_update(hdev); + + hdev->tm_info.fc_mode = HCLGE_FC_NONE; + hdev->fc_mode_last_time = hdev->tm_info.fc_mode; + + return 0; +} + +static int hclge_tm_pg_to_pri_map(struct hclge_dev *hdev) +{ + int ret; + u32 i; + + if (hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) + return 0; + + for (i = 0; i < hdev->tm_info.num_pg; i++) { + /* Cfg mapping */ + ret = hclge_tm_pg_to_pri_map_cfg( + hdev, i, hdev->tm_info.pg_info[i].tc_bit_map); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pg_shaper_cfg(struct hclge_dev *hdev) +{ + u8 ir_u, ir_b, ir_s; + int ret; + u32 i; + + /* Cfg pg schd */ + if (hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) + return 0; + + /* Pg to pri */ + for (i = 0; i < hdev->tm_info.num_pg; i++) { + /* Calc shaper para */ + ret = hclge_shaper_para_calc( + hdev->tm_info.pg_info[i].bw_limit, + HCLGE_SHAPER_LVL_PG, + &ir_b, &ir_u, &ir_s); + if (ret) + return ret; + + ret = hclge_tm_pg_shapping_cfg(hdev, + HCLGE_TM_SHAP_C_BUCKET, i, + 0, 0, 0, HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + + ret = hclge_tm_pg_shapping_cfg(hdev, + HCLGE_TM_SHAP_P_BUCKET, i, + ir_b, ir_u, ir_s, + HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pg_dwrr_cfg(struct hclge_dev *hdev) +{ + int ret; + u32 i; + + /* cfg pg schd */ + if (hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) + return 0; + + /* pg to prio */ + for (i = 0; i < hdev->tm_info.num_pg; i++) { + /* Cfg dwrr */ + ret = hclge_tm_pg_weight_cfg(hdev, i, + hdev->tm_info.pg_dwrr[i]); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_vport_q_to_qs_map(struct hclge_dev *hdev, + struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hnae3_queue **tqp = kinfo->tqp; + struct hnae3_tc_info *v_tc_info; + u32 i, j; + int ret; + + for (i = 0; i < kinfo->num_tc; i++) { + v_tc_info = &kinfo->tc_info[i]; + for (j = 0; j < v_tc_info->tqp_count; j++) { + struct hnae3_queue *q = tqp[v_tc_info->tqp_offset + j]; + + ret = hclge_tm_q_to_qs_map_cfg(hdev, + hclge_get_queue_id(q), + vport->qs_offset + i); + if (ret) + return ret; + } + } + + return 0; +} + +static int hclge_tm_pri_q_qs_cfg(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + int ret; + u32 i; + + if (hdev->tx_sch_mode == HCLGE_FLAG_TC_BASE_SCH_MODE) { + /* Cfg qs -> pri mapping, one by one mapping */ + for (i = 0; i < hdev->tm_info.num_tc; i++) { + ret = hclge_tm_qs_to_pri_map_cfg(hdev, i, i); + if (ret) + return ret; + } + } else if (hdev->tx_sch_mode == HCLGE_FLAG_VNET_BASE_SCH_MODE) { + int k; + /* Cfg qs -> pri mapping, qs = tc, pri = vf, 8 qs -> 1 pri */ + for (k = 0; k < hdev->num_alloc_vport; k++) + for (i = 0; i < HNAE3_MAX_TC; i++) { + ret = hclge_tm_qs_to_pri_map_cfg( + hdev, vport[k].qs_offset + i, k); + if (ret) + return ret; + } + } else { + return -EINVAL; + } + + /* Cfg q -> qs mapping */ + for (i = 0; i < hdev->num_alloc_vport; i++) { + ret = hclge_vport_q_to_qs_map(hdev, vport); + if (ret) + return ret; + + vport++; + } + + return 0; +} + +static int hclge_tm_pri_tc_base_shaper_cfg(struct hclge_dev *hdev) +{ + u8 ir_u, ir_b, ir_s; + int ret; + u32 i; + + for (i = 0; i < hdev->tm_info.num_tc; i++) { + ret = hclge_shaper_para_calc( + hdev->tm_info.tc_info[i].bw_limit, + HCLGE_SHAPER_LVL_PRI, + &ir_b, &ir_u, &ir_s); + if (ret) + return ret; + + ret = hclge_tm_pri_shapping_cfg( + hdev, HCLGE_TM_SHAP_C_BUCKET, i, + 0, 0, 0, HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + + ret = hclge_tm_pri_shapping_cfg( + hdev, HCLGE_TM_SHAP_P_BUCKET, i, + ir_b, ir_u, ir_s, HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_vnet_base_shaper_pri_cfg(struct hclge_vport *vport) +{ + struct hclge_dev *hdev = vport->back; + u8 ir_u, ir_b, ir_s; + int ret; + + ret = hclge_shaper_para_calc(vport->bw_limit, HCLGE_SHAPER_LVL_VF, + &ir_b, &ir_u, &ir_s); + if (ret) + return ret; + + ret = hclge_tm_pri_shapping_cfg(hdev, HCLGE_TM_SHAP_C_BUCKET, + vport->vport_id, + 0, 0, 0, HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + + ret = hclge_tm_pri_shapping_cfg(hdev, HCLGE_TM_SHAP_P_BUCKET, + vport->vport_id, + ir_b, ir_u, ir_s, + HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + + return 0; +} + +static int hclge_tm_pri_vnet_base_shaper_qs_cfg(struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hclge_dev *hdev = vport->back; + struct hnae3_tc_info *v_tc_info; + u8 ir_u, ir_b, ir_s; + u32 i; + int ret; + + for (i = 0; i < kinfo->num_tc; i++) { + v_tc_info = &kinfo->tc_info[i]; + ret = hclge_shaper_para_calc( + hdev->tm_info.tc_info[i].bw_limit, + HCLGE_SHAPER_LVL_QSET, + &ir_b, &ir_u, &ir_s); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_vnet_base_shaper_cfg(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + int ret; + u32 i; + + /* Need config vport shaper */ + for (i = 0; i < hdev->num_alloc_vport; i++) { + ret = hclge_tm_pri_vnet_base_shaper_pri_cfg(vport); + if (ret) + return ret; + + ret = hclge_tm_pri_vnet_base_shaper_qs_cfg(vport); + if (ret) + return ret; + + vport++; + } + + return 0; +} + +static int hclge_tm_pri_shaper_cfg(struct hclge_dev *hdev) +{ + int ret; + + if (hdev->tx_sch_mode == HCLGE_FLAG_TC_BASE_SCH_MODE) { + ret = hclge_tm_pri_tc_base_shaper_cfg(hdev); + if (ret) + return ret; + } else { + ret = hclge_tm_pri_vnet_base_shaper_cfg(hdev); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_tc_base_dwrr_cfg(struct hclge_dev *hdev) +{ + struct hclge_pg_info *pg_info; + u8 dwrr; + int ret; + u32 i; + + for (i = 0; i < hdev->tm_info.num_tc; i++) { + pg_info = + &hdev->tm_info.pg_info[hdev->tm_info.tc_info[i].pgid]; + dwrr = pg_info->tc_dwrr[i]; + + ret = hclge_tm_pri_weight_cfg(hdev, i, dwrr); + if (ret) + return ret; + + ret = hclge_tm_qs_weight_cfg(hdev, i, dwrr); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_vnet_base_dwrr_pri_cfg(struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hclge_dev *hdev = vport->back; + int ret; + u8 i; + + /* Vf dwrr */ + ret = hclge_tm_pri_weight_cfg(hdev, vport->vport_id, vport->dwrr); + if (ret) + return ret; + + /* Qset dwrr */ + for (i = 0; i < kinfo->num_tc; i++) { + ret = hclge_tm_qs_weight_cfg( + hdev, vport->qs_offset + i, + hdev->tm_info.pg_info[0].tc_dwrr[i]); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_vnet_base_dwrr_cfg(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + int ret; + u32 i; + + for (i = 0; i < hdev->num_alloc_vport; i++) { + ret = hclge_tm_pri_vnet_base_dwrr_pri_cfg(vport); + if (ret) + return ret; + + vport++; + } + + return 0; +} + +static int hclge_tm_pri_dwrr_cfg(struct hclge_dev *hdev) +{ + int ret; + + if (hdev->tx_sch_mode == HCLGE_FLAG_TC_BASE_SCH_MODE) { + ret = hclge_tm_pri_tc_base_dwrr_cfg(hdev); + if (ret) + return ret; + } else { + ret = hclge_tm_pri_vnet_base_dwrr_cfg(hdev); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_map_cfg(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_tm_pg_to_pri_map(hdev); + if (ret) + return ret; + + return hclge_tm_pri_q_qs_cfg(hdev); +} + +static int hclge_tm_shaper_cfg(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_tm_pg_shaper_cfg(hdev); + if (ret) + return ret; + + return hclge_tm_pri_shaper_cfg(hdev); +} + +int hclge_tm_dwrr_cfg(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_tm_pg_dwrr_cfg(hdev); + if (ret) + return ret; + + return hclge_tm_pri_dwrr_cfg(hdev); +} + +static int hclge_tm_lvl2_schd_mode_cfg(struct hclge_dev *hdev) +{ + int ret; + u8 i; + + /* Only being config on TC-Based scheduler mode */ + if (hdev->tx_sch_mode == HCLGE_FLAG_VNET_BASE_SCH_MODE) + return 0; + + for (i = 0; i < hdev->tm_info.num_pg; i++) { + ret = hclge_tm_pg_schd_mode_cfg(hdev, i); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_schd_mode_vnet_base_cfg(struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hclge_dev *hdev = vport->back; + int ret; + u8 i; + + ret = hclge_tm_pri_schd_mode_cfg(hdev, vport->vport_id); + if (ret) + return ret; + + for (i = 0; i < kinfo->num_tc; i++) { + ret = hclge_tm_qs_schd_mode_cfg(hdev, vport->qs_offset + i); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_lvl34_schd_mode_cfg(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + int ret; + u8 i; + + if (hdev->tx_sch_mode == HCLGE_FLAG_TC_BASE_SCH_MODE) { + for (i = 0; i < hdev->tm_info.num_tc; i++) { + ret = hclge_tm_pri_schd_mode_cfg(hdev, i); + if (ret) + return ret; + + ret = hclge_tm_qs_schd_mode_cfg(hdev, i); + if (ret) + return ret; + } + } else { + for (i = 0; i < hdev->num_alloc_vport; i++) { + ret = hclge_tm_schd_mode_vnet_base_cfg(vport); + if (ret) + return ret; + + vport++; + } + } + + return 0; +} + +static int hclge_tm_schd_mode_hw(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_tm_lvl2_schd_mode_cfg(hdev); + if (ret) + return ret; + + return hclge_tm_lvl34_schd_mode_cfg(hdev); +} + +static int hclge_tm_schd_setup_hw(struct hclge_dev *hdev) +{ + int ret; + + /* Cfg tm mapping */ + ret = hclge_tm_map_cfg(hdev); + if (ret) + return ret; + + /* Cfg tm shaper */ + ret = hclge_tm_shaper_cfg(hdev); + if (ret) + return ret; + + /* Cfg dwrr */ + ret = hclge_tm_dwrr_cfg(hdev); + if (ret) + return ret; + + /* Cfg schd mode for each level schd */ + return hclge_tm_schd_mode_hw(hdev); +} + +int hclge_pause_setup_hw(struct hclge_dev *hdev) +{ + bool en = hdev->tm_info.fc_mode != HCLGE_FC_PFC; + int ret; + u8 i; + + ret = hclge_mac_pause_en_cfg(hdev, en, en); + if (ret) + return ret; + + for (i = 0; i < hdev->tm_info.num_tc; i++) { + ret = hclge_tm_qs_bp_cfg(hdev, i); + if (ret) + return ret; + } + + return hclge_up_to_tc_map(hdev); +} + +int hclge_tm_init_hw(struct hclge_dev *hdev) +{ + int ret; + + if ((hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) && + (hdev->tx_sch_mode != HCLGE_FLAG_VNET_BASE_SCH_MODE)) + return -ENOTSUPP; + + ret = hclge_tm_schd_setup_hw(hdev); + if (ret) + return ret; + + ret = hclge_pause_setup_hw(hdev); + if (ret) + return ret; + + return 0; +} + +int hclge_tm_schd_init(struct hclge_dev *hdev) +{ + int ret = hclge_tm_schd_info_init(hdev); + + if (ret) + return ret; + + return hclge_tm_init_hw(hdev); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h new file mode 100644 index 000000000000..7e67337dfaf2 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HCLGE_TM_H +#define __HCLGE_TM_H + +#include + +/* MAC Pause */ +#define HCLGE_TX_MAC_PAUSE_EN_MSK BIT(0) +#define HCLGE_RX_MAC_PAUSE_EN_MSK BIT(1) + +#define HCLGE_TM_PORT_BASE_MODE_MSK BIT(0) + +/* SP or DWRR */ +#define HCLGE_TM_TX_SCHD_DWRR_MSK BIT(0) +#define HCLGE_TM_TX_SCHD_SP_MSK (0xFE) + +struct hclge_pg_to_pri_link_cmd { + u8 pg_id; + u8 rsvd1[3]; + u8 pri_bit_map; +}; + +struct hclge_qs_to_pri_link_cmd { + __le16 qs_id; + __le16 rsvd; + u8 priority; +#define HCLGE_TM_QS_PRI_LINK_VLD_MSK BIT(0) + u8 link_vld; +}; + +struct hclge_nq_to_qs_link_cmd { + __le16 nq_id; + __le16 rsvd; +#define HCLGE_TM_Q_QS_LINK_VLD_MSK BIT(10) + __le16 qset_id; +}; + +struct hclge_pg_weight_cmd { + u8 pg_id; + u8 dwrr; +}; + +struct hclge_priority_weight_cmd { + u8 pri_id; + u8 dwrr; +}; + +struct hclge_qs_weight_cmd { + __le16 qs_id; + u8 dwrr; +}; + +#define HCLGE_TM_SHAP_IR_B_MSK GENMASK(7, 0) +#define HCLGE_TM_SHAP_IR_B_LSH 0 +#define HCLGE_TM_SHAP_IR_U_MSK GENMASK(11, 8) +#define HCLGE_TM_SHAP_IR_U_LSH 8 +#define HCLGE_TM_SHAP_IR_S_MSK GENMASK(15, 12) +#define HCLGE_TM_SHAP_IR_S_LSH 12 +#define HCLGE_TM_SHAP_BS_B_MSK GENMASK(20, 16) +#define HCLGE_TM_SHAP_BS_B_LSH 16 +#define HCLGE_TM_SHAP_BS_S_MSK GENMASK(25, 21) +#define HCLGE_TM_SHAP_BS_S_LSH 21 + +enum hclge_shap_bucket { + HCLGE_TM_SHAP_C_BUCKET = 0, + HCLGE_TM_SHAP_P_BUCKET, +}; + +struct hclge_pri_shapping_cmd { + u8 pri_id; + u8 rsvd[3]; + __le32 pri_shapping_para; +}; + +struct hclge_pg_shapping_cmd { + u8 pg_id; + u8 rsvd[3]; + __le32 pg_shapping_para; +}; + +struct hclge_bp_to_qs_map_cmd { + u8 tc_id; + u8 rsvd[2]; + u8 qs_group_id; + __le32 qs_bit_map; + u32 rsvd1; +}; + +#define hclge_tm_set_feild(dest, string, val) \ + hnae_set_field((dest), (HCLGE_TM_SHAP_##string##_MSK), \ + (HCLGE_TM_SHAP_##string##_LSH), val) +#define hclge_tm_get_feild(src, string) \ + hnae_get_field((src), (HCLGE_TM_SHAP_##string##_MSK), \ + (HCLGE_TM_SHAP_##string##_LSH)) + +int hclge_tm_schd_init(struct hclge_dev *hdev); +int hclge_pause_setup_hw(struct hclge_dev *hdev); +#endif -- cgit v1.2.3 From 256727da73951b0cbb97105db921bd9e8221aec9 Mon Sep 17 00:00:00 2001 From: Salil Date: Wed, 2 Aug 2017 16:59:50 +0100 Subject: net: hns3: Add MDIO support to HNS3 Ethernet driver for hip08 SoC This patch adds the support of MDIO bus interface for HNS3 driver. Code provides various interfaces to start and stop the PHY layer and to read and write the MDIO bus or PHY. Signed-off-by: Daode Huang Signed-off-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: Yisen Zhuang Signed-off-by: David S. Miller --- .../ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c | 213 +++++++++++++++++++++ .../ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h | 17 ++ 2 files changed, 230 insertions(+) create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c new file mode 100644 index 000000000000..a2add8bb1945 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "hclge_cmd.h" +#include "hclge_main.h" +#include "hclge_mdio.h" + +enum hclge_mdio_c22_op_seq { + HCLGE_MDIO_C22_WRITE = 1, + HCLGE_MDIO_C22_READ = 2 +}; + +#define HCLGE_MDIO_CTRL_START_B 0 +#define HCLGE_MDIO_CTRL_ST_S 1 +#define HCLGE_MDIO_CTRL_ST_M (0x3 << HCLGE_MDIO_CTRL_ST_S) +#define HCLGE_MDIO_CTRL_OP_S 3 +#define HCLGE_MDIO_CTRL_OP_M (0x3 << HCLGE_MDIO_CTRL_OP_S) + +#define HCLGE_MDIO_PHYID_S 0 +#define HCLGE_MDIO_PHYID_M (0x1f << HCLGE_MDIO_PHYID_S) + +#define HCLGE_MDIO_PHYREG_S 0 +#define HCLGE_MDIO_PHYREG_M (0x1f << HCLGE_MDIO_PHYREG_S) + +#define HCLGE_MDIO_STA_B 0 + +struct hclge_mdio_cfg_cmd { + u8 ctrl_bit; + u8 phyid; + u8 phyad; + u8 rsvd; + __le16 reserve; + __le16 data_wr; + __le16 data_rd; + __le16 sta; +}; + +static int hclge_mdio_write(struct mii_bus *bus, int phyid, int regnum, + u16 data) +{ + struct hclge_mdio_cfg_cmd *mdio_cmd; + struct hclge_dev *hdev = bus->priv; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, false); + + mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data; + + hnae_set_field(mdio_cmd->phyid, HCLGE_MDIO_PHYID_M, + HCLGE_MDIO_PHYID_S, phyid); + hnae_set_field(mdio_cmd->phyad, HCLGE_MDIO_PHYREG_M, + HCLGE_MDIO_PHYREG_S, regnum); + + hnae_set_bit(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_START_B, 1); + hnae_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_ST_M, + HCLGE_MDIO_CTRL_ST_S, 1); + hnae_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_OP_M, + HCLGE_MDIO_CTRL_OP_S, HCLGE_MDIO_C22_WRITE); + + mdio_cmd->data_wr = cpu_to_le16(data); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "mdio write fail when sending cmd, status is %d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_mdio_read(struct mii_bus *bus, int phyid, int regnum) +{ + struct hclge_mdio_cfg_cmd *mdio_cmd; + struct hclge_dev *hdev = bus->priv; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, true); + + mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data; + + hnae_set_field(mdio_cmd->phyid, HCLGE_MDIO_PHYID_M, + HCLGE_MDIO_PHYID_S, phyid); + hnae_set_field(mdio_cmd->phyad, HCLGE_MDIO_PHYREG_M, + HCLGE_MDIO_PHYREG_S, regnum); + + hnae_set_bit(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_START_B, 1); + hnae_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_ST_M, + HCLGE_MDIO_CTRL_ST_S, 1); + hnae_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_OP_M, + HCLGE_MDIO_CTRL_OP_S, HCLGE_MDIO_C22_READ); + + /* Read out phy data */ + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "mdio read fail when get data, status is %d.\n", + ret); + return ret; + } + + if (hnae_get_bit(le16_to_cpu(mdio_cmd->sta), HCLGE_MDIO_STA_B)) { + dev_err(&hdev->pdev->dev, "mdio read data error\n"); + return -EIO; + } + + return le16_to_cpu(mdio_cmd->data_rd); +} + +int hclge_mac_mdio_config(struct hclge_dev *hdev) +{ + struct hclge_mac *mac = &hdev->hw.mac; + struct phy_device *phydev; + struct mii_bus *mdio_bus; + int ret; + + if (hdev->hw.mac.phy_addr >= PHY_MAX_ADDR) + return 0; + + mdio_bus = devm_mdiobus_alloc(&hdev->pdev->dev); + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->name = "hisilicon MII bus"; + mdio_bus->read = hclge_mdio_read; + mdio_bus->write = hclge_mdio_write; + snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%s", "mii", + dev_name(&hdev->pdev->dev)); + + mdio_bus->parent = &hdev->pdev->dev; + mdio_bus->priv = hdev; + mdio_bus->phy_mask = ~(1 << mac->phy_addr); + ret = mdiobus_register(mdio_bus); + if (ret) { + dev_err(mdio_bus->parent, + "Failed to register MDIO bus ret = %#x\n", ret); + return ret; + } + + phydev = mdiobus_get_phy(mdio_bus, mac->phy_addr); + if (!phydev || IS_ERR(phydev)) { + dev_err(mdio_bus->parent, "Failed to get phy device\n"); + mdiobus_unregister(mdio_bus); + return -EIO; + } + + mac->phydev = phydev; + mac->mdio_bus = mdio_bus; + + return 0; +} + +static void hclge_mac_adjust_link(struct net_device *netdev) +{ + struct hnae3_handle *h = *((void **)netdev_priv(netdev)); + struct hclge_vport *vport = hclge_get_vport(h); + struct hclge_dev *hdev = vport->back; + int duplex, speed; + int ret; + + speed = netdev->phydev->speed; + duplex = netdev->phydev->duplex; + + ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex); + if (ret) + netdev_err(netdev, "failed to adjust link.\n"); +} + +int hclge_mac_start_phy(struct hclge_dev *hdev) +{ + struct net_device *netdev = hdev->vport[0].nic.netdev; + struct phy_device *phydev = hdev->hw.mac.phydev; + int ret; + + if (!phydev) + return 0; + + ret = phy_connect_direct(netdev, phydev, + hclge_mac_adjust_link, + PHY_INTERFACE_MODE_SGMII); + if (ret) { + netdev_err(netdev, "phy_connect_direct err.\n"); + return ret; + } + + phy_start(phydev); + + return 0; +} + +void hclge_mac_stop_phy(struct hclge_dev *hdev) +{ + struct net_device *netdev = hdev->vport[0].nic.netdev; + struct phy_device *phydev = netdev->phydev; + + if (!phydev) + return; + + phy_stop(phydev); + phy_disconnect(phydev); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h new file mode 100644 index 000000000000..c5e91cfb8f2c --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016-2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HCLGE_MDIO_H +#define __HCLGE_MDIO_H + +int hclge_mac_mdio_config(struct hclge_dev *hdev); +int hclge_mac_start_phy(struct hclge_dev *hdev); +void hclge_mac_stop_phy(struct hclge_dev *hdev); + +#endif -- cgit v1.2.3 From 496d03e960ae2067b81c26a9ea46a028e9d4055d Mon Sep 17 00:00:00 2001 From: Salil Date: Wed, 2 Aug 2017 16:59:51 +0100 Subject: net: hns3: Add Ethtool support to HNS3 driver This patch adds the support of the Ethtool interface to the HNS3 Ethernet driver. Various commands to read the statistics, configure the offloading, loopback selftest etc. are supported. Signed-off-by: Daode Huang Signed-off-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: Yisen Zhuang Signed-off-by: David S. Miller --- .../ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c | 482 +++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c new file mode 100644 index 000000000000..0ad65e47c77e --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "hns3_enet.h" + +struct hns3_stats { + char stats_string[ETH_GSTRING_LEN]; + int stats_size; + int stats_offset; +}; + +/* tqp related stats */ +#define HNS3_TQP_STAT(_string, _member) { \ + .stats_string = _string, \ + .stats_size = FIELD_SIZEOF(struct ring_stats, _member), \ + .stats_offset = offsetof(struct hns3_enet_ring, stats), \ +} \ + +static const struct hns3_stats hns3_txq_stats[] = { + /* Tx per-queue statistics */ + HNS3_TQP_STAT("tx_io_err_cnt", io_err_cnt), + HNS3_TQP_STAT("tx_sw_err_cnt", sw_err_cnt), + HNS3_TQP_STAT("tx_seg_pkt_cnt", seg_pkt_cnt), + HNS3_TQP_STAT("tx_pkts", tx_pkts), + HNS3_TQP_STAT("tx_bytes", tx_bytes), + HNS3_TQP_STAT("tx_err_cnt", tx_err_cnt), + HNS3_TQP_STAT("tx_restart_queue", restart_queue), + HNS3_TQP_STAT("tx_busy", tx_busy), +}; + +#define HNS3_TXQ_STATS_COUNT ARRAY_SIZE(hns3_txq_stats) + +static const struct hns3_stats hns3_rxq_stats[] = { + /* Rx per-queue statistics */ + HNS3_TQP_STAT("rx_io_err_cnt", io_err_cnt), + HNS3_TQP_STAT("rx_sw_err_cnt", sw_err_cnt), + HNS3_TQP_STAT("rx_seg_pkt_cnt", seg_pkt_cnt), + HNS3_TQP_STAT("rx_pkts", rx_pkts), + HNS3_TQP_STAT("rx_bytes", rx_bytes), + HNS3_TQP_STAT("rx_err_cnt", rx_err_cnt), + HNS3_TQP_STAT("rx_reuse_pg_cnt", reuse_pg_cnt), + HNS3_TQP_STAT("rx_err_pkt_len", err_pkt_len), + HNS3_TQP_STAT("rx_non_vld_descs", non_vld_descs), + HNS3_TQP_STAT("rx_err_bd_num", err_bd_num), + HNS3_TQP_STAT("rx_l2_err", l2_err), + HNS3_TQP_STAT("rx_l3l4_csum_err", l3l4_csum_err), +}; + +#define HNS3_RXQ_STATS_COUNT ARRAY_SIZE(hns3_rxq_stats) + +#define HNS3_TQP_STATS_COUNT (HNS3_TXQ_STATS_COUNT + HNS3_RXQ_STATS_COUNT) + +struct hns3_link_mode_mapping { + u32 hns3_link_mode; + u32 ethtool_link_mode; +}; + +static const struct hns3_link_mode_mapping hns3_lm_map[] = { + {HNS3_LM_FIBRE_BIT, ETHTOOL_LINK_MODE_FIBRE_BIT}, + {HNS3_LM_AUTONEG_BIT, ETHTOOL_LINK_MODE_Autoneg_BIT}, + {HNS3_LM_TP_BIT, ETHTOOL_LINK_MODE_TP_BIT}, + {HNS3_LM_PAUSE_BIT, ETHTOOL_LINK_MODE_Pause_BIT}, + {HNS3_LM_BACKPLANE_BIT, ETHTOOL_LINK_MODE_Backplane_BIT}, + {HNS3_LM_10BASET_HALF_BIT, ETHTOOL_LINK_MODE_10baseT_Half_BIT}, + {HNS3_LM_10BASET_FULL_BIT, ETHTOOL_LINK_MODE_10baseT_Full_BIT}, + {HNS3_LM_100BASET_HALF_BIT, ETHTOOL_LINK_MODE_100baseT_Half_BIT}, + {HNS3_LM_100BASET_FULL_BIT, ETHTOOL_LINK_MODE_100baseT_Full_BIT}, + {HNS3_LM_1000BASET_FULL_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT}, +}; + +static void hns3_driv_to_eth_caps(u32 caps, struct ethtool_link_ksettings *cmd, + bool is_advertised) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hns3_lm_map); i++) { + if (!(caps & hns3_lm_map[i].hns3_link_mode)) + continue; + + if (is_advertised) { + ethtool_link_ksettings_zero_link_mode(cmd, + advertising); + __set_bit(hns3_lm_map[i].ethtool_link_mode, + cmd->link_modes.advertising); + } else { + ethtool_link_ksettings_zero_link_mode(cmd, + supported); + __set_bit(hns3_lm_map[i].ethtool_link_mode, + cmd->link_modes.supported); + } + } +} + +static int hns3_get_sset_count(struct net_device *netdev, int stringset) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + const struct hnae3_ae_ops *ops = h->ae_algo->ops; + + if (!ops->get_sset_count) + return -EOPNOTSUPP; + + switch (stringset) { + case ETH_SS_STATS: + return ((HNS3_TQP_STATS_COUNT * h->kinfo.num_tqps) + + ops->get_sset_count(h, stringset)); + + case ETH_SS_TEST: + return ops->get_sset_count(h, stringset); + } + + return 0; +} + +static void *hns3_update_strings(u8 *data, const struct hns3_stats *stats, + u32 stat_count, u32 num_tqps) +{ +#define MAX_PREFIX_SIZE (8 + 4) + u32 size_left; + u32 i, j; + u32 n1; + + for (i = 0; i < num_tqps; i++) { + for (j = 0; j < stat_count; j++) { + data[ETH_GSTRING_LEN - 1] = '\0'; + + /* first, prepend the prefix string */ + n1 = snprintf(data, MAX_PREFIX_SIZE, "rcb_q%d_", i); + n1 = min_t(uint, n1, MAX_PREFIX_SIZE - 1); + size_left = (ETH_GSTRING_LEN - 1) - n1; + + /* now, concatenate the stats string to it */ + strncat(data, stats[j].stats_string, size_left); + data += ETH_GSTRING_LEN; + } + } + + return data; +} + +static u8 *hns3_get_strings_tqps(struct hnae3_handle *handle, u8 *data) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + + /* get strings for Tx */ + data = hns3_update_strings(data, hns3_txq_stats, HNS3_TXQ_STATS_COUNT, + kinfo->num_tqps); + + /* get strings for Rx */ + data = hns3_update_strings(data, hns3_rxq_stats, HNS3_RXQ_STATS_COUNT, + kinfo->num_tqps); + + return data; +} + +static void hns3_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + const struct hnae3_ae_ops *ops = h->ae_algo->ops; + char *buff = (char *)data; + + if (!ops->get_strings) + return; + + switch (stringset) { + case ETH_SS_STATS: + buff = hns3_get_strings_tqps(h, buff); + h->ae_algo->ops->get_strings(h, stringset, (u8 *)buff); + break; + case ETH_SS_TEST: + ops->get_strings(h, stringset, data); + break; + } +} + +static u64 *hns3_get_stats_tqps(struct hnae3_handle *handle, u64 *data) +{ + struct hns3_nic_priv *nic_priv = (struct hns3_nic_priv *)handle->priv; + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + struct hns3_enet_ring *ring; + u8 *stat; + u32 i; + + /* get stats for Tx */ + for (i = 0; i < kinfo->num_tqps; i++) { + ring = nic_priv->ring_data[i].ring; + for (i = 0; i < HNS3_TXQ_STATS_COUNT; i++) { + stat = (u8 *)ring + hns3_txq_stats[i].stats_offset; + *data++ = *(u64 *)stat; + } + } + + /* get stats for Rx */ + for (i = 0; i < kinfo->num_tqps; i++) { + ring = nic_priv->ring_data[i + kinfo->num_tqps].ring; + for (i = 0; i < HNS3_RXQ_STATS_COUNT; i++) { + stat = (u8 *)ring + hns3_rxq_stats[i].stats_offset; + *data++ = *(u64 *)stat; + } + } + + return data; +} + +/* hns3_get_stats - get detail statistics. + * @netdev: net device + * @stats: statistics info. + * @data: statistics data. + */ +void hns3_get_stats(struct net_device *netdev, struct ethtool_stats *stats, + u64 *data) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + u64 *p = data; + + if (!h->ae_algo->ops->get_stats || !h->ae_algo->ops->update_stats) { + netdev_err(netdev, "could not get any statistics\n"); + return; + } + + h->ae_algo->ops->update_stats(h, &netdev->stats); + + /* get per-queue stats */ + p = hns3_get_stats_tqps(h, p); + + /* get MAC & other misc hardware stats */ + h->ae_algo->ops->get_stats(h, p); +} + +static void hns3_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + strncpy(drvinfo->version, hns3_driver_version, + sizeof(drvinfo->version)); + drvinfo->version[sizeof(drvinfo->version) - 1] = '\0'; + + strncpy(drvinfo->driver, h->pdev->driver->name, + sizeof(drvinfo->driver)); + drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; + + strncpy(drvinfo->bus_info, pci_name(h->pdev), + sizeof(drvinfo->bus_info)); + drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; + + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "0x%08x", + priv->ae_handle->ae_algo->ops->get_fw_version(h)); +} + +static u32 hns3_get_link(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h; + + h = priv->ae_handle; + + if (h->ae_algo && h->ae_algo->ops && h->ae_algo->ops->get_status) + return h->ae_algo->ops->get_status(h); + else + return 0; +} + +static void hns3_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *param) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + int queue_num = priv->ae_handle->kinfo.num_tqps; + + param->tx_max_pending = HNS3_RING_MAX_PENDING; + param->rx_max_pending = HNS3_RING_MAX_PENDING; + + param->tx_pending = priv->ring_data[0].ring->desc_num; + param->rx_pending = priv->ring_data[queue_num].ring->desc_num; +} + +static void hns3_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *param) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo && h->ae_algo->ops && h->ae_algo->ops->get_pauseparam) + h->ae_algo->ops->get_pauseparam(h, ¶m->autoneg, + ¶m->rx_pause, ¶m->tx_pause); +} + +static int hns3_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + u32 supported_caps; + u32 advertised_caps; + u8 media_type; + u8 link_stat; + u8 auto_neg; + u8 duplex; + u32 speed; + + if (!h->ae_algo || !h->ae_algo->ops) + return -EOPNOTSUPP; + + /* 1.auto_neg&speed&duplex from cmd */ + if (h->ae_algo->ops->get_ksettings_an_result) { + h->ae_algo->ops->get_ksettings_an_result(h, &auto_neg, + &speed, &duplex); + cmd->base.autoneg = auto_neg; + cmd->base.speed = speed; + cmd->base.duplex = duplex; + + link_stat = hns3_get_link(netdev); + if (!link_stat) { + cmd->base.speed = (u32)SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; + } + } + + /* 2.media_type get from bios parameter block */ + if (h->ae_algo->ops->get_media_type) + h->ae_algo->ops->get_media_type(h, &media_type); + + switch (media_type) { + case HNAE3_MEDIA_TYPE_FIBER: + cmd->base.port = PORT_FIBRE; + supported_caps = HNS3_LM_FIBRE_BIT | HNS3_LM_AUTONEG_BIT | + HNS3_LM_PAUSE_BIT | HNS3_LM_1000BASET_FULL_BIT; + + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_COPPER: + cmd->base.port = PORT_TP; + supported_caps = HNS3_LM_TP_BIT | HNS3_LM_AUTONEG_BIT | + HNS3_LM_PAUSE_BIT | HNS3_LM_1000BASET_FULL_BIT | + HNS3_LM_100BASET_FULL_BIT | HNS3_LM_100BASET_HALF_BIT | + HNS3_LM_10BASET_FULL_BIT | HNS3_LM_10BASET_HALF_BIT; + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_BACKPLANE: + cmd->base.port = PORT_NONE; + supported_caps = HNS3_LM_BACKPLANE_BIT | HNS3_LM_PAUSE_BIT | + HNS3_LM_AUTONEG_BIT | HNS3_LM_1000BASET_FULL_BIT | + HNS3_LM_100BASET_FULL_BIT | HNS3_LM_100BASET_HALF_BIT | + HNS3_LM_10BASET_FULL_BIT | HNS3_LM_10BASET_HALF_BIT; + + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_UNKNOWN: + default: + cmd->base.port = PORT_OTHER; + supported_caps = 0; + advertised_caps = 0; + break; + } + + /* now, map driver link modes to ethtool link modes */ + hns3_driv_to_eth_caps(supported_caps, cmd, false); + hns3_driv_to_eth_caps(advertised_caps, cmd, true); + + /* 3.mdix_ctrl&mdix get from phy reg */ + if (h->ae_algo->ops->get_mdix_mode) + h->ae_algo->ops->get_mdix_mode(h, &cmd->base.eth_tp_mdix_ctrl, + &cmd->base.eth_tp_mdix); + /* 4.mdio_support */ + cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22; + + return 0; +} + +static u32 hns3_get_rss_key_size(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || + !h->ae_algo->ops->get_rss_key_size) + return -EOPNOTSUPP; + + return h->ae_algo->ops->get_rss_key_size(h); +} + +static u32 hns3_get_rss_indir_size(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || + !h->ae_algo->ops->get_rss_indir_size) + return -EOPNOTSUPP; + + return h->ae_algo->ops->get_rss_indir_size(h); +} + +static int hns3_get_rss(struct net_device *netdev, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || !h->ae_algo->ops->get_rss) + return -EOPNOTSUPP; + + return h->ae_algo->ops->get_rss(h, indir, key, hfunc); +} + +static int hns3_set_rss(struct net_device *netdev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || !h->ae_algo->ops->set_rss) + return -EOPNOTSUPP; + + /* currently we only support Toeplitz hash */ + if ((hfunc != ETH_RSS_HASH_NO_CHANGE) && (hfunc != ETH_RSS_HASH_TOP)) { + netdev_err(netdev, + "hash func not supported (only Toeplitz hash)\n"); + return -EOPNOTSUPP; + } + if (!indir) { + netdev_err(netdev, + "set rss failed for indir is empty\n"); + return -EOPNOTSUPP; + } + + return h->ae_algo->ops->set_rss(h, indir, key, hfunc); +} + +static int hns3_get_rxnfc(struct net_device *netdev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || !h->ae_algo->ops->get_tc_size) + return -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = h->ae_algo->ops->get_tc_size(h); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct ethtool_ops hns3_ethtool_ops = { + .get_drvinfo = hns3_get_drvinfo, + .get_link = hns3_get_link, + .get_ringparam = hns3_get_ringparam, + .get_pauseparam = hns3_get_pauseparam, + .get_strings = hns3_get_strings, + .get_ethtool_stats = hns3_get_stats, + .get_sset_count = hns3_get_sset_count, + .get_rxnfc = hns3_get_rxnfc, + .get_rxfh_key_size = hns3_get_rss_key_size, + .get_rxfh_indir_size = hns3_get_rss_indir_size, + .get_rxfh = hns3_get_rss, + .set_rxfh = hns3_set_rss, + .get_link_ksettings = hns3_get_link_ksettings, +}; + +void hns3_ethtool_set_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &hns3_ethtool_ops; +} -- cgit v1.2.3 From 15e8e5ffd624702ba2fa0d27566069922561ae95 Mon Sep 17 00:00:00 2001 From: Salil Date: Wed, 2 Aug 2017 16:59:52 +0100 Subject: net: hns3: Add HNS3 driver to kernel build framework & MAINTAINERS This patch updates the MAINTAINERS file with HNS3 Ethernet driver maintainers names and other details. This also introduces the new Makefiles required to build the HNS3 Ethernet driver and updates the existing Kconfig file in the hisilicon folder. Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- MAINTAINERS | 8 +++++++ drivers/net/ethernet/hisilicon/Kconfig | 27 ++++++++++++++++++++++ drivers/net/ethernet/hisilicon/Makefile | 1 + drivers/net/ethernet/hisilicon/hns3/Makefile | 7 ++++++ .../net/ethernet/hisilicon/hns3/hns3pf/Makefile | 11 +++++++++ 5 files changed, 54 insertions(+) create mode 100644 drivers/net/ethernet/hisilicon/hns3/Makefile create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile (limited to 'drivers/net') diff --git a/MAINTAINERS b/MAINTAINERS index c67618c97c5d..a515da73c7e4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6148,6 +6148,14 @@ S: Maintained F: drivers/net/ethernet/hisilicon/ F: Documentation/devicetree/bindings/net/hisilicon*.txt +HISILICON NETWORK SUBSYSTEM 3 DRIVER (HNS3) +M: Yisen Zhuang +M: Salil Mehta +L: netdev@vger.kernel.org +W: http://www.hisilicon.com +S: Maintained +F: drivers/net/ethernet/hisilicon/hns3/ + HISILICON ROCE DRIVER M: Lijun Ou M: Wei Hu(Xavier) diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig index d11287e11371..91c7bdb9b43c 100644 --- a/drivers/net/ethernet/hisilicon/Kconfig +++ b/drivers/net/ethernet/hisilicon/Kconfig @@ -76,4 +76,31 @@ config HNS_ENET This selects the general ethernet driver for HNS. This module make use of any HNS AE driver, such as HNS_DSAF +config HNS3 + tristate "Hisilicon Network Subsystem Support HNS3 (Framework)" + depends on PCI + ---help--- + This selects the framework support for Hisilicon Network Subsystem 3. + This layer facilitates clients like ENET, RoCE and user-space ethernet + drivers(like ODP)to register with HNAE devices and their associated + operations. + +config HNS3_HCLGE + tristate "Hisilicon HNS3 HCLGE Acceleration Engine & Compatibility Layer Support" + depends on PCI_MSI + depends on HNS3 + ---help--- + This selects the HNS3_HCLGE network acceleration engine & its hardware + compatibility layer. The engine would be used in Hisilicon hip08 family of + SoCs and further upcoming SoCs. + +config HNS3_ENET + tristate "Hisilicon HNS3 Ethernet Device Support" + depends on 64BIT && PCI + depends on HNS3 && HNS3_HCLGE + ---help--- + This selects the Ethernet Driver for Hisilicon Network Subsystem 3 for hip08 + family of SoCs. This module depends upon HNAE3 driver to access the HNAE3 + devices and their associated operations. + endif # NET_VENDOR_HISILICON diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile index 8661695024dc..3828c435c18f 100644 --- a/drivers/net/ethernet/hisilicon/Makefile +++ b/drivers/net/ethernet/hisilicon/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o obj-$(CONFIG_HIP04_ETH) += hip04_eth.o obj-$(CONFIG_HNS_MDIO) += hns_mdio.o obj-$(CONFIG_HNS) += hns/ +obj-$(CONFIG_HNS3) += hns3/ obj-$(CONFIG_HISI_FEMAC) += hisi_femac.o diff --git a/drivers/net/ethernet/hisilicon/hns3/Makefile b/drivers/net/ethernet/hisilicon/hns3/Makefile new file mode 100644 index 000000000000..a9349e1f3e51 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the HISILICON network device drivers. +# + +obj-$(CONFIG_HNS3) += hns3pf/ + +obj-$(CONFIG_HNS3) += hnae3.o diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile b/drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile new file mode 100644 index 000000000000..162e8a42acd0 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the HISILICON network device drivers. +# + +ccflags-y := -Idrivers/net/ethernet/hisilicon/hns3 + +obj-$(CONFIG_HNS3_HCLGE) += hclge.o +hclge-objs = hclge_main.o hclge_cmd.o hclge_mdio.o hclge_tm.o + +obj-$(CONFIG_HNS3_ENET) += hns3.o +hns3-objs = hns3_enet.o hns3_ethtool.o -- cgit v1.2.3 From e11e8729fafd3dc49397c28f320ac3b2d483abbc Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Thu, 3 Aug 2017 09:49:03 +0200 Subject: net: arc_emac: Add support for ndo_do_ioctl net_device_ops operation This operation is required for handling ioctl commands like SIOCGMIIREG, when debugging MDIO registers from userspace. This commit adds support for this operation. Signed-off-by: Romain Perier Signed-off-by: David S. Miller --- drivers/net/ethernet/arc/emac_main.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c index 68de2f2652f2..3241af1ce718 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -720,6 +720,18 @@ static int arc_emac_set_address(struct net_device *ndev, void *p) return 0; } +static int arc_emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + if (!netif_running(dev)) + return -EINVAL; + + if (!dev->phydev) + return -ENODEV; + + return phy_mii_ioctl(dev->phydev, rq, cmd); +} + + static const struct net_device_ops arc_emac_netdev_ops = { .ndo_open = arc_emac_open, .ndo_stop = arc_emac_stop, @@ -727,6 +739,7 @@ static const struct net_device_ops arc_emac_netdev_ops = { .ndo_set_mac_address = arc_emac_set_address, .ndo_get_stats = arc_emac_stats, .ndo_set_rx_mode = arc_emac_set_rx_mode, + .ndo_do_ioctl = arc_emac_ioctl, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = arc_emac_poll_controller, #endif -- cgit v1.2.3 From eb1e93a10ef5bedfc15d1404dc6a2c46d843cde9 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 3 Aug 2017 10:41:55 +0200 Subject: net: mvpp2: fix MVPP21_ISR_RXQ_GROUP_REG definition The MVPP21_ISR_RXQ_GROUP_REG register is not indexed by rxq, but by port, so we fix the parameter name accordingly. There are no functional changes. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 48d21c1e09f2..4b36a153328a 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -185,7 +185,7 @@ /* Interrupt Cause and Mask registers */ #define MVPP2_ISR_RX_THRESHOLD_REG(rxq) (0x5200 + 4 * (rxq)) #define MVPP2_MAX_ISR_RX_THRESHOLD 0xfffff0 -#define MVPP21_ISR_RXQ_GROUP_REG(rxq) (0x5400 + 4 * (rxq)) +#define MVPP21_ISR_RXQ_GROUP_REG(port) (0x5400 + 4 * (port)) #define MVPP22_ISR_RXQ_GROUP_INDEX_REG 0x5400 #define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf -- cgit v1.2.3 From b5635ad2e4906e8f8bd3ab145059ce413c03a7ad Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 3 Aug 2017 10:41:56 +0200 Subject: net: mvpp2: remove RX queue group reset code The RX queue group allocation is anyway re-done later in mvpp2_port_init(), so resetting it in mvpp2_init() is not very useful, and will be annoying as we are going to rework the RX queue group allocation logic. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 4b36a153328a..537d2b4adef3 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -6845,23 +6845,6 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) /* Rx Fifo Init */ mvpp2_rx_fifo_init(priv); - /* Reset Rx queue group interrupt configuration */ - for (i = 0; i < MVPP2_MAX_PORTS; i++) { - if (priv->hw_version == MVPP21) { - mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(i), - rxq_number); - continue; - } else { - u32 val; - - val = (i << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET); - mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); - - val = (rxq_number << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET); - mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); - } - } - if (priv->hw_version == MVPP21) writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT, priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG); -- cgit v1.2.3 From 09f8397553a24c66e0141ec57f2c1801acd4e4a0 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 3 Aug 2017 10:41:57 +0200 Subject: net: mvpp2: introduce per-port nrxqs/ntxqs variables Currently, the global variables rxq_number and txq_number hold the number of per-port TXQs and RXQs. Until now, such numbers were constant regardless of the driver configuration. As we are going to introduce different modes for TX and RX queues, these numbers will depend on the configuration (PPv2.1 vs. PPv2.2, exact queue distribution logic). Therefore, as a preparation, we move the number of RXQs and TXQs in the 'struct mvpp2_port' structure, next to the RXQs and TXQs descriptor arrays. For now, they remain initialized to the same default values as rxq_number/txq_number used to be initialized, but this will change in future commits. The only non-mechanical change in this patch is that the check to verify hardware constraints on the number of RXQs and TXQs is moved from mvpp2_probe() to mvpp2_port_probe(), since it's now in mvpp2_port_probe() that we initialize the per-port count of RXQ and TXQ. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 83 ++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 42 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 537d2b4adef3..84908aa88374 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -768,7 +768,9 @@ struct mvpp2_port { void __iomem *base; struct mvpp2_rx_queue **rxqs; + unsigned int nrxqs; struct mvpp2_tx_queue **txqs; + unsigned int ntxqs; struct net_device *dev; int pkt_size; @@ -1062,13 +1064,6 @@ struct mvpp2_bm_pool { u32 port_map; }; -/* Static declaractions */ - -/* Number of RXQs used by single port */ -static int rxq_number = MVPP2_DEFAULT_RXQ; -/* Number of TXQs used by single port */ -static int txq_number = MVPP2_MAX_TXQ; - #define MVPP2_DRIVER_NAME "mvpp2" #define MVPP2_DRIVER_VERSION "1.0" @@ -4070,7 +4065,7 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) port->pool_long->port_map |= (1 << port->id); - for (rxq = 0; rxq < rxq_number; rxq++) + for (rxq = 0; rxq < port->nrxqs; rxq++) mvpp2_rxq_long_pool_set(port, rxq, port->pool_long->id); } @@ -4084,7 +4079,7 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) port->pool_short->port_map |= (1 << port->id); - for (rxq = 0; rxq < rxq_number; rxq++) + for (rxq = 0; rxq < port->nrxqs; rxq++) mvpp2_rxq_short_pool_set(port, rxq, port->pool_short->id); } @@ -4376,7 +4371,7 @@ static void mvpp2_defaults_set(struct mvpp2_port *port) MVPP2_RX_LOW_LATENCY_PKT_SIZE(256)); /* Enable Rx cache snoop */ - for (lrxq = 0; lrxq < rxq_number; lrxq++) { + for (lrxq = 0; lrxq < port->nrxqs; lrxq++) { queue = port->rxqs[lrxq]->id; val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); val |= MVPP2_SNOOP_PKT_SIZE_MASK | @@ -4394,7 +4389,7 @@ static void mvpp2_ingress_enable(struct mvpp2_port *port) u32 val; int lrxq, queue; - for (lrxq = 0; lrxq < rxq_number; lrxq++) { + for (lrxq = 0; lrxq < port->nrxqs; lrxq++) { queue = port->rxqs[lrxq]->id; val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); val &= ~MVPP2_RXQ_DISABLE_MASK; @@ -4407,7 +4402,7 @@ static void mvpp2_ingress_disable(struct mvpp2_port *port) u32 val; int lrxq, queue; - for (lrxq = 0; lrxq < rxq_number; lrxq++) { + for (lrxq = 0; lrxq < port->nrxqs; lrxq++) { queue = port->rxqs[lrxq]->id; val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); val |= MVPP2_RXQ_DISABLE_MASK; @@ -4426,7 +4421,7 @@ static void mvpp2_egress_enable(struct mvpp2_port *port) /* Enable all initialized TXs. */ qmap = 0; - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { struct mvpp2_tx_queue *txq = port->txqs[queue]; if (txq->descs) @@ -4712,7 +4707,7 @@ static void mvpp2_txq_sent_counter_clear(void *arg) struct mvpp2_port *port = arg; int queue; - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { int id = port->txqs[queue]->id; mvpp2_percpu_read(port->priv, smp_processor_id(), @@ -4753,7 +4748,7 @@ static void mvpp2_txp_max_tx_size_set(struct mvpp2_port *port) mvpp2_write(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val); } - for (txq = 0; txq < txq_number; txq++) { + for (txq = 0; txq < port->ntxqs; txq++) { val = mvpp2_read(port->priv, MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq)); size = val & MVPP2_TXQ_TOKEN_SIZE_MAX; @@ -5229,7 +5224,7 @@ static void mvpp2_cleanup_txqs(struct mvpp2_port *port) val |= MVPP2_TX_PORT_FLUSH_MASK(port->id); mvpp2_write(port->priv, MVPP2_TX_PORT_FLUSH_REG, val); - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { txq = port->txqs[queue]; mvpp2_txq_clean(port, txq); mvpp2_txq_deinit(port, txq); @@ -5246,7 +5241,7 @@ static void mvpp2_cleanup_rxqs(struct mvpp2_port *port) { int queue; - for (queue = 0; queue < rxq_number; queue++) + for (queue = 0; queue < port->nrxqs; queue++) mvpp2_rxq_deinit(port, port->rxqs[queue]); } @@ -5255,7 +5250,7 @@ static int mvpp2_setup_rxqs(struct mvpp2_port *port) { int queue, err; - for (queue = 0; queue < rxq_number; queue++) { + for (queue = 0; queue < port->nrxqs; queue++) { err = mvpp2_rxq_init(port, port->rxqs[queue]); if (err) goto err_cleanup; @@ -5273,7 +5268,7 @@ static int mvpp2_setup_txqs(struct mvpp2_port *port) struct mvpp2_tx_queue *txq; int queue, err; - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { txq = port->txqs[queue]; err = mvpp2_txq_init(port, txq); if (err) @@ -5385,7 +5380,7 @@ static void mvpp2_tx_proc_cb(unsigned long data) port_pcpu->timer_scheduled = false; /* Process all the Tx queues */ - cause = (1 << txq_number) - 1; + cause = (1 << port->ntxqs) - 1; tx_todo = mvpp2_tx_done(port, cause); /* Set the timer in case not all the packets were processed */ @@ -6228,7 +6223,7 @@ static int mvpp2_ethtool_set_coalesce(struct net_device *dev, struct mvpp2_port *port = netdev_priv(dev); int queue; - for (queue = 0; queue < rxq_number; queue++) { + for (queue = 0; queue < port->nrxqs; queue++) { struct mvpp2_rx_queue *rxq = port->rxqs[queue]; rxq->time_coal = c->rx_coalesce_usecs; @@ -6237,7 +6232,7 @@ static int mvpp2_ethtool_set_coalesce(struct net_device *dev, mvpp2_rx_time_coal_set(port, rxq); } - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { struct mvpp2_tx_queue *txq = port->txqs[queue]; txq->done_pkts_coal = c->tx_max_coalesced_frames; @@ -6373,15 +6368,20 @@ static int mvpp2_port_init(struct mvpp2_port *port) struct mvpp2_txq_pcpu *txq_pcpu; int queue, cpu, err; - if (port->first_rxq + rxq_number > + /* Checks for hardware constraints */ + if (port->first_rxq + port->nrxqs > MVPP2_MAX_PORTS * priv->max_port_rxqs) return -EINVAL; + if (port->nrxqs % 4 || (port->nrxqs > priv->max_port_rxqs) || + (port->ntxqs > MVPP2_MAX_TXQ)) + return -EINVAL; + /* Disable port */ mvpp2_egress_disable(port); mvpp2_port_disable(port); - port->txqs = devm_kcalloc(dev, txq_number, sizeof(*port->txqs), + port->txqs = devm_kcalloc(dev, port->ntxqs, sizeof(*port->txqs), GFP_KERNEL); if (!port->txqs) return -ENOMEM; @@ -6389,7 +6389,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) /* Associate physical Tx queues to this port and initialize. * The mapping is predefined. */ - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { int queue_phy_id = mvpp2_txq_phys(port->id, queue); struct mvpp2_tx_queue *txq; @@ -6416,7 +6416,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) port->txqs[queue] = txq; } - port->rxqs = devm_kcalloc(dev, rxq_number, sizeof(*port->rxqs), + port->rxqs = devm_kcalloc(dev, port->nrxqs, sizeof(*port->rxqs), GFP_KERNEL); if (!port->rxqs) { err = -ENOMEM; @@ -6424,7 +6424,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) } /* Allocate and initialize Rx queue for this port */ - for (queue = 0; queue < rxq_number; queue++) { + for (queue = 0; queue < port->nrxqs; queue++) { struct mvpp2_rx_queue *rxq; /* Map physical Rx queue to port's logical Rx queue */ @@ -6444,19 +6444,19 @@ static int mvpp2_port_init(struct mvpp2_port *port) /* Configure Rx queue group interrupt for this port */ if (priv->hw_version == MVPP21) { mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(port->id), - rxq_number); + port->nrxqs); } else { u32 val; val = (port->id << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET); mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); - val = (rxq_number << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET); + val = (port->nrxqs << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET); mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); } /* Create Rx descriptor rings */ - for (queue = 0; queue < rxq_number; queue++) { + for (queue = 0; queue < port->nrxqs; queue++) { struct mvpp2_rx_queue *rxq = port->rxqs[queue]; rxq->size = port->rx_ring_size; @@ -6484,7 +6484,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) return 0; err_free_percpu: - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { if (!port->txqs[queue]) continue; free_percpu(port->txqs[queue]->pcpu); @@ -6505,12 +6505,16 @@ static int mvpp2_port_probe(struct platform_device *pdev, const char *dt_mac_addr; const char *mac_from; char hw_mac_addr[ETH_ALEN]; + unsigned int ntxqs, nrxqs; u32 id; int features; int phy_mode; int err, i, cpu; - dev = alloc_etherdev_mqs(sizeof(*port), txq_number, rxq_number); + ntxqs = MVPP2_MAX_TXQ; + nrxqs = MVPP2_DEFAULT_RXQ; + + dev = alloc_etherdev_mqs(sizeof(*port), ntxqs, nrxqs); if (!dev) return -ENOMEM; @@ -6540,6 +6544,8 @@ static int mvpp2_port_probe(struct platform_device *pdev, dev->ethtool_ops = &mvpp2_eth_tool_ops; port = netdev_priv(dev); + port->ntxqs = ntxqs; + port->nrxqs = nrxqs; port->irq = irq_of_parse_and_map(port_node, 0); if (port->irq <= 0) { @@ -6553,7 +6559,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->priv = priv; port->id = id; if (priv->hw_version == MVPP21) - port->first_rxq = port->id * rxq_number; + port->first_rxq = port->id * port->nrxqs; else port->first_rxq = port->id * priv->max_port_rxqs; @@ -6662,7 +6668,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, err_free_port_pcpu: free_percpu(port->pcpu); err_free_txq_pcpu: - for (i = 0; i < txq_number; i++) + for (i = 0; i < port->ntxqs; i++) free_percpu(port->txqs[i]->pcpu); err_free_stats: free_percpu(port->stats); @@ -6683,7 +6689,7 @@ static void mvpp2_port_remove(struct mvpp2_port *port) of_node_put(port->phy_node); free_percpu(port->pcpu); free_percpu(port->stats); - for (i = 0; i < txq_number; i++) + for (i = 0; i < port->ntxqs; i++) free_percpu(port->txqs[i]->pcpu); irq_dispose_mapping(port->irq); free_netdev(port->dev); @@ -6800,13 +6806,6 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) int err, i; u32 val; - /* Checks for hardware constraints */ - if (rxq_number % 4 || (rxq_number > priv->max_port_rxqs) || - (txq_number > MVPP2_MAX_TXQ)) { - dev_err(&pdev->dev, "invalid queue size parameter\n"); - return -EINVAL; - } - /* MBUS windows configuration */ dram_target_info = mv_mbus_dram_info(); if (dram_target_info) -- cgit v1.2.3 From df089aa0acd75bb605e4cce72982942173e742ad Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 3 Aug 2017 10:41:58 +0200 Subject: net: mvpp2: move from cpu-centric naming to "software thread" naming The PPv2.2 IP has a concept of "software thread", with all registers of the PPv2.2 mapped 8 times, for concurrent accesses by 8 "software threads". In addition, interrupts on RX queues are associated to such "software thread". For most cases, we map a "software thread" to the more conventional concept of CPU, but we will soon have one exception: we will have a model where we have one TX interrupt per CPU (each using one software thread), and all RX events mapped to another software thread (associated to another interrupt). In preparation for this change, it makes sense to change the naming from MVPP2_MAX_CPUS to MVPP2_MAX_THREADS, and plan for 8 software threads instead of 4 currently. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 84908aa88374..af38a215f0e0 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -685,7 +685,7 @@ enum mvpp2_prs_l3_cast { #define MVPP21_ADDR_SPACE_SZ 0 #define MVPP22_ADDR_SPACE_SZ SZ_64K -#define MVPP2_MAX_CPUS 4 +#define MVPP2_MAX_THREADS 8 enum mvpp2_bm_type { MVPP2_BM_FREE, @@ -701,11 +701,12 @@ struct mvpp2 { void __iomem *lms_base; void __iomem *iface_base; - /* On PPv2.2, each CPU can access the base register through a - * separate address space, each 64 KB apart from each - * other. + /* On PPv2.2, each "software thread" can access the base + * register through a separate address space, each 64 KB apart + * from each other. Typically, such address spaces will be + * used per CPU. */ - void __iomem *cpu_base[MVPP2_MAX_CPUS]; + void __iomem *swth_base[MVPP2_MAX_THREADS]; /* Common clocks */ struct clk *pp_clk; @@ -1071,12 +1072,12 @@ struct mvpp2_bm_pool { static void mvpp2_write(struct mvpp2 *priv, u32 offset, u32 data) { - writel(data, priv->cpu_base[0] + offset); + writel(data, priv->swth_base[0] + offset); } static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) { - return readl(priv->cpu_base[0] + offset); + return readl(priv->swth_base[0] + offset); } /* These accessors should be used to access: @@ -1118,13 +1119,13 @@ static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) static void mvpp2_percpu_write(struct mvpp2 *priv, int cpu, u32 offset, u32 data) { - writel(data, priv->cpu_base[cpu] + offset); + writel(data, priv->swth_base[cpu] + offset); } static u32 mvpp2_percpu_read(struct mvpp2 *priv, int cpu, u32 offset) { - return readl(priv->cpu_base[cpu] + offset); + return readl(priv->swth_base[cpu] + offset); } static dma_addr_t mvpp2_txdesc_dma_addr_get(struct mvpp2_port *port, @@ -6874,7 +6875,7 @@ static int mvpp2_probe(struct platform_device *pdev) struct mvpp2 *priv; struct resource *res; void __iomem *base; - int port_count, cpu; + int port_count, i; int err; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); @@ -6901,12 +6902,12 @@ static int mvpp2_probe(struct platform_device *pdev) return PTR_ERR(priv->iface_base); } - for_each_present_cpu(cpu) { + for (i = 0; i < MVPP2_MAX_THREADS; i++) { u32 addr_space_sz; addr_space_sz = (priv->hw_version == MVPP21 ? MVPP21_ADDR_SPACE_SZ : MVPP22_ADDR_SPACE_SZ); - priv->cpu_base[cpu] = base + cpu * addr_space_sz; + priv->swth_base[i] = base + i * addr_space_sz; } if (priv->hw_version == MVPP21) -- cgit v1.2.3 From 591f4cfab38a6e69573210b21c0554e1acbbd532 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 3 Aug 2017 10:41:59 +0200 Subject: net: mvpp2: introduce queue_vector concept In preparation to the introduction of TX interrupts and improved RX queue distribution, this commit introduces the concept of "queue vector". A queue vector represents a number of RX and/or TX queues, and an associated NAPI instance and interrupt. This commit currently only creates a single queue_vector, so there are no changes in behavior, but it paves the way for additional queue_vector in the next commits. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 223 ++++++++++++++++++++++++++--------- 1 file changed, 169 insertions(+), 54 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index af38a215f0e0..1bf327271cee 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -686,6 +686,7 @@ enum mvpp2_prs_l3_cast { #define MVPP22_ADDR_SPACE_SZ SZ_64K #define MVPP2_MAX_THREADS 8 +#define MVPP2_MAX_QVECS MVPP2_MAX_THREADS enum mvpp2_bm_type { MVPP2_BM_FREE, @@ -753,6 +754,18 @@ struct mvpp2_port_pcpu { struct tasklet_struct tx_done_tasklet; }; +struct mvpp2_queue_vector { + int irq; + struct napi_struct napi; + enum { MVPP2_QUEUE_VECTOR_SHARED, MVPP2_QUEUE_VECTOR_PRIVATE } type; + int sw_thread_id; + u16 sw_thread_mask; + int first_rxq; + int nrxqs; + u32 pending_cause_rx; + struct mvpp2_port *port; +}; + struct mvpp2_port { u8 id; @@ -761,8 +774,6 @@ struct mvpp2_port { */ int gop_id; - int irq; - struct mvpp2 *priv; /* Per-port registers' base address */ @@ -776,9 +787,6 @@ struct mvpp2_port { int pkt_size; - u32 pending_cause_rx; - struct napi_struct napi; - /* Per-CPU port control */ struct mvpp2_port_pcpu __percpu *pcpu; @@ -800,6 +808,9 @@ struct mvpp2_port { /* Index of first port's physical RXQ */ u8 first_rxq; + + struct mvpp2_queue_vector qvecs[MVPP2_MAX_QVECS]; + unsigned int nqvecs; }; /* The mvpp2_tx_desc and mvpp2_rx_desc structures describe the @@ -4121,22 +4132,40 @@ static int mvpp2_bm_update_mtu(struct net_device *dev, int mtu) static inline void mvpp2_interrupts_enable(struct mvpp2_port *port) { - int cpu, cpu_mask = 0; + int i, sw_thread_mask = 0; + + for (i = 0; i < port->nqvecs; i++) + sw_thread_mask |= port->qvecs[i].sw_thread_mask; - for_each_present_cpu(cpu) - cpu_mask |= 1 << cpu; mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), - MVPP2_ISR_ENABLE_INTERRUPT(cpu_mask)); + MVPP2_ISR_ENABLE_INTERRUPT(sw_thread_mask)); } static inline void mvpp2_interrupts_disable(struct mvpp2_port *port) { - int cpu, cpu_mask = 0; + int i, sw_thread_mask = 0; + + for (i = 0; i < port->nqvecs; i++) + sw_thread_mask |= port->qvecs[i].sw_thread_mask; + + mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), + MVPP2_ISR_DISABLE_INTERRUPT(sw_thread_mask)); +} + +static inline void mvpp2_qvec_interrupt_enable(struct mvpp2_queue_vector *qvec) +{ + struct mvpp2_port *port = qvec->port; - for_each_present_cpu(cpu) - cpu_mask |= 1 << cpu; mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), - MVPP2_ISR_DISABLE_INTERRUPT(cpu_mask)); + MVPP2_ISR_ENABLE_INTERRUPT(qvec->sw_thread_mask)); +} + +static inline void mvpp2_qvec_interrupt_disable(struct mvpp2_queue_vector *qvec) +{ + struct mvpp2_port *port = qvec->port; + + mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), + MVPP2_ISR_DISABLE_INTERRUPT(qvec->sw_thread_mask)); } /* Mask the current CPU's Rx/Tx interrupts @@ -5287,11 +5316,11 @@ err_cleanup: /* The callback for per-port interrupt */ static irqreturn_t mvpp2_isr(int irq, void *dev_id) { - struct mvpp2_port *port = (struct mvpp2_port *)dev_id; + struct mvpp2_queue_vector *qv = dev_id; - mvpp2_interrupts_disable(port); + mvpp2_qvec_interrupt_disable(qv); - napi_schedule(&port->napi); + napi_schedule(&qv->napi); return IRQ_HANDLED; } @@ -5494,8 +5523,8 @@ static u32 mvpp2_skb_tx_csum(struct mvpp2_port *port, struct sk_buff *skb) } /* Main rx processing */ -static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, - struct mvpp2_rx_queue *rxq) +static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, + int rx_todo, struct mvpp2_rx_queue *rxq) { struct net_device *dev = port->dev; int rx_received; @@ -5573,7 +5602,7 @@ err_drop_frame: skb->protocol = eth_type_trans(skb, dev); mvpp2_rx_csum(port, rx_status, skb); - napi_gro_receive(&port->napi, skb); + napi_gro_receive(napi, skb); } if (rcvd_pkts) { @@ -5782,8 +5811,11 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) u32 cause_rx_tx, cause_rx, cause_misc; int rx_done = 0; struct mvpp2_port *port = netdev_priv(napi->dev); + struct mvpp2_queue_vector *qv; int cpu = smp_processor_id(); + qv = container_of(napi, struct mvpp2_queue_vector, napi); + /* Rx/Tx cause register * * Bits 0-15: each bit indicates received packets on the Rx queue @@ -5812,7 +5844,7 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; /* Process RX packets */ - cause_rx |= port->pending_cause_rx; + cause_rx |= qv->pending_cause_rx; while (cause_rx && budget > 0) { int count; struct mvpp2_rx_queue *rxq; @@ -5821,7 +5853,7 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) if (!rxq) break; - count = mvpp2_rx(port, budget, rxq); + count = mvpp2_rx(port, napi, budget, rxq); rx_done += count; budget -= count; if (budget > 0) { @@ -5837,9 +5869,9 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) cause_rx = 0; napi_complete_done(napi, rx_done); - mvpp2_interrupts_enable(port); + mvpp2_qvec_interrupt_enable(qv); } - port->pending_cause_rx = cause_rx; + qv->pending_cause_rx = cause_rx; return rx_done; } @@ -5847,11 +5879,13 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) static void mvpp2_start_dev(struct mvpp2_port *port) { struct net_device *ndev = port->dev; + int i; mvpp2_gmac_max_rx_size_set(port); mvpp2_txp_max_tx_size_set(port); - napi_enable(&port->napi); + for (i = 0; i < port->nqvecs; i++) + napi_enable(&port->qvecs[i].napi); /* Enable interrupts on all CPUs */ mvpp2_interrupts_enable(port); @@ -5865,6 +5899,7 @@ static void mvpp2_start_dev(struct mvpp2_port *port) static void mvpp2_stop_dev(struct mvpp2_port *port) { struct net_device *ndev = port->dev; + int i; /* Stop new packets from arriving to RXQs */ mvpp2_ingress_disable(port); @@ -5874,7 +5909,8 @@ static void mvpp2_stop_dev(struct mvpp2_port *port) /* Disable interrupts on all CPUs */ mvpp2_interrupts_disable(port); - napi_disable(&port->napi); + for (i = 0; i < port->nqvecs; i++) + napi_disable(&port->qvecs[i].napi); netif_carrier_off(port->dev); netif_tx_stop_all_queues(port->dev); @@ -5960,6 +5996,40 @@ static void mvpp2_phy_disconnect(struct mvpp2_port *port) phy_disconnect(ndev->phydev); } +static int mvpp2_irqs_init(struct mvpp2_port *port) +{ + int err, i; + + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *qv = port->qvecs + i; + + err = request_irq(qv->irq, mvpp2_isr, 0, port->dev->name, qv); + if (err) + goto err; + } + + return 0; +err: + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *qv = port->qvecs + i; + + free_irq(qv->irq, qv); + } + + return err; +} + +static void mvpp2_irqs_deinit(struct mvpp2_port *port) +{ + int i; + + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *qv = port->qvecs + i; + + free_irq(qv->irq, qv); + } +} + static int mvpp2_open(struct net_device *dev) { struct mvpp2_port *port = netdev_priv(dev); @@ -6002,9 +6072,9 @@ static int mvpp2_open(struct net_device *dev) goto err_cleanup_rxqs; } - err = request_irq(port->irq, mvpp2_isr, 0, dev->name, port); + err = mvpp2_irqs_init(port); if (err) { - netdev_err(port->dev, "cannot request IRQ %d\n", port->irq); + netdev_err(port->dev, "cannot init IRQs\n"); goto err_cleanup_txqs; } @@ -6023,7 +6093,7 @@ static int mvpp2_open(struct net_device *dev) return 0; err_free_irq: - free_irq(port->irq, port); + mvpp2_irqs_deinit(port); err_cleanup_txqs: mvpp2_cleanup_txqs(port); err_cleanup_rxqs: @@ -6043,7 +6113,7 @@ static int mvpp2_stop(struct net_device *dev) /* Mask interrupts on all CPUs */ on_each_cpu(mvpp2_interrupts_mask, port, 1); - free_irq(port->irq, port); + mvpp2_irqs_deinit(port); for_each_present_cpu(cpu) { port_pcpu = per_cpu_ptr(port->pcpu, cpu); @@ -6361,6 +6431,66 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = { .set_link_ksettings = phy_ethtool_set_link_ksettings, }; +static int mvpp2_queue_vectors_init(struct mvpp2_port *port, + struct device_node *port_node) +{ + struct mvpp2_queue_vector *v = &port->qvecs[0]; + + v->first_rxq = 0; + v->nrxqs = port->nrxqs; + v->type = MVPP2_QUEUE_VECTOR_SHARED; + v->sw_thread_id = 0; + v->sw_thread_mask = *cpumask_bits(cpu_online_mask); + v->port = port; + v->irq = irq_of_parse_and_map(port_node, 0); + if (v->irq <= 0) + return -EINVAL; + netif_napi_add(port->dev, &v->napi, mvpp2_poll, + NAPI_POLL_WEIGHT); + + port->nqvecs = 1; + + return 0; +} + +static void mvpp2_queue_vectors_deinit(struct mvpp2_port *port) +{ + int i; + + for (i = 0; i < port->nqvecs; i++) + irq_dispose_mapping(port->qvecs[i].irq); +} + +/* Configure Rx queue group interrupt for this port */ +static void mvpp2_rx_irqs_setup(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + u32 val; + int i; + + if (priv->hw_version == MVPP21) { + mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(port->id), + port->nrxqs); + return; + } + + /* Handle the more complicated PPv2.2 case */ + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *qv = port->qvecs + i; + + if (!qv->nrxqs) + continue; + + val = qv->sw_thread_id; + val |= port->id << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET; + mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); + + val = qv->first_rxq; + val |= qv->nrxqs << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET; + mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); + } +} + /* Initialize port HW */ static int mvpp2_port_init(struct mvpp2_port *port) { @@ -6442,19 +6572,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) port->rxqs[queue] = rxq; } - /* Configure Rx queue group interrupt for this port */ - if (priv->hw_version == MVPP21) { - mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(port->id), - port->nrxqs); - } else { - u32 val; - - val = (port->id << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET); - mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); - - val = (port->nrxqs << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET); - mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); - } + mvpp2_rx_irqs_setup(port); /* Create Rx descriptor rings */ for (queue = 0; queue < port->nrxqs; queue++) { @@ -6545,14 +6663,13 @@ static int mvpp2_port_probe(struct platform_device *pdev, dev->ethtool_ops = &mvpp2_eth_tool_ops; port = netdev_priv(dev); + port->dev = dev; port->ntxqs = ntxqs; port->nrxqs = nrxqs; - port->irq = irq_of_parse_and_map(port_node, 0); - if (port->irq <= 0) { - err = -EINVAL; + err = mvpp2_queue_vectors_init(port, port_node); + if (err) goto err_free_netdev; - } if (of_property_read_bool(port_node, "marvell,loopback")) port->flags |= MVPP2_F_LOOPBACK; @@ -6572,14 +6689,14 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(port->base)) { err = PTR_ERR(port->base); - goto err_free_irq; + goto err_deinit_qvecs; } } else { if (of_property_read_u32(port_node, "gop-port-id", &port->gop_id)) { err = -EINVAL; dev_err(&pdev->dev, "missing gop-port-id value\n"); - goto err_free_irq; + goto err_deinit_qvecs; } port->base = priv->iface_base + MVPP22_GMAC_BASE(port->gop_id); @@ -6589,7 +6706,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->stats = netdev_alloc_pcpu_stats(struct mvpp2_pcpu_stats); if (!port->stats) { err = -ENOMEM; - goto err_free_irq; + goto err_deinit_qvecs; } dt_mac_addr = of_get_mac_address(port_node); @@ -6610,7 +6727,6 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->tx_ring_size = MVPP2_MAX_TXD; port->rx_ring_size = MVPP2_MAX_RXD; - port->dev = dev; SET_NETDEV_DEV(dev, &pdev->dev); err = mvpp2_port_init(port); @@ -6645,7 +6761,6 @@ static int mvpp2_port_probe(struct platform_device *pdev, (unsigned long)dev); } - netif_napi_add(dev, &port->napi, mvpp2_poll, NAPI_POLL_WEIGHT); features = NETIF_F_SG | NETIF_F_IP_CSUM; dev->features = features | NETIF_F_RXCSUM; dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO; @@ -6673,8 +6788,8 @@ err_free_txq_pcpu: free_percpu(port->txqs[i]->pcpu); err_free_stats: free_percpu(port->stats); -err_free_irq: - irq_dispose_mapping(port->irq); +err_deinit_qvecs: + mvpp2_queue_vectors_deinit(port); err_free_netdev: of_node_put(phy_node); free_netdev(dev); @@ -6692,7 +6807,7 @@ static void mvpp2_port_remove(struct mvpp2_port *port) free_percpu(port->stats); for (i = 0; i < port->ntxqs; i++) free_percpu(port->txqs[i]->pcpu); - irq_dispose_mapping(port->irq); + mvpp2_queue_vectors_deinit(port); free_netdev(port->dev); } -- cgit v1.2.3 From 213f428f5056affa627056a5953eec58e3adf5a3 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 3 Aug 2017 10:42:00 +0200 Subject: net: mvpp2: add support for TX interrupts and RX queue distribution modes This commit adds the support for two related features: - Support for TX interrupts, with one interrupt for each CPU - Support for different RX queue distribution modes MVPP2_QDIST_SINGLE_MODE where a single interrupt, shared by all CPUs, receives the RX events, and MVPP2_QDIST_MULTI_MODE, where the per-CPU interrupts used for TX events are also used for RX events. Since additional interrupts are needed, an update to the Device Tree binding is needed. However, backward compatibility is preserved with the old Device Tree binding, by gracefully degrading to the original behavior, with only one RX interrupt, and TX completion being handled by an hrtimer. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 275 +++++++++++++++++++++++++++++++---- 1 file changed, 246 insertions(+), 29 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 1bf327271cee..39bc8fbbdd65 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -120,6 +120,9 @@ #define MVPP2_TXQ_DESC_ADDR_REG 0x2084 #define MVPP2_TXQ_DESC_SIZE_REG 0x2088 #define MVPP2_TXQ_DESC_SIZE_MASK 0x3ff0 +#define MVPP2_TXQ_THRESH_REG 0x2094 +#define MVPP2_TXQ_THRESH_OFFSET 16 +#define MVPP2_TXQ_THRESH_MASK 0x3fff #define MVPP2_AGGR_TXQ_UPDATE_REG 0x2090 #define MVPP2_TXQ_INDEX_REG 0x2098 #define MVPP2_TXQ_PREF_BUF_REG 0x209c @@ -183,6 +186,9 @@ #define MVPP22_AXI_CODE_DOMAIN_SYSTEM 3 /* Interrupt Cause and Mask registers */ +#define MVPP2_ISR_TX_THRESHOLD_REG(port) (0x5140 + 4 * (port)) +#define MVPP2_MAX_ISR_TX_THRESHOLD 0xfffff0 + #define MVPP2_ISR_RX_THRESHOLD_REG(rxq) (0x5200 + 4 * (rxq)) #define MVPP2_MAX_ISR_RX_THRESHOLD 0xfffff0 #define MVPP21_ISR_RXQ_GROUP_REG(port) (0x5400 + 4 * (port)) @@ -206,6 +212,7 @@ #define MVPP2_ISR_RX_TX_CAUSE_REG(port) (0x5480 + 4 * (port)) #define MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK 0xffff #define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK 0xff0000 +#define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET 16 #define MVPP2_CAUSE_RX_FIFO_OVERRUN_MASK BIT(24) #define MVPP2_CAUSE_FCS_ERR_MASK BIT(25) #define MVPP2_CAUSE_TX_FIFO_UNDERRUN_MASK BIT(26) @@ -372,6 +379,7 @@ /* Coalescing */ #define MVPP2_TXDONE_COAL_PKTS_THRESH 15 #define MVPP2_TXDONE_HRTIMER_PERIOD_NS 1000000UL +#define MVPP2_TXDONE_COAL_USEC 1000 #define MVPP2_RX_COAL_PKTS 32 #define MVPP2_RX_COAL_USEC 100 @@ -811,6 +819,9 @@ struct mvpp2_port { struct mvpp2_queue_vector qvecs[MVPP2_MAX_QVECS]; unsigned int nqvecs; + bool has_tx_irqs; + + u32 tx_time_coal; }; /* The mvpp2_tx_desc and mvpp2_rx_desc structures describe the @@ -1076,6 +1087,15 @@ struct mvpp2_bm_pool { u32 port_map; }; +/* Queue modes */ +#define MVPP2_QDIST_SINGLE_MODE 0 +#define MVPP2_QDIST_MULTI_MODE 1 + +static int queue_mode = MVPP2_QDIST_SINGLE_MODE; + +module_param(queue_mode, int, 0444); +MODULE_PARM_DESC(queue_mode, "Set queue_mode (single=0, multi=1)"); + #define MVPP2_DRIVER_NAME "mvpp2" #define MVPP2_DRIVER_VERSION "1.0" @@ -4187,11 +4207,40 @@ static void mvpp2_interrupts_mask(void *arg) static void mvpp2_interrupts_unmask(void *arg) { struct mvpp2_port *port = arg; + u32 val; + + val = MVPP2_CAUSE_MISC_SUM_MASK | + MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; + if (port->has_tx_irqs) + val |= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; mvpp2_percpu_write(port->priv, smp_processor_id(), - MVPP2_ISR_RX_TX_MASK_REG(port->id), - (MVPP2_CAUSE_MISC_SUM_MASK | - MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK)); + MVPP2_ISR_RX_TX_MASK_REG(port->id), val); +} + +static void +mvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask) +{ + u32 val; + int i; + + if (port->priv->hw_version != MVPP22) + return; + + if (mask) + val = 0; + else + val = MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; + + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *v = port->qvecs + i; + + if (v->type != MVPP2_QUEUE_VECTOR_SHARED) + continue; + + mvpp2_percpu_write(port->priv, v->sw_thread_id, + MVPP2_ISR_RX_TX_MASK_REG(port->id), val); + } } /* Port configuration routines */ @@ -4812,6 +4861,23 @@ static void mvpp2_rx_pkts_coal_set(struct mvpp2_port *port, put_cpu(); } +/* For some reason in the LSP this is done on each CPU. Why ? */ +static void mvpp2_tx_pkts_coal_set(struct mvpp2_port *port, + struct mvpp2_tx_queue *txq) +{ + int cpu = get_cpu(); + u32 val; + + if (txq->done_pkts_coal > MVPP2_TXQ_THRESH_MASK) + txq->done_pkts_coal = MVPP2_TXQ_THRESH_MASK; + + val = (txq->done_pkts_coal << MVPP2_TXQ_THRESH_OFFSET); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_NUM_REG, txq->id); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_THRESH_REG, val); + + put_cpu(); +} + static u32 mvpp2_usec_to_cycles(u32 usec, unsigned long clk_hz) { u64 tmp = (u64)clk_hz * usec; @@ -4848,6 +4914,22 @@ static void mvpp2_rx_time_coal_set(struct mvpp2_port *port, mvpp2_write(port->priv, MVPP2_ISR_RX_THRESHOLD_REG(rxq->id), val); } +static void mvpp2_tx_time_coal_set(struct mvpp2_port *port) +{ + unsigned long freq = port->priv->tclk; + u32 val = mvpp2_usec_to_cycles(port->tx_time_coal, freq); + + if (val > MVPP2_MAX_ISR_TX_THRESHOLD) { + port->tx_time_coal = + mvpp2_cycles_to_usec(MVPP2_MAX_ISR_TX_THRESHOLD, freq); + + /* re-evaluate to get actual register value */ + val = mvpp2_usec_to_cycles(port->tx_time_coal, freq); + } + + mvpp2_write(port->priv, MVPP2_ISR_TX_THRESHOLD_REG(port->id), val); +} + /* Free Tx queue skbuffs */ static void mvpp2_txq_bufs_free(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, @@ -4906,7 +4988,8 @@ static void mvpp2_txq_done(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, netif_tx_wake_queue(nq); } -static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause) +static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause, + int cpu) { struct mvpp2_tx_queue *txq; struct mvpp2_txq_pcpu *txq_pcpu; @@ -4917,7 +5000,7 @@ static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause) if (!txq) break; - txq_pcpu = this_cpu_ptr(txq->pcpu); + txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); if (txq_pcpu->count) { mvpp2_txq_done(port, txq, txq_pcpu); @@ -5305,6 +5388,14 @@ static int mvpp2_setup_txqs(struct mvpp2_port *port) goto err_cleanup; } + if (port->has_tx_irqs) { + mvpp2_tx_time_coal_set(port); + for (queue = 0; queue < port->ntxqs; queue++) { + txq = port->txqs[queue]; + mvpp2_tx_pkts_coal_set(port, txq); + } + } + on_each_cpu(mvpp2_txq_sent_counter_clear, port, 1); return 0; @@ -5411,7 +5502,7 @@ static void mvpp2_tx_proc_cb(unsigned long data) /* Process all the Tx queues */ cause = (1 << port->ntxqs) - 1; - tx_todo = mvpp2_tx_done(port, cause); + tx_todo = mvpp2_tx_done(port, cause, smp_processor_id()); /* Set the timer in case not all the packets were processed */ if (tx_todo) @@ -5787,7 +5878,8 @@ out: mvpp2_txq_done(port, txq, txq_pcpu); /* Set the timer in case not all frags were processed */ - if (txq_pcpu->count <= frags && txq_pcpu->count > 0) { + if (!port->has_tx_irqs && txq_pcpu->count <= frags && + txq_pcpu->count > 0) { struct mvpp2_port_pcpu *port_pcpu = this_cpu_ptr(port->pcpu); mvpp2_timer_set(port_pcpu); @@ -5808,7 +5900,7 @@ static inline void mvpp2_cause_error(struct net_device *dev, int cause) static int mvpp2_poll(struct napi_struct *napi, int budget) { - u32 cause_rx_tx, cause_rx, cause_misc; + u32 cause_rx_tx, cause_rx, cause_tx, cause_misc; int rx_done = 0; struct mvpp2_port *port = netdev_priv(napi->dev); struct mvpp2_queue_vector *qv; @@ -5826,11 +5918,10 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) * * Each CPU has its own Rx/Tx cause register */ - cause_rx_tx = mvpp2_percpu_read(port->priv, cpu, + cause_rx_tx = mvpp2_percpu_read(port->priv, qv->sw_thread_id, MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); - cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; - cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; + cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; if (cause_misc) { mvpp2_cause_error(port->dev, cause_misc); @@ -5841,9 +5932,15 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK); } - cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; + cause_tx = cause_rx_tx & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; + if (cause_tx) { + cause_tx >>= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET; + mvpp2_tx_done(port, cause_tx, qv->sw_thread_id); + } /* Process RX packets */ + cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; + cause_rx <<= qv->first_rxq; cause_rx |= qv->pending_cause_rx; while (cause_rx && budget > 0) { int count; @@ -6006,6 +6103,10 @@ static int mvpp2_irqs_init(struct mvpp2_port *port) err = request_irq(qv->irq, mvpp2_isr, 0, port->dev->name, qv); if (err) goto err; + + if (qv->type == MVPP2_QUEUE_VECTOR_PRIVATE) + irq_set_affinity_hint(qv->irq, + cpumask_of(qv->sw_thread_id)); } return 0; @@ -6013,6 +6114,7 @@ err: for (i = 0; i < port->nqvecs; i++) { struct mvpp2_queue_vector *qv = port->qvecs + i; + irq_set_affinity_hint(qv->irq, NULL); free_irq(qv->irq, qv); } @@ -6026,6 +6128,7 @@ static void mvpp2_irqs_deinit(struct mvpp2_port *port) for (i = 0; i < port->nqvecs; i++) { struct mvpp2_queue_vector *qv = port->qvecs + i; + irq_set_affinity_hint(qv->irq, NULL); free_irq(qv->irq, qv); } } @@ -6087,6 +6190,7 @@ static int mvpp2_open(struct net_device *dev) /* Unmask interrupts on all CPUs */ on_each_cpu(mvpp2_interrupts_unmask, port, 1); + mvpp2_shared_interrupt_mask_unmask(port, false); mvpp2_start_dev(port); @@ -6112,14 +6216,17 @@ static int mvpp2_stop(struct net_device *dev) /* Mask interrupts on all CPUs */ on_each_cpu(mvpp2_interrupts_mask, port, 1); + mvpp2_shared_interrupt_mask_unmask(port, true); mvpp2_irqs_deinit(port); - for_each_present_cpu(cpu) { - port_pcpu = per_cpu_ptr(port->pcpu, cpu); + if (!port->has_tx_irqs) { + for_each_present_cpu(cpu) { + port_pcpu = per_cpu_ptr(port->pcpu, cpu); - hrtimer_cancel(&port_pcpu->tx_done_timer); - port_pcpu->timer_scheduled = false; - tasklet_kill(&port_pcpu->tx_done_tasklet); + hrtimer_cancel(&port_pcpu->tx_done_timer); + port_pcpu->timer_scheduled = false; + tasklet_kill(&port_pcpu->tx_done_tasklet); + } } mvpp2_cleanup_rxqs(port); mvpp2_cleanup_txqs(port); @@ -6303,10 +6410,18 @@ static int mvpp2_ethtool_set_coalesce(struct net_device *dev, mvpp2_rx_time_coal_set(port, rxq); } + if (port->has_tx_irqs) { + port->tx_time_coal = c->tx_coalesce_usecs; + mvpp2_tx_time_coal_set(port); + } + for (queue = 0; queue < port->ntxqs; queue++) { struct mvpp2_tx_queue *txq = port->txqs[queue]; txq->done_pkts_coal = c->tx_max_coalesced_frames; + + if (port->has_tx_irqs) + mvpp2_tx_pkts_coal_set(port, txq); } return 0; @@ -6431,8 +6546,11 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = { .set_link_ksettings = phy_ethtool_set_link_ksettings, }; -static int mvpp2_queue_vectors_init(struct mvpp2_port *port, - struct device_node *port_node) +/* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that + * had a single IRQ defined per-port. + */ +static int mvpp2_simple_queue_vectors_init(struct mvpp2_port *port, + struct device_node *port_node) { struct mvpp2_queue_vector *v = &port->qvecs[0]; @@ -6453,6 +6571,66 @@ static int mvpp2_queue_vectors_init(struct mvpp2_port *port, return 0; } +static int mvpp2_multi_queue_vectors_init(struct mvpp2_port *port, + struct device_node *port_node) +{ + struct mvpp2_queue_vector *v; + int i, ret; + + port->nqvecs = num_possible_cpus(); + if (queue_mode == MVPP2_QDIST_SINGLE_MODE) + port->nqvecs += 1; + + for (i = 0; i < port->nqvecs; i++) { + char irqname[16]; + + v = port->qvecs + i; + + v->port = port; + v->type = MVPP2_QUEUE_VECTOR_PRIVATE; + v->sw_thread_id = i; + v->sw_thread_mask = BIT(i); + + snprintf(irqname, sizeof(irqname), "tx-cpu%d", i); + + if (queue_mode == MVPP2_QDIST_MULTI_MODE) { + v->first_rxq = i * MVPP2_DEFAULT_RXQ; + v->nrxqs = MVPP2_DEFAULT_RXQ; + } else if (queue_mode == MVPP2_QDIST_SINGLE_MODE && + i == (port->nqvecs - 1)) { + v->first_rxq = 0; + v->nrxqs = port->nrxqs; + v->type = MVPP2_QUEUE_VECTOR_SHARED; + strncpy(irqname, "rx-shared", sizeof(irqname)); + } + + v->irq = of_irq_get_byname(port_node, irqname); + if (v->irq <= 0) { + ret = -EINVAL; + goto err; + } + + netif_napi_add(port->dev, &v->napi, mvpp2_poll, + NAPI_POLL_WEIGHT); + } + + return 0; + +err: + for (i = 0; i < port->nqvecs; i++) + irq_dispose_mapping(port->qvecs[i].irq); + return ret; +} + +static int mvpp2_queue_vectors_init(struct mvpp2_port *port, + struct device_node *port_node) +{ + if (port->has_tx_irqs) + return mvpp2_multi_queue_vectors_init(port, port_node); + else + return mvpp2_simple_queue_vectors_init(port, port_node); +} + static void mvpp2_queue_vectors_deinit(struct mvpp2_port *port) { int i; @@ -6512,6 +6690,8 @@ static int mvpp2_port_init(struct mvpp2_port *port) mvpp2_egress_disable(port); mvpp2_port_disable(port); + port->tx_time_coal = MVPP2_TXDONE_COAL_USEC; + port->txqs = devm_kcalloc(dev, port->ntxqs, sizeof(*port->txqs), GFP_KERNEL); if (!port->txqs) @@ -6611,6 +6791,30 @@ err_free_percpu: return err; } +/* Checks if the port DT description has the TX interrupts + * described. On PPv2.1, there are no such interrupts. On PPv2.2, + * there are available, but we need to keep support for old DTs. + */ +static bool mvpp2_port_has_tx_irqs(struct mvpp2 *priv, + struct device_node *port_node) +{ + char *irqs[5] = { "rx-shared", "tx-cpu0", "tx-cpu1", + "tx-cpu2", "tx-cpu3" }; + int ret, i; + + if (priv->hw_version == MVPP21) + return false; + + for (i = 0; i < 5; i++) { + ret = of_property_match_string(port_node, "interrupt-names", + irqs[i]); + if (ret < 0) + return false; + } + + return true; +} + /* Ports initialization */ static int mvpp2_port_probe(struct platform_device *pdev, struct device_node *port_node, @@ -6625,13 +6829,22 @@ static int mvpp2_port_probe(struct platform_device *pdev, const char *mac_from; char hw_mac_addr[ETH_ALEN]; unsigned int ntxqs, nrxqs; + bool has_tx_irqs; u32 id; int features; int phy_mode; int err, i, cpu; + has_tx_irqs = mvpp2_port_has_tx_irqs(priv, port_node); + + if (!has_tx_irqs) + queue_mode = MVPP2_QDIST_SINGLE_MODE; + ntxqs = MVPP2_MAX_TXQ; - nrxqs = MVPP2_DEFAULT_RXQ; + if (priv->hw_version == MVPP22 && queue_mode == MVPP2_QDIST_MULTI_MODE) + nrxqs = MVPP2_DEFAULT_RXQ * num_possible_cpus(); + else + nrxqs = MVPP2_DEFAULT_RXQ; dev = alloc_etherdev_mqs(sizeof(*port), ntxqs, nrxqs); if (!dev) @@ -6666,6 +6879,8 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->dev = dev; port->ntxqs = ntxqs; port->nrxqs = nrxqs; + port->priv = priv; + port->has_tx_irqs = has_tx_irqs; err = mvpp2_queue_vectors_init(port, port_node); if (err) @@ -6674,7 +6889,6 @@ static int mvpp2_port_probe(struct platform_device *pdev, if (of_property_read_bool(port_node, "marvell,loopback")) port->flags |= MVPP2_F_LOOPBACK; - port->priv = priv; port->id = id; if (priv->hw_version == MVPP21) port->first_rxq = port->id * port->nrxqs; @@ -6749,16 +6963,19 @@ static int mvpp2_port_probe(struct platform_device *pdev, goto err_free_txq_pcpu; } - for_each_present_cpu(cpu) { - port_pcpu = per_cpu_ptr(port->pcpu, cpu); + if (!port->has_tx_irqs) { + for_each_present_cpu(cpu) { + port_pcpu = per_cpu_ptr(port->pcpu, cpu); - hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL_PINNED); - port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb; - port_pcpu->timer_scheduled = false; + hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_PINNED); + port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb; + port_pcpu->timer_scheduled = false; - tasklet_init(&port_pcpu->tx_done_tasklet, mvpp2_tx_proc_cb, - (unsigned long)dev); + tasklet_init(&port_pcpu->tx_done_tasklet, + mvpp2_tx_proc_cb, + (unsigned long)dev); + } } features = NETIF_F_SG | NETIF_F_IP_CSUM; -- cgit v1.2.3 From 04b1d4e50e82536c12da00ee04a77510c459c844 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:11 +0200 Subject: net: core: Make the FIB notification chain generic The FIB notification chain is currently soley used by IPv4 code. However, we're going to introduce IPv6 FIB offload support, which requires these notification as well. As explained in commit c3852ef7f2f8 ("ipv4: fib: Replay events when registering FIB notifier"), upon registration to the chain, the callee receives a full dump of the FIB tables and rules by traversing all the net namespaces. The integrity of the dump is ensured by a per-namespace sequence counter that is incremented whenever a change to the tables or rules occurs. In order to allow more address families to use the chain, each family is expected to register its fib_notifier_ops in its pernet init. These operations allow the common code to read the family's sequence counter as well as dump its tables and rules in the given net namespace. Additionally, a 'family' parameter is added to sent notifications, so that listeners could distinguish between the different families. Implement the common code that allows listeners to register to the chain and for address families to register their fib_notifier_ops. Subsequent patches will implement these operations in IPv6. In the future, ipmr and ip6mr will be extended to provide these notifications as well. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 1 + drivers/net/ethernet/rocker/rocker_main.c | 1 + include/net/fib_notifier.h | 44 ++++++ include/net/ip_fib.h | 30 +--- include/net/net_namespace.h | 1 + include/net/netns/ipv4.h | 1 + net/core/Makefile | 3 +- net/core/fib_notifier.c | 164 +++++++++++++++++++++ net/ipv4/fib_frontend.c | 17 ++- net/ipv4/fib_notifier.c | 94 +++++------- net/ipv4/fib_rules.c | 5 +- net/ipv4/fib_semantics.c | 9 +- net/ipv4/fib_trie.c | 5 +- 13 files changed, 282 insertions(+), 93 deletions(-) create mode 100644 include/net/fib_notifier.h create mode 100644 net/core/fib_notifier.c (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2f03c7e71584..b79f9b67f285 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "spectrum.h" #include "core.h" diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index b1e5c07099fa..ef38c1a41bdd 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include diff --git a/include/net/fib_notifier.h b/include/net/fib_notifier.h new file mode 100644 index 000000000000..241475224f74 --- /dev/null +++ b/include/net/fib_notifier.h @@ -0,0 +1,44 @@ +#ifndef __NET_FIB_NOTIFIER_H +#define __NET_FIB_NOTIFIER_H + +#include +#include +#include + +struct fib_notifier_info { + struct net *net; + int family; +}; + +enum fib_event_type { + FIB_EVENT_ENTRY_REPLACE, + FIB_EVENT_ENTRY_APPEND, + FIB_EVENT_ENTRY_ADD, + FIB_EVENT_ENTRY_DEL, + FIB_EVENT_RULE_ADD, + FIB_EVENT_RULE_DEL, + FIB_EVENT_NH_ADD, + FIB_EVENT_NH_DEL, +}; + +struct fib_notifier_ops { + int family; + struct list_head list; + unsigned int (*fib_seq_read)(struct net *net); + int (*fib_dump)(struct net *net, struct notifier_block *nb); + struct rcu_head rcu; +}; + +int call_fib_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info); +int call_fib_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info); +int register_fib_notifier(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb)); +int unregister_fib_notifier(struct notifier_block *nb); +struct fib_notifier_ops * +fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net); +void fib_notifier_ops_unregister(struct fib_notifier_ops *ops); + +#endif diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index ef8992d49bc3..c0295c3ec5f3 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -188,10 +189,6 @@ __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh); #define FIB_RES_PREFSRC(net, res) ((res).fi->fib_prefsrc ? : \ FIB_RES_SADDR(net, res)) -struct fib_notifier_info { - struct net *net; -}; - struct fib_entry_notifier_info { struct fib_notifier_info info; /* must be first */ u32 dst; @@ -212,25 +209,14 @@ struct fib_nh_notifier_info { struct fib_nh *fib_nh; }; -enum fib_event_type { - FIB_EVENT_ENTRY_REPLACE, - FIB_EVENT_ENTRY_APPEND, - FIB_EVENT_ENTRY_ADD, - FIB_EVENT_ENTRY_DEL, - FIB_EVENT_RULE_ADD, - FIB_EVENT_RULE_DEL, - FIB_EVENT_NH_ADD, - FIB_EVENT_NH_DEL, -}; - -int register_fib_notifier(struct notifier_block *nb, - void (*cb)(struct notifier_block *nb)); -int unregister_fib_notifier(struct notifier_block *nb); -int call_fib_notifier(struct notifier_block *nb, struct net *net, - enum fib_event_type event_type, - struct fib_notifier_info *info); -int call_fib_notifiers(struct net *net, enum fib_event_type event_type, +int call_fib4_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, struct fib_notifier_info *info); +int call_fib4_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info); + +int __net_init fib4_notifier_init(struct net *net); +void __net_exit fib4_notifier_exit(struct net *net); void fib_notify(struct net *net, struct notifier_block *nb); #ifdef CONFIG_IP_MULTIPLE_TABLES diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 1c401bd4c2e0..57faa375eab9 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -88,6 +88,7 @@ struct net { /* core fib_rules */ struct list_head rules_ops; + struct list_head fib_notifier_ops; /* protected by net_mutex */ struct net_device *loopback_dev; /* The loopback */ struct netns_core core; diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 9a14a0850b0e..20d061c805e3 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -159,6 +159,7 @@ struct netns_ipv4 { int sysctl_fib_multipath_hash_policy; #endif + struct fib_notifier_ops *notifier_ops; unsigned int fib_seq; /* protected by rtnl_mutex */ atomic_t rt_genid; diff --git a/net/core/Makefile b/net/core/Makefile index d501c4278015..56d771a887b6 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -9,7 +9,8 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ - sock_diag.o dev_ioctl.o tso.o sock_reuseport.o + sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \ + fib_notifier.o obj-y += net-sysfs.o obj-$(CONFIG_PROC_FS) += net-procfs.o diff --git a/net/core/fib_notifier.c b/net/core/fib_notifier.c new file mode 100644 index 000000000000..292aab83702f --- /dev/null +++ b/net/core/fib_notifier.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include + +static ATOMIC_NOTIFIER_HEAD(fib_chain); + +int call_fib_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info) +{ + info->net = net; + return nb->notifier_call(nb, event_type, info); +} +EXPORT_SYMBOL(call_fib_notifier); + +int call_fib_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info) +{ + info->net = net; + return atomic_notifier_call_chain(&fib_chain, event_type, info); +} +EXPORT_SYMBOL(call_fib_notifiers); + +static unsigned int fib_seq_sum(void) +{ + struct fib_notifier_ops *ops; + unsigned int fib_seq = 0; + struct net *net; + + rtnl_lock(); + for_each_net(net) { + list_for_each_entry(ops, &net->fib_notifier_ops, list) + fib_seq += ops->fib_seq_read(net); + } + rtnl_unlock(); + + return fib_seq; +} + +static int fib_net_dump(struct net *net, struct notifier_block *nb) +{ + struct fib_notifier_ops *ops; + + list_for_each_entry_rcu(ops, &net->fib_notifier_ops, list) { + int err = ops->fib_dump(net, nb); + + if (err) + return err; + } + + return 0; +} + +static bool fib_dump_is_consistent(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb), + unsigned int fib_seq) +{ + atomic_notifier_chain_register(&fib_chain, nb); + if (fib_seq == fib_seq_sum()) + return true; + atomic_notifier_chain_unregister(&fib_chain, nb); + if (cb) + cb(nb); + return false; +} + +#define FIB_DUMP_MAX_RETRIES 5 +int register_fib_notifier(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb)) +{ + int retries = 0; + int err; + + do { + unsigned int fib_seq = fib_seq_sum(); + struct net *net; + + rcu_read_lock(); + for_each_net_rcu(net) { + err = fib_net_dump(net, nb); + if (err) + goto err_fib_net_dump; + } + rcu_read_unlock(); + + if (fib_dump_is_consistent(nb, cb, fib_seq)) + return 0; + } while (++retries < FIB_DUMP_MAX_RETRIES); + + return -EBUSY; + +err_fib_net_dump: + rcu_read_unlock(); + return err; +} +EXPORT_SYMBOL(register_fib_notifier); + +int unregister_fib_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&fib_chain, nb); +} +EXPORT_SYMBOL(unregister_fib_notifier); + +static int __fib_notifier_ops_register(struct fib_notifier_ops *ops, + struct net *net) +{ + struct fib_notifier_ops *o; + + list_for_each_entry(o, &net->fib_notifier_ops, list) + if (ops->family == o->family) + return -EEXIST; + list_add_tail_rcu(&ops->list, &net->fib_notifier_ops); + return 0; +} + +struct fib_notifier_ops * +fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net) +{ + struct fib_notifier_ops *ops; + int err; + + ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL); + if (!ops) + return ERR_PTR(-ENOMEM); + + err = __fib_notifier_ops_register(ops, net); + if (err) + goto err_register; + + return ops; + +err_register: + kfree(ops); + return ERR_PTR(err); +} +EXPORT_SYMBOL(fib_notifier_ops_register); + +void fib_notifier_ops_unregister(struct fib_notifier_ops *ops) +{ + list_del_rcu(&ops->list); + kfree_rcu(ops, rcu); +} +EXPORT_SYMBOL(fib_notifier_ops_unregister); + +static int __net_init fib_notifier_net_init(struct net *net) +{ + INIT_LIST_HEAD(&net->fib_notifier_ops); + return 0; +} + +static struct pernet_operations fib_notifier_net_ops = { + .init = fib_notifier_net_init, +}; + +static int __init fib_notifier_init(void) +{ + return register_pernet_subsys(&fib_notifier_net_ops); +} + +subsys_initcall(fib_notifier_init); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 044d2a159a3c..2cba559f14df 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1247,22 +1247,28 @@ static int __net_init ip_fib_net_init(struct net *net) int err; size_t size = sizeof(struct hlist_head) * FIB_TABLE_HASHSZ; - net->ipv4.fib_seq = 0; + err = fib4_notifier_init(net); + if (err) + return err; /* Avoid false sharing : Use at least a full cache line */ size = max_t(size_t, size, L1_CACHE_BYTES); net->ipv4.fib_table_hash = kzalloc(size, GFP_KERNEL); - if (!net->ipv4.fib_table_hash) - return -ENOMEM; + if (!net->ipv4.fib_table_hash) { + err = -ENOMEM; + goto err_table_hash_alloc; + } err = fib4_rules_init(net); if (err < 0) - goto fail; + goto err_rules_init; return 0; -fail: +err_rules_init: kfree(net->ipv4.fib_table_hash); +err_table_hash_alloc: + fib4_notifier_exit(net); return err; } @@ -1292,6 +1298,7 @@ static void ip_fib_net_exit(struct net *net) #endif rtnl_unlock(); kfree(net->ipv4.fib_table_hash); + fib4_notifier_exit(net); } static int __net_init fib_net_init(struct net *net) diff --git a/net/ipv4/fib_notifier.c b/net/ipv4/fib_notifier.c index e0714d975947..7cf1954bbadc 100644 --- a/net/ipv4/fib_notifier.c +++ b/net/ipv4/fib_notifier.c @@ -1,86 +1,66 @@ #include #include -#include +#include #include #include +#include #include #include -static ATOMIC_NOTIFIER_HEAD(fib_chain); - -int call_fib_notifier(struct notifier_block *nb, struct net *net, - enum fib_event_type event_type, - struct fib_notifier_info *info) +int call_fib4_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info) { - info->net = net; - return nb->notifier_call(nb, event_type, info); + info->family = AF_INET; + return call_fib_notifier(nb, net, event_type, info); } -int call_fib_notifiers(struct net *net, enum fib_event_type event_type, - struct fib_notifier_info *info) +int call_fib4_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info) { + ASSERT_RTNL(); + + info->family = AF_INET; net->ipv4.fib_seq++; - info->net = net; - return atomic_notifier_call_chain(&fib_chain, event_type, info); + return call_fib_notifiers(net, event_type, info); } -static unsigned int fib_seq_sum(void) +static unsigned int fib4_seq_read(struct net *net) { - unsigned int fib_seq = 0; - struct net *net; + ASSERT_RTNL(); - rtnl_lock(); - for_each_net(net) - fib_seq += net->ipv4.fib_seq; - rtnl_unlock(); - - return fib_seq; + return net->ipv4.fib_seq; } -static bool fib_dump_is_consistent(struct notifier_block *nb, - void (*cb)(struct notifier_block *nb), - unsigned int fib_seq) +static int fib4_dump(struct net *net, struct notifier_block *nb) { - atomic_notifier_chain_register(&fib_chain, nb); - if (fib_seq == fib_seq_sum()) - return true; - atomic_notifier_chain_unregister(&fib_chain, nb); - if (cb) - cb(nb); - return false; + fib_rules_notify(net, nb); + fib_notify(net, nb); + + return 0; } -#define FIB_DUMP_MAX_RETRIES 5 -int register_fib_notifier(struct notifier_block *nb, - void (*cb)(struct notifier_block *nb)) -{ - int retries = 0; +static const struct fib_notifier_ops fib4_notifier_ops_template = { + .family = AF_INET, + .fib_seq_read = fib4_seq_read, + .fib_dump = fib4_dump, +}; - do { - unsigned int fib_seq = fib_seq_sum(); - struct net *net; +int __net_init fib4_notifier_init(struct net *net) +{ + struct fib_notifier_ops *ops; - /* Mutex semantics guarantee that every change done to - * FIB tries before we read the change sequence counter - * is now visible to us. - */ - rcu_read_lock(); - for_each_net_rcu(net) { - fib_rules_notify(net, nb); - fib_notify(net, nb); - } - rcu_read_unlock(); + net->ipv4.fib_seq = 0; - if (fib_dump_is_consistent(nb, cb, fib_seq)) - return 0; - } while (++retries < FIB_DUMP_MAX_RETRIES); + ops = fib_notifier_ops_register(&fib4_notifier_ops_template, net); + if (IS_ERR(ops)) + return PTR_ERR(ops); + net->ipv4.notifier_ops = ops; - return -EBUSY; + return 0; } -EXPORT_SYMBOL(register_fib_notifier); -int unregister_fib_notifier(struct notifier_block *nb) +void __net_exit fib4_notifier_exit(struct net *net) { - return atomic_notifier_chain_unregister(&fib_chain, nb); + fib_notifier_ops_unregister(net->ipv4.notifier_ops); } -EXPORT_SYMBOL(unregister_fib_notifier); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 778ecf977eb2..acdbf5a24ac9 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -32,6 +32,7 @@ #include #include #include +#include struct fib4_rule { struct fib_rule common; @@ -193,7 +194,7 @@ static int call_fib_rule_notifier(struct notifier_block *nb, struct net *net, .rule = rule, }; - return call_fib_notifier(nb, net, event_type, &info.info); + return call_fib4_notifier(nb, net, event_type, &info.info); } static int call_fib_rule_notifiers(struct net *net, @@ -204,7 +205,7 @@ static int call_fib_rule_notifiers(struct net *net, .rule = rule, }; - return call_fib_notifiers(net, event_type, &info.info); + return call_fib4_notifiers(net, event_type, &info.info); } /* Called with rcu_read_lock() */ diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index f62dc2463280..632b454ce77c 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "fib_lookup.h" @@ -1451,14 +1452,14 @@ static int call_fib_nh_notifiers(struct fib_nh *fib_nh, if (IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && fib_nh->nh_flags & RTNH_F_LINKDOWN) break; - return call_fib_notifiers(dev_net(fib_nh->nh_dev), event_type, - &info.info); + return call_fib4_notifiers(dev_net(fib_nh->nh_dev), event_type, + &info.info); case FIB_EVENT_NH_DEL: if ((in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && fib_nh->nh_flags & RTNH_F_LINKDOWN) || (fib_nh->nh_flags & RTNH_F_DEAD)) - return call_fib_notifiers(dev_net(fib_nh->nh_dev), - event_type, &info.info); + return call_fib4_notifiers(dev_net(fib_nh->nh_dev), + event_type, &info.info); default: break; } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 64668c69dda6..1a6ffb0dab9c 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include "fib_lookup.h" @@ -97,7 +98,7 @@ static int call_fib_entry_notifier(struct notifier_block *nb, struct net *net, .type = type, .tb_id = tb_id, }; - return call_fib_notifier(nb, net, event_type, &info.info); + return call_fib4_notifier(nb, net, event_type, &info.info); } static int call_fib_entry_notifiers(struct net *net, @@ -113,7 +114,7 @@ static int call_fib_entry_notifiers(struct net *net, .type = type, .tb_id = tb_id, }; - return call_fib_notifiers(net, event_type, &info.info); + return call_fib4_notifiers(net, event_type, &info.info); } #define MAX_STAT_DEPTH 32 -- cgit v1.2.3 From 64e5e8252d69c68ae76258328ac7e5d2e5e923b0 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:12 +0200 Subject: mlxsw: spectrum_router: Ignore address families other than IPv4 We're about to add IPv6 notifications in the FIB notification chain, but the driver currently doesn't support these, so ignore them. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index b79f9b67f285..78c19512250d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3093,7 +3093,7 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, struct fib_notifier_info *info = ptr; struct mlxsw_sp_router *router; - if (!net_eq(info->net, &init_net)) + if (!net_eq(info->net, &init_net) || info->family != AF_INET) return NOTIFY_DONE; fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); -- cgit v1.2.3 From d371ac1e1b1124e22bc9a82a5d170ea721a73bef Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:13 +0200 Subject: rocker: Ignore address families other than IPv4 As in previous patch, ignore IPv6 notifications since the driver doesn't support these. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_main.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index ef38c1a41bdd..fc8f8bdf6579 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2192,6 +2192,10 @@ static int rocker_router_fib_event(struct notifier_block *nb, { struct rocker *rocker = container_of(nb, struct rocker, fib_nb); struct rocker_fib_event_work *fib_work; + struct fib_notifier_info *info = ptr; + + if (info->family != AF_INET) + return NOTIFY_DONE; fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); if (WARN_ON(!fib_work)) -- cgit v1.2.3 From 66a5763ac180d43f4a16770791669dc1e085cd5d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:26 +0200 Subject: mlxsw: spectrum_router: Demultiplex FIB event based on family The FIB notification block currently only handles IPv4 events, but we want to start handling IPv6 events soon, so lay the groundwork now. Do that by preparing the work item and process it according to the notified address family. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 65 +++++++++++++++------- 1 file changed, 44 insertions(+), 21 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 78c19512250d..166ecf5d58f1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3040,7 +3040,7 @@ struct mlxsw_sp_fib_event_work { unsigned long event; }; -static void mlxsw_sp_router_fib_event_work(struct work_struct *work) +static void mlxsw_sp_router_fib4_event_work(struct work_struct *work) { struct mlxsw_sp_fib_event_work *fib_work = container_of(work, struct mlxsw_sp_fib_event_work, work); @@ -3085,6 +3085,42 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) kfree(fib_work); } +static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) +{ +} + +static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work, + struct fib_notifier_info *info) +{ + switch (fib_work->event) { + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ + case FIB_EVENT_ENTRY_APPEND: /* fall through */ + case FIB_EVENT_ENTRY_ADD: /* fall through */ + case FIB_EVENT_ENTRY_DEL: + memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info)); + /* Take referece on fib_info to prevent it from being + * freed while work is queued. Release it afterwards. + */ + fib_info_hold(fib_work->fen_info.fi); + break; + case FIB_EVENT_RULE_ADD: /* fall through */ + case FIB_EVENT_RULE_DEL: + memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info)); + fib_rule_get(fib_work->fr_info.rule); + break; + case FIB_EVENT_NH_ADD: /* fall through */ + case FIB_EVENT_NH_DEL: + memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info)); + fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent); + break; + } +} + +static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work, + struct fib_notifier_info *info) +{ +} + /* Called with rcu_read_lock() */ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, unsigned long event, void *ptr) @@ -3100,31 +3136,18 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, if (WARN_ON(!fib_work)) return NOTIFY_BAD; - INIT_WORK(&fib_work->work, mlxsw_sp_router_fib_event_work); router = container_of(nb, struct mlxsw_sp_router, fib_nb); fib_work->mlxsw_sp = router->mlxsw_sp; fib_work->event = event; - switch (event) { - case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_APPEND: /* fall through */ - case FIB_EVENT_ENTRY_ADD: /* fall through */ - case FIB_EVENT_ENTRY_DEL: - memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info)); - /* Take referece on fib_info to prevent it from being - * freed while work is queued. Release it afterwards. - */ - fib_info_hold(fib_work->fen_info.fi); + switch (info->family) { + case AF_INET: + INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work); + mlxsw_sp_router_fib4_event(fib_work, info); break; - case FIB_EVENT_RULE_ADD: /* fall through */ - case FIB_EVENT_RULE_DEL: - memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info)); - fib_rule_get(fib_work->fr_info.rule); - break; - case FIB_EVENT_NH_ADD: /* fall through */ - case FIB_EVENT_NH_DEL: - memcpy(&fib_work->fnh_info, ptr, sizeof(fib_work->fnh_info)); - fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent); + case AF_INET6: + INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work); + mlxsw_sp_router_fib6_event(fib_work, info); break; } -- cgit v1.2.3 From 583419fdf22bd2fc39e49520b29960f206b7ab44 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:27 +0200 Subject: mlxsw: spectrum_router: Sanitize IPv6 FIB rules We only allow FIB offload in the presence of default rules or an l3mdev rule. In a similar fashion to IPv4 FIB rules, sanitize IPv6 rules. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 166ecf5d58f1..6c7fc6a66aca 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -3087,6 +3088,23 @@ static void mlxsw_sp_router_fib4_event_work(struct work_struct *work) static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) { + struct mlxsw_sp_fib_event_work *fib_work = + container_of(work, struct mlxsw_sp_fib_event_work, work); + struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; + struct fib_rule *rule; + + rtnl_lock(); + switch (fib_work->event) { + case FIB_EVENT_RULE_ADD: /* fall through */ + case FIB_EVENT_RULE_DEL: + rule = fib_work->fr_info.rule; + if (!fib6_rule_default(rule) && !rule->l3mdev) + mlxsw_sp_router_fib_abort(mlxsw_sp); + fib_rule_put(rule); + break; + } + rtnl_unlock(); + kfree(fib_work); } static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work, @@ -3119,6 +3137,13 @@ static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work, static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work, struct fib_notifier_info *info) { + switch (fib_work->event) { + case FIB_EVENT_RULE_ADD: /* fall through */ + case FIB_EVENT_RULE_DEL: + memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info)); + fib_rule_get(fib_work->fr_info.rule); + break; + } } /* Called with rcu_read_lock() */ -- cgit v1.2.3 From 428b851f565f11c483cfa62021d674b6fb9d6ddc Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:28 +0200 Subject: mlxsw: spectrum_router: Add support for IPv6 routes addition / deletion Allow directly connected and remote unicast IPv6 routes to be programmed to the device's tables. As with IPv4, identical routes - sharing the same destination prefix - are ordered in a FIB node according to their table ID and then the metric. While the kernel doesn't share the same trie for the local and main table, this does happen in the device, so ordering according to table ID is needed. Since individual nexthops can be added and deleted in IPv6, each FIB entry stores a linked list of the rt6_info structs it represents. Upon the addition or deletion of a nexthop, a new nexthop group is allocated according to the new configuration and the old one is destroyed. Identical groups aren't currently consolidated, but will be in a follow-up patchset. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 1 + .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 726 ++++++++++++++++++++- 2 files changed, 724 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index 695adff89d71..d56eea310509 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -75,6 +75,7 @@ config MLXSW_SPECTRUM depends on MLXSW_CORE && MLXSW_PCI && NET_SWITCHDEV && VLAN_8021Q depends on PSAMPLE || PSAMPLE=n depends on BRIDGE || BRIDGE=n + depends on IPV6 || IPV6=n select PARMAN select MLXFW default m diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 6c7fc6a66aca..2345c00ef706 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -406,6 +407,17 @@ struct mlxsw_sp_fib4_entry { u8 type; }; +struct mlxsw_sp_fib6_entry { + struct mlxsw_sp_fib_entry common; + struct list_head rt6_list; + unsigned int nrt6; +}; + +struct mlxsw_sp_rt6 { + struct list_head list; + struct rt6_info *rt; +}; + enum mlxsw_sp_l3proto { MLXSW_SP_L3_PROTO_IPV4, MLXSW_SP_L3_PROTO_IPV6, @@ -2126,6 +2138,26 @@ mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry) } } +static struct mlxsw_sp_nexthop * +mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp, + const struct mlxsw_sp_rt6 *mlxsw_sp_rt6) +{ + int i; + + for (i = 0; i < nh_grp->count; i++) { + struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; + struct rt6_info *rt = mlxsw_sp_rt6->rt; + + if (nh->rif && nh->rif->dev == rt->dst.dev && + ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr, + &rt->rt6i_gateway)) + return nh; + continue; + } + + return NULL; +} + static void mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) { @@ -2160,6 +2192,48 @@ mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) } } +static void +mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry, + common); + + if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) { + list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6, + list)->rt->rt6i_flags |= RTF_OFFLOAD; + return; + } + + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; + struct mlxsw_sp_nexthop *nh; + + nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6); + if (nh && nh->offloaded) + mlxsw_sp_rt6->rt->rt6i_flags |= RTF_OFFLOAD; + else + mlxsw_sp_rt6->rt->rt6i_flags &= ~RTF_OFFLOAD; + } +} + +static void +mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry, + common); + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + struct rt6_info *rt = mlxsw_sp_rt6->rt; + + rt->rt6i_flags &= ~RTF_OFFLOAD; + } +} + static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) { switch (fib_entry->fib_node->fib->proto) { @@ -2167,7 +2241,8 @@ static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) mlxsw_sp_fib4_entry_offload_set(fib_entry); break; case MLXSW_SP_L3_PROTO_IPV6: - WARN_ON_ONCE(1); + mlxsw_sp_fib6_entry_offload_set(fib_entry); + break; } } @@ -2179,7 +2254,8 @@ mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) mlxsw_sp_fib4_entry_offload_unset(fib_entry); break; case MLXSW_SP_L3_PROTO_IPV6: - WARN_ON_ONCE(1); + mlxsw_sp_fib6_entry_offload_unset(fib_entry); + break; } } @@ -2887,6 +2963,615 @@ static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } +static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt) +{ + /* Packets with link-local destination IP arriving to the router + * are trapped to the CPU, so no need to program specific routes + * for them. + */ + if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL) + return true; + + /* Multicast routes aren't supported, so ignore them. Neighbour + * Discovery packets are specifically trapped. + */ + if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST) + return true; + + /* Cloned routes are irrelevant in the forwarding path. */ + if (rt->rt6i_flags & RTF_CACHE) + return true; + + return false; +} + +static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL); + if (!mlxsw_sp_rt6) + return ERR_PTR(-ENOMEM); + + /* In case of route replace, replaced route is deleted with + * no notification. Take reference to prevent accessing freed + * memory. + */ + mlxsw_sp_rt6->rt = rt; + rt6_hold(rt); + + return mlxsw_sp_rt6; +} + +#if IS_ENABLED(CONFIG_IPV6) +static void mlxsw_sp_rt6_release(struct rt6_info *rt) +{ + rt6_release(rt); +} +#else +static void mlxsw_sp_rt6_release(struct rt6_info *rt) +{ +} +#endif + +static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6) +{ + mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt); + kfree(mlxsw_sp_rt6); +} + +static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt) +{ + /* RTF_CACHE routes are ignored */ + return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY; +} + +static struct rt6_info * +mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry) +{ + return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6, + list)->rt; +} + +static struct mlxsw_sp_fib6_entry * +mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node, + const struct rt6_info *nrt) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + + if (!mlxsw_sp_fib6_rt_can_mp(nrt)) + return NULL; + + list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { + struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); + + /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same + * virtual router. + */ + if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id) + continue; + if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id) + break; + if (rt->rt6i_metric < nrt->rt6i_metric) + continue; + if (rt->rt6i_metric == nrt->rt6i_metric && + mlxsw_sp_fib6_rt_can_mp(rt)) + return fib6_entry; + if (rt->rt6i_metric > nrt->rt6i_metric) + break; + } + + return NULL; +} + +static struct mlxsw_sp_rt6 * +mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry, + const struct rt6_info *rt) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + if (mlxsw_sp_rt6->rt == rt) + return mlxsw_sp_rt6; + } + + return NULL; +} + +static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp, + struct mlxsw_sp_nexthop *nh, + const struct rt6_info *rt) +{ + struct net_device *dev = rt->dst.dev; + struct mlxsw_sp_rif *rif; + int err; + + nh->nh_grp = nh_grp; + memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr)); + + if (!dev) + return 0; + + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!rif) + return 0; + mlxsw_sp_nexthop_rif_init(nh, rif); + + err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); + if (err) + goto err_nexthop_neigh_init; + + return 0; + +err_nexthop_neigh_init: + mlxsw_sp_nexthop_rif_fini(nh); + return err; +} + +static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); + mlxsw_sp_nexthop_rif_fini(nh); +} + +static struct mlxsw_sp_nexthop_group * +mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_nexthop_group *nh_grp; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + struct mlxsw_sp_nexthop *nh; + size_t alloc_size; + int i = 0; + int err; + + alloc_size = sizeof(*nh_grp) + + fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop); + nh_grp = kzalloc(alloc_size, GFP_KERNEL); + if (!nh_grp) + return ERR_PTR(-ENOMEM); + INIT_LIST_HEAD(&nh_grp->fib_list); +#if IS_ENABLED(CONFIG_IPV6) + nh_grp->neigh_tbl = &nd_tbl; +#endif + mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list, + struct mlxsw_sp_rt6, list); + nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY); + nh_grp->count = fib6_entry->nrt6; + for (i = 0; i < nh_grp->count; i++) { + struct rt6_info *rt = mlxsw_sp_rt6->rt; + + nh = &nh_grp->nexthops[i]; + err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt); + if (err) + goto err_nexthop6_init; + mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list); + } + mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); + return nh_grp; + +err_nexthop6_init: + for (i--; i >= 0; i--) { + nh = &nh_grp->nexthops[i]; + mlxsw_sp_nexthop6_fini(mlxsw_sp, nh); + } + kfree(nh_grp); + return ERR_PTR(err); +} + +static void +mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp) +{ + struct mlxsw_sp_nexthop *nh; + int i = nh_grp->count; + + for (i--; i >= 0; i--) { + nh = &nh_grp->nexthops[i]; + mlxsw_sp_nexthop6_fini(mlxsw_sp, nh); + } + mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); + WARN_ON(nh_grp->adj_index_valid); + kfree(nh_grp); +} + +static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_nexthop_group *nh_grp; + + /* For now, don't consolidate nexthop groups */ + nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry); + if (IS_ERR(nh_grp)) + return PTR_ERR(nh_grp); + + list_add_tail(&fib6_entry->common.nexthop_group_node, + &nh_grp->fib_list); + fib6_entry->common.nh_group = nh_grp; + + return 0; +} + +static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; + + list_del(&fib_entry->nexthop_group_node); + if (!list_empty(&nh_grp->fib_list)) + return; + mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp); +} + +static int +mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group; + int err; + + fib6_entry->common.nh_group = NULL; + list_del(&fib6_entry->common.nexthop_group_node); + + err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry); + if (err) + goto err_nexthop6_group_get; + + /* In case this entry is offloaded, then the adjacency index + * currently associated with it in the device's table is that + * of the old group. Start using the new one instead. + */ + err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common); + if (err) + goto err_fib_node_entry_add; + + if (list_empty(&old_nh_grp->fib_list)) + mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp); + + return 0; + +err_fib_node_entry_add: + mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common); +err_nexthop6_group_get: + list_add_tail(&fib6_entry->common.nexthop_group_node, + &old_nh_grp->fib_list); + fib6_entry->common.nh_group = old_nh_grp; + return err; +} + +static int +mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry, + struct rt6_info *rt) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + int err; + + mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt); + if (IS_ERR(mlxsw_sp_rt6)) + return PTR_ERR(mlxsw_sp_rt6); + + list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list); + fib6_entry->nrt6++; + + err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry); + if (err) + goto err_nexthop6_group_update; + + return 0; + +err_nexthop6_group_update: + fib6_entry->nrt6--; + list_del(&mlxsw_sp_rt6->list); + mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); + return err; +} + +static void +mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry, + struct rt6_info *rt) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt); + if (WARN_ON(!mlxsw_sp_rt6)) + return; + + fib6_entry->nrt6--; + list_del(&mlxsw_sp_rt6->list); + mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry); + mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); +} + +static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry, + const struct rt6_info *rt) +{ + /* Packets hitting RTF_REJECT routes need to be discarded by the + * stack. We can rely on their destination device not having a + * RIF (it's the loopback device) and can thus use action type + * local, which will cause them to be trapped with a lower + * priority than packets that need to be locally received. + */ + if (rt->rt6i_flags & RTF_LOCAL) + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; + else if (rt->rt6i_flags & RTF_REJECT) + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; + else if (rt->rt6i_flags & RTF_GATEWAY) + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; + else + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; +} + +static void +mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp; + + list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list, + list) { + fib6_entry->nrt6--; + list_del(&mlxsw_sp_rt6->list); + mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); + } +} + +static struct mlxsw_sp_fib6_entry * +mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_node *fib_node, + struct rt6_info *rt) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + int err; + + fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL); + if (!fib6_entry) + return ERR_PTR(-ENOMEM); + fib_entry = &fib6_entry->common; + + mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt); + if (IS_ERR(mlxsw_sp_rt6)) { + err = PTR_ERR(mlxsw_sp_rt6); + goto err_rt6_create; + } + + mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt); + + INIT_LIST_HEAD(&fib6_entry->rt6_list); + list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list); + fib6_entry->nrt6 = 1; + err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry); + if (err) + goto err_nexthop6_group_get; + + fib_entry->fib_node = fib_node; + + return fib6_entry; + +err_nexthop6_group_get: + list_del(&mlxsw_sp_rt6->list); + mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); +err_rt6_create: + kfree(fib6_entry); + return ERR_PTR(err); +} + +static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common); + mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry); + WARN_ON(fib6_entry->nrt6); + kfree(fib6_entry); +} + +static struct mlxsw_sp_fib6_entry * +mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, + const struct rt6_info *nrt) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + + list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { + struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); + + if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id) + continue; + if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id) + break; + if (rt->rt6i_metric > nrt->rt6i_metric) + return fib6_entry; + } + + return NULL; +} + +static int +mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry) +{ + struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node; + struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry); + struct mlxsw_sp_fib6_entry *fib6_entry; + + fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt); + + if (fib6_entry) { + list_add_tail(&new6_entry->common.list, + &fib6_entry->common.list); + } else { + struct mlxsw_sp_fib6_entry *last; + + list_for_each_entry(last, &fib_node->entry_list, common.list) { + struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last); + + if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id) + break; + fib6_entry = last; + } + + if (fib6_entry) + list_add(&new6_entry->common.list, + &fib6_entry->common.list); + else + list_add(&new6_entry->common.list, + &fib_node->entry_list); + } + + return 0; +} + +static void +mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry) +{ + list_del(&fib6_entry->common.list); +} + +static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + int err; + + err = mlxsw_sp_fib6_node_list_insert(fib6_entry); + if (err) + return err; + + err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common); + if (err) + goto err_fib_node_entry_add; + + return 0; + +err_fib_node_entry_add: + mlxsw_sp_fib6_node_list_remove(fib6_entry); + return err; +} + +static void +mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common); + mlxsw_sp_fib6_node_list_remove(fib6_entry); +} + +static struct mlxsw_sp_fib6_entry * +mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp, + const struct rt6_info *rt) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_fib_node *fib_node; + struct mlxsw_sp_fib *fib; + struct mlxsw_sp_vr *vr; + + vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id); + if (!vr) + return NULL; + fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6); + + fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr, + sizeof(rt->rt6i_dst.addr), + rt->rt6i_dst.plen); + if (!fib_node) + return NULL; + + list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { + struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry); + + if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id && + rt->rt6i_metric == iter_rt->rt6i_metric && + mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt)) + return fib6_entry; + } + + return NULL; +} + +static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, + struct rt6_info *rt) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_fib_node *fib_node; + int err; + + if (mlxsw_sp->router->aborted) + return 0; + + if (mlxsw_sp_fib6_rt_should_ignore(rt)) + return 0; + + fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id, + &rt->rt6i_dst.addr, + sizeof(rt->rt6i_dst.addr), + rt->rt6i_dst.plen, + MLXSW_SP_L3_PROTO_IPV6); + if (IS_ERR(fib_node)) + return PTR_ERR(fib_node); + + /* Before creating a new entry, try to append route to an existing + * multipath entry. + */ + fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt); + if (fib6_entry) { + err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt); + if (err) + goto err_fib6_entry_nexthop_add; + return 0; + } + + fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt); + if (IS_ERR(fib6_entry)) { + err = PTR_ERR(fib6_entry); + goto err_fib6_entry_create; + } + + err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry); + if (err) + goto err_fib6_node_entry_link; + + return 0; + +err_fib6_node_entry_link: + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); +err_fib6_entry_create: +err_fib6_entry_nexthop_add: + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); + return err; +} + +static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp, + struct rt6_info *rt) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_fib_node *fib_node; + + if (mlxsw_sp->router->aborted) + return; + + if (mlxsw_sp_fib6_rt_should_ignore(rt)) + return; + + fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt); + if (WARN_ON(!fib6_entry)) + return; + + /* If route is part of a multipath entry, but not the last one + * removed, then only reduce its nexthop group. + */ + if (!list_is_singular(&fib6_entry->rt6_list)) { + mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt); + return; + } + + fib_node = fib6_entry->common.fib_node; + + mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry); + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); +} + static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp, enum mlxsw_reg_ralxx_protocol proto, u8 tree_id) @@ -2967,6 +3652,23 @@ static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, } } +static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_node *fib_node) +{ + struct mlxsw_sp_fib6_entry *fib6_entry, *tmp; + + list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list, + common.list) { + bool do_break = &tmp->common.list == &fib_node->entry_list; + + mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry); + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); + if (do_break) + break; + } +} + static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { @@ -2975,7 +3677,7 @@ static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node); break; case MLXSW_SP_L3_PROTO_IPV6: - WARN_ON_ONCE(1); + mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node); break; } } @@ -3033,6 +3735,7 @@ static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp) struct mlxsw_sp_fib_event_work { struct work_struct work; union { + struct fib6_entry_notifier_info fen6_info; struct fib_entry_notifier_info fen_info; struct fib_rule_notifier_info fr_info; struct fib_nh_notifier_info fnh_info; @@ -3092,9 +3795,21 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) container_of(work, struct mlxsw_sp_fib_event_work, work); struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; struct fib_rule *rule; + int err; rtnl_lock(); switch (fib_work->event) { + case FIB_EVENT_ENTRY_ADD: + err = mlxsw_sp_router_fib6_add(mlxsw_sp, + fib_work->fen6_info.rt); + if (err) + mlxsw_sp_router_fib_abort(mlxsw_sp); + mlxsw_sp_rt6_release(fib_work->fen6_info.rt); + break; + case FIB_EVENT_ENTRY_DEL: + mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt); + mlxsw_sp_rt6_release(fib_work->fen6_info.rt); + break; case FIB_EVENT_RULE_ADD: /* fall through */ case FIB_EVENT_RULE_DEL: rule = fib_work->fr_info.rule; @@ -3138,6 +3853,11 @@ static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work, struct fib_notifier_info *info) { switch (fib_work->event) { + case FIB_EVENT_ENTRY_ADD: /* fall through */ + case FIB_EVENT_ENTRY_DEL: + memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info)); + rt6_hold(fib_work->fen6_info.rt); + break; case FIB_EVENT_RULE_ADD: /* fall through */ case FIB_EVENT_RULE_DEL: memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info)); -- cgit v1.2.3 From 0a7fd1ac2a6b316ceeb9a57a41ce0c45f6bff549 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:29 +0200 Subject: mlxsw: spectrum_router: Add support for route replace In case we got a replace event, then the replaced route must exist. If the route isn't capable of multipath, then replace first matching non-multipath capable route. If the route is capable of multipath and matching multipath capable route is found, then replace it. Otherwise, replace first matching non-multipath capable route. The new route is inserted before the replaced one. In case the replaced route is currently offloaded, then it's overwritten in the device's table by the new route and later deleted, thus not impacting routed traffic. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 63 +++++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2345c00ef706..cded8e8039bd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3035,11 +3035,11 @@ mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry) static struct mlxsw_sp_fib6_entry * mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node, - const struct rt6_info *nrt) + const struct rt6_info *nrt, bool replace) { struct mlxsw_sp_fib6_entry *fib6_entry; - if (!mlxsw_sp_fib6_rt_can_mp(nrt)) + if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace) return NULL; list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { @@ -3371,9 +3371,9 @@ static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp, static struct mlxsw_sp_fib6_entry * mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, - const struct rt6_info *nrt) + const struct rt6_info *nrt, bool replace) { - struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL; list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); @@ -3382,21 +3382,32 @@ mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, continue; if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id) break; + if (replace && rt->rt6i_metric == nrt->rt6i_metric) { + if (mlxsw_sp_fib6_rt_can_mp(rt) == + mlxsw_sp_fib6_rt_can_mp(nrt)) + return fib6_entry; + if (mlxsw_sp_fib6_rt_can_mp(nrt)) + fallback = fallback ?: fib6_entry; + } if (rt->rt6i_metric > nrt->rt6i_metric) - return fib6_entry; + return fallback ?: fib6_entry; } - return NULL; + return fallback; } static int -mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry) +mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry, + bool replace) { struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node; struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry); struct mlxsw_sp_fib6_entry *fib6_entry; - fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt); + fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace); + + if (replace && WARN_ON(!fib6_entry)) + return -EINVAL; if (fib6_entry) { list_add_tail(&new6_entry->common.list, @@ -3430,11 +3441,12 @@ mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry) } static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib6_entry *fib6_entry) + struct mlxsw_sp_fib6_entry *fib6_entry, + bool replace) { int err; - err = mlxsw_sp_fib6_node_list_insert(fib6_entry); + err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace); if (err) return err; @@ -3489,8 +3501,25 @@ mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp, return NULL; } +static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry, + bool replace) +{ + struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node; + struct mlxsw_sp_fib6_entry *replaced; + + if (!replace) + return; + + replaced = list_next_entry(fib6_entry, common.list); + + mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced); + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); +} + static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, - struct rt6_info *rt) + struct rt6_info *rt, bool replace) { struct mlxsw_sp_fib6_entry *fib6_entry; struct mlxsw_sp_fib_node *fib_node; @@ -3513,7 +3542,7 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, /* Before creating a new entry, try to append route to an existing * multipath entry. */ - fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt); + fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace); if (fib6_entry) { err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt); if (err) @@ -3527,10 +3556,12 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, goto err_fib6_entry_create; } - err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry); + err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace); if (err) goto err_fib6_node_entry_link; + mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace); + return 0; err_fib6_node_entry_link: @@ -3795,13 +3826,16 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) container_of(work, struct mlxsw_sp_fib_event_work, work); struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; struct fib_rule *rule; + bool replace; int err; rtnl_lock(); switch (fib_work->event) { + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ case FIB_EVENT_ENTRY_ADD: + replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE; err = mlxsw_sp_router_fib6_add(mlxsw_sp, - fib_work->fen6_info.rt); + fib_work->fen6_info.rt, replace); if (err) mlxsw_sp_router_fib_abort(mlxsw_sp); mlxsw_sp_rt6_release(fib_work->fen6_info.rt); @@ -3853,6 +3887,7 @@ static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work, struct fib_notifier_info *info) { switch (fib_work->event) { + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ case FIB_EVENT_ENTRY_ADD: /* fall through */ case FIB_EVENT_ENTRY_DEL: memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info)); -- cgit v1.2.3 From f36f5ac677d184a62404169c781339c0cc64ea87 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:30 +0200 Subject: mlxsw: spectrum_router: Abort on source-specific routes Without resorting to ACLs, the device performs route lookup solely based on the destination IP address. In case source-specific routing is needed, an error is returned and the abort mechanism is activated, thus allowing the kernel to take over forwarding decisions. Instead of aborting, we can trap specific destination prefixes where source-specific routes are present, but this will result in a lot more code that is unlikely to ever be used. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index cded8e8039bd..45cf32ca4126 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3528,6 +3528,9 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, if (mlxsw_sp->router->aborted) return 0; + if (rt->rt6i_src.plen) + return -EINVAL; + if (mlxsw_sp_fib6_rt_should_ignore(rt)) return 0; -- cgit v1.2.3 From 65e65ec137f4abe78b6c90c72c0a6ca7474e9ae6 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:31 +0200 Subject: mlxsw: spectrum_router: Don't ignore IPv6 notifications We now have all the necessary IPv6 infrastructure in place, so stop ignoring these notifications. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 45cf32ca4126..93b6da88e79c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3912,7 +3912,7 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, struct fib_notifier_info *info = ptr; struct mlxsw_sp_router *router; - if (!net_eq(info->net, &init_net) || info->family != AF_INET) + if (!net_eq(info->net, &init_net)) return NOTIFY_DONE; fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); -- cgit v1.2.3 From 1f8b977ab32dc5d148f103326e80d9097f1cefb5 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 3 Aug 2017 16:29:41 -0400 Subject: sock: enable MSG_ZEROCOPY Prepare the datapath for refcounted ubuf_info. Clone ubuf_info with skb_zerocopy_clone() wherever needed due to skb split, merge, resize or clone. Split skb_orphan_frags into two variants. The split, merge, .. paths support reference counted zerocopy buffers, so do not do a deep copy. Add skb_orphan_frags_rx for paths that may loop packets to receive sockets. That is not allowed, as it may cause unbounded latency. Deep copy all zerocopy copy buffers, ref-counted or not, in this path. The exact locations to modify were chosen by exhaustively searching through all code that might modify skb_frag references and/or the the SKBTX_DEV_ZEROCOPY tx_flags bit. The changes err on the safe side, in two ways. (1) legacy ubuf_info paths virtio and tap are not modified. They keep a 1:1 ubuf_info to sk_buff relationship. Calls to skb_orphan_frags still call skb_copy_ubufs and thus copy frags in this case. (2) not all copies deep in the stack are addressed yet. skb_shift, skb_split and skb_try_coalesce can be refined to avoid copying. These are not in the hot path and this patch is hairy enough as is, so that is left for future refinement. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- drivers/net/tun.c | 2 +- drivers/vhost/net.c | 1 + include/linux/skbuff.h | 14 +++++++++++++- net/core/dev.c | 4 ++-- net/core/skbuff.c | 48 +++++++++++++++++++----------------------------- 5 files changed, 36 insertions(+), 33 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 68add55f8460..d21510d47aa2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -892,7 +892,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) sk_filter(tfile->socket.sk, skb)) goto drop; - if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) + if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) goto drop; skb_tx_timestamp(skb); diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 06d044862e58..ba08b78ed630 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -533,6 +533,7 @@ static void handle_tx(struct vhost_net *net) ubuf->callback = vhost_zerocopy_callback; ubuf->ctx = nvq->ubufs; ubuf->desc = nvq->upend_idx; + atomic_set(&ubuf->refcnt, 1); msg.msg_control = ubuf; msg.msg_controllen = sizeof(ubuf); ubufs = nvq->ubufs; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 59cff7aa494e..e5387932c266 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2512,7 +2512,17 @@ static inline void skb_orphan(struct sk_buff *skb) */ static inline int skb_orphan_frags(struct sk_buff *skb, gfp_t gfp_mask) { - if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY))) + if (likely(!skb_zcopy(skb))) + return 0; + if (skb_uarg(skb)->callback == sock_zerocopy_callback) + return 0; + return skb_copy_ubufs(skb, gfp_mask); +} + +/* Frags must be orphaned, even if refcounted, if skb might loop to rx path */ +static inline int skb_orphan_frags_rx(struct sk_buff *skb, gfp_t gfp_mask) +{ + if (likely(!skb_zcopy(skb))) return 0; return skb_copy_ubufs(skb, gfp_mask); } @@ -2944,6 +2954,8 @@ static inline int skb_add_data(struct sk_buff *skb, static inline bool skb_can_coalesce(struct sk_buff *skb, int i, const struct page *page, int off) { + if (skb_zcopy(skb)) + return false; if (i) { const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1]; diff --git a/net/core/dev.c b/net/core/dev.c index 8ea6b4b42611..1d75499add72 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1853,7 +1853,7 @@ static inline int deliver_skb(struct sk_buff *skb, struct packet_type *pt_prev, struct net_device *orig_dev) { - if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) + if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) return -ENOMEM; refcount_inc(&skb->users); return pt_prev->func(skb, skb->dev, pt_prev, orig_dev); @@ -4412,7 +4412,7 @@ skip_classify: } if (pt_prev) { - if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) + if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) goto drop; else ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 29e34bc6a17c..74d3c36f8419 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -567,21 +567,10 @@ static void skb_release_data(struct sk_buff *skb) for (i = 0; i < shinfo->nr_frags; i++) __skb_frag_unref(&shinfo->frags[i]); - /* - * If skb buf is from userspace, we need to notify the caller - * the lower device DMA has done; - */ - if (shinfo->tx_flags & SKBTX_DEV_ZEROCOPY) { - struct ubuf_info *uarg; - - uarg = shinfo->destructor_arg; - if (uarg->callback) - uarg->callback(uarg, true); - } - if (shinfo->frag_list) kfree_skb_list(shinfo->frag_list); + skb_zcopy_clear(skb, true); skb_free_head(skb); } @@ -695,14 +684,7 @@ EXPORT_SYMBOL(kfree_skb_list); */ void skb_tx_error(struct sk_buff *skb) { - if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { - struct ubuf_info *uarg; - - uarg = skb_shinfo(skb)->destructor_arg; - if (uarg->callback) - uarg->callback(uarg, false); - skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; - } + skb_zcopy_clear(skb, true); } EXPORT_SYMBOL(skb_tx_error); @@ -1029,9 +1011,7 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb, } EXPORT_SYMBOL_GPL(skb_zerocopy_iter_stream); -/* unused only until next patch in the series; will remove attribute */ -static int __attribute__((unused)) - skb_zerocopy_clone(struct sk_buff *nskb, struct sk_buff *orig, +static int skb_zerocopy_clone(struct sk_buff *nskb, struct sk_buff *orig, gfp_t gfp_mask) { if (skb_zcopy(orig)) { @@ -1068,7 +1048,6 @@ static int __attribute__((unused)) */ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) { - struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg; int num_frags = skb_shinfo(skb)->nr_frags; struct page *page, *head = NULL; int i, new_frags; @@ -1127,8 +1106,6 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) for (i = 0; i < num_frags; i++) skb_frag_unref(skb, i); - uarg->callback(uarg, false); - /* skb frags point to kernel buffers */ for (i = 0; i < new_frags - 1; i++) { __skb_fill_page_desc(skb, i, head, 0, PAGE_SIZE); @@ -1137,7 +1114,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) __skb_fill_page_desc(skb, new_frags - 1, head, 0, d_off); skb_shinfo(skb)->nr_frags = new_frags; - skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; + skb_zcopy_clear(skb, false); return 0; } EXPORT_SYMBOL_GPL(skb_copy_ubufs); @@ -1298,7 +1275,8 @@ struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom, if (skb_shinfo(skb)->nr_frags) { int i; - if (skb_orphan_frags(skb, gfp_mask)) { + if (skb_orphan_frags(skb, gfp_mask) || + skb_zerocopy_clone(n, skb, gfp_mask)) { kfree_skb(n); n = NULL; goto out; @@ -1375,9 +1353,10 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, * be since all we did is relocate the values */ if (skb_cloned(skb)) { - /* copy this zero copy skb frags */ if (skb_orphan_frags(skb, gfp_mask)) goto nofrags; + if (skb_zcopy(skb)) + atomic_inc(&skb_uarg(skb)->refcnt); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) skb_frag_ref(skb, i); @@ -1872,6 +1851,9 @@ end: skb->tail += delta; skb->data_len -= delta; + if (!skb->data_len) + skb_zcopy_clear(skb, false); + return skb_tail_pointer(skb); } EXPORT_SYMBOL(__pskb_pull_tail); @@ -2627,6 +2609,7 @@ skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) skb_tx_error(from); return -ENOMEM; } + skb_zerocopy_clone(to, from, GFP_ATOMIC); for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { if (!len) @@ -2924,6 +2907,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) skb_shinfo(skb1)->tx_flags |= skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; + skb_zerocopy_clone(skb1, skb, 0); if (len < pos) /* Split line is inside header. */ skb_split_inside_header(skb, skb1, len, pos); else /* Second chunk has no header, nothing to copy. */ @@ -2967,6 +2951,8 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) if (skb_headlen(skb)) return 0; + if (skb_zcopy(tgt) || skb_zcopy(skb)) + return 0; todo = shiftlen; from = 0; @@ -3540,6 +3526,8 @@ normal: skb_shinfo(nskb)->tx_flags |= skb_shinfo(head_skb)->tx_flags & SKBTX_SHARED_FRAG; + if (skb_zerocopy_clone(nskb, head_skb, GFP_ATOMIC)) + goto err; while (pos < offset + len) { if (i >= nfrags) { @@ -4663,6 +4651,8 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, if (skb_has_frag_list(to) || skb_has_frag_list(from)) return false; + if (skb_zcopy(to) || skb_zcopy(from)) + return false; if (skb_headlen(from) != 0) { struct page *page; -- cgit v1.2.3 From 5987feb38aa55e035ce5376c02aba88a604cc881 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Aug 2017 11:17:21 +0300 Subject: net: phy: marvell: logical vs bitwise OR typo This was supposed to be a bitwise OR but there is a || vs | typo. Fixes: 864dc729d528 ("net: phy: marvell: Refactor m88e1121 RGMII delay configuration") Signed-off-by: Dan Carpenter Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 361fe9927ef2..15cbcdba618a 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -83,7 +83,7 @@ #define MII_88E1121_PHY_MSCR_REG 21 #define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5) #define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4) -#define MII_88E1121_PHY_MSCR_DELAY_MASK (~(BIT(5) || BIT(4))) +#define MII_88E1121_PHY_MSCR_DELAY_MASK (~(BIT(5) | BIT(4))) #define MII_88E1121_MISC_TEST 0x1a #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK 0x1f00 -- cgit v1.2.3 From 967b2e2a76e380abdebe7f3c7ab17e8831accd21 Mon Sep 17 00:00:00 2001 From: Lin Yun Sheng Date: Fri, 4 Aug 2017 17:24:59 +0800 Subject: net: hns: Fix for __udivdi3 compiler error This patch fixes the __udivdi3 undefined error reported by test robot. Fixes: b8c17f708831 ("net: hns: Add self-adaptive interrupt coalesce support in hns driver") Signed-off-by: Yunsheng Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_enet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 832f27792e3f..36520634c96a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -847,7 +847,8 @@ static void hns_update_rx_rate(struct hnae_ring *ring) total_bytes = ring->stats.rx_bytes - ring->coal_last_rx_bytes; time_passed_ms = jiffies_to_msecs(jiffies - ring->coal_last_jiffies); - ring->coal_rx_rate = (total_bytes / time_passed_ms) >> 10; + do_div(total_bytes, time_passed_ms); + ring->coal_rx_rate = total_bytes >> 10; ring->coal_last_rx_bytes = ring->stats.rx_bytes; ring->coal_last_jiffies = jiffies; -- cgit v1.2.3 From 3bcc0cec818fa969fe555b44443347211ed787a3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:28:58 +0200 Subject: net: sched: change names of action number helpers to be aligned with the rest The rest of the helpers are named tcf_exts_*, so change the name of the action number helpers to be aligned. While at it, change to inline functions. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 4 +-- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +- drivers/net/ethernet/netronome/nfp/bpf/offload.c | 4 +-- include/net/pkt_cls.h | 36 ++++++++++++++++------ net/dsa/slave.c | 2 +- net/sched/cls_api.c | 2 +- 9 files changed, 37 insertions(+), 19 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c index ef06ce8247ab..6f734c52ef25 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c @@ -96,7 +96,7 @@ static int fill_action_fields(struct adapter *adap, LIST_HEAD(actions); exts = cls->knode.exts; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; tcf_exts_to_list(exts, &actions); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 96606e3eb965..091fcc7e6e43 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8953,7 +8953,7 @@ static int parse_tc_actions(struct ixgbe_adapter *adapter, LIST_HEAD(actions); int err; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; tcf_exts_to_list(exts, &actions); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 3c536f560dd2..78f50d9f621d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1326,7 +1326,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, LIST_HEAD(actions); int err; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; @@ -1839,7 +1839,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, bool encap = false; int err = 0; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; memset(attr, 0, sizeof(*attr)); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 88b668ba0d8a..66d511d45c25 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1626,7 +1626,7 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, LIST_HEAD(actions); int err; - if (!tc_single_action(cls->exts)) { + if (!tcf_exts_has_one_action(cls->exts)) { netdev_err(mlxsw_sp_port->dev, "only singular actions are supported\n"); return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 400ad4081660..9be48d2e43ca 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -53,7 +53,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, LIST_HEAD(actions); int err; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return 0; /* Count action is inserted first */ diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c index 78d80a364edb..a88bb5bc0082 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c @@ -115,14 +115,14 @@ nfp_net_bpf_get_act(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) /* TC direct action */ if (cls_bpf->exts_integrated) { - if (tc_no_actions(cls_bpf->exts)) + if (!tcf_exts_has_actions(cls_bpf->exts)) return NN_ACT_DIRECT; return -EOPNOTSUPP; } /* TC legacy mode */ - if (!tc_single_action(cls_bpf->exts)) + if (!tcf_exts_has_one_action(cls_bpf->exts)) return -EOPNOTSUPP; tcf_exts_to_list(cls_bpf->exts, &actions); diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index f4462ec8b2f4..7f2563636df0 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -199,17 +199,35 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, return 0; } +/** + * tcf_exts_has_actions - check if at least one action is present + * @exts: tc filter extensions handle + * + * Returns true if at least one action is present. + */ +static inline bool tcf_exts_has_actions(struct tcf_exts *exts) +{ #ifdef CONFIG_NET_CLS_ACT + return exts->nr_actions; +#else + return false; +#endif +} -#define tc_no_actions(_exts) ((_exts)->nr_actions == 0) -#define tc_single_action(_exts) ((_exts)->nr_actions == 1) - -#else /* CONFIG_NET_CLS_ACT */ - -#define tc_no_actions(_exts) true -#define tc_single_action(_exts) false - -#endif /* CONFIG_NET_CLS_ACT */ +/** + * tcf_exts_has_one_action - check if exactly one action is present + * @exts: tc filter extensions handle + * + * Returns true if exactly one action is present. + */ +static inline bool tcf_exts_has_one_action(struct tcf_exts *exts) +{ +#ifdef CONFIG_NET_CLS_ACT + return exts->nr_actions == 1; +#else + return false; +#endif +} int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, struct nlattr *rate_tlv, diff --git a/net/dsa/slave.c b/net/dsa/slave.c index e196562035b1..83252e8426d7 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -779,7 +779,7 @@ static int dsa_slave_add_cls_matchall(struct net_device *dev, if (!ds->ops->port_mirror_add) return err; - if (!tc_single_action(cls->exts)) + if (!tcf_exts_has_one_action(cls->exts)) return err; tcf_exts_to_list(cls->exts, &actions); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 39da0c5801c9..287ae6cbf73b 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -972,7 +972,7 @@ int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, const struct tc_action *a; LIST_HEAD(actions); - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; tcf_exts_to_list(exts, &actions); -- cgit v1.2.3 From a54df682e559da9cf09b41779ee62bc9f11d3804 Mon Sep 17 00:00:00 2001 From: Pavel Belous Date: Thu, 3 Aug 2017 18:15:32 +0300 Subject: aquantia: Switch to use napi_gro_receive Add support for GRO (generic receive offload) for aQuantia Atlantic driver. This results in a perfomance improvement when GRO is enabled. Signed-off-by: Pavel Belous Signed-off-by: David S. Miller --- drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 7 +++++-- drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 5 ++++- drivers/net/ethernet/aquantia/atlantic/aq_vec.c | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index 9a0817938eca..4b445750b93e 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -134,7 +134,10 @@ static inline unsigned int aq_ring_dx_in_range(unsigned int h, unsigned int i, } #define AQ_SKB_ALIGN SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) -int aq_ring_rx_clean(struct aq_ring_s *self, int *work_done, int budget) +int aq_ring_rx_clean(struct aq_ring_s *self, + struct napi_struct *napi, + int *work_done, + int budget) { struct net_device *ndev = aq_nic_get_ndev(self->aq_nic); int err = 0; @@ -240,7 +243,7 @@ int aq_ring_rx_clean(struct aq_ring_s *self, int *work_done, int budget) skb_record_rx_queue(skb, self->idx); - netif_receive_skb(skb); + napi_gro_receive(napi, skb); ++self->stats.rx.packets; self->stats.rx.bytes += skb->len; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h index eecd6d1c4d73..782176c5f4f8 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h @@ -148,7 +148,10 @@ int aq_ring_init(struct aq_ring_s *self); void aq_ring_rx_deinit(struct aq_ring_s *self); void aq_ring_free(struct aq_ring_s *self); void aq_ring_tx_clean(struct aq_ring_s *self); -int aq_ring_rx_clean(struct aq_ring_s *self, int *work_done, int budget); +int aq_ring_rx_clean(struct aq_ring_s *self, + struct napi_struct *napi, + int *work_done, + int budget); int aq_ring_rx_fill(struct aq_ring_s *self); #endif /* AQ_RING_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c index ad5b4d4dac7f..ec390c5eed35 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c @@ -78,6 +78,7 @@ __acquires(&self->lock) if (ring[AQ_VEC_RX_ID].sw_head != ring[AQ_VEC_RX_ID].hw_head) { err = aq_ring_rx_clean(&ring[AQ_VEC_RX_ID], + napi, &work_done, budget - work_done); if (err < 0) -- cgit v1.2.3 From c3ecbe757c973d59994d9496b829052ed688ae1e Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:02:37 +0100 Subject: net: phy: allow settings table to support more than 32 link modes Allow the phy settings table to support more than 32 link modes by switching to the ethtool link mode bit number representation, rather than storing the mask. This will allow phylink and other ethtool code to share the settings table to look up settings. Signed-off-by: Russell King Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index b9d4922581de..abae9167b5fc 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -199,7 +199,7 @@ EXPORT_SYMBOL(phy_aneg_done); struct phy_setting { int speed; int duplex; - u32 setting; + int bit; }; /* A mapping of all SUPPORTED settings to speed/duplex. This table @@ -209,57 +209,57 @@ static const struct phy_setting settings[] = { { .speed = SPEED_10000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_10000baseKR_Full, + .bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, }, { .speed = SPEED_10000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_10000baseKX4_Full, + .bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, }, { .speed = SPEED_10000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_10000baseT_Full, + .bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT, }, { .speed = SPEED_2500, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_2500baseX_Full, + .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, }, { .speed = SPEED_1000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_1000baseKX_Full, + .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, }, { .speed = SPEED_1000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_1000baseT_Full, + .bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, }, { .speed = SPEED_1000, .duplex = DUPLEX_HALF, - .setting = SUPPORTED_1000baseT_Half, + .bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, }, { .speed = SPEED_100, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_100baseT_Full, + .bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT, }, { .speed = SPEED_100, .duplex = DUPLEX_HALF, - .setting = SUPPORTED_100baseT_Half, + .bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT, }, { .speed = SPEED_10, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_10baseT_Full, + .bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT, }, { .speed = SPEED_10, .duplex = DUPLEX_HALF, - .setting = SUPPORTED_10baseT_Half, + .bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT, }, }; @@ -267,7 +267,8 @@ static const struct phy_setting settings[] = { * phy_lookup_setting - lookup a PHY setting * @speed: speed to match * @duplex: duplex to match - * @features: allowed link modes + * @mask: allowed link modes + * @maxbit: bit size of link modes * @exact: an exact match is required * * Search the settings array for a setting that matches the speed and @@ -281,13 +282,14 @@ static const struct phy_setting settings[] = { * they all fail, %NULL will be returned. */ static const struct phy_setting * -phy_lookup_setting(int speed, int duplex, u32 features, bool exact) +phy_lookup_setting(int speed, int duplex, const unsigned long *mask, + size_t maxbit, bool exact) { const struct phy_setting *p, *match = NULL, *last = NULL; int i; for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { - if (p->setting & features) { + if (p->bit < maxbit && test_bit(p->bit, mask)) { last = p; if (p->speed == speed && p->duplex == duplex) { /* Exact match for speed and duplex */ @@ -326,7 +328,9 @@ phy_lookup_setting(int speed, int duplex, u32 features, bool exact) static const struct phy_setting * phy_find_valid(int speed, int duplex, u32 supported) { - return phy_lookup_setting(speed, duplex, supported, false); + unsigned long mask = supported; + + return phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, false); } /** @@ -343,12 +347,14 @@ unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int *speeds, unsigned int size) { + unsigned long supported = phy->supported; unsigned int count = 0; unsigned int idx = 0; for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) /* Assumes settings are grouped by speed */ - if ((settings[idx].setting & phy->supported) && + if (settings[idx].bit < BITS_PER_LONG && + !test_bit(settings[idx].bit, &supported) && (count == 0 || speeds[count - 1] != settings[idx].speed)) speeds[count++] = settings[idx].speed; @@ -366,7 +372,9 @@ unsigned int phy_supported_speeds(struct phy_device *phy, */ static inline bool phy_check_valid(int speed, int duplex, u32 features) { - return !!phy_lookup_setting(speed, duplex, features, true); + unsigned long mask = features; + + return !!phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, true); } /** -- cgit v1.2.3 From da4625ac2637e4e5249dc08a10f8dce7643603d2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:02:42 +0100 Subject: net: phy: split out PHY speed and duplex string generation Other code would like to make use of this, so make the speed and duplex string generation visible, and place it in a separate file. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phy.c | 38 +---------------------------------- include/linux/phy.h | 3 +++ 3 files changed, 53 insertions(+), 37 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 6739b738bbaf..bf01a24f21ce 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -9,6 +9,55 @@ #include #include +const char *phy_speed_to_str(int speed) +{ + switch (speed) { + case SPEED_10: + return "10Mbps"; + case SPEED_100: + return "100Mbps"; + case SPEED_1000: + return "1Gbps"; + case SPEED_2500: + return "2.5Gbps"; + case SPEED_5000: + return "5Gbps"; + case SPEED_10000: + return "10Gbps"; + case SPEED_14000: + return "14Gbps"; + case SPEED_20000: + return "20Gbps"; + case SPEED_25000: + return "25Gbps"; + case SPEED_40000: + return "40Gbps"; + case SPEED_50000: + return "50Gbps"; + case SPEED_56000: + return "56Gbps"; + case SPEED_100000: + return "100Gbps"; + case SPEED_UNKNOWN: + return "Unknown"; + default: + return "Unsupported (update phy-core.c)"; + } +} +EXPORT_SYMBOL_GPL(phy_speed_to_str); + +const char *phy_duplex_to_str(unsigned int duplex) +{ + if (duplex == DUPLEX_HALF) + return "Half"; + if (duplex == DUPLEX_FULL) + return "Full"; + if (duplex == DUPLEX_UNKNOWN) + return "Unknown"; + return "Unsupported (update phy-core.c)"; +} +EXPORT_SYMBOL_GPL(phy_duplex_to_str); + static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad, u16 regnum) { diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index abae9167b5fc..7b7fe6beae7e 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -38,42 +38,6 @@ #include -static const char *phy_speed_to_str(int speed) -{ - switch (speed) { - case SPEED_10: - return "10Mbps"; - case SPEED_100: - return "100Mbps"; - case SPEED_1000: - return "1Gbps"; - case SPEED_2500: - return "2.5Gbps"; - case SPEED_5000: - return "5Gbps"; - case SPEED_10000: - return "10Gbps"; - case SPEED_14000: - return "14Gbps"; - case SPEED_20000: - return "20Gbps"; - case SPEED_25000: - return "25Gbps"; - case SPEED_40000: - return "40Gbps"; - case SPEED_50000: - return "50Gbps"; - case SPEED_56000: - return "56Gbps"; - case SPEED_100000: - return "100Gbps"; - case SPEED_UNKNOWN: - return "Unknown"; - default: - return "Unsupported (update phy.c)"; - } -} - #define PHY_STATE_STR(_state) \ case PHY_##_state: \ return __stringify(_state); \ @@ -109,7 +73,7 @@ void phy_print_status(struct phy_device *phydev) netdev_info(phydev->attached_dev, "Link is Up - %s/%s - flow control %s\n", phy_speed_to_str(phydev->speed), - DUPLEX_FULL == phydev->duplex ? "Full" : "Half", + phy_duplex_to_str(phydev->duplex), phydev->pause ? "rx/tx" : "off"); } else { netdev_info(phydev->attached_dev, "Link is Down\n"); diff --git a/include/linux/phy.h b/include/linux/phy.h index 0bb5b212ab42..e8264c78b75b 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -667,6 +667,9 @@ struct phy_fixup { int (*run)(struct phy_device *phydev); }; +const char *phy_speed_to_str(int speed); +const char *phy_duplex_to_str(unsigned int duplex); + /** * phy_read_mmd - Convenience function for reading a register * from an MMD on a given PHY. -- cgit v1.2.3 From 0ccb4fc65d2799a315d5ee8732d75f35a114379c Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:02:47 +0100 Subject: net: phy: move phy_lookup_setting() and guts of phy_supported_speeds() to phy-core phy_lookup_setting() provides useful functionality in ethtool code outside phylib. Move it to phy-core and allow it to be re-used (eg, in phylink) rather than duplicated elsewhere. Note that this supports the larger linkmode space. As we move the phy settings table, we also need to move the guts of phy_supported_speeds() as well. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 126 +++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phy.c | 130 +-------------------------------------------- include/linux/phy.h | 15 ++++++ 3 files changed, 142 insertions(+), 129 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index bf01a24f21ce..c07845e77004 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -58,6 +58,132 @@ const char *phy_duplex_to_str(unsigned int duplex) } EXPORT_SYMBOL_GPL(phy_duplex_to_str); +/* A mapping of all SUPPORTED settings to speed/duplex. This table + * must be grouped by speed and sorted in descending match priority + * - iow, descending speed. */ +static const struct phy_setting settings[] = { + { + .speed = SPEED_10000, + .duplex = DUPLEX_FULL, + .bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + }, + { + .speed = SPEED_10000, + .duplex = DUPLEX_FULL, + .bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + }, + { + .speed = SPEED_10000, + .duplex = DUPLEX_FULL, + .bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + }, + { + .speed = SPEED_2500, + .duplex = DUPLEX_FULL, + .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + }, + { + .speed = SPEED_1000, + .duplex = DUPLEX_FULL, + .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + }, + { + .speed = SPEED_1000, + .duplex = DUPLEX_FULL, + .bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + }, + { + .speed = SPEED_1000, + .duplex = DUPLEX_HALF, + .bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + }, + { + .speed = SPEED_100, + .duplex = DUPLEX_FULL, + .bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT, + }, + { + .speed = SPEED_100, + .duplex = DUPLEX_HALF, + .bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT, + }, + { + .speed = SPEED_10, + .duplex = DUPLEX_FULL, + .bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT, + }, + { + .speed = SPEED_10, + .duplex = DUPLEX_HALF, + .bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT, + }, +}; + +/** + * phy_lookup_setting - lookup a PHY setting + * @speed: speed to match + * @duplex: duplex to match + * @mask: allowed link modes + * @maxbit: bit size of link modes + * @exact: an exact match is required + * + * Search the settings array for a setting that matches the speed and + * duplex, and which is supported. + * + * If @exact is unset, either an exact match or %NULL for no match will + * be returned. + * + * If @exact is set, an exact match, the fastest supported setting at + * or below the specified speed, the slowest supported setting, or if + * they all fail, %NULL will be returned. + */ +const struct phy_setting * +phy_lookup_setting(int speed, int duplex, const unsigned long *mask, + size_t maxbit, bool exact) +{ + const struct phy_setting *p, *match = NULL, *last = NULL; + int i; + + for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { + if (p->bit < maxbit && test_bit(p->bit, mask)) { + last = p; + if (p->speed == speed && p->duplex == duplex) { + /* Exact match for speed and duplex */ + match = p; + break; + } else if (!exact) { + if (!match && p->speed <= speed) + /* Candidate */ + match = p; + + if (p->speed < speed) + break; + } + } + } + + if (!match && !exact) + match = last; + + return match; +} +EXPORT_SYMBOL_GPL(phy_lookup_setting); + +size_t phy_speeds(unsigned int *speeds, size_t size, + unsigned long *mask, size_t maxbit) +{ + size_t count; + int i; + + for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++) + if (settings[i].bit < maxbit && + test_bit(settings[i].bit, mask) && + (count == 0 || speeds[count - 1] != settings[i].speed)) + speeds[count++] = settings[i].speed; + + return count; +} + static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad, u16 regnum) { diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 7b7fe6beae7e..71c64a774856 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -157,125 +157,6 @@ int phy_aneg_done(struct phy_device *phydev) } EXPORT_SYMBOL(phy_aneg_done); -/* A structure for mapping a particular speed and duplex - * combination to a particular SUPPORTED and ADVERTISED value - */ -struct phy_setting { - int speed; - int duplex; - int bit; -}; - -/* A mapping of all SUPPORTED settings to speed/duplex. This table - * must be grouped by speed and sorted in descending match priority - * - iow, descending speed. */ -static const struct phy_setting settings[] = { - { - .speed = SPEED_10000, - .duplex = DUPLEX_FULL, - .bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, - }, - { - .speed = SPEED_10000, - .duplex = DUPLEX_FULL, - .bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, - }, - { - .speed = SPEED_10000, - .duplex = DUPLEX_FULL, - .bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT, - }, - { - .speed = SPEED_2500, - .duplex = DUPLEX_FULL, - .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, - }, - { - .speed = SPEED_1000, - .duplex = DUPLEX_FULL, - .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, - }, - { - .speed = SPEED_1000, - .duplex = DUPLEX_FULL, - .bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, - }, - { - .speed = SPEED_1000, - .duplex = DUPLEX_HALF, - .bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, - }, - { - .speed = SPEED_100, - .duplex = DUPLEX_FULL, - .bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT, - }, - { - .speed = SPEED_100, - .duplex = DUPLEX_HALF, - .bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT, - }, - { - .speed = SPEED_10, - .duplex = DUPLEX_FULL, - .bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT, - }, - { - .speed = SPEED_10, - .duplex = DUPLEX_HALF, - .bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT, - }, -}; - -/** - * phy_lookup_setting - lookup a PHY setting - * @speed: speed to match - * @duplex: duplex to match - * @mask: allowed link modes - * @maxbit: bit size of link modes - * @exact: an exact match is required - * - * Search the settings array for a setting that matches the speed and - * duplex, and which is supported. - * - * If @exact is unset, either an exact match or %NULL for no match will - * be returned. - * - * If @exact is set, an exact match, the fastest supported setting at - * or below the specified speed, the slowest supported setting, or if - * they all fail, %NULL will be returned. - */ -static const struct phy_setting * -phy_lookup_setting(int speed, int duplex, const unsigned long *mask, - size_t maxbit, bool exact) -{ - const struct phy_setting *p, *match = NULL, *last = NULL; - int i; - - for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { - if (p->bit < maxbit && test_bit(p->bit, mask)) { - last = p; - if (p->speed == speed && p->duplex == duplex) { - /* Exact match for speed and duplex */ - match = p; - break; - } else if (!exact) { - if (!match && p->speed <= speed) - /* Candidate */ - match = p; - - if (p->speed < speed) - break; - } - } - } - - if (!match && !exact) - match = last; - - return match; -} - /** * phy_find_valid - find a PHY setting that matches the requested parameters * @speed: desired speed @@ -312,17 +193,8 @@ unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int size) { unsigned long supported = phy->supported; - unsigned int count = 0; - unsigned int idx = 0; - - for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) - /* Assumes settings are grouped by speed */ - if (settings[idx].bit < BITS_PER_LONG && - !test_bit(settings[idx].bit, &supported) && - (count == 0 || speeds[count - 1] != settings[idx].speed)) - speeds[count++] = settings[idx].speed; - return count; + return phy_speeds(speeds, size, &supported, BITS_PER_LONG); } /** diff --git a/include/linux/phy.h b/include/linux/phy.h index e8264c78b75b..8a280257778c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -670,6 +670,21 @@ struct phy_fixup { const char *phy_speed_to_str(int speed); const char *phy_duplex_to_str(unsigned int duplex); +/* A structure for mapping a particular speed and duplex + * combination to a particular SUPPORTED and ADVERTISED value + */ +struct phy_setting { + u32 speed; + u8 duplex; + u8 bit; +}; + +const struct phy_setting * +phy_lookup_setting(int speed, int duplex, const unsigned long *mask, + size_t maxbit, bool exact); +size_t phy_speeds(unsigned int *speeds, size_t size, + unsigned long *mask, size_t maxbit); + /** * phy_read_mmd - Convenience function for reading a register * from an MMD on a given PHY. -- cgit v1.2.3 From 1f3645bb41f2e2b15fe5799476284bf2cd138722 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:02:52 +0100 Subject: net: phy: add 1000Base-X to phy settings table Add the missing 1000Base-X entry to the phy settings table. This was not included because the original code could not cope with more than 32 bits of link mode mask. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index c07845e77004..21f75ae244b3 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -87,6 +87,11 @@ static const struct phy_setting settings[] = { .duplex = DUPLEX_FULL, .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, }, + { + .speed = SPEED_1000, + .duplex = DUPLEX_FULL, + .bit = ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + }, { .speed = SPEED_1000, .duplex = DUPLEX_FULL, -- cgit v1.2.3 From a81497bee70eb15039594b3116913133aa9c9b29 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:02:58 +0100 Subject: net: phy: provide a hook for link up/link down events Sometimes, we need to do additional work between the PHY coming up and marking the carrier present - for example, we may need to wait for the PHY to MAC link to finish negotiation. This changes phylib to provide a notification function pointer which avoids the built-in netif_carrier_on() and netif_carrier_off() functions. Standard ->adjust_link functionality is provided by hooking a helper into the new ->phy_link_change method. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 42 ++++++++++++++++++++---------------------- drivers/net/phy/phy_device.c | 14 ++++++++++++++ include/linux/phy.h | 1 + 3 files changed, 35 insertions(+), 22 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 71c64a774856..d5f2af2c5ddb 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -865,9 +865,15 @@ void phy_start(struct phy_device *phydev) } EXPORT_SYMBOL(phy_start); -static void phy_adjust_link(struct phy_device *phydev) +static void phy_link_up(struct phy_device *phydev) { - phydev->adjust_link(phydev->attached_dev); + phydev->phy_link_change(phydev, true, true); + phy_led_trigger_change_speed(phydev); +} + +static void phy_link_down(struct phy_device *phydev, bool do_carrier) +{ + phydev->phy_link_change(phydev, false, do_carrier); phy_led_trigger_change_speed(phydev); } @@ -912,8 +918,7 @@ void phy_state_machine(struct work_struct *work) /* If the link is down, give up on negotiation for now */ if (!phydev->link) { phydev->state = PHY_NOLINK; - netif_carrier_off(phydev->attached_dev); - phy_adjust_link(phydev); + phy_link_down(phydev, true); break; } @@ -925,9 +930,7 @@ void phy_state_machine(struct work_struct *work) /* If AN is done, we're running */ if (err > 0) { phydev->state = PHY_RUNNING; - netif_carrier_on(phydev->attached_dev); - phy_adjust_link(phydev); - + phy_link_up(phydev); } else if (0 == phydev->link_timeout--) needs_aneg = true; break; @@ -952,8 +955,7 @@ void phy_state_machine(struct work_struct *work) } } phydev->state = PHY_RUNNING; - netif_carrier_on(phydev->attached_dev); - phy_adjust_link(phydev); + phy_link_up(phydev); } break; case PHY_FORCING: @@ -963,13 +965,12 @@ void phy_state_machine(struct work_struct *work) if (phydev->link) { phydev->state = PHY_RUNNING; - netif_carrier_on(phydev->attached_dev); + phy_link_up(phydev); } else { if (0 == phydev->link_timeout--) needs_aneg = true; + phy_link_down(phydev, false); } - - phy_adjust_link(phydev); break; case PHY_RUNNING: /* Only register a CHANGE if we are polling and link changed @@ -1001,14 +1002,12 @@ void phy_state_machine(struct work_struct *work) if (phydev->link) { phydev->state = PHY_RUNNING; - netif_carrier_on(phydev->attached_dev); + phy_link_up(phydev); } else { phydev->state = PHY_NOLINK; - netif_carrier_off(phydev->attached_dev); + phy_link_down(phydev, true); } - phy_adjust_link(phydev); - if (phy_interrupt_is_valid(phydev)) err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); @@ -1016,8 +1015,7 @@ void phy_state_machine(struct work_struct *work) case PHY_HALTED: if (phydev->link) { phydev->link = 0; - netif_carrier_off(phydev->attached_dev); - phy_adjust_link(phydev); + phy_link_down(phydev, true); do_suspend = true; } break; @@ -1037,11 +1035,11 @@ void phy_state_machine(struct work_struct *work) if (phydev->link) { phydev->state = PHY_RUNNING; - netif_carrier_on(phydev->attached_dev); + phy_link_up(phydev); } else { phydev->state = PHY_NOLINK; + phy_link_down(phydev, false); } - phy_adjust_link(phydev); } else { phydev->state = PHY_AN; phydev->link_timeout = PHY_AN_TIMEOUT; @@ -1053,11 +1051,11 @@ void phy_state_machine(struct work_struct *work) if (phydev->link) { phydev->state = PHY_RUNNING; - netif_carrier_on(phydev->attached_dev); + phy_link_up(phydev); } else { phydev->state = PHY_NOLINK; + phy_link_down(phydev, false); } - phy_adjust_link(phydev); } break; } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 1790f7fec125..d536a9a7cd2b 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -688,6 +688,19 @@ struct phy_device *phy_find_first(struct mii_bus *bus) } EXPORT_SYMBOL(phy_find_first); +static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier) +{ + struct net_device *netdev = phydev->attached_dev; + + if (do_carrier) { + if (up) + netif_carrier_on(netdev); + else + netif_carrier_off(netdev); + } + phydev->adjust_link(netdev); +} + /** * phy_prepare_link - prepares the PHY layer to monitor link status * @phydev: target phy_device struct @@ -951,6 +964,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, goto error; } + phydev->phy_link_change = phy_link_change; phydev->attached_dev = dev; dev->phydev = phydev; diff --git a/include/linux/phy.h b/include/linux/phy.h index 8a280257778c..0a5e8e62c9e0 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -474,6 +474,7 @@ struct phy_device { u8 mdix; u8 mdix_ctrl; + void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier); void (*adjust_link)(struct net_device *dev); }; #define to_phy_device(d) container_of(to_mdio_device(d), \ -- cgit v1.2.3 From 5e5758d9d84256d55da831e940276c3216997f3e Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:03:03 +0100 Subject: net: phy: export phy_start_machine() for phylink phylink will need phy_start_machine exported, so lets export it as a GPL symbol. Documentation/networking/phy.txt indicates that this should be a PHY API function. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d5f2af2c5ddb..dae13f028c84 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -556,6 +556,7 @@ void phy_start_machine(struct phy_device *phydev) { queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ); } +EXPORT_SYMBOL_GPL(phy_start_machine); /** * phy_trigger_machine - trigger the state machine to run -- cgit v1.2.3 From 453d00defba502a48e3f9a218a519b233ff83d16 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:03:08 +0100 Subject: net: phy: add I2C mdio bus Add an I2C MDIO bus bridge library, to allow phylib to access PHYs which are connected to an I2C bus instead of the more conventional MDIO bus. Such PHYs can be found in SFP adapters and SFF modules. Since PHYs appear at I2C bus address 0x40..0x5f, and 0x50/0x51 are reserved for SFP EEPROMs/diagnostics, we must not allow the MDIO bus to access these I2C addresses. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 10 +++++ drivers/net/phy/Makefile | 1 + drivers/net/phy/mdio-i2c.c | 109 +++++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/mdio-i2c.h | 19 ++++++++ 4 files changed, 139 insertions(+) create mode 100644 drivers/net/phy/mdio-i2c.c create mode 100644 drivers/net/phy/mdio-i2c.h (limited to 'drivers/net') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 928fd892f167..a1d6fdba8980 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -106,6 +106,16 @@ config MDIO_HISI_FEMAC This module provides a driver for the MDIO busses found in the Hisilicon SoC that have an Fast Ethernet MAC. +config MDIO_I2C + tristate + depends on I2C + help + Support I2C based PHYs. This provides a MDIO bus bridged + to I2C to allow PHYs connected in I2C mode to be accessed + using the existing infrastructure. + + This is library mode. + config MDIO_MOXART tristate "MOXA ART MDIO interface support" depends on ARCH_MOXART diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 8e9b9f349384..113e8d525c5e 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o +obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o diff --git a/drivers/net/phy/mdio-i2c.c b/drivers/net/phy/mdio-i2c.c new file mode 100644 index 000000000000..6d24fd13ca86 --- /dev/null +++ b/drivers/net/phy/mdio-i2c.c @@ -0,0 +1,109 @@ +/* + * MDIO I2C bridge + * + * Copyright (C) 2015-2016 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Network PHYs can appear on I2C buses when they are part of SFP module. + * This driver exposes these PHYs to the networking PHY code, allowing + * our PHY drivers access to these PHYs, and so allowing configuration + * of their settings. + */ +#include +#include + +#include "mdio-i2c.h" + +/* + * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is + * specified to be present in SFP modules. These correspond with PHY + * addresses 16 and 17. Disallow access to these "phy" addresses. + */ +static bool i2c_mii_valid_phy_id(int phy_id) +{ + return phy_id != 0x10 && phy_id != 0x11; +} + +static unsigned int i2c_mii_phy_addr(int phy_id) +{ + return phy_id + 0x40; +} + +static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct i2c_adapter *i2c = bus->priv; + struct i2c_msg msgs[2]; + u8 data[2], dev_addr = reg; + int bus_addr, ret; + + if (!i2c_mii_valid_phy_id(phy_id)) + return 0xffff; + + bus_addr = i2c_mii_phy_addr(phy_id); + msgs[0].addr = bus_addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &dev_addr; + msgs[1].addr = bus_addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(data); + msgs[1].buf = data; + + ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return 0xffff; + + return data[0] << 8 | data[1]; +} + +static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val) +{ + struct i2c_adapter *i2c = bus->priv; + struct i2c_msg msg; + int ret; + u8 data[3]; + + if (!i2c_mii_valid_phy_id(phy_id)) + return 0; + + data[0] = reg; + data[1] = val >> 8; + data[2] = val; + + msg.addr = i2c_mii_phy_addr(phy_id); + msg.flags = 0; + msg.len = 3; + msg.buf = data; + + ret = i2c_transfer(i2c, &msg, 1); + + return ret < 0 ? ret : 0; +} + +struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c) +{ + struct mii_bus *mii; + + if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) + return ERR_PTR(-EINVAL); + + mii = mdiobus_alloc(); + if (!mii) + return ERR_PTR(-ENOMEM); + + snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent)); + mii->parent = parent; + mii->read = i2c_mii_read; + mii->write = i2c_mii_write; + mii->priv = i2c; + + return mii; +} +EXPORT_SYMBOL_GPL(mdio_i2c_alloc); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("MDIO I2C bridge library"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/phy/mdio-i2c.h b/drivers/net/phy/mdio-i2c.h new file mode 100644 index 000000000000..889ab57d7f3e --- /dev/null +++ b/drivers/net/phy/mdio-i2c.h @@ -0,0 +1,19 @@ +/* + * MDIO I2C bridge + * + * Copyright (C) 2015 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef MDIO_I2C_H +#define MDIO_I2C_H + +struct device; +struct i2c_adapter; +struct mii_bus; + +struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c); + +#endif -- cgit v1.2.3 From 9525ae83959b60c6061fe2f2caabdc8f69a48bc6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:03:13 +0100 Subject: phylink: add phylink infrastructure The link between the ethernet MAC and its PHY has become more complex as the interface evolves. This is especially true with serdes links, where the part of the PHY is effectively integrated into the MAC. Serdes links can be connected to a variety of devices, including SFF modules soldered down onto the board with the MAC, a SFP cage with a hotpluggable SFP module which may contain a PHY or directly modulate the serdes signals onto optical media with or without a PHY, or even a classical PHY connection. Moreover, the negotiation information on serdes links comes in two varieties - SGMII mode, where the PHY provides its speed/duplex/flow control information to the MAC, and 1000base-X mode where both ends exchange their abilities and each resolve the link capabilities. This means we need a more flexible means to support these arrangements, particularly with the hotpluggable nature of SFP, where the PHY can be attached or detached after the network device has been brought up. Ethtool information can come from multiple sources: - we may have a PHY operating in either SGMII or 1000base-X mode, in which case we take ethtool/mii data directly from the PHY. - we may have a optical SFP module without a PHY, with the MAC operating in 1000base-X mode - the ethtool/mii data needs to come from the MAC. - we may have a copper SFP module with a PHY whic can't be accessed, which means we need to take ethtool/mii data from the MAC. Phylink aims to solve this by providing an intermediary between the MAC and PHY, providing a safe way for PHYs to be hotplugged, and allowing a SFP driver to reconfigure the serdes connection. Phylink also takes over support of fixed link connections, where the speed/duplex/flow control are fixed, but link status may be controlled by a GPIO signal. By avoiding the fixed-phy implementation, phylink can provide a faster response to link events: fixed-phy has to wait for phylib to operate its state machine, which can take several seconds. In comparison, phylink takes milliseconds. Signed-off-by: Russell King - remove sync status - rework supported and advertisment handling - add 1000base-x speed for fixed links - use functionality exported from phy-core, reworking __phylink_ethtool_ksettings_set for it Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 10 + drivers/net/phy/Makefile | 1 + drivers/net/phy/phy_device.c | 1 + drivers/net/phy/phylink.c | 1169 ++++++++++++++++++++++++++++++++++++++++++ include/linux/phy.h | 2 + include/linux/phylink.h | 145 ++++++ 6 files changed, 1328 insertions(+) create mode 100644 drivers/net/phy/phylink.c create mode 100644 include/linux/phylink.h (limited to 'drivers/net') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index a1d6fdba8980..a0a9e03e2f80 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -169,6 +169,16 @@ menuconfig PHYLIB devices. This option provides infrastructure for managing PHY devices. +config PHYLINK + tristate + depends on NETDEVICES + select PHYLIB + select SWPHY + help + PHYlink models the link between the PHY and MAC, allowing fixed + configuration links, PHYs, and Serdes links with MAC level + autonegotiation modes. + if PHYLIB config SWPHY diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 113e8d525c5e..c43e5b99fda4 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -18,6 +18,7 @@ endif libphy-$(CONFIG_SWPHY) += swphy.o libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_led_triggers.o +obj-$(CONFIG_PHYLINK) += phylink.o obj-$(CONFIG_PHYLIB) += libphy.o obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index d536a9a7cd2b..9493fb369682 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1084,6 +1084,7 @@ void phy_detach(struct phy_device *phydev) phydev->attached_dev->phydev = NULL; phydev->attached_dev = NULL; phy_suspend(phydev); + phydev->phylink = NULL; phy_led_triggers_unregister(phydev); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c new file mode 100644 index 000000000000..af61d7d400af --- /dev/null +++ b/drivers/net/phy/phylink.c @@ -0,0 +1,1169 @@ +/* + * phylink models the MAC to optional PHY connection, supporting + * technologies such as SFP cages where the PHY is hot-pluggable. + * + * Copyright (C) 2015 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "swphy.h" + +#define SUPPORTED_INTERFACES \ + (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_FIBRE | \ + SUPPORTED_BNC | SUPPORTED_AUI | SUPPORTED_Backplane) +#define ADVERTISED_INTERFACES \ + (ADVERTISED_TP | ADVERTISED_MII | ADVERTISED_FIBRE | \ + ADVERTISED_BNC | ADVERTISED_AUI | ADVERTISED_Backplane) + +enum { + PHYLINK_DISABLE_STOPPED, +}; + +struct phylink { + struct net_device *netdev; + const struct phylink_mac_ops *ops; + + unsigned long phylink_disable_state; /* bitmask of disables */ + struct phy_device *phydev; + phy_interface_t link_interface; /* PHY_INTERFACE_xxx */ + u8 link_an_mode; /* MLO_AN_xxx */ + u8 link_port; /* The current non-phy ethtool port */ + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + + /* The link configuration settings */ + struct phylink_link_state link_config; + struct gpio_desc *link_gpio; + + struct mutex state_mutex; + struct phylink_link_state phy_state; + struct work_struct resolve; + + bool mac_link_dropped; +}; + +static inline void linkmode_zero(unsigned long *dst) +{ + bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static inline void linkmode_copy(unsigned long *dst, const unsigned long *src) +{ + bitmap_copy(dst, src, __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static inline void linkmode_and(unsigned long *dst, const unsigned long *a, + const unsigned long *b) +{ + bitmap_and(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static inline void linkmode_or(unsigned long *dst, const unsigned long *a, + const unsigned long *b) +{ + bitmap_or(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static inline bool linkmode_empty(const unsigned long *src) +{ + return bitmap_empty(src, __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +void phylink_set_port_modes(unsigned long *mask) +{ + phylink_set(mask, TP); + phylink_set(mask, AUI); + phylink_set(mask, MII); + phylink_set(mask, FIBRE); + phylink_set(mask, BNC); + phylink_set(mask, Backplane); +} +EXPORT_SYMBOL_GPL(phylink_set_port_modes); + +static int phylink_is_empty_linkmode(const unsigned long *linkmode) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, }; + + phylink_set_port_modes(tmp); + phylink_set(tmp, Autoneg); + phylink_set(tmp, Pause); + phylink_set(tmp, Asym_Pause); + + bitmap_andnot(tmp, linkmode, tmp, __ETHTOOL_LINK_MODE_MASK_NBITS); + + return linkmode_empty(tmp); +} + +static const char *phylink_an_mode_str(unsigned int mode) +{ + static const char *modestr[] = { + [MLO_AN_PHY] = "phy", + [MLO_AN_FIXED] = "fixed", + [MLO_AN_SGMII] = "SGMII", + [MLO_AN_8023Z] = "802.3z", + }; + + return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; +} + +static int phylink_validate(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state) +{ + pl->ops->validate(pl->netdev, supported, state); + + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; +} + +static int phylink_parse_fixedlink(struct phylink *pl, struct device_node *np) +{ + struct device_node *fixed_node; + const struct phy_setting *s; + struct gpio_desc *desc; + const __be32 *fixed_prop; + u32 speed; + int ret, len; + + fixed_node = of_get_child_by_name(np, "fixed-link"); + if (fixed_node) { + ret = of_property_read_u32(fixed_node, "speed", &speed); + + pl->link_config.speed = speed; + pl->link_config.duplex = DUPLEX_HALF; + + if (of_property_read_bool(fixed_node, "full-duplex")) + pl->link_config.duplex = DUPLEX_FULL; + + /* We treat the "pause" and "asym-pause" terminology as + * defining the link partner's ability. */ + if (of_property_read_bool(fixed_node, "pause")) + pl->link_config.pause |= MLO_PAUSE_SYM; + if (of_property_read_bool(fixed_node, "asym-pause")) + pl->link_config.pause |= MLO_PAUSE_ASYM; + + if (ret == 0) { + desc = fwnode_get_named_gpiod(&fixed_node->fwnode, + "link-gpios", 0, + GPIOD_IN, "?"); + + if (!IS_ERR(desc)) + pl->link_gpio = desc; + else if (desc == ERR_PTR(-EPROBE_DEFER)) + ret = -EPROBE_DEFER; + } + of_node_put(fixed_node); + + if (ret) + return ret; + } else { + fixed_prop = of_get_property(np, "fixed-link", &len); + if (!fixed_prop) { + netdev_err(pl->netdev, "broken fixed-link?\n"); + return -EINVAL; + } + if (len == 5 * sizeof(*fixed_prop)) { + pl->link_config.duplex = be32_to_cpu(fixed_prop[1]) ? + DUPLEX_FULL : DUPLEX_HALF; + pl->link_config.speed = be32_to_cpu(fixed_prop[2]); + if (be32_to_cpu(fixed_prop[3])) + pl->link_config.pause |= MLO_PAUSE_SYM; + if (be32_to_cpu(fixed_prop[4])) + pl->link_config.pause |= MLO_PAUSE_ASYM; + } + } + + if (pl->link_config.speed > SPEED_1000 && + pl->link_config.duplex != DUPLEX_FULL) + netdev_warn(pl->netdev, "fixed link specifies half duplex for %dMbps link?\n", + pl->link_config.speed); + + bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + linkmode_copy(pl->link_config.advertising, pl->supported); + phylink_validate(pl, pl->supported, &pl->link_config); + + s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex, + pl->supported, + __ETHTOOL_LINK_MODE_MASK_NBITS, true); + linkmode_zero(pl->supported); + phylink_set(pl->supported, MII); + if (s) { + __set_bit(s->bit, pl->supported); + } else { + netdev_warn(pl->netdev, "fixed link %s duplex %dMbps not recognised\n", + pl->link_config.duplex == DUPLEX_FULL ? "full" : "half", + pl->link_config.speed); + } + + linkmode_and(pl->link_config.advertising, pl->link_config.advertising, + pl->supported); + + pl->link_config.link = 1; + pl->link_config.an_complete = 1; + + return 0; +} + +static int phylink_parse_mode(struct phylink *pl, struct device_node *np) +{ + struct device_node *dn; + const char *managed; + + dn = of_get_child_by_name(np, "fixed-link"); + if (dn || of_find_property(np, "fixed-link", NULL)) + pl->link_an_mode = MLO_AN_FIXED; + of_node_put(dn); + + if (of_property_read_string(np, "managed", &managed) == 0 && + strcmp(managed, "in-band-status") == 0) { + if (pl->link_an_mode == MLO_AN_FIXED) { + netdev_err(pl->netdev, + "can't use both fixed-link and in-band-status\n"); + return -EINVAL; + } + + linkmode_zero(pl->supported); + phylink_set(pl->supported, MII); + phylink_set(pl->supported, Autoneg); + phylink_set(pl->supported, Asym_Pause); + phylink_set(pl->supported, Pause); + pl->link_config.an_enabled = true; + + switch (pl->link_config.interface) { + case PHY_INTERFACE_MODE_SGMII: + phylink_set(pl->supported, 10baseT_Half); + phylink_set(pl->supported, 10baseT_Full); + phylink_set(pl->supported, 100baseT_Half); + phylink_set(pl->supported, 100baseT_Full); + phylink_set(pl->supported, 1000baseT_Half); + phylink_set(pl->supported, 1000baseT_Full); + pl->link_an_mode = MLO_AN_SGMII; + break; + + case PHY_INTERFACE_MODE_1000BASEX: + phylink_set(pl->supported, 1000baseX_Full); + pl->link_an_mode = MLO_AN_8023Z; + break; + + case PHY_INTERFACE_MODE_2500BASEX: + phylink_set(pl->supported, 2500baseX_Full); + pl->link_an_mode = MLO_AN_8023Z; + break; + + default: + netdev_err(pl->netdev, + "incorrect link mode %s for in-band status\n", + phy_modes(pl->link_config.interface)); + return -EINVAL; + } + + linkmode_copy(pl->link_config.advertising, pl->supported); + + if (phylink_validate(pl, pl->supported, &pl->link_config)) { + netdev_err(pl->netdev, + "failed to validate link configuration for in-band status\n"); + return -EINVAL; + } + } + + return 0; +} + +static void phylink_mac_config(struct phylink *pl, + const struct phylink_link_state *state) +{ + netdev_dbg(pl->netdev, + "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", + __func__, phylink_an_mode_str(pl->link_an_mode), + phy_modes(state->interface), + phy_speed_to_str(state->speed), + phy_duplex_to_str(state->duplex), + __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, + state->pause, state->link, state->an_enabled); + + pl->ops->mac_config(pl->netdev, pl->link_an_mode, state); +} + +static void phylink_mac_an_restart(struct phylink *pl) +{ + if (pl->link_config.an_enabled && + (pl->link_config.interface == PHY_INTERFACE_MODE_1000BASEX || + pl->link_config.interface == PHY_INTERFACE_MODE_2500BASEX)) + pl->ops->mac_an_restart(pl->netdev); +} + +static int phylink_get_mac_state(struct phylink *pl, struct phylink_link_state *state) +{ + struct net_device *ndev = pl->netdev; + + linkmode_copy(state->advertising, pl->link_config.advertising); + linkmode_zero(state->lp_advertising); + state->interface = pl->link_config.interface; + state->an_enabled = pl->link_config.an_enabled; + state->link = 1; + + return pl->ops->mac_link_state(ndev, state); +} + +/* The fixed state is... fixed except for the link state, + * which may be determined by a GPIO. + */ +static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_state *state) +{ + *state = pl->link_config; + if (pl->link_gpio) + state->link = !!gpiod_get_value(pl->link_gpio); +} + +/* Flow control is resolved according to our and the link partners + * advertisments using the following drawn from the 802.3 specs: + * Local device Link partner + * Pause AsymDir Pause AsymDir Result + * 1 X 1 X TX+RX + * 0 1 1 1 RX + * 1 1 0 1 TX + */ +static void phylink_resolve_flow(struct phylink *pl, + struct phylink_link_state *state) +{ + int new_pause = 0; + + if (pl->link_config.pause & MLO_PAUSE_AN) { + int pause = 0; + + if (phylink_test(pl->link_config.advertising, Pause)) + pause |= MLO_PAUSE_SYM; + if (phylink_test(pl->link_config.advertising, Asym_Pause)) + pause |= MLO_PAUSE_ASYM; + + pause &= state->pause; + + if (pause & MLO_PAUSE_SYM) + new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX; + else if (pause & MLO_PAUSE_ASYM) + new_pause = state->pause & MLO_PAUSE_SYM ? + MLO_PAUSE_RX : MLO_PAUSE_TX; + } else { + new_pause = pl->link_config.pause & MLO_PAUSE_TXRX_MASK; + } + + state->pause &= ~MLO_PAUSE_TXRX_MASK; + state->pause |= new_pause; +} + +static const char *phylink_pause_to_str(int pause) +{ + switch (pause & MLO_PAUSE_TXRX_MASK) { + case MLO_PAUSE_TX | MLO_PAUSE_RX: + return "rx/tx"; + case MLO_PAUSE_TX: + return "tx"; + case MLO_PAUSE_RX: + return "rx"; + default: + return "off"; + } +} + +static void phylink_resolve(struct work_struct *w) +{ + struct phylink *pl = container_of(w, struct phylink, resolve); + struct phylink_link_state link_state; + struct net_device *ndev = pl->netdev; + + mutex_lock(&pl->state_mutex); + if (pl->phylink_disable_state) { + pl->mac_link_dropped = false; + link_state.link = false; + } else if (pl->mac_link_dropped) { + link_state.link = false; + } else { + switch (pl->link_an_mode) { + case MLO_AN_PHY: + link_state = pl->phy_state; + phylink_resolve_flow(pl, &link_state); + phylink_mac_config(pl, &link_state); + break; + + case MLO_AN_FIXED: + phylink_get_fixed_state(pl, &link_state); + phylink_mac_config(pl, &link_state); + break; + + case MLO_AN_SGMII: + phylink_get_mac_state(pl, &link_state); + if (pl->phydev) { + bool changed = false; + + link_state.link = link_state.link && + pl->phy_state.link; + + if (pl->phy_state.interface != + link_state.interface) { + link_state.interface = pl->phy_state.interface; + changed = true; + } + + /* Propagate the flow control from the PHY + * to the MAC. Also propagate the interface + * if changed. + */ + if (pl->phy_state.link || changed) { + link_state.pause |= pl->phy_state.pause; + phylink_resolve_flow(pl, &link_state); + + phylink_mac_config(pl, &link_state); + } + } + break; + + case MLO_AN_8023Z: + phylink_get_mac_state(pl, &link_state); + break; + } + } + + if (link_state.link != netif_carrier_ok(ndev)) { + if (!link_state.link) { + netif_carrier_off(ndev); + pl->ops->mac_link_down(ndev, pl->link_an_mode); + netdev_info(ndev, "Link is Down\n"); + } else { + pl->ops->mac_link_up(ndev, pl->link_an_mode, + pl->phydev); + + netif_carrier_on(ndev); + + netdev_info(ndev, + "Link is Up - %s/%s - flow control %s\n", + phy_speed_to_str(link_state.speed), + phy_duplex_to_str(link_state.duplex), + phylink_pause_to_str(link_state.pause)); + } + } + if (!link_state.link && pl->mac_link_dropped) { + pl->mac_link_dropped = false; + queue_work(system_power_efficient_wq, &pl->resolve); + } + mutex_unlock(&pl->state_mutex); +} + +static void phylink_run_resolve(struct phylink *pl) +{ + if (!pl->phylink_disable_state) + queue_work(system_power_efficient_wq, &pl->resolve); +} + +struct phylink *phylink_create(struct net_device *ndev, struct device_node *np, + phy_interface_t iface, const struct phylink_mac_ops *ops) +{ + struct phylink *pl; + int ret; + + pl = kzalloc(sizeof(*pl), GFP_KERNEL); + if (!pl) + return ERR_PTR(-ENOMEM); + + mutex_init(&pl->state_mutex); + INIT_WORK(&pl->resolve, phylink_resolve); + pl->netdev = ndev; + pl->phy_state.interface = iface; + pl->link_interface = iface; + pl->link_port = PORT_MII; + pl->link_config.interface = iface; + pl->link_config.pause = MLO_PAUSE_AN; + pl->link_config.speed = SPEED_UNKNOWN; + pl->link_config.duplex = DUPLEX_UNKNOWN; + pl->ops = ops; + __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); + + bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + linkmode_copy(pl->link_config.advertising, pl->supported); + phylink_validate(pl, pl->supported, &pl->link_config); + + ret = phylink_parse_mode(pl, np); + if (ret < 0) { + kfree(pl); + return ERR_PTR(ret); + } + + if (pl->link_an_mode == MLO_AN_FIXED) { + ret = phylink_parse_fixedlink(pl, np); + if (ret < 0) { + kfree(pl); + return ERR_PTR(ret); + } + } + + return pl; +} +EXPORT_SYMBOL_GPL(phylink_create); + +void phylink_destroy(struct phylink *pl) +{ + cancel_work_sync(&pl->resolve); + kfree(pl); +} +EXPORT_SYMBOL_GPL(phylink_destroy); + +void phylink_phy_change(struct phy_device *phydev, bool up, bool do_carrier) +{ + struct phylink *pl = phydev->phylink; + + mutex_lock(&pl->state_mutex); + pl->phy_state.speed = phydev->speed; + pl->phy_state.duplex = phydev->duplex; + pl->phy_state.pause = MLO_PAUSE_NONE; + if (phydev->pause) + pl->phy_state.pause |= MLO_PAUSE_SYM; + if (phydev->asym_pause) + pl->phy_state.pause |= MLO_PAUSE_ASYM; + pl->phy_state.interface = phydev->interface; + pl->phy_state.link = up; + mutex_unlock(&pl->state_mutex); + + phylink_run_resolve(pl); + + netdev_dbg(pl->netdev, "phy link %s %s/%s/%s\n", up ? "up" : "down", + phy_modes(phydev->interface), + phy_speed_to_str(phydev->speed), + phy_duplex_to_str(phydev->duplex)); +} + +static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) +{ + struct phylink_link_state config; + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + u32 advertising; + int ret; + + memset(&config, 0, sizeof(config)); + ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported); + ethtool_convert_legacy_u32_to_link_mode(config.advertising, + phy->advertising); + config.interface = pl->link_config.interface; + + /* + * This is the new way of dealing with flow control for PHYs, + * as described by Timur Tabi in commit 529ed1275263 ("net: phy: + * phy drivers should not set SUPPORTED_[Asym_]Pause") except + * using our validate call to the MAC, we rely upon the MAC + * clearing the bits from both supported and advertising fields. + */ + if (phylink_test(supported, Pause)) + phylink_set(config.advertising, Pause); + if (phylink_test(supported, Asym_Pause)) + phylink_set(config.advertising, Asym_Pause); + + ret = phylink_validate(pl, supported, &config); + if (ret) + return ret; + + phy->phylink = pl; + phy->phy_link_change = phylink_phy_change; + + netdev_info(pl->netdev, + "PHY [%s] driver [%s]\n", dev_name(&phy->mdio.dev), + phy->drv->name); + + mutex_lock(&phy->lock); + mutex_lock(&pl->state_mutex); + pl->netdev->phydev = phy; + pl->phydev = phy; + linkmode_copy(pl->supported, supported); + linkmode_copy(pl->link_config.advertising, config.advertising); + + /* Restrict the phy advertisment according to the MAC support. */ + ethtool_convert_link_mode_to_legacy_u32(&advertising, config.advertising); + phy->advertising = advertising; + mutex_unlock(&pl->state_mutex); + mutex_unlock(&phy->lock); + + netdev_dbg(pl->netdev, + "phy: setting supported %*pb advertising 0x%08x\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported, + phy->advertising); + + phy_start_machine(phy); + if (phy->irq > 0) + phy_start_interrupts(phy); + + return 0; +} + +int phylink_connect_phy(struct phylink *pl, struct phy_device *phy) +{ + int ret; + + ret = phy_attach_direct(pl->netdev, phy, 0, pl->link_interface); + if (ret) + return ret; + + ret = phylink_bringup_phy(pl, phy); + if (ret) + phy_detach(phy); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_connect_phy); + +int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn) +{ + struct device_node *phy_node; + struct phy_device *phy_dev; + int ret; + + /* Fixed links are handled without needing a PHY */ + if (pl->link_an_mode == MLO_AN_FIXED) + return 0; + + phy_node = of_parse_phandle(dn, "phy-handle", 0); + if (!phy_node) + phy_node = of_parse_phandle(dn, "phy", 0); + if (!phy_node) + phy_node = of_parse_phandle(dn, "phy-device", 0); + + if (!phy_node) { + if (pl->link_an_mode == MLO_AN_PHY) { + netdev_err(pl->netdev, "unable to find PHY node\n"); + return -ENODEV; + } + return 0; + } + + phy_dev = of_phy_attach(pl->netdev, phy_node, 0, pl->link_interface); + /* We're done with the phy_node handle */ + of_node_put(phy_node); + + if (!phy_dev) + return -ENODEV; + + ret = phylink_bringup_phy(pl, phy_dev); + if (ret) + phy_detach(phy_dev); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_of_phy_connect); + +void phylink_disconnect_phy(struct phylink *pl) +{ + struct phy_device *phy; + + WARN_ON(!lockdep_rtnl_is_held()); + + phy = pl->phydev; + if (phy) { + mutex_lock(&phy->lock); + mutex_lock(&pl->state_mutex); + pl->netdev->phydev = NULL; + pl->phydev = NULL; + mutex_unlock(&pl->state_mutex); + mutex_unlock(&phy->lock); + flush_work(&pl->resolve); + + phy_disconnect(phy); + } +} +EXPORT_SYMBOL_GPL(phylink_disconnect_phy); + +void phylink_mac_change(struct phylink *pl, bool up) +{ + if (!up) + pl->mac_link_dropped = true; + phylink_run_resolve(pl); + netdev_dbg(pl->netdev, "mac link %s\n", up ? "up" : "down"); +} +EXPORT_SYMBOL_GPL(phylink_mac_change); + +void phylink_start(struct phylink *pl) +{ + WARN_ON(!lockdep_rtnl_is_held()); + + netdev_info(pl->netdev, "configuring for %s/%s link mode\n", + phylink_an_mode_str(pl->link_an_mode), + phy_modes(pl->link_config.interface)); + + /* Apply the link configuration to the MAC when starting. This allows + * a fixed-link to start with the correct parameters, and also + * ensures that we set the appropriate advertisment for Serdes links. + */ + phylink_resolve_flow(pl, &pl->link_config); + phylink_mac_config(pl, &pl->link_config); + + clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); + phylink_run_resolve(pl); + + if (pl->phydev) + phy_start(pl->phydev); +} +EXPORT_SYMBOL_GPL(phylink_start); + +void phylink_stop(struct phylink *pl) +{ + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->phydev) + phy_stop(pl->phydev); + + set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); + flush_work(&pl->resolve); +} +EXPORT_SYMBOL_GPL(phylink_stop); + +void phylink_ethtool_get_wol(struct phylink *pl, struct ethtool_wolinfo *wol) +{ + WARN_ON(!lockdep_rtnl_is_held()); + + wol->supported = 0; + wol->wolopts = 0; + + if (pl->phydev) + phy_ethtool_get_wol(pl->phydev, wol); +} +EXPORT_SYMBOL_GPL(phylink_ethtool_get_wol); + +int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol) +{ + int ret = -EOPNOTSUPP; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->phydev) + ret = phy_ethtool_set_wol(pl->phydev, wol); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_ethtool_set_wol); + +static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); + + linkmode_zero(mask); + phylink_set_port_modes(mask); + + linkmode_and(dst, dst, mask); + linkmode_or(dst, dst, b); +} + +static void phylink_get_ksettings(const struct phylink_link_state *state, + struct ethtool_link_ksettings *kset) +{ + phylink_merge_link_mode(kset->link_modes.advertising, state->advertising); + linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising); + kset->base.speed = state->speed; + kset->base.duplex = state->duplex; + kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE : + AUTONEG_DISABLE; +} + +int phylink_ethtool_ksettings_get(struct phylink *pl, + struct ethtool_link_ksettings *kset) +{ + struct phylink_link_state link_state; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->phydev) { + phy_ethtool_ksettings_get(pl->phydev, kset); + } else { + kset->base.port = pl->link_port; + } + + linkmode_copy(kset->link_modes.supported, pl->supported); + + switch (pl->link_an_mode) { + case MLO_AN_FIXED: + /* We are using fixed settings. Report these as the + * current link settings - and note that these also + * represent the supported speeds/duplex/pause modes. + */ + phylink_get_fixed_state(pl, &link_state); + phylink_get_ksettings(&link_state, kset); + break; + + case MLO_AN_SGMII: + /* If there is a phy attached, then use the reported + * settings from the phy with no modification. + */ + if (pl->phydev) + break; + + case MLO_AN_8023Z: + phylink_get_mac_state(pl, &link_state); + + /* The MAC is reporting the link results from its own PCS + * layer via in-band status. Report these as the current + * link settings. + */ + phylink_get_ksettings(&link_state, kset); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get); + +int phylink_ethtool_ksettings_set(struct phylink *pl, + const struct ethtool_link_ksettings *kset) +{ + struct ethtool_link_ksettings our_kset; + struct phylink_link_state config; + int ret; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (kset->base.autoneg != AUTONEG_DISABLE && + kset->base.autoneg != AUTONEG_ENABLE) + return -EINVAL; + + config = pl->link_config; + + /* Mask out unsupported advertisments */ + linkmode_and(config.advertising, kset->link_modes.advertising, + pl->supported); + + /* FIXME: should we reject autoneg if phy/mac does not support it? */ + if (kset->base.autoneg == AUTONEG_DISABLE) { + const struct phy_setting *s; + + /* Autonegotiation disabled, select a suitable speed and + * duplex. + */ + s = phy_lookup_setting(kset->base.speed, kset->base.duplex, + pl->supported, + __ETHTOOL_LINK_MODE_MASK_NBITS, false); + if (!s) + return -EINVAL; + + /* If we have a fixed link (as specified by firmware), refuse + * to change link parameters. + */ + if (pl->link_an_mode == MLO_AN_FIXED && + (s->speed != pl->link_config.speed || + s->duplex != pl->link_config.duplex)) + return -EINVAL; + + config.speed = s->speed; + config.duplex = s->duplex; + config.an_enabled = false; + + __clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); + } else { + /* If we have a fixed link, refuse to enable autonegotiation */ + if (pl->link_an_mode == MLO_AN_FIXED) + return -EINVAL; + + config.speed = SPEED_UNKNOWN; + config.duplex = DUPLEX_UNKNOWN; + config.an_enabled = true; + + __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); + } + + if (phylink_validate(pl, pl->supported, &config)) + return -EINVAL; + + /* If autonegotiation is enabled, we must have an advertisment */ + if (config.an_enabled && phylink_is_empty_linkmode(config.advertising)) + return -EINVAL; + + our_kset = *kset; + linkmode_copy(our_kset.link_modes.advertising, config.advertising); + our_kset.base.speed = config.speed; + our_kset.base.duplex = config.duplex; + + /* If we have a PHY, configure the phy */ + if (pl->phydev) { + ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset); + if (ret) + return ret; + } + + mutex_lock(&pl->state_mutex); + /* Configure the MAC to match the new settings */ + linkmode_copy(pl->link_config.advertising, our_kset.link_modes.advertising); + pl->link_config.speed = our_kset.base.speed; + pl->link_config.duplex = our_kset.base.duplex; + pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE; + + if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { + phylink_mac_config(pl, &pl->link_config); + phylink_mac_an_restart(pl); + } + mutex_unlock(&pl->state_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_set); + +int phylink_ethtool_nway_reset(struct phylink *pl) +{ + int ret = 0; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->phydev) + ret = phy_restart_aneg(pl->phydev); + phylink_mac_an_restart(pl); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset); + +void phylink_ethtool_get_pauseparam(struct phylink *pl, + struct ethtool_pauseparam *pause) +{ + WARN_ON(!lockdep_rtnl_is_held()); + + pause->autoneg = !!(pl->link_config.pause & MLO_PAUSE_AN); + pause->rx_pause = !!(pl->link_config.pause & MLO_PAUSE_RX); + pause->tx_pause = !!(pl->link_config.pause & MLO_PAUSE_TX); +} +EXPORT_SYMBOL_GPL(phylink_ethtool_get_pauseparam); + +int phylink_ethtool_set_pauseparam(struct phylink *pl, + struct ethtool_pauseparam *pause) +{ + struct phylink_link_state *config = &pl->link_config; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (!phylink_test(pl->supported, Pause) && + !phylink_test(pl->supported, Asym_Pause)) + return -EOPNOTSUPP; + + if (!phylink_test(pl->supported, Asym_Pause) && + !pause->autoneg && pause->rx_pause != pause->tx_pause) + return -EINVAL; + + config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK); + + if (pause->autoneg) + config->pause |= MLO_PAUSE_AN; + if (pause->rx_pause) + config->pause |= MLO_PAUSE_RX; + if (pause->tx_pause) + config->pause |= MLO_PAUSE_TX; + + if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { + switch (pl->link_an_mode) { + case MLO_AN_PHY: + /* Silently mark the carrier down, and then trigger a resolve */ + netif_carrier_off(pl->netdev); + phylink_run_resolve(pl); + break; + + case MLO_AN_FIXED: + /* Should we allow fixed links to change against the config? */ + phylink_resolve_flow(pl, config); + phylink_mac_config(pl, config); + break; + + case MLO_AN_SGMII: + case MLO_AN_8023Z: + phylink_mac_config(pl, config); + phylink_mac_an_restart(pl); + break; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam); + +int phylink_init_eee(struct phylink *pl, bool clk_stop_enable) +{ + int ret = -EPROTONOSUPPORT; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->phydev) + ret = phy_init_eee(pl->phydev, clk_stop_enable); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_init_eee); + +int phylink_get_eee_err(struct phylink *pl) +{ + int ret = 0; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->phydev) + ret = phy_get_eee_err(pl->phydev); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_get_eee_err); + +int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee) +{ + int ret = -EOPNOTSUPP; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->phydev) + ret = phy_ethtool_get_eee(pl->phydev, eee); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee); + +int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee) +{ + int ret = -EOPNOTSUPP; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->phydev) + ret = phy_ethtool_set_eee(pl->phydev, eee); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee); + +/* This emulates MII registers for a fixed-mode phy operating as per the + * passed in state. "aneg" defines if we report negotiation is possible. + * + * FIXME: should deal with negotiation state too. + */ +static int phylink_mii_emul_read(struct net_device *ndev, unsigned int reg, + struct phylink_link_state *state, bool aneg) +{ + struct fixed_phy_status fs; + int val; + + fs.link = state->link; + fs.speed = state->speed; + fs.duplex = state->duplex; + fs.pause = state->pause & MLO_PAUSE_SYM; + fs.asym_pause = state->pause & MLO_PAUSE_ASYM; + + val = swphy_read_reg(reg, &fs); + if (reg == MII_BMSR) { + if (!state->an_complete) + val &= ~BMSR_ANEGCOMPLETE; + if (!aneg) + val &= ~BMSR_ANEGCAPABLE; + } + return val; +} + +static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, + unsigned int reg) +{ + struct phylink_link_state state; + int val = 0xffff; + + /* PHYs only exist for MLO_AN_PHY and MLO_AN_SGMII */ + if (pl->phydev) + return mdiobus_read(pl->phydev->mdio.bus, phy_id, reg); + + switch (pl->link_an_mode) { + case MLO_AN_FIXED: + if (phy_id == 0) { + phylink_get_fixed_state(pl, &state); + val = phylink_mii_emul_read(pl->netdev, reg, &state, + true); + } + break; + + case MLO_AN_PHY: + return -EOPNOTSUPP; + + case MLO_AN_SGMII: + /* No phy, fall through to 8023z method */ + case MLO_AN_8023Z: + if (phy_id == 0) { + val = phylink_get_mac_state(pl, &state); + if (val < 0) + return val; + + val = phylink_mii_emul_read(pl->netdev, reg, &state, + true); + } + break; + } + + return val & 0xffff; +} + +static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, + unsigned int reg, unsigned int val) +{ + /* PHYs only exist for MLO_AN_PHY and MLO_AN_SGMII */ + if (pl->phydev) { + mdiobus_write(pl->phydev->mdio.bus, phy_id, reg, val); + return 0; + } + + switch (pl->link_an_mode) { + case MLO_AN_FIXED: + break; + + case MLO_AN_PHY: + return -EOPNOTSUPP; + + case MLO_AN_SGMII: + /* No phy, fall through to 8023z method */ + case MLO_AN_8023Z: + break; + } + + return 0; +} + +int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) +{ + struct mii_ioctl_data *mii_data = if_mii(ifr); + int val, ret; + + WARN_ON(!lockdep_rtnl_is_held()); + + switch (cmd) { + case SIOCGMIIPHY: + mii_data->phy_id = pl->phydev ? pl->phydev->mdio.addr : 0; + /* fallthrough */ + + case SIOCGMIIREG: + val = phylink_mii_read(pl, mii_data->phy_id, mii_data->reg_num); + if (val < 0) { + ret = val; + } else { + mii_data->val_out = val; + ret = 0; + } + break; + + case SIOCSMIIREG: + ret = phylink_mii_write(pl, mii_data->phy_id, mii_data->reg_num, + mii_data->val_in); + break; + + default: + ret = -EOPNOTSUPP; + if (pl->phydev) + ret = phy_mii_ioctl(pl->phydev, ifr, cmd); + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_mii_ioctl); + +MODULE_LICENSE("GPL"); diff --git a/include/linux/phy.h b/include/linux/phy.h index 0a5e8e62c9e0..d78cd01ea513 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -182,6 +182,7 @@ static inline const char *phy_modes(phy_interface_t interface) #define MII_ADDR_C45 (1<<30) struct device; +struct phylink; struct sk_buff; /* @@ -469,6 +470,7 @@ struct phy_device { struct mutex lock; + struct phylink *phylink; struct net_device *attached_dev; u8 mdix; diff --git a/include/linux/phylink.h b/include/linux/phylink.h new file mode 100644 index 000000000000..76f054f39684 --- /dev/null +++ b/include/linux/phylink.h @@ -0,0 +1,145 @@ +#ifndef NETDEV_PCS_H +#define NETDEV_PCS_H + +#include +#include +#include + +struct device_node; +struct ethtool_cmd; +struct net_device; + +enum { + MLO_PAUSE_NONE, + MLO_PAUSE_ASYM = BIT(0), + MLO_PAUSE_SYM = BIT(1), + MLO_PAUSE_RX = BIT(2), + MLO_PAUSE_TX = BIT(3), + MLO_PAUSE_TXRX_MASK = MLO_PAUSE_TX | MLO_PAUSE_RX, + MLO_PAUSE_AN = BIT(4), + + MLO_AN_PHY = 0, /* Conventional PHY */ + MLO_AN_FIXED, /* Fixed-link mode */ + MLO_AN_SGMII, /* Cisco SGMII protocol */ + MLO_AN_8023Z, /* 1000base-X protocol */ +}; + +static inline bool phylink_autoneg_inband(unsigned int mode) +{ + return mode == MLO_AN_SGMII || mode == MLO_AN_8023Z; +} + +struct phylink_link_state { + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + __ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising); + phy_interface_t interface; /* PHY_INTERFACE_xxx */ + int speed; + int duplex; + int pause; + unsigned int link:1; + unsigned int an_enabled:1; + unsigned int an_complete:1; +}; + +struct phylink_mac_ops { + /** + * validate: validate and update the link configuration + * @ndev: net_device structure associated with MAC + * @config: configuration to validate + * + * Update the %config->supported and %config->advertised masks + * clearing bits that can not be supported. + * + * Note: the PHY may be able to transform from one connection + * technology to another, so, eg, don't clear 1000BaseX just + * because the MAC is unable to support it. This is more about + * clearing unsupported speeds and duplex settings. + * + * If the %config->interface mode is %PHY_INTERFACE_MODE_1000BASEX + * or %PHY_INTERFACE_MODE_2500BASEX, select the appropriate mode + * based on %config->advertised and/or %config->speed. + */ + void (*validate)(struct net_device *ndev, unsigned long *supported, + struct phylink_link_state *state); + + /* Read the current link state from the hardware */ + int (*mac_link_state)(struct net_device *, struct phylink_link_state *); + + /* Configure the MAC */ + /** + * mac_config: configure the MAC for the selected mode and state + * @ndev: net_device structure for the MAC + * @mode: one of MLO_AN_FIXED, MLO_AN_PHY, MLO_AN_8023Z, MLO_AN_SGMII + * @state: state structure + * + * The action performed depends on the currently selected mode: + * + * %MLO_AN_FIXED, %MLO_AN_PHY: + * set the specified speed, duplex, pause mode, and phy interface + * mode in the provided @state. + * %MLO_AN_8023Z: + * place the link in 1000base-X mode, advertising the parameters + * given in advertising in @state. + * %MLO_AN_SGMII: + * place the link in Cisco SGMII mode - there is no advertisment + * to make as the PHY communicates the speed and duplex to the + * MAC over the in-band control word. Configuration of the pause + * mode is as per MLO_AN_PHY since this is not included. + */ + void (*mac_config)(struct net_device *ndev, unsigned int mode, + const struct phylink_link_state *state); + + /** + * mac_an_restart: restart 802.3z BaseX autonegotiation + * @ndev: net_device structure for the MAC + */ + void (*mac_an_restart)(struct net_device *ndev); + + void (*mac_link_down)(struct net_device *, unsigned int mode); + void (*mac_link_up)(struct net_device *, unsigned int mode, + struct phy_device *); +}; + +struct phylink *phylink_create(struct net_device *, struct device_node *, + phy_interface_t iface, const struct phylink_mac_ops *ops); +void phylink_destroy(struct phylink *); + +int phylink_connect_phy(struct phylink *, struct phy_device *); +int phylink_of_phy_connect(struct phylink *, struct device_node *); +void phylink_disconnect_phy(struct phylink *); + +void phylink_mac_change(struct phylink *, bool up); + +void phylink_start(struct phylink *); +void phylink_stop(struct phylink *); + +void phylink_ethtool_get_wol(struct phylink *, struct ethtool_wolinfo *); +int phylink_ethtool_set_wol(struct phylink *, struct ethtool_wolinfo *); + +int phylink_ethtool_ksettings_get(struct phylink *, + struct ethtool_link_ksettings *); +int phylink_ethtool_ksettings_set(struct phylink *, + const struct ethtool_link_ksettings *); +int phylink_ethtool_nway_reset(struct phylink *); +void phylink_ethtool_get_pauseparam(struct phylink *, + struct ethtool_pauseparam *); +int phylink_ethtool_set_pauseparam(struct phylink *, + struct ethtool_pauseparam *); +int phylink_init_eee(struct phylink *, bool); +int phylink_get_eee_err(struct phylink *); +int phylink_ethtool_get_eee(struct phylink *, struct ethtool_eee *); +int phylink_ethtool_set_eee(struct phylink *, struct ethtool_eee *); +int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); + +#define phylink_zero(bm) \ + bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS) +#define __phylink_do_bit(op, bm, mode) \ + op(ETHTOOL_LINK_MODE_ ## mode ## _BIT, bm) + +#define phylink_set(bm, mode) __phylink_do_bit(__set_bit, bm, mode) +#define phylink_clear(bm, mode) __phylink_do_bit(__clear_bit, bm, mode) +#define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode) + +void phylink_set_port_modes(unsigned long *bits); + +#endif -- cgit v1.2.3 From ce0aa27ff3f68ed4ea1631d33797e573b3508bfa Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:03:18 +0100 Subject: sfp: add sfp-bus to bridge between network devices and sfp cages Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/Makefile | 3 + drivers/net/phy/phylink.c | 157 +++++++++++++++ drivers/net/phy/sfp-bus.c | 475 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/sfp.h | 28 +++ include/linux/sfp.h | 434 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1097 insertions(+) create mode 100644 drivers/net/phy/sfp-bus.c create mode 100644 drivers/net/phy/sfp.h create mode 100644 include/linux/sfp.h (limited to 'drivers/net') diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index c43e5b99fda4..4c16a10f420e 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -38,6 +38,9 @@ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o +sfp-obj-$(CONFIG_SFP) += sfp-bus.o +obj-y += $(sfp-obj-y) $(sfp-obj-m) + obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o obj-$(CONFIG_AT803X_PHY) += at803x.o diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index af61d7d400af..02082f4a8a95 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -21,6 +21,7 @@ #include #include +#include "sfp.h" #include "swphy.h" #define SUPPORTED_INTERFACES \ @@ -32,6 +33,7 @@ enum { PHYLINK_DISABLE_STOPPED, + PHYLINK_DISABLE_LINK, }; struct phylink { @@ -54,6 +56,8 @@ struct phylink { struct work_struct resolve; bool mac_link_dropped; + + struct sfp_bus *sfp_bus; }; static inline void linkmode_zero(unsigned long *dst) @@ -466,6 +470,24 @@ static void phylink_run_resolve(struct phylink *pl) queue_work(system_power_efficient_wq, &pl->resolve); } +static const struct sfp_upstream_ops sfp_phylink_ops; + +static int phylink_register_sfp(struct phylink *pl, struct device_node *np) +{ + struct device_node *sfp_np; + + sfp_np = of_parse_phandle(np, "sfp", 0); + if (!sfp_np) + return 0; + + pl->sfp_bus = sfp_register_upstream(sfp_np, pl->netdev, pl, + &sfp_phylink_ops); + if (!pl->sfp_bus) + return -ENOMEM; + + return 0; +} + struct phylink *phylink_create(struct net_device *ndev, struct device_node *np, phy_interface_t iface, const struct phylink_mac_ops *ops) { @@ -507,12 +529,21 @@ struct phylink *phylink_create(struct net_device *ndev, struct device_node *np, } } + ret = phylink_register_sfp(pl, np); + if (ret < 0) { + kfree(pl); + return ERR_PTR(ret); + } + return pl; } EXPORT_SYMBOL_GPL(phylink_create); void phylink_destroy(struct phylink *pl) { + if (pl->sfp_bus) + sfp_unregister_upstream(pl->sfp_bus); + cancel_work_sync(&pl->resolve); kfree(pl); } @@ -706,6 +737,8 @@ void phylink_start(struct phylink *pl) clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); phylink_run_resolve(pl); + if (pl->sfp_bus) + sfp_upstream_start(pl->sfp_bus); if (pl->phydev) phy_start(pl->phydev); } @@ -717,6 +750,8 @@ void phylink_stop(struct phylink *pl) if (pl->phydev) phy_stop(pl->phydev); + if (pl->sfp_bus) + sfp_upstream_stop(pl->sfp_bus); set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); flush_work(&pl->resolve); @@ -1166,4 +1201,126 @@ int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) } EXPORT_SYMBOL_GPL(phylink_mii_ioctl); + + +static int phylink_sfp_module_insert(void *upstream, + const struct sfp_eeprom_id *id) +{ + struct phylink *pl = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; + struct phylink_link_state config; + phy_interface_t iface; + int mode, ret = 0; + bool changed; + u8 port; + + sfp_parse_support(pl->sfp_bus, id, support); + port = sfp_parse_port(pl->sfp_bus, id, support); + iface = sfp_parse_interface(pl->sfp_bus, id); + + WARN_ON(!lockdep_rtnl_is_held()); + + switch (iface) { + case PHY_INTERFACE_MODE_SGMII: + mode = MLO_AN_SGMII; + break; + case PHY_INTERFACE_MODE_1000BASEX: + mode = MLO_AN_8023Z; + break; + default: + return -EINVAL; + } + + memset(&config, 0, sizeof(config)); + linkmode_copy(config.advertising, support); + config.interface = iface; + config.speed = SPEED_UNKNOWN; + config.duplex = DUPLEX_UNKNOWN; + config.pause = MLO_PAUSE_AN; + config.an_enabled = pl->link_config.an_enabled; + + /* Ignore errors if we're expecting a PHY to attach later */ + ret = phylink_validate(pl, support, &config); + if (ret) { + netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n", + phylink_an_mode_str(mode), phy_modes(config.interface), + __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret); + return ret; + } + + netdev_dbg(pl->netdev, "requesting link mode %s/%s with support %*pb\n", + phylink_an_mode_str(mode), phy_modes(config.interface), + __ETHTOOL_LINK_MODE_MASK_NBITS, support); + + if (mode == MLO_AN_8023Z && pl->phydev) + return -EINVAL; + + changed = !bitmap_equal(pl->supported, support, + __ETHTOOL_LINK_MODE_MASK_NBITS); + if (changed) { + linkmode_copy(pl->supported, support); + linkmode_copy(pl->link_config.advertising, config.advertising); + } + + if (pl->link_an_mode != mode || + pl->link_config.interface != config.interface) { + pl->link_config.interface = config.interface; + pl->link_an_mode = mode; + + changed = true; + + netdev_info(pl->netdev, "switched to %s/%s link mode\n", + phylink_an_mode_str(mode), + phy_modes(config.interface)); + } + + pl->link_port = port; + + if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, + &pl->phylink_disable_state)) + phylink_mac_config(pl, &pl->link_config); + + return ret; +} + +static void phylink_sfp_link_down(void *upstream) +{ + struct phylink *pl = upstream; + + WARN_ON(!lockdep_rtnl_is_held()); + + set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state); + flush_work(&pl->resolve); + + netif_carrier_off(pl->netdev); +} + +static void phylink_sfp_link_up(void *upstream) +{ + struct phylink *pl = upstream; + + WARN_ON(!lockdep_rtnl_is_held()); + + clear_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state); + phylink_run_resolve(pl); +} + +static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) +{ + return phylink_connect_phy(upstream, phy); +} + +static void phylink_sfp_disconnect_phy(void *upstream) +{ + phylink_disconnect_phy(upstream); +} + +static const struct sfp_upstream_ops sfp_phylink_ops = { + .module_insert = phylink_sfp_module_insert, + .link_up = phylink_sfp_link_up, + .link_down = phylink_sfp_link_down, + .connect_phy = phylink_sfp_connect_phy, + .disconnect_phy = phylink_sfp_disconnect_phy, +}; + MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c new file mode 100644 index 000000000000..5cb5384697ea --- /dev/null +++ b/drivers/net/phy/sfp-bus.c @@ -0,0 +1,475 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "sfp.h" + +struct sfp_bus { + struct kref kref; + struct list_head node; + struct device_node *device_node; + + const struct sfp_socket_ops *socket_ops; + struct device *sfp_dev; + struct sfp *sfp; + + const struct sfp_upstream_ops *upstream_ops; + void *upstream; + struct net_device *netdev; + struct phy_device *phydev; + + bool registered; + bool started; +}; + + +int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + unsigned long *support) +{ + int port; + + /* port is the physical connector, set this from the connector field. */ + switch (id->base.connector) { + case SFP_CONNECTOR_SC: + case SFP_CONNECTOR_FIBERJACK: + case SFP_CONNECTOR_LC: + case SFP_CONNECTOR_MT_RJ: + case SFP_CONNECTOR_MU: + case SFP_CONNECTOR_OPTICAL_PIGTAIL: + if (support) + phylink_set(support, FIBRE); + port = PORT_FIBRE; + break; + + case SFP_CONNECTOR_RJ45: + if (support) + phylink_set(support, TP); + port = PORT_TP; + break; + + case SFP_CONNECTOR_UNSPEC: + if (id->base.e1000_base_t) { + if (support) + phylink_set(support, TP); + port = PORT_TP; + break; + } + /* fallthrough */ + case SFP_CONNECTOR_SG: /* guess */ + case SFP_CONNECTOR_MPO_1X12: + case SFP_CONNECTOR_MPO_2X16: + case SFP_CONNECTOR_HSSDC_II: + case SFP_CONNECTOR_COPPER_PIGTAIL: + case SFP_CONNECTOR_NOSEPARATE: + case SFP_CONNECTOR_MXC_2X16: + port = PORT_OTHER; + break; + default: + dev_warn(bus->sfp_dev, "SFP: unknown connector id 0x%02x\n", + id->base.connector); + port = PORT_OTHER; + break; + } + + return port; +} +EXPORT_SYMBOL_GPL(sfp_parse_port); + +phy_interface_t sfp_parse_interface(struct sfp_bus *bus, + const struct sfp_eeprom_id *id) +{ + phy_interface_t iface; + + /* Setting the serdes link mode is guesswork: there's no field in + * the EEPROM which indicates what mode should be used. + * + * If the module wants 64b66b, then it must be >= 10G. + * + * If it's a gigabit-only fiber module, it probably does not have + * a PHY, so switch to 802.3z negotiation mode. Otherwise, switch + * to SGMII mode (which is required to support non-gigabit speeds). + */ + switch (id->base.encoding) { + case SFP_ENCODING_8472_64B66B: + iface = PHY_INTERFACE_MODE_10GKR; + break; + + case SFP_ENCODING_8B10B: + if (!id->base.e1000_base_t && + !id->base.e100_base_lx && + !id->base.e100_base_fx) + iface = PHY_INTERFACE_MODE_1000BASEX; + else + iface = PHY_INTERFACE_MODE_SGMII; + break; + + default: + iface = PHY_INTERFACE_MODE_NA; + dev_err(bus->sfp_dev, + "SFP module encoding does not support 8b10b nor 64b66b\n"); + break; + } + + return iface; +} +EXPORT_SYMBOL_GPL(sfp_parse_interface); + +void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + unsigned long *support) +{ + phylink_set(support, Autoneg); + phylink_set(support, Pause); + phylink_set(support, Asym_Pause); + + /* Set ethtool support from the compliance fields. */ + if (id->base.e10g_base_sr) + phylink_set(support, 10000baseSR_Full); + if (id->base.e10g_base_lr) + phylink_set(support, 10000baseLR_Full); + if (id->base.e10g_base_lrm) + phylink_set(support, 10000baseLRM_Full); + if (id->base.e10g_base_er) + phylink_set(support, 10000baseER_Full); + if (id->base.e1000_base_sx || + id->base.e1000_base_lx || + id->base.e1000_base_cx) + phylink_set(support, 1000baseX_Full); + if (id->base.e1000_base_t) { + phylink_set(support, 1000baseT_Half); + phylink_set(support, 1000baseT_Full); + } + + switch (id->base.extended_cc) { + case 0x00: /* Unspecified */ + break; + case 0x02: /* 100Gbase-SR4 or 25Gbase-SR */ + phylink_set(support, 100000baseSR4_Full); + phylink_set(support, 25000baseSR_Full); + break; + case 0x03: /* 100Gbase-LR4 or 25Gbase-LR */ + case 0x04: /* 100Gbase-ER4 or 25Gbase-ER */ + phylink_set(support, 100000baseLR4_ER4_Full); + break; + case 0x0b: /* 100Gbase-CR4 or 25Gbase-CR CA-L */ + case 0x0c: /* 25Gbase-CR CA-S */ + case 0x0d: /* 25Gbase-CR CA-N */ + phylink_set(support, 100000baseCR4_Full); + phylink_set(support, 25000baseCR_Full); + break; + default: + dev_warn(bus->sfp_dev, + "Unknown/unsupported extended compliance code: 0x%02x\n", + id->base.extended_cc); + break; + } + + /* For fibre channel SFP, derive possible BaseX modes */ + if (id->base.fc_speed_100 || + id->base.fc_speed_200 || + id->base.fc_speed_400) { + if (id->base.br_nominal >= 31) + phylink_set(support, 2500baseX_Full); + if (id->base.br_nominal >= 12) + phylink_set(support, 1000baseX_Full); + } + + switch (id->base.connector) { + case SFP_CONNECTOR_SC: + case SFP_CONNECTOR_FIBERJACK: + case SFP_CONNECTOR_LC: + case SFP_CONNECTOR_MT_RJ: + case SFP_CONNECTOR_MU: + case SFP_CONNECTOR_OPTICAL_PIGTAIL: + break; + + case SFP_CONNECTOR_UNSPEC: + if (id->base.e1000_base_t) + break; + + case SFP_CONNECTOR_SG: /* guess */ + case SFP_CONNECTOR_MPO_1X12: + case SFP_CONNECTOR_MPO_2X16: + case SFP_CONNECTOR_HSSDC_II: + case SFP_CONNECTOR_COPPER_PIGTAIL: + case SFP_CONNECTOR_NOSEPARATE: + case SFP_CONNECTOR_MXC_2X16: + default: + /* a guess at the supported link modes */ + dev_warn(bus->sfp_dev, + "Guessing link modes, please report...\n"); + phylink_set(support, 1000baseT_Half); + phylink_set(support, 1000baseT_Full); + break; + } +} +EXPORT_SYMBOL_GPL(sfp_parse_support); + + +static LIST_HEAD(sfp_buses); +static DEFINE_MUTEX(sfp_mutex); + +static const struct sfp_upstream_ops *sfp_get_upstream_ops(struct sfp_bus *bus) +{ + return bus->registered ? bus->upstream_ops : NULL; +} + +static struct sfp_bus *sfp_bus_get(struct device_node *np) +{ + struct sfp_bus *sfp, *new, *found = NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + + mutex_lock(&sfp_mutex); + + list_for_each_entry(sfp, &sfp_buses, node) { + if (sfp->device_node == np) { + kref_get(&sfp->kref); + found = sfp; + break; + } + } + + if (!found && new) { + kref_init(&new->kref); + new->device_node = np; + list_add(&new->node, &sfp_buses); + found = new; + new = NULL; + } + + mutex_unlock(&sfp_mutex); + + kfree(new); + + return found; +} + +static void sfp_bus_release(struct kref *kref) __releases(sfp_mutex) +{ + struct sfp_bus *bus = container_of(kref, struct sfp_bus, kref); + + list_del(&bus->node); + mutex_unlock(&sfp_mutex); + kfree(bus); +} + +static void sfp_bus_put(struct sfp_bus *bus) +{ + kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); +} + +static int sfp_register_bus(struct sfp_bus *bus) +{ + const struct sfp_upstream_ops *ops = bus->upstream_ops; + int ret; + + if (ops) { + if (ops->link_down) + ops->link_down(bus->upstream); + if (ops->connect_phy && bus->phydev) { + ret = ops->connect_phy(bus->upstream, bus->phydev); + if (ret) + return ret; + } + } + if (bus->started) + bus->socket_ops->start(bus->sfp); + bus->registered = true; + return 0; +} + +static void sfp_unregister_bus(struct sfp_bus *bus) +{ + const struct sfp_upstream_ops *ops = bus->upstream_ops; + + if (bus->registered) { + if (bus->started) + bus->socket_ops->stop(bus->sfp); + if (bus->phydev && ops && ops->disconnect_phy) + ops->disconnect_phy(bus->upstream); + } + bus->registered = false; +} + + +int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo) +{ + if (!bus->registered) + return -ENOIOCTLCMD; + return bus->socket_ops->module_info(bus->sfp, modinfo); +} +EXPORT_SYMBOL_GPL(sfp_get_module_info); + +int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee, + u8 *data) +{ + if (!bus->registered) + return -ENOIOCTLCMD; + return bus->socket_ops->module_eeprom(bus->sfp, ee, data); +} +EXPORT_SYMBOL_GPL(sfp_get_module_eeprom); + +void sfp_upstream_start(struct sfp_bus *bus) +{ + if (bus->registered) + bus->socket_ops->start(bus->sfp); + bus->started = true; +} +EXPORT_SYMBOL_GPL(sfp_upstream_start); + +void sfp_upstream_stop(struct sfp_bus *bus) +{ + if (bus->registered) + bus->socket_ops->stop(bus->sfp); + bus->started = false; +} +EXPORT_SYMBOL_GPL(sfp_upstream_stop); + +struct sfp_bus *sfp_register_upstream(struct device_node *np, + struct net_device *ndev, void *upstream, + const struct sfp_upstream_ops *ops) +{ + struct sfp_bus *bus = sfp_bus_get(np); + int ret = 0; + + if (bus) { + rtnl_lock(); + bus->upstream_ops = ops; + bus->upstream = upstream; + bus->netdev = ndev; + + if (bus->sfp) + ret = sfp_register_bus(bus); + rtnl_unlock(); + } + + if (ret) { + sfp_bus_put(bus); + bus = NULL; + } + + return bus; +} +EXPORT_SYMBOL_GPL(sfp_register_upstream); + +void sfp_unregister_upstream(struct sfp_bus *bus) +{ + rtnl_lock(); + sfp_unregister_bus(bus); + bus->upstream = NULL; + bus->netdev = NULL; + rtnl_unlock(); + + sfp_bus_put(bus); +} +EXPORT_SYMBOL_GPL(sfp_unregister_upstream); + + +/* Socket driver entry points */ +int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev) +{ + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + int ret = 0; + + if (ops && ops->connect_phy) + ret = ops->connect_phy(bus->upstream, phydev); + + if (ret == 0) + bus->phydev = phydev; + + return ret; +} +EXPORT_SYMBOL_GPL(sfp_add_phy); + +void sfp_remove_phy(struct sfp_bus *bus) +{ + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + + if (ops && ops->disconnect_phy) + ops->disconnect_phy(bus->upstream); + bus->phydev = NULL; +} +EXPORT_SYMBOL_GPL(sfp_remove_phy); + + +void sfp_link_up(struct sfp_bus *bus) +{ + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + + if (ops && ops->link_up) + ops->link_up(bus->upstream); +} +EXPORT_SYMBOL_GPL(sfp_link_up); + +void sfp_link_down(struct sfp_bus *bus) +{ + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + + if (ops && ops->link_down) + ops->link_down(bus->upstream); +} +EXPORT_SYMBOL_GPL(sfp_link_down); + +int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id) +{ + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + int ret = 0; + + if (ops && ops->module_insert) + ret = ops->module_insert(bus->upstream, id); + + return ret; +} +EXPORT_SYMBOL_GPL(sfp_module_insert); + +void sfp_module_remove(struct sfp_bus *bus) +{ + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + + if (ops && ops->module_remove) + ops->module_remove(bus->upstream); +} +EXPORT_SYMBOL_GPL(sfp_module_remove); + +struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp, + const struct sfp_socket_ops *ops) +{ + struct sfp_bus *bus = sfp_bus_get(dev->of_node); + int ret = 0; + + if (bus) { + rtnl_lock(); + bus->sfp_dev = dev; + bus->sfp = sfp; + bus->socket_ops = ops; + + if (bus->netdev) + ret = sfp_register_bus(bus); + rtnl_unlock(); + } + + if (ret) { + sfp_bus_put(bus); + bus = NULL; + } + + return bus; +} +EXPORT_SYMBOL_GPL(sfp_register_socket); + +void sfp_unregister_socket(struct sfp_bus *bus) +{ + rtnl_lock(); + sfp_unregister_bus(bus); + bus->sfp_dev = NULL; + bus->sfp = NULL; + bus->socket_ops = NULL; + rtnl_unlock(); + + sfp_bus_put(bus); +} +EXPORT_SYMBOL_GPL(sfp_unregister_socket); diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h new file mode 100644 index 000000000000..31b0acf337e2 --- /dev/null +++ b/drivers/net/phy/sfp.h @@ -0,0 +1,28 @@ +#ifndef SFP_H +#define SFP_H + +#include +#include + +struct sfp; + +struct sfp_socket_ops { + void (*start)(struct sfp *sfp); + void (*stop)(struct sfp *sfp); + int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo); + int (*module_eeprom)(struct sfp *sfp, struct ethtool_eeprom *ee, + u8 *data); +}; + +int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev); +void sfp_remove_phy(struct sfp_bus *bus); +void sfp_link_up(struct sfp_bus *bus); +void sfp_link_down(struct sfp_bus *bus); +int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id); +void sfp_module_remove(struct sfp_bus *bus); +int sfp_link_configure(struct sfp_bus *bus, const struct sfp_eeprom_id *id); +struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp, + const struct sfp_socket_ops *ops); +void sfp_unregister_socket(struct sfp_bus *bus); + +#endif diff --git a/include/linux/sfp.h b/include/linux/sfp.h new file mode 100644 index 000000000000..4a906f560817 --- /dev/null +++ b/include/linux/sfp.h @@ -0,0 +1,434 @@ +#ifndef LINUX_SFP_H +#define LINUX_SFP_H + +#include + +struct __packed sfp_eeprom_base { + u8 phys_id; + u8 phys_ext_id; + u8 connector; +#if defined __BIG_ENDIAN_BITFIELD + u8 e10g_base_er:1; + u8 e10g_base_lrm:1; + u8 e10g_base_lr:1; + u8 e10g_base_sr:1; + u8 if_1x_sx:1; + u8 if_1x_lx:1; + u8 if_1x_copper_active:1; + u8 if_1x_copper_passive:1; + + u8 escon_mmf_1310_led:1; + u8 escon_smf_1310_laser:1; + u8 sonet_oc192_short_reach:1; + u8 sonet_reach_bit1:1; + u8 sonet_reach_bit2:1; + u8 sonet_oc48_long_reach:1; + u8 sonet_oc48_intermediate_reach:1; + u8 sonet_oc48_short_reach:1; + + u8 unallocated_5_7:1; + u8 sonet_oc12_smf_long_reach:1; + u8 sonet_oc12_smf_intermediate_reach:1; + u8 sonet_oc12_short_reach:1; + u8 unallocated_5_3:1; + u8 sonet_oc3_smf_long_reach:1; + u8 sonet_oc3_smf_intermediate_reach:1; + u8 sonet_oc3_short_reach:1; + + u8 e_base_px:1; + u8 e_base_bx10:1; + u8 e100_base_fx:1; + u8 e100_base_lx:1; + u8 e1000_base_t:1; + u8 e1000_base_cx:1; + u8 e1000_base_lx:1; + u8 e1000_base_sx:1; + + u8 fc_ll_v:1; + u8 fc_ll_s:1; + u8 fc_ll_i:1; + u8 fc_ll_l:1; + u8 fc_ll_m:1; + u8 fc_tech_sa:1; + u8 fc_tech_lc:1; + u8 fc_tech_electrical_inter_enclosure:1; + + u8 fc_tech_electrical_intra_enclosure:1; + u8 fc_tech_sn:1; + u8 fc_tech_sl:1; + u8 fc_tech_ll:1; + u8 sfp_ct_active:1; + u8 sfp_ct_passive:1; + u8 unallocated_8_1:1; + u8 unallocated_8_0:1; + + u8 fc_media_tw:1; + u8 fc_media_tp:1; + u8 fc_media_mi:1; + u8 fc_media_tv:1; + u8 fc_media_m6:1; + u8 fc_media_m5:1; + u8 unallocated_9_1:1; + u8 fc_media_sm:1; + + u8 fc_speed_1200:1; + u8 fc_speed_800:1; + u8 fc_speed_1600:1; + u8 fc_speed_400:1; + u8 fc_speed_3200:1; + u8 fc_speed_200:1; + u8 unallocated_10_1:1; + u8 fc_speed_100:1; +#elif defined __LITTLE_ENDIAN_BITFIELD + u8 if_1x_copper_passive:1; + u8 if_1x_copper_active:1; + u8 if_1x_lx:1; + u8 if_1x_sx:1; + u8 e10g_base_sr:1; + u8 e10g_base_lr:1; + u8 e10g_base_lrm:1; + u8 e10g_base_er:1; + + u8 sonet_oc3_short_reach:1; + u8 sonet_oc3_smf_intermediate_reach:1; + u8 sonet_oc3_smf_long_reach:1; + u8 unallocated_5_3:1; + u8 sonet_oc12_short_reach:1; + u8 sonet_oc12_smf_intermediate_reach:1; + u8 sonet_oc12_smf_long_reach:1; + u8 unallocated_5_7:1; + + u8 sonet_oc48_short_reach:1; + u8 sonet_oc48_intermediate_reach:1; + u8 sonet_oc48_long_reach:1; + u8 sonet_reach_bit2:1; + u8 sonet_reach_bit1:1; + u8 sonet_oc192_short_reach:1; + u8 escon_smf_1310_laser:1; + u8 escon_mmf_1310_led:1; + + u8 e1000_base_sx:1; + u8 e1000_base_lx:1; + u8 e1000_base_cx:1; + u8 e1000_base_t:1; + u8 e100_base_lx:1; + u8 e100_base_fx:1; + u8 e_base_bx10:1; + u8 e_base_px:1; + + u8 fc_tech_electrical_inter_enclosure:1; + u8 fc_tech_lc:1; + u8 fc_tech_sa:1; + u8 fc_ll_m:1; + u8 fc_ll_l:1; + u8 fc_ll_i:1; + u8 fc_ll_s:1; + u8 fc_ll_v:1; + + u8 unallocated_8_0:1; + u8 unallocated_8_1:1; + u8 sfp_ct_passive:1; + u8 sfp_ct_active:1; + u8 fc_tech_ll:1; + u8 fc_tech_sl:1; + u8 fc_tech_sn:1; + u8 fc_tech_electrical_intra_enclosure:1; + + u8 fc_media_sm:1; + u8 unallocated_9_1:1; + u8 fc_media_m5:1; + u8 fc_media_m6:1; + u8 fc_media_tv:1; + u8 fc_media_mi:1; + u8 fc_media_tp:1; + u8 fc_media_tw:1; + + u8 fc_speed_100:1; + u8 unallocated_10_1:1; + u8 fc_speed_200:1; + u8 fc_speed_3200:1; + u8 fc_speed_400:1; + u8 fc_speed_1600:1; + u8 fc_speed_800:1; + u8 fc_speed_1200:1; +#else +#error Unknown Endian +#endif + u8 encoding; + u8 br_nominal; + u8 rate_id; + u8 link_len[6]; + char vendor_name[16]; + u8 extended_cc; + char vendor_oui[3]; + char vendor_pn[16]; + char vendor_rev[4]; + union { + __be16 optical_wavelength; + u8 cable_spec; + }; + u8 reserved62; + u8 cc_base; +}; + +struct __packed sfp_eeprom_ext { + __be16 options; + u8 br_max; + u8 br_min; + char vendor_sn[16]; + char datecode[8]; + u8 diagmon; + u8 enhopts; + u8 sff8472_compliance; + u8 cc_ext; +}; + +struct __packed sfp_eeprom_id { + struct sfp_eeprom_base base; + struct sfp_eeprom_ext ext; +}; + +/* SFP EEPROM registers */ +enum { + SFP_PHYS_ID = 0x00, + SFP_PHYS_EXT_ID = 0x01, + SFP_CONNECTOR = 0x02, + SFP_COMPLIANCE = 0x03, + SFP_ENCODING = 0x0b, + SFP_BR_NOMINAL = 0x0c, + SFP_RATE_ID = 0x0d, + SFP_LINK_LEN_SM_KM = 0x0e, + SFP_LINK_LEN_SM_100M = 0x0f, + SFP_LINK_LEN_50UM_OM2_10M = 0x10, + SFP_LINK_LEN_62_5UM_OM1_10M = 0x11, + SFP_LINK_LEN_COPPER_1M = 0x12, + SFP_LINK_LEN_50UM_OM4_10M = 0x12, + SFP_LINK_LEN_50UM_OM3_10M = 0x13, + SFP_VENDOR_NAME = 0x14, + SFP_VENDOR_OUI = 0x25, + SFP_VENDOR_PN = 0x28, + SFP_VENDOR_REV = 0x38, + SFP_OPTICAL_WAVELENGTH_MSB = 0x3c, + SFP_OPTICAL_WAVELENGTH_LSB = 0x3d, + SFP_CABLE_SPEC = 0x3c, + SFP_CC_BASE = 0x3f, + SFP_OPTIONS = 0x40, /* 2 bytes, MSB, LSB */ + SFP_BR_MAX = 0x42, + SFP_BR_MIN = 0x43, + SFP_VENDOR_SN = 0x44, + SFP_DATECODE = 0x54, + SFP_DIAGMON = 0x5c, + SFP_ENHOPTS = 0x5d, + SFP_SFF8472_COMPLIANCE = 0x5e, + SFP_CC_EXT = 0x5f, + + SFP_PHYS_ID_SFP = 0x03, + SFP_PHYS_EXT_ID_SFP = 0x04, + SFP_CONNECTOR_UNSPEC = 0x00, + /* codes 01-05 not supportable on SFP, but some modules have single SC */ + SFP_CONNECTOR_SC = 0x01, + SFP_CONNECTOR_FIBERJACK = 0x06, + SFP_CONNECTOR_LC = 0x07, + SFP_CONNECTOR_MT_RJ = 0x08, + SFP_CONNECTOR_MU = 0x09, + SFP_CONNECTOR_SG = 0x0a, + SFP_CONNECTOR_OPTICAL_PIGTAIL = 0x0b, + SFP_CONNECTOR_MPO_1X12 = 0x0c, + SFP_CONNECTOR_MPO_2X16 = 0x0d, + SFP_CONNECTOR_HSSDC_II = 0x20, + SFP_CONNECTOR_COPPER_PIGTAIL = 0x21, + SFP_CONNECTOR_RJ45 = 0x22, + SFP_CONNECTOR_NOSEPARATE = 0x23, + SFP_CONNECTOR_MXC_2X16 = 0x24, + SFP_ENCODING_UNSPEC = 0x00, + SFP_ENCODING_8B10B = 0x01, + SFP_ENCODING_4B5B = 0x02, + SFP_ENCODING_NRZ = 0x03, + SFP_ENCODING_8472_MANCHESTER = 0x04, + SFP_ENCODING_8472_SONET = 0x05, + SFP_ENCODING_8472_64B66B = 0x06, + SFP_ENCODING_256B257B = 0x07, + SFP_ENCODING_PAM4 = 0x08, + SFP_OPTIONS_HIGH_POWER_LEVEL = BIT(13), + SFP_OPTIONS_PAGING_A2 = BIT(12), + SFP_OPTIONS_RETIMER = BIT(11), + SFP_OPTIONS_COOLED_XCVR = BIT(10), + SFP_OPTIONS_POWER_DECL = BIT(9), + SFP_OPTIONS_RX_LINEAR_OUT = BIT(8), + SFP_OPTIONS_RX_DECISION_THRESH = BIT(7), + SFP_OPTIONS_TUNABLE_TX = BIT(6), + SFP_OPTIONS_RATE_SELECT = BIT(5), + SFP_OPTIONS_TX_DISABLE = BIT(4), + SFP_OPTIONS_TX_FAULT = BIT(3), + SFP_OPTIONS_LOS_INVERTED = BIT(2), + SFP_OPTIONS_LOS_NORMAL = BIT(1), + SFP_DIAGMON_DDM = BIT(6), + SFP_DIAGMON_INT_CAL = BIT(5), + SFP_DIAGMON_EXT_CAL = BIT(4), + SFP_DIAGMON_RXPWR_AVG = BIT(3), + SFP_DIAGMON_ADDRMODE = BIT(2), + SFP_ENHOPTS_ALARMWARN = BIT(7), + SFP_ENHOPTS_SOFT_TX_DISABLE = BIT(6), + SFP_ENHOPTS_SOFT_TX_FAULT = BIT(5), + SFP_ENHOPTS_SOFT_RX_LOS = BIT(4), + SFP_ENHOPTS_SOFT_RATE_SELECT = BIT(3), + SFP_ENHOPTS_APP_SELECT_SFF8079 = BIT(2), + SFP_ENHOPTS_SOFT_RATE_SFF8431 = BIT(1), + SFP_SFF8472_COMPLIANCE_NONE = 0x00, + SFP_SFF8472_COMPLIANCE_REV9_3 = 0x01, + SFP_SFF8472_COMPLIANCE_REV9_5 = 0x02, + SFP_SFF8472_COMPLIANCE_REV10_2 = 0x03, + SFP_SFF8472_COMPLIANCE_REV10_4 = 0x04, + SFP_SFF8472_COMPLIANCE_REV11_0 = 0x05, + SFP_SFF8472_COMPLIANCE_REV11_3 = 0x06, + SFP_SFF8472_COMPLIANCE_REV11_4 = 0x07, + SFP_SFF8472_COMPLIANCE_REV12_0 = 0x08, +}; + +/* SFP Diagnostics */ +enum { + /* Alarm and warnings stored MSB at lower address then LSB */ + SFP_TEMP_HIGH_ALARM = 0x00, + SFP_TEMP_LOW_ALARM = 0x02, + SFP_TEMP_HIGH_WARN = 0x04, + SFP_TEMP_LOW_WARN = 0x06, + SFP_VOLT_HIGH_ALARM = 0x08, + SFP_VOLT_LOW_ALARM = 0x0a, + SFP_VOLT_HIGH_WARN = 0x0c, + SFP_VOLT_LOW_WARN = 0x0e, + SFP_BIAS_HIGH_ALARM = 0x10, + SFP_BIAS_LOW_ALARM = 0x12, + SFP_BIAS_HIGH_WARN = 0x14, + SFP_BIAS_LOW_WARN = 0x16, + SFP_TXPWR_HIGH_ALARM = 0x18, + SFP_TXPWR_LOW_ALARM = 0x1a, + SFP_TXPWR_HIGH_WARN = 0x1c, + SFP_TXPWR_LOW_WARN = 0x1e, + SFP_RXPWR_HIGH_ALARM = 0x20, + SFP_RXPWR_LOW_ALARM = 0x22, + SFP_RXPWR_HIGH_WARN = 0x24, + SFP_RXPWR_LOW_WARN = 0x26, + SFP_LASER_TEMP_HIGH_ALARM = 0x28, + SFP_LASER_TEMP_LOW_ALARM = 0x2a, + SFP_LASER_TEMP_HIGH_WARN = 0x2c, + SFP_LASER_TEMP_LOW_WARN = 0x2e, + SFP_TEC_CUR_HIGH_ALARM = 0x30, + SFP_TEC_CUR_LOW_ALARM = 0x32, + SFP_TEC_CUR_HIGH_WARN = 0x34, + SFP_TEC_CUR_LOW_WARN = 0x36, + SFP_CAL_RXPWR4 = 0x38, + SFP_CAL_RXPWR3 = 0x3c, + SFP_CAL_RXPWR2 = 0x40, + SFP_CAL_RXPWR1 = 0x44, + SFP_CAL_RXPWR0 = 0x48, + SFP_CAL_TXI_SLOPE = 0x4c, + SFP_CAL_TXI_OFFSET = 0x4e, + SFP_CAL_TXPWR_SLOPE = 0x50, + SFP_CAL_TXPWR_OFFSET = 0x52, + SFP_CAL_T_SLOPE = 0x54, + SFP_CAL_T_OFFSET = 0x56, + SFP_CAL_V_SLOPE = 0x58, + SFP_CAL_V_OFFSET = 0x5a, + SFP_CHKSUM = 0x5f, + + SFP_TEMP = 0x60, + SFP_VCC = 0x62, + SFP_TX_BIAS = 0x64, + SFP_TX_POWER = 0x66, + SFP_RX_POWER = 0x68, + SFP_LASER_TEMP = 0x6a, + SFP_TEC_CUR = 0x6c, + + SFP_STATUS = 0x6e, + SFP_ALARM = 0x70, + + SFP_EXT_STATUS = 0x76, + SFP_VSL = 0x78, + SFP_PAGE = 0x7f, +}; + +struct device_node; +struct ethtool_eeprom; +struct ethtool_modinfo; +struct net_device; +struct sfp_bus; + +struct sfp_upstream_ops { + int (*module_insert)(void *, const struct sfp_eeprom_id *id); + void (*module_remove)(void *); + void (*link_down)(void *); + void (*link_up)(void *); + int (*connect_phy)(void *, struct phy_device *); + void (*disconnect_phy)(void *); +}; + +#if IS_ENABLED(CONFIG_SFP) +int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + unsigned long *support); +phy_interface_t sfp_parse_interface(struct sfp_bus *bus, + const struct sfp_eeprom_id *id); +void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + unsigned long *support); + +int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo); +int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee, + u8 *data); +void sfp_upstream_start(struct sfp_bus *bus); +void sfp_upstream_stop(struct sfp_bus *bus); +struct sfp_bus *sfp_register_upstream(struct device_node *np, + struct net_device *ndev, void *upstream, + const struct sfp_upstream_ops *ops); +void sfp_unregister_upstream(struct sfp_bus *bus); +#else +static inline int sfp_parse_port(struct sfp_bus *bus, + const struct sfp_eeprom_id *id, + unsigned long *support) +{ + return PORT_OTHER; +} + +static inline phy_interface_t sfp_parse_interface(struct sfp_bus *bus, + const struct sfp_eeprom_id *id) +{ + return PHY_INTERFACE_MODE_NA; +} + +static inline void sfp_parse_support(struct sfp_bus *bus, + const struct sfp_eeprom_id *id, + unsigned long *support) +{ +} + +static inline int sfp_get_module_info(struct sfp_bus *bus, + struct ethtool_modinfo *modinfo) +{ + return -EOPNOTSUPP; +} + +static inline int sfp_get_module_eeprom(struct sfp_bus *bus, + struct ethtool_eeprom *ee, u8 *data) +{ + return -EOPNOTSUPP; +} + +static inline void sfp_upstream_start(struct sfp_bus *bus) +{ +} + +static inline void sfp_upstream_stop(struct sfp_bus *bus) +{ +} + +static inline struct sfp_bus *sfp_register_upstream(struct device_node *np, + struct net_device *ndev, void *upstream, + const struct sfp_upstream_ops *ops) +{ + return (struct sfp_bus *)-1; +} + +static inline void sfp_unregister_upstream(struct sfp_bus *bus) +{ +} +#endif + +#endif -- cgit v1.2.3 From 770a1ad55763a8a783cb71078e0b33a6b91ad92b Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:03:23 +0100 Subject: phylink: add module EEPROM support Add support for reading module EEPROMs through phylink. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 28 ++++++++++++++++++++++++++++ include/linux/phylink.h | 3 +++ 2 files changed, 31 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 02082f4a8a95..026060c95b82 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1020,6 +1020,34 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, } EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam); +int phylink_ethtool_get_module_info(struct phylink *pl, + struct ethtool_modinfo *modinfo) +{ + int ret = -EOPNOTSUPP; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->sfp_bus) + ret = sfp_get_module_info(pl->sfp_bus, modinfo); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_info); + +int phylink_ethtool_get_module_eeprom(struct phylink *pl, + struct ethtool_eeprom *ee, u8 *buf) +{ + int ret = -EOPNOTSUPP; + + WARN_ON(!lockdep_rtnl_is_held()); + + if (pl->sfp_bus) + ret = sfp_get_module_eeprom(pl->sfp_bus, ee, buf); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_eeprom); + int phylink_init_eee(struct phylink *pl, bool clk_stop_enable) { int ret = -EPROTONOSUPPORT; diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 76f054f39684..af67edd4ae38 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -125,6 +125,9 @@ void phylink_ethtool_get_pauseparam(struct phylink *, struct ethtool_pauseparam *); int phylink_ethtool_set_pauseparam(struct phylink *, struct ethtool_pauseparam *); +int phylink_ethtool_get_module_info(struct phylink *, struct ethtool_modinfo *); +int phylink_ethtool_get_module_eeprom(struct phylink *, + struct ethtool_eeprom *, u8 *); int phylink_init_eee(struct phylink *, bool); int phylink_get_eee_err(struct phylink *); int phylink_ethtool_get_eee(struct phylink *, struct ethtool_eee *); -- cgit v1.2.3 From ecbd87b8430419199cc9dd91598d5552a180f558 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:03:28 +0100 Subject: phylink: add support for MII ioctl access to Clause 45 PHYs Add support for reading and writing the clause 45 MII registers. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 157 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 124 insertions(+), 33 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 026060c95b82..dc0f4d7b7dd2 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1127,16 +1127,93 @@ static int phylink_mii_emul_read(struct net_device *ndev, unsigned int reg, return val; } +static int phylink_phy_read(struct phylink *pl, unsigned int phy_id, + unsigned int reg) +{ + struct phy_device *phydev = pl->phydev; + int prtad, devad; + + if (mdio_phy_id_is_c45(phy_id)) { + prtad = mdio_phy_id_prtad(phy_id); + devad = mdio_phy_id_devad(phy_id); + devad = MII_ADDR_C45 | devad << 16 | reg; + } else if (phydev->is_c45) { + switch (reg) { + case MII_BMCR: + case MII_BMSR: + case MII_PHYSID1: + case MII_PHYSID2: + devad = __ffs(phydev->c45_ids.devices_in_package); + break; + case MII_ADVERTISE: + case MII_LPA: + if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_AN)) + return -EINVAL; + devad = MDIO_MMD_AN; + if (reg == MII_ADVERTISE) + reg = MDIO_AN_ADVERTISE; + else + reg = MDIO_AN_LPA; + break; + default: + return -EINVAL; + } + prtad = phy_id; + devad = MII_ADDR_C45 | devad << 16 | reg; + } else { + prtad = phy_id; + devad = reg; + } + return mdiobus_read(pl->phydev->mdio.bus, prtad, devad); +} + +static int phylink_phy_write(struct phylink *pl, unsigned int phy_id, + unsigned int reg, unsigned int val) +{ + struct phy_device *phydev = pl->phydev; + int prtad, devad; + + if (mdio_phy_id_is_c45(phy_id)) { + prtad = mdio_phy_id_prtad(phy_id); + devad = mdio_phy_id_devad(phy_id); + devad = MII_ADDR_C45 | devad << 16 | reg; + } else if (phydev->is_c45) { + switch (reg) { + case MII_BMCR: + case MII_BMSR: + case MII_PHYSID1: + case MII_PHYSID2: + devad = __ffs(phydev->c45_ids.devices_in_package); + break; + case MII_ADVERTISE: + case MII_LPA: + if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_AN)) + return -EINVAL; + devad = MDIO_MMD_AN; + if (reg == MII_ADVERTISE) + reg = MDIO_AN_ADVERTISE; + else + reg = MDIO_AN_LPA; + break; + default: + return -EINVAL; + } + prtad = phy_id; + devad = MII_ADDR_C45 | devad << 16 | reg; + } else { + prtad = phy_id; + devad = reg; + } + + return mdiobus_write(phydev->mdio.bus, prtad, devad, val); +} + static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, unsigned int reg) { struct phylink_link_state state; int val = 0xffff; - /* PHYs only exist for MLO_AN_PHY and MLO_AN_SGMII */ - if (pl->phydev) - return mdiobus_read(pl->phydev->mdio.bus, phy_id, reg); - switch (pl->link_an_mode) { case MLO_AN_FIXED: if (phy_id == 0) { @@ -1169,12 +1246,6 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, unsigned int reg, unsigned int val) { - /* PHYs only exist for MLO_AN_PHY and MLO_AN_SGMII */ - if (pl->phydev) { - mdiobus_write(pl->phydev->mdio.bus, phy_id, reg, val); - return 0; - } - switch (pl->link_an_mode) { case MLO_AN_FIXED: break; @@ -1193,36 +1264,56 @@ static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) { - struct mii_ioctl_data *mii_data = if_mii(ifr); - int val, ret; + struct mii_ioctl_data *mii = if_mii(ifr); + int ret; WARN_ON(!lockdep_rtnl_is_held()); - switch (cmd) { - case SIOCGMIIPHY: - mii_data->phy_id = pl->phydev ? pl->phydev->mdio.addr : 0; - /* fallthrough */ + if (pl->phydev) { + /* PHYs only exist for MLO_AN_PHY and MLO_AN_SGMII */ + switch (cmd) { + case SIOCGMIIPHY: + mii->phy_id = pl->phydev->mdio.addr; + + case SIOCGMIIREG: + ret = phylink_phy_read(pl, mii->phy_id, mii->reg_num); + if (ret >= 0) { + mii->val_out = ret; + ret = 0; + } + break; - case SIOCGMIIREG: - val = phylink_mii_read(pl, mii_data->phy_id, mii_data->reg_num); - if (val < 0) { - ret = val; - } else { - mii_data->val_out = val; - ret = 0; + case SIOCSMIIREG: + ret = phylink_phy_write(pl, mii->phy_id, mii->reg_num, + mii->val_in); + break; + + default: + ret = phy_mii_ioctl(pl->phydev, ifr, cmd); + break; } - break; + } else { + switch (cmd) { + case SIOCGMIIPHY: + mii->phy_id = 0; + + case SIOCGMIIREG: + ret = phylink_mii_read(pl, mii->phy_id, mii->reg_num); + if (ret >= 0) { + mii->val_out = ret; + ret = 0; + } + break; - case SIOCSMIIREG: - ret = phylink_mii_write(pl, mii_data->phy_id, mii_data->reg_num, - mii_data->val_in); - break; + case SIOCSMIIREG: + ret = phylink_mii_write(pl, mii->phy_id, mii->reg_num, + mii->val_in); + break; - default: - ret = -EOPNOTSUPP; - if (pl->phydev) - ret = phy_mii_ioctl(pl->phydev, ifr, cmd); - break; + default: + ret = -EOPNOTSUPP; + break; + } } return ret; -- cgit v1.2.3 From da7c1862f05842236490a8e65cc6f57d8160c05d Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:03:34 +0100 Subject: phylink: add in-band autonegotiation support for 10GBase-KR mode. Add in-band autonegotation support for 10GBase-KR mode. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index dc0f4d7b7dd2..32917bdd1432 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -266,6 +266,23 @@ static int phylink_parse_mode(struct phylink *pl, struct device_node *np) pl->link_an_mode = MLO_AN_8023Z; break; + case PHY_INTERFACE_MODE_10GKR: + phylink_set(pl->supported, 10baseT_Half); + phylink_set(pl->supported, 10baseT_Full); + phylink_set(pl->supported, 100baseT_Half); + phylink_set(pl->supported, 100baseT_Full); + phylink_set(pl->supported, 1000baseT_Half); + phylink_set(pl->supported, 1000baseT_Full); + phylink_set(pl->supported, 1000baseX_Full); + phylink_set(pl->supported, 10000baseKR_Full); + phylink_set(pl->supported, 10000baseCR_Full); + phylink_set(pl->supported, 10000baseSR_Full); + phylink_set(pl->supported, 10000baseLR_Full); + phylink_set(pl->supported, 10000baseLRM_Full); + phylink_set(pl->supported, 10000baseER_Full); + pl->link_an_mode = MLO_AN_SGMII; + break; + default: netdev_err(pl->netdev, "incorrect link mode %s for in-band status\n", -- cgit v1.2.3 From 73970055450eebc6fc36fd170e56cc45889d0093 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 25 Jul 2017 15:03:39 +0100 Subject: sfp: add SFP module support Add support for SFP hotpluggable modules via sfp-bus and phylink. This supports both copper and optical SFP modules, which require different Serdes modes in order to properly negotiate the link. Optical SFP modules typically require the Serdes link to be talking 1000BaseX mode - this is the gigabit ethernet mode defined by the 802.3 standard. Copper SFP modules typically integrate a PHY in the module to convert from Serdes to copper, and the PHY will be configured by the vendor to either present a 1000BaseX Serdes link (for fixed 1000BaseT) or a SGMII Serdes link. However, this is vendor defined, so we instead detect the PHY, switch the link to SGMII mode, and use traditional PHY based negotiation. Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + drivers/net/phy/sfp.c | 915 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 921 insertions(+) create mode 100644 drivers/net/phy/sfp.c (limited to 'drivers/net') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index a0a9e03e2f80..bf73969a9d2b 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -200,6 +200,11 @@ config LED_TRIGGER_PHY comment "MII PHY device drivers" +config SFP + tristate "SFP cage support" + depends on I2C && PHYLINK + select MDIO_I2C + config AMD_PHY tristate "AMD PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 4c16a10f420e..7237255bad68 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o +obj-$(CONFIG_SFP) += sfp.o sfp-obj-$(CONFIG_SFP) += sfp-bus.o obj-y += $(sfp-obj-y) $(sfp-obj-m) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c new file mode 100644 index 000000000000..fb2cf4342f48 --- /dev/null +++ b/drivers/net/phy/sfp.c @@ -0,0 +1,915 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdio-i2c.h" +#include "sfp.h" +#include "swphy.h" + +enum { + GPIO_MODDEF0, + GPIO_LOS, + GPIO_TX_FAULT, + GPIO_TX_DISABLE, + GPIO_RATE_SELECT, + GPIO_MAX, + + SFP_F_PRESENT = BIT(GPIO_MODDEF0), + SFP_F_LOS = BIT(GPIO_LOS), + SFP_F_TX_FAULT = BIT(GPIO_TX_FAULT), + SFP_F_TX_DISABLE = BIT(GPIO_TX_DISABLE), + SFP_F_RATE_SELECT = BIT(GPIO_RATE_SELECT), + + SFP_E_INSERT = 0, + SFP_E_REMOVE, + SFP_E_DEV_DOWN, + SFP_E_DEV_UP, + SFP_E_TX_FAULT, + SFP_E_TX_CLEAR, + SFP_E_LOS_HIGH, + SFP_E_LOS_LOW, + SFP_E_TIMEOUT, + + SFP_MOD_EMPTY = 0, + SFP_MOD_PROBE, + SFP_MOD_PRESENT, + SFP_MOD_ERROR, + + SFP_DEV_DOWN = 0, + SFP_DEV_UP, + + SFP_S_DOWN = 0, + SFP_S_INIT, + SFP_S_WAIT_LOS, + SFP_S_LINK_UP, + SFP_S_TX_FAULT, + SFP_S_REINIT, + SFP_S_TX_DISABLE, +}; + +static const char *gpio_of_names[] = { + "moddef0", + "los", + "tx-fault", + "tx-disable", + "rate-select", +}; + +static const enum gpiod_flags gpio_flags[] = { + GPIOD_IN, + GPIOD_IN, + GPIOD_IN, + GPIOD_ASIS, + GPIOD_ASIS, +}; + +#define T_INIT_JIFFIES msecs_to_jiffies(300) +#define T_RESET_US 10 +#define T_FAULT_RECOVER msecs_to_jiffies(1000) + +/* SFP module presence detection is poor: the three MOD DEF signals are + * the same length on the PCB, which means it's possible for MOD DEF 0 to + * connect before the I2C bus on MOD DEF 1/2. + * + * The SFP MSA specifies 300ms as t_init (the time taken for TX_FAULT to + * be deasserted) but makes no mention of the earliest time before we can + * access the I2C EEPROM. However, Avago modules require 300ms. + */ +#define T_PROBE_INIT msecs_to_jiffies(300) +#define T_PROBE_RETRY msecs_to_jiffies(100) + +/* + * SFP modules appear to always have their PHY configured for bus address + * 0x56 (which with mdio-i2c, translates to a PHY address of 22). + */ +#define SFP_PHY_ADDR 22 + +/* + * Give this long for the PHY to reset. + */ +#define T_PHY_RESET_MS 50 + +static DEFINE_MUTEX(sfp_mutex); + +struct sfp { + struct device *dev; + struct i2c_adapter *i2c; + struct mii_bus *i2c_mii; + struct sfp_bus *sfp_bus; + struct phy_device *mod_phy; + + unsigned int (*get_state)(struct sfp *); + void (*set_state)(struct sfp *, unsigned int); + int (*read)(struct sfp *, bool, u8, void *, size_t); + + struct gpio_desc *gpio[GPIO_MAX]; + + unsigned int state; + struct delayed_work poll; + struct delayed_work timeout; + struct mutex sm_mutex; + unsigned char sm_mod_state; + unsigned char sm_dev_state; + unsigned short sm_state; + unsigned int sm_retries; + + struct sfp_eeprom_id id; +}; + +static unsigned long poll_jiffies; + +static unsigned int sfp_gpio_get_state(struct sfp *sfp) +{ + unsigned int i, state, v; + + for (i = state = 0; i < GPIO_MAX; i++) { + if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i]) + continue; + + v = gpiod_get_value_cansleep(sfp->gpio[i]); + if (v) + state |= BIT(i); + } + + return state; +} + +static void sfp_gpio_set_state(struct sfp *sfp, unsigned int state) +{ + if (state & SFP_F_PRESENT) { + /* If the module is present, drive the signals */ + if (sfp->gpio[GPIO_TX_DISABLE]) + gpiod_direction_output(sfp->gpio[GPIO_TX_DISABLE], + state & SFP_F_TX_DISABLE); + if (state & SFP_F_RATE_SELECT) + gpiod_direction_output(sfp->gpio[GPIO_RATE_SELECT], + state & SFP_F_RATE_SELECT); + } else { + /* Otherwise, let them float to the pull-ups */ + if (sfp->gpio[GPIO_TX_DISABLE]) + gpiod_direction_input(sfp->gpio[GPIO_TX_DISABLE]); + if (state & SFP_F_RATE_SELECT) + gpiod_direction_input(sfp->gpio[GPIO_RATE_SELECT]); + } +} + +static int sfp__i2c_read(struct i2c_adapter *i2c, u8 bus_addr, u8 dev_addr, + void *buf, size_t len) +{ + struct i2c_msg msgs[2]; + int ret; + + msgs[0].addr = bus_addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &dev_addr; + msgs[1].addr = bus_addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = buf; + + ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + + return ret == ARRAY_SIZE(msgs) ? len : 0; +} + +static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 addr, void *buf, + size_t len) +{ + return sfp__i2c_read(sfp->i2c, a2 ? 0x51 : 0x50, addr, buf, len); +} + +static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) +{ + struct mii_bus *i2c_mii; + int ret; + + if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) + return -EINVAL; + + sfp->i2c = i2c; + sfp->read = sfp_i2c_read; + + i2c_mii = mdio_i2c_alloc(sfp->dev, i2c); + if (IS_ERR(i2c_mii)) + return PTR_ERR(i2c_mii); + + i2c_mii->name = "SFP I2C Bus"; + i2c_mii->phy_mask = ~0; + + ret = mdiobus_register(i2c_mii); + if (ret < 0) { + mdiobus_free(i2c_mii); + return ret; + } + + sfp->i2c_mii = i2c_mii; + + return 0; +} + + +/* Interface */ +static unsigned int sfp_get_state(struct sfp *sfp) +{ + return sfp->get_state(sfp); +} + +static void sfp_set_state(struct sfp *sfp, unsigned int state) +{ + sfp->set_state(sfp, state); +} + +static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) +{ + return sfp->read(sfp, a2, addr, buf, len); +} + +static unsigned int sfp_check(void *buf, size_t len) +{ + u8 *p, check; + + for (p = buf, check = 0; len; p++, len--) + check += *p; + + return check; +} + +/* Helpers */ +static void sfp_module_tx_disable(struct sfp *sfp) +{ + dev_dbg(sfp->dev, "tx disable %u -> %u\n", + sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 1); + sfp->state |= SFP_F_TX_DISABLE; + sfp_set_state(sfp, sfp->state); +} + +static void sfp_module_tx_enable(struct sfp *sfp) +{ + dev_dbg(sfp->dev, "tx disable %u -> %u\n", + sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 0); + sfp->state &= ~SFP_F_TX_DISABLE; + sfp_set_state(sfp, sfp->state); +} + +static void sfp_module_tx_fault_reset(struct sfp *sfp) +{ + unsigned int state = sfp->state; + + if (state & SFP_F_TX_DISABLE) + return; + + sfp_set_state(sfp, state | SFP_F_TX_DISABLE); + + udelay(T_RESET_US); + + sfp_set_state(sfp, state); +} + +/* SFP state machine */ +static void sfp_sm_set_timer(struct sfp *sfp, unsigned int timeout) +{ + if (timeout) + mod_delayed_work(system_power_efficient_wq, &sfp->timeout, + timeout); + else + cancel_delayed_work(&sfp->timeout); +} + +static void sfp_sm_next(struct sfp *sfp, unsigned int state, + unsigned int timeout) +{ + sfp->sm_state = state; + sfp_sm_set_timer(sfp, timeout); +} + +static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state, unsigned int timeout) +{ + sfp->sm_mod_state = state; + sfp_sm_set_timer(sfp, timeout); +} + +static void sfp_sm_phy_detach(struct sfp *sfp) +{ + phy_stop(sfp->mod_phy); + sfp_remove_phy(sfp->sfp_bus); + phy_device_remove(sfp->mod_phy); + phy_device_free(sfp->mod_phy); + sfp->mod_phy = NULL; +} + +static void sfp_sm_probe_phy(struct sfp *sfp) +{ + struct phy_device *phy; + int err; + + msleep(T_PHY_RESET_MS); + + phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR); + if (IS_ERR(phy)) { + dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy)); + return; + } + if (!phy) { + dev_info(sfp->dev, "no PHY detected\n"); + return; + } + + err = sfp_add_phy(sfp->sfp_bus, phy); + if (err) { + phy_device_remove(phy); + phy_device_free(phy); + dev_err(sfp->dev, "sfp_add_phy failed: %d\n", err); + return; + } + + sfp->mod_phy = phy; + phy_start(phy); +} + +static void sfp_sm_link_up(struct sfp *sfp) +{ + sfp_link_up(sfp->sfp_bus); + sfp_sm_next(sfp, SFP_S_LINK_UP, 0); +} + +static void sfp_sm_link_down(struct sfp *sfp) +{ + sfp_link_down(sfp->sfp_bus); +} + +static void sfp_sm_link_check_los(struct sfp *sfp) +{ + unsigned int los = sfp->state & SFP_F_LOS; + + /* FIXME: what if neither SFP_OPTIONS_LOS_INVERTED nor + * SFP_OPTIONS_LOS_NORMAL are set? For now, we assume + * the same as SFP_OPTIONS_LOS_NORMAL set. + */ + if (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED) + los ^= SFP_F_LOS; + + if (los) + sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0); + else + sfp_sm_link_up(sfp); +} + +static void sfp_sm_fault(struct sfp *sfp, bool warn) +{ + if (sfp->sm_retries && !--sfp->sm_retries) { + dev_err(sfp->dev, "module persistently indicates fault, disabling\n"); + sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0); + } else { + if (warn) + dev_err(sfp->dev, "module transmit fault indicated\n"); + + sfp_sm_next(sfp, SFP_S_TX_FAULT, T_FAULT_RECOVER); + } +} + +static void sfp_sm_mod_init(struct sfp *sfp) +{ + sfp_module_tx_enable(sfp); + + /* Wait t_init before indicating that the link is up, provided the + * current state indicates no TX_FAULT. If TX_FAULT clears before + * this time, that's fine too. + */ + sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); + sfp->sm_retries = 5; + + /* Setting the serdes link mode is guesswork: there's no + * field in the EEPROM which indicates what mode should + * be used. + * + * If it's a gigabit-only fiber module, it probably does + * not have a PHY, so switch to 802.3z negotiation mode. + * Otherwise, switch to SGMII mode (which is required to + * support non-gigabit speeds) and probe for a PHY. + */ + if (sfp->id.base.e1000_base_t || + sfp->id.base.e100_base_lx || + sfp->id.base.e100_base_fx) + sfp_sm_probe_phy(sfp); +} + +static int sfp_sm_mod_probe(struct sfp *sfp) +{ + /* SFP module inserted - read I2C data */ + struct sfp_eeprom_id id; + char vendor[17]; + char part[17]; + char sn[17]; + char date[9]; + char rev[5]; + u8 check; + int err; + + err = sfp_read(sfp, false, 0, &id, sizeof(id)); + if (err < 0) { + dev_err(sfp->dev, "failed to read EEPROM: %d\n", err); + return -EAGAIN; + } + + if (err != sizeof(id)) { + dev_err(sfp->dev, "EEPROM short read: %d\n", err); + return -EAGAIN; + } + + /* Validate the checksum over the base structure */ + check = sfp_check(&id.base, sizeof(id.base) - 1); + if (check != id.base.cc_base) { + dev_err(sfp->dev, + "EEPROM base structure checksum failure: 0x%02x\n", + check); + print_hex_dump(KERN_ERR, "sfp EE: ", DUMP_PREFIX_OFFSET, + 16, 1, &id, sizeof(id.base) - 1, true); + return -EINVAL; + } + + check = sfp_check(&id.ext, sizeof(id.ext) - 1); + if (check != id.ext.cc_ext) { + dev_err(sfp->dev, + "EEPROM extended structure checksum failure: 0x%02x\n", + check); + memset(&id.ext, 0, sizeof(id.ext)); + } + + sfp->id = id; + + memcpy(vendor, sfp->id.base.vendor_name, 16); + vendor[16] = '\0'; + memcpy(part, sfp->id.base.vendor_pn, 16); + part[16] = '\0'; + memcpy(rev, sfp->id.base.vendor_rev, 4); + rev[4] = '\0'; + memcpy(sn, sfp->id.ext.vendor_sn, 16); + sn[16] = '\0'; + memcpy(date, sfp->id.ext.datecode, 8); + date[8] = '\0'; + + dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", vendor, part, rev, sn, date); + + /* We only support SFP modules, not the legacy GBIC modules. */ + if (sfp->id.base.phys_id != SFP_PHYS_ID_SFP || + sfp->id.base.phys_ext_id != SFP_PHYS_EXT_ID_SFP) { + dev_err(sfp->dev, "module is not SFP - phys id 0x%02x 0x%02x\n", + sfp->id.base.phys_id, sfp->id.base.phys_ext_id); + return -EINVAL; + } + + return sfp_module_insert(sfp->sfp_bus, &sfp->id); +} + +static void sfp_sm_mod_remove(struct sfp *sfp) +{ + sfp_module_remove(sfp->sfp_bus); + + if (sfp->mod_phy) + sfp_sm_phy_detach(sfp); + + sfp_module_tx_disable(sfp); + + memset(&sfp->id, 0, sizeof(sfp->id)); + + dev_info(sfp->dev, "module removed\n"); +} + +static void sfp_sm_event(struct sfp *sfp, unsigned int event) +{ + mutex_lock(&sfp->sm_mutex); + + dev_dbg(sfp->dev, "SM: enter %u:%u:%u event %u\n", + sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state, event); + + /* This state machine tracks the insert/remove state of + * the module, and handles probing the on-board EEPROM. + */ + switch (sfp->sm_mod_state) { + default: + if (event == SFP_E_INSERT) { + sfp_module_tx_disable(sfp); + sfp_sm_ins_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT); + } + break; + + case SFP_MOD_PROBE: + if (event == SFP_E_REMOVE) { + sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0); + } else if (event == SFP_E_TIMEOUT) { + int err = sfp_sm_mod_probe(sfp); + + if (err == 0) + sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0); + else if (err == -EAGAIN) + sfp_sm_set_timer(sfp, T_PROBE_RETRY); + else + sfp_sm_ins_next(sfp, SFP_MOD_ERROR, 0); + } + break; + + case SFP_MOD_PRESENT: + case SFP_MOD_ERROR: + if (event == SFP_E_REMOVE) { + sfp_sm_mod_remove(sfp); + sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0); + } + break; + } + + /* This state machine tracks the netdev up/down state */ + switch (sfp->sm_dev_state) { + default: + if (event == SFP_E_DEV_UP) + sfp->sm_dev_state = SFP_DEV_UP; + break; + + case SFP_DEV_UP: + if (event == SFP_E_DEV_DOWN) { + /* If the module has a PHY, avoid raising TX disable + * as this resets the PHY. Otherwise, raise it to + * turn the laser off. + */ + if (!sfp->mod_phy) + sfp_module_tx_disable(sfp); + sfp->sm_dev_state = SFP_DEV_DOWN; + } + break; + } + + /* Some events are global */ + if (sfp->sm_state != SFP_S_DOWN && + (sfp->sm_mod_state != SFP_MOD_PRESENT || + sfp->sm_dev_state != SFP_DEV_UP)) { + if (sfp->sm_state == SFP_S_LINK_UP && + sfp->sm_dev_state == SFP_DEV_UP) + sfp_sm_link_down(sfp); + if (sfp->mod_phy) + sfp_sm_phy_detach(sfp); + sfp_sm_next(sfp, SFP_S_DOWN, 0); + mutex_unlock(&sfp->sm_mutex); + return; + } + + /* The main state machine */ + switch (sfp->sm_state) { + case SFP_S_DOWN: + if (sfp->sm_mod_state == SFP_MOD_PRESENT && + sfp->sm_dev_state == SFP_DEV_UP) + sfp_sm_mod_init(sfp); + break; + + case SFP_S_INIT: + if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) + sfp_sm_fault(sfp, true); + else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) + sfp_sm_link_check_los(sfp); + break; + + case SFP_S_WAIT_LOS: + if (event == SFP_E_TX_FAULT) + sfp_sm_fault(sfp, true); + else if (event == + (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ? + SFP_E_LOS_HIGH : SFP_E_LOS_LOW)) + sfp_sm_link_up(sfp); + break; + + case SFP_S_LINK_UP: + if (event == SFP_E_TX_FAULT) { + sfp_sm_link_down(sfp); + sfp_sm_fault(sfp, true); + } else if (event == + (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ? + SFP_E_LOS_LOW : SFP_E_LOS_HIGH)) { + sfp_sm_link_down(sfp); + sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0); + } + break; + + case SFP_S_TX_FAULT: + if (event == SFP_E_TIMEOUT) { + sfp_module_tx_fault_reset(sfp); + sfp_sm_next(sfp, SFP_S_REINIT, T_INIT_JIFFIES); + } + break; + + case SFP_S_REINIT: + if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { + sfp_sm_fault(sfp, false); + } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { + dev_info(sfp->dev, "module transmit fault recovered\n"); + sfp_sm_link_check_los(sfp); + } + break; + + case SFP_S_TX_DISABLE: + break; + } + + dev_dbg(sfp->dev, "SM: exit %u:%u:%u\n", + sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state); + + mutex_unlock(&sfp->sm_mutex); +} + +static void sfp_start(struct sfp *sfp) +{ + sfp_sm_event(sfp, SFP_E_DEV_UP); +} + +static void sfp_stop(struct sfp *sfp) +{ + sfp_sm_event(sfp, SFP_E_DEV_DOWN); +} + +static int sfp_module_info(struct sfp *sfp, struct ethtool_modinfo *modinfo) +{ + /* locking... and check module is present */ + + if (sfp->id.ext.sff8472_compliance) { + modinfo->type = ETH_MODULE_SFF_8472; + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + } else { + modinfo->type = ETH_MODULE_SFF_8079; + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; + } + return 0; +} + +static int sfp_module_eeprom(struct sfp *sfp, struct ethtool_eeprom *ee, + u8 *data) +{ + unsigned int first, last, len; + int ret; + + if (ee->len == 0) + return -EINVAL; + + first = ee->offset; + last = ee->offset + ee->len; + if (first < ETH_MODULE_SFF_8079_LEN) { + len = min_t(unsigned int, last, ETH_MODULE_SFF_8079_LEN); + len -= first; + + ret = sfp->read(sfp, false, first, data, len); + if (ret < 0) + return ret; + + first += len; + data += len; + } + if (first >= ETH_MODULE_SFF_8079_LEN && + first < ETH_MODULE_SFF_8472_LEN) { + len = min_t(unsigned int, last, ETH_MODULE_SFF_8472_LEN); + len -= first; + first -= ETH_MODULE_SFF_8079_LEN; + + ret = sfp->read(sfp, true, first, data, len); + if (ret < 0) + return ret; + } + return 0; +} + +static const struct sfp_socket_ops sfp_module_ops = { + .start = sfp_start, + .stop = sfp_stop, + .module_info = sfp_module_info, + .module_eeprom = sfp_module_eeprom, +}; + +static void sfp_timeout(struct work_struct *work) +{ + struct sfp *sfp = container_of(work, struct sfp, timeout.work); + + rtnl_lock(); + sfp_sm_event(sfp, SFP_E_TIMEOUT); + rtnl_unlock(); +} + +static void sfp_check_state(struct sfp *sfp) +{ + unsigned int state, i, changed; + + state = sfp_get_state(sfp); + changed = state ^ sfp->state; + changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT; + + for (i = 0; i < GPIO_MAX; i++) + if (changed & BIT(i)) + dev_dbg(sfp->dev, "%s %u -> %u\n", gpio_of_names[i], + !!(sfp->state & BIT(i)), !!(state & BIT(i))); + + state |= sfp->state & (SFP_F_TX_DISABLE | SFP_F_RATE_SELECT); + sfp->state = state; + + rtnl_lock(); + if (changed & SFP_F_PRESENT) + sfp_sm_event(sfp, state & SFP_F_PRESENT ? + SFP_E_INSERT : SFP_E_REMOVE); + + if (changed & SFP_F_TX_FAULT) + sfp_sm_event(sfp, state & SFP_F_TX_FAULT ? + SFP_E_TX_FAULT : SFP_E_TX_CLEAR); + + if (changed & SFP_F_LOS) + sfp_sm_event(sfp, state & SFP_F_LOS ? + SFP_E_LOS_HIGH : SFP_E_LOS_LOW); + rtnl_unlock(); +} + +static irqreturn_t sfp_irq(int irq, void *data) +{ + struct sfp *sfp = data; + + sfp_check_state(sfp); + + return IRQ_HANDLED; +} + +static void sfp_poll(struct work_struct *work) +{ + struct sfp *sfp = container_of(work, struct sfp, poll.work); + + sfp_check_state(sfp); + mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); +} + +static struct sfp *sfp_alloc(struct device *dev) +{ + struct sfp *sfp; + + sfp = kzalloc(sizeof(*sfp), GFP_KERNEL); + if (!sfp) + return ERR_PTR(-ENOMEM); + + sfp->dev = dev; + + mutex_init(&sfp->sm_mutex); + INIT_DELAYED_WORK(&sfp->poll, sfp_poll); + INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout); + + return sfp; +} + +static void sfp_cleanup(void *data) +{ + struct sfp *sfp = data; + + cancel_delayed_work_sync(&sfp->poll); + cancel_delayed_work_sync(&sfp->timeout); + if (sfp->i2c_mii) { + mdiobus_unregister(sfp->i2c_mii); + mdiobus_free(sfp->i2c_mii); + } + if (sfp->i2c) + i2c_put_adapter(sfp->i2c); + kfree(sfp); +} + +static int sfp_probe(struct platform_device *pdev) +{ + struct sfp *sfp; + bool poll = false; + int irq, err, i; + + sfp = sfp_alloc(&pdev->dev); + if (IS_ERR(sfp)) + return PTR_ERR(sfp); + + platform_set_drvdata(pdev, sfp); + + err = devm_add_action(sfp->dev, sfp_cleanup, sfp); + if (err < 0) + return err; + + if (pdev->dev.of_node) { + struct device_node *node = pdev->dev.of_node; + struct device_node *np; + + np = of_parse_phandle(node, "i2c-bus", 0); + if (np) { + struct i2c_adapter *i2c; + + i2c = of_find_i2c_adapter_by_node(np); + of_node_put(np); + if (!i2c) + return -EPROBE_DEFER; + + err = sfp_i2c_configure(sfp, i2c); + if (err < 0) { + i2c_put_adapter(i2c); + return err; + } + } + + for (i = 0; i < GPIO_MAX; i++) { + sfp->gpio[i] = devm_gpiod_get_optional(sfp->dev, + gpio_of_names[i], gpio_flags[i]); + if (IS_ERR(sfp->gpio[i])) + return PTR_ERR(sfp->gpio[i]); + } + + sfp->get_state = sfp_gpio_get_state; + sfp->set_state = sfp_gpio_set_state; + } + + sfp->sfp_bus = sfp_register_socket(sfp->dev, sfp, &sfp_module_ops); + if (!sfp->sfp_bus) + return -ENOMEM; + + /* Get the initial state, and always signal TX disable, + * since the network interface will not be up. + */ + sfp->state = sfp_get_state(sfp) | SFP_F_TX_DISABLE; + + if (sfp->gpio[GPIO_RATE_SELECT] && + gpiod_get_value_cansleep(sfp->gpio[GPIO_RATE_SELECT])) + sfp->state |= SFP_F_RATE_SELECT; + sfp_set_state(sfp, sfp->state); + sfp_module_tx_disable(sfp); + rtnl_lock(); + if (sfp->state & SFP_F_PRESENT) + sfp_sm_event(sfp, SFP_E_INSERT); + rtnl_unlock(); + + for (i = 0; i < GPIO_MAX; i++) { + if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i]) + continue; + + irq = gpiod_to_irq(sfp->gpio[i]); + if (!irq) { + poll = true; + continue; + } + + err = devm_request_threaded_irq(sfp->dev, irq, NULL, sfp_irq, + IRQF_ONESHOT | + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + dev_name(sfp->dev), sfp); + if (err) + poll = true; + } + + if (poll) + mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); + + return 0; +} + +static int sfp_remove(struct platform_device *pdev) +{ + struct sfp *sfp = platform_get_drvdata(pdev); + + sfp_unregister_socket(sfp->sfp_bus); + + return 0; +} + +static const struct of_device_id sfp_of_match[] = { + { .compatible = "sff,sfp", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sfp_of_match); + +static struct platform_driver sfp_driver = { + .probe = sfp_probe, + .remove = sfp_remove, + .driver = { + .name = "sfp", + .of_match_table = sfp_of_match, + }, +}; + +static int sfp_init(void) +{ + poll_jiffies = msecs_to_jiffies(100); + + return platform_driver_register(&sfp_driver); +} +module_init(sfp_init); + +static void sfp_exit(void) +{ + platform_driver_unregister(&sfp_driver); +} +module_exit(sfp_exit); + +MODULE_ALIAS("platform:sfp"); +MODULE_AUTHOR("Russell King"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 9060e6bae61a253f83a39145419f23fc67b401cf Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Thu, 3 Aug 2017 13:08:24 -0700 Subject: liquidio: add missing strings in oct_dev_state_str array There's supposed to be a one-to-one correspondence between the 18 macros that #define the OCT_DEV states (in octeon_device.h) and the strings in the oct_dev_state_str array, but there are only 14 strings in the array. Add the missing strings (so they become 18 in total), and also revise some incorrect/outdated text of existing strings. Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_device.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index f10014f7ae88..495cc8880646 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -528,9 +528,10 @@ static struct octeon_config_ptr { }; static char oct_dev_state_str[OCT_DEV_STATES + 1][32] = { - "BEGIN", "PCI-MAP-DONE", "DISPATCH-INIT-DONE", + "BEGIN", "PCI-ENABLE-DONE", "PCI-MAP-DONE", "DISPATCH-INIT-DONE", "IQ-INIT-DONE", "SCBUFF-POOL-INIT-DONE", "RESPLIST-INIT-DONE", - "DROQ-INIT-DONE", "IO-QUEUES-INIT-DONE", "CONSOLE-INIT-DONE", + "DROQ-INIT-DONE", "MBOX-SETUP-DONE", "MSIX-ALLOC-VECTOR-DONE", + "INTR-SET-DONE", "IO-QUEUES-INIT-DONE", "CONSOLE-INIT-DONE", "HOST-READY", "CORE-READY", "RUNNING", "IN-RESET", "INVALID" }; -- cgit v1.2.3 From 2470f3a2946083fc7b0b9fcbdc6aaee9646733e3 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Thu, 3 Aug 2017 15:10:17 -0700 Subject: liquidio: moved console_bitmask module param to lio_main.c Moving PF module param console_bitmask to lio_main.c for consistency. Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 15 +++++++++++++++ drivers/net/ethernet/cavium/liquidio/octeon_console.c | 14 -------------- drivers/net/ethernet/cavium/liquidio/octeon_device.h | 2 ++ 3 files changed, 17 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 39a8dca35ffa..8c2cd8011bae 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -59,6 +59,21 @@ static char fw_type[LIO_MAX_FW_TYPE_LEN]; module_param_string(fw_type, fw_type, sizeof(fw_type), 0000); MODULE_PARM_DESC(fw_type, "Type of firmware to be loaded. Default \"nic\""); +static u32 console_bitmask; +module_param(console_bitmask, int, 0644); +MODULE_PARM_DESC(console_bitmask, + "Bitmask indicating which consoles have debug output redirected to syslog."); + +/** + * \brief determines if a given console has debug enabled. + * @param console console to check + * @returns 1 = enabled. 0 otherwise + */ +int octeon_console_debug_enabled(u32 console) +{ + return (console_bitmask >> (console)) & 0x1; +} + static int ptp_enable = 1; /* Polling interval for determining when NIC application is alive */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index 15ad1ab2c0c7..dd0efc9b4286 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -37,10 +37,6 @@ static u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct, u32 flags); static int octeon_console_read(struct octeon_device *oct, u32 console_num, char *buffer, u32 buf_size); -static u32 console_bitmask; -module_param(console_bitmask, int, 0644); -MODULE_PARM_DESC(console_bitmask, - "Bitmask indicating which consoles have debug output redirected to syslog."); #define BOOTLOADER_PCI_READ_BUFFER_DATA_ADDR 0x0006c008 #define BOOTLOADER_PCI_READ_BUFFER_LEN_ADDR 0x0006c004 @@ -135,16 +131,6 @@ struct octeon_pci_console_desc { /* Implicit storage for console_addr_array */ }; -/** - * \brief determines if a given console has debug enabled. - * @param console console to check - * @returns 1 = enabled. 0 otherwise - */ -static int octeon_console_debug_enabled(u32 console) -{ - return (console_bitmask >> (console)) & 0x1; -} - /** * This function is the implementation of the get macros defined * for individual structure members. The argument are generated diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index ad464788c923..31efdef02a24 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -739,6 +739,8 @@ int octeon_wait_for_bootloader(struct octeon_device *oct, */ int octeon_init_consoles(struct octeon_device *oct); +int octeon_console_debug_enabled(u32 console); + /** * Adds access to a console to the device. * -- cgit v1.2.3 From 10377ba7673d19e28d92846955d51f49acd25c35 Mon Sep 17 00:00:00 2001 From: kiki good Date: Fri, 4 Aug 2017 00:07:45 +0100 Subject: net: systemport: Support 64bit statistics When using Broadcom Systemport device in 32bit Platform, ifconfig can only report up to 4G tx,rx status, which will be wrapped to 0 when the number of incoming or outgoing packets exceeds 4G, only taking around 2 hours in busy network environment (such as streaming). Therefore, it makes hard for network diagnostic tool to get reliable statistical result, so the patch is used to add 64bit support for Broadcom Systemport device in 32bit Platform. This patch provides 64bit statistics capability on both ethtool and ifconfig. Signed-off-by: Jianming.qiao Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 78 +++++++++++++++++++++++------- drivers/net/ethernet/broadcom/bcmsysport.h | 21 ++++++++ 2 files changed, 82 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 5333601f855f..bf9ca3c79d1a 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -201,10 +201,10 @@ static int bcm_sysport_set_features(struct net_device *dev, */ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = { /* general stats */ - STAT_NETDEV(rx_packets), - STAT_NETDEV(tx_packets), - STAT_NETDEV(rx_bytes), - STAT_NETDEV(tx_bytes), + STAT_NETDEV64(rx_packets), + STAT_NETDEV64(tx_packets), + STAT_NETDEV64(rx_bytes), + STAT_NETDEV64(tx_bytes), STAT_NETDEV(rx_errors), STAT_NETDEV(tx_errors), STAT_NETDEV(rx_dropped), @@ -316,6 +316,7 @@ static inline bool bcm_sysport_lite_stat_valid(enum bcm_sysport_stat_type type) { switch (type) { case BCM_SYSPORT_STAT_NETDEV: + case BCM_SYSPORT_STAT_NETDEV64: case BCM_SYSPORT_STAT_RXCHK: case BCM_SYSPORT_STAT_RBUF: case BCM_SYSPORT_STAT_SOFT: @@ -398,6 +399,7 @@ static void bcm_sysport_update_mib_counters(struct bcm_sysport_priv *priv) s = &bcm_sysport_gstrings_stats[i]; switch (s->type) { case BCM_SYSPORT_STAT_NETDEV: + case BCM_SYSPORT_STAT_NETDEV64: case BCM_SYSPORT_STAT_SOFT: continue; case BCM_SYSPORT_STAT_MIB_RX: @@ -434,7 +436,10 @@ static void bcm_sysport_get_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct bcm_sysport_priv *priv = netdev_priv(dev); + struct bcm_sysport_stats64 *stats64 = &priv->stats64; + struct u64_stats_sync *syncp = &priv->syncp; struct bcm_sysport_tx_ring *ring; + unsigned int start; int i, j; if (netif_running(dev)) @@ -447,10 +452,20 @@ static void bcm_sysport_get_stats(struct net_device *dev, s = &bcm_sysport_gstrings_stats[i]; if (s->type == BCM_SYSPORT_STAT_NETDEV) p = (char *)&dev->stats; + else if (s->type == BCM_SYSPORT_STAT_NETDEV64) + p = (char *)stats64; else p = (char *)priv; + p += s->stat_offset; - data[j] = *(unsigned long *)p; + + if (s->stat_sizeof == sizeof(u64)) + do { + start = u64_stats_fetch_begin_irq(syncp); + data[i] = *(u64 *)p; + } while (u64_stats_fetch_retry_irq(syncp, start)); + else + data[i] = *(u32 *)p; j++; } @@ -662,6 +677,7 @@ static int bcm_sysport_alloc_rx_bufs(struct bcm_sysport_priv *priv) static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, unsigned int budget) { + struct bcm_sysport_stats64 *stats64 = &priv->stats64; struct net_device *ndev = priv->netdev; unsigned int processed = 0, to_process; struct bcm_sysport_cb *cb; @@ -765,6 +781,10 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, skb->protocol = eth_type_trans(skb, ndev); ndev->stats.rx_packets++; ndev->stats.rx_bytes += len; + u64_stats_update_begin(&priv->syncp); + stats64->rx_packets++; + stats64->rx_bytes += len; + u64_stats_update_end(&priv->syncp); napi_gro_receive(&priv->napi, skb); next: @@ -787,17 +807,15 @@ static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_tx_ring *ring, struct device *kdev = &priv->pdev->dev; if (cb->skb) { - ring->bytes += cb->skb->len; *bytes_compl += cb->skb->len; dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); - ring->packets++; (*pkts_compl)++; bcm_sysport_free_cb(cb); /* SKB fragment */ } else if (dma_unmap_addr(cb, dma_addr)) { - ring->bytes += dma_unmap_len(cb, dma_len); + *bytes_compl += dma_unmap_len(cb, dma_len); dma_unmap_page(kdev, dma_unmap_addr(cb, dma_addr), dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); dma_unmap_addr_set(cb, dma_addr, 0); @@ -808,9 +826,9 @@ static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_tx_ring *ring, static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, struct bcm_sysport_tx_ring *ring) { - struct net_device *ndev = priv->netdev; unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs; unsigned int pkts_compl = 0, bytes_compl = 0; + struct net_device *ndev = priv->netdev; struct bcm_sysport_cb *cb; u32 hw_ind; @@ -849,6 +867,11 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, last_c_index &= (num_tx_cbs - 1); } + u64_stats_update_begin(&priv->syncp); + ring->packets += pkts_compl; + ring->bytes += bytes_compl; + u64_stats_update_end(&priv->syncp); + ring->c_index = c_index; netif_dbg(priv, tx_done, ndev, @@ -1671,22 +1694,41 @@ static int bcm_sysport_change_mac(struct net_device *dev, void *p) return 0; } -static struct net_device_stats *bcm_sysport_get_nstats(struct net_device *dev) +static void bcm_sysport_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) { struct bcm_sysport_priv *priv = netdev_priv(dev); - unsigned long tx_bytes = 0, tx_packets = 0; + struct bcm_sysport_stats64 *stats64 = &priv->stats64; struct bcm_sysport_tx_ring *ring; + u64 tx_packets = 0, tx_bytes = 0; + unsigned int start; unsigned int q; + netdev_stats_to_stats64(stats, &dev->stats); + for (q = 0; q < dev->num_tx_queues; q++) { ring = &priv->tx_rings[q]; - tx_bytes += ring->bytes; - tx_packets += ring->packets; + do { + start = u64_stats_fetch_begin_irq(&priv->syncp); + tx_bytes = ring->bytes; + tx_packets = ring->packets; + } while (u64_stats_fetch_retry_irq(&priv->syncp, start)); + + stats->tx_bytes += tx_bytes; + stats->tx_packets += tx_packets; } - dev->stats.tx_bytes = tx_bytes; - dev->stats.tx_packets = tx_packets; - return &dev->stats; + /* lockless update tx_bytes and tx_packets */ + u64_stats_update_begin(&priv->syncp); + stats64->tx_bytes = stats->tx_bytes; + stats64->tx_packets = stats->tx_packets; + u64_stats_update_end(&priv->syncp); + + do { + start = u64_stats_fetch_begin_irq(&priv->syncp); + stats->rx_packets = stats64->rx_packets; + stats->rx_bytes = stats64->rx_bytes; + } while (u64_stats_fetch_retry_irq(&priv->syncp, start)); } static void bcm_sysport_netif_start(struct net_device *dev) @@ -1950,7 +1992,7 @@ static const struct net_device_ops bcm_sysport_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = bcm_sysport_poll_controller, #endif - .ndo_get_stats = bcm_sysport_get_nstats, + .ndo_get_stats64 = bcm_sysport_get_stats64, }; #define REV_FMT "v%2x.%02x" @@ -2098,6 +2140,8 @@ static int bcm_sysport_probe(struct platform_device *pdev) /* libphy will adjust the link state accordingly */ netif_carrier_off(dev); + u64_stats_init(&priv->syncp); + ret = register_netdev(dev); if (ret) { dev_err(&pdev->dev, "failed to register net_device\n"); diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h index 77a51c167a69..80b4ffff63b7 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -603,6 +603,7 @@ struct bcm_sysport_mib { /* HW maintains a large list of counters */ enum bcm_sysport_stat_type { BCM_SYSPORT_STAT_NETDEV = -1, + BCM_SYSPORT_STAT_NETDEV64, BCM_SYSPORT_STAT_MIB_RX, BCM_SYSPORT_STAT_MIB_TX, BCM_SYSPORT_STAT_RUNT, @@ -619,6 +620,13 @@ enum bcm_sysport_stat_type { .type = BCM_SYSPORT_STAT_NETDEV, \ } +#define STAT_NETDEV64(m) { \ + .stat_string = __stringify(m), \ + .stat_sizeof = sizeof(((struct bcm_sysport_stats64 *)0)->m), \ + .stat_offset = offsetof(struct bcm_sysport_stats64, m), \ + .type = BCM_SYSPORT_STAT_NETDEV64, \ +} + #define STAT_MIB(str, m, _type) { \ .stat_string = str, \ .stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \ @@ -659,6 +667,14 @@ struct bcm_sysport_stats { u16 reg_offset; }; +struct bcm_sysport_stats64 { + /* 64bit stats on 32bit/64bit Machine */ + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; +}; + /* Software house keeping helper structure */ struct bcm_sysport_cb { struct sk_buff *skb; /* SKB for RX packets */ @@ -743,5 +759,10 @@ struct bcm_sysport_priv { /* Ethtool */ u32 msg_enable; + + struct bcm_sysport_stats64 stats64; + + /* For atomic update generic 64bit value on 32bit Machine */ + struct u64_stats_sync syncp; }; #endif /* __BCM_SYSPORT_H */ -- cgit v1.2.3 From fb84af8a4397ee664a51c2da1dd64fb3d582ee24 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 4 Aug 2017 12:14:00 -0700 Subject: netvsc: fix rtnl deadlock on unregister of vf With new transparent VF support, it is possible to get a deadlock when some of the deferred work is running and the unregister_vf is trying to cancel the work element. The solution is to use trylock and reschedule (similar to bonding and team device). Reported-by: Vitaly Kuznetsov Fixes: 0c195567a8f6 ("netvsc: transparent VF management") Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index c71728d82049..e75c0f852a63 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1601,7 +1601,11 @@ static void netvsc_vf_setup(struct work_struct *w) struct net_device *ndev = hv_get_drvdata(ndev_ctx->device_ctx); struct net_device *vf_netdev; - rtnl_lock(); + if (!rtnl_trylock()) { + schedule_work(w); + return; + } + vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); if (vf_netdev) __netvsc_vf_setup(ndev, vf_netdev); @@ -1655,7 +1659,11 @@ static void netvsc_vf_update(struct work_struct *w) struct net_device *vf_netdev; bool vf_is_up; - rtnl_lock(); + if (!rtnl_trylock()) { + schedule_work(w); + return; + } + vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); if (!vf_netdev) goto unlock; -- cgit v1.2.3 From 2d1611aff3f22a58ae8331ada6c1592e784ccb93 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 4 Aug 2017 13:08:51 -0700 Subject: net: stmmac: Add Adaptrum Anarion GMAC glue layer Before the GMAC on the Anarion chip can be used, the PHY interface selection must be configured with the DWMAC block in reset. This layer covers a block containing only two registers. Although it is possible to model this as a reset controller and use the "resets" property of stmmac, it's much more intuitive to include this in the glue layer instead. At this time only RGMII is supported, because it is the only mode which has been validated hardware-wise. Signed-off-by: Alexandru Gagniuc Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/Kconfig | 9 ++ drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + .../net/ethernet/stmicro/stmmac/dwmac-anarion.c | 152 +++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c (limited to 'drivers/net') diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 85c0e41f8021..97035766c291 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -45,6 +45,15 @@ config DWMAC_GENERIC platform specific code to function or is using platform data for setup. +config DWMAC_ANARION + tristate "Adaptrum Anarion GMAC support" + default ARC + depends on OF && (ARC || COMPILE_TEST) + help + Support for Adaptrum Anarion GMAC Ethernet controller. + + This selects the Anarion SoC glue layer support for the stmmac driver. + config DWMAC_IPQ806X tristate "QCA IPQ806x DWMAC support" default ARCH_QCOM diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index fd4937a7fcab..238307fadcdb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -7,6 +7,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ # Ordering matters. Generic driver must be last. obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o +obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c new file mode 100644 index 000000000000..85ce80c600c7 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c @@ -0,0 +1,152 @@ +/* + * Adaptrum Anarion DWMAC glue layer + * + * Copyright (C) 2017, Adaptrum, Inc. + * (Written by Alexandru Gagniuc for Adaptrum, Inc.) + * Licensed under the GPLv2 or (at your option) any later version. + */ + +#include +#include +#include +#include + +#include "stmmac.h" +#include "stmmac_platform.h" + +#define GMAC_RESET_CONTROL_REG 0 +#define GMAC_SW_CONFIG_REG 4 +#define GMAC_CONFIG_INTF_SEL_MASK (0x7 << 0) +#define GMAC_CONFIG_INTF_RGMII (0x1 << 0) + +struct anarion_gmac { + uintptr_t ctl_block; + uint32_t phy_intf_sel; +}; + +static uint32_t gmac_read_reg(struct anarion_gmac *gmac, uint8_t reg) +{ + return readl((void *)(gmac->ctl_block + reg)); +}; + +static void gmac_write_reg(struct anarion_gmac *gmac, uint8_t reg, uint32_t val) +{ + writel(val, (void *)(gmac->ctl_block + reg)); +} + +static int anarion_gmac_init(struct platform_device *pdev, void *priv) +{ + uint32_t sw_config; + struct anarion_gmac *gmac = priv; + + /* Reset logic, configure interface mode, then release reset. SIMPLE! */ + gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1); + + sw_config = gmac_read_reg(gmac, GMAC_SW_CONFIG_REG); + sw_config &= ~GMAC_CONFIG_INTF_SEL_MASK; + sw_config |= (gmac->phy_intf_sel & GMAC_CONFIG_INTF_SEL_MASK); + gmac_write_reg(gmac, GMAC_SW_CONFIG_REG, sw_config); + + gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 0); + + return 0; +} + +static void anarion_gmac_exit(struct platform_device *pdev, void *priv) +{ + struct anarion_gmac *gmac = priv; + + gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1); +} + +static struct anarion_gmac *anarion_config_dt(struct platform_device *pdev) +{ + int phy_mode; + struct resource *res; + void __iomem *ctl_block; + struct anarion_gmac *gmac; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + ctl_block = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ctl_block)) { + dev_err(&pdev->dev, "Cannot get reset region (%ld)!\n", + PTR_ERR(ctl_block)); + return ctl_block; + } + + gmac = devm_kzalloc(&pdev->dev, sizeof(*gmac), GFP_KERNEL); + if (!gmac) + return ERR_PTR(-ENOMEM); + + gmac->ctl_block = (uintptr_t)ctl_block; + + phy_mode = of_get_phy_mode(pdev->dev.of_node); + switch (phy_mode) { + case PHY_INTERFACE_MODE_RGMII: /* Fall through */ + case PHY_INTERFACE_MODE_RGMII_ID /* Fall through */: + case PHY_INTERFACE_MODE_RGMII_RXID: /* Fall through */ + case PHY_INTERFACE_MODE_RGMII_TXID: + gmac->phy_intf_sel = GMAC_CONFIG_INTF_RGMII; + break; + default: + dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n", + phy_mode); + return ERR_PTR(-ENOTSUPP); + } + + return gmac; +} + +static int anarion_dwmac_probe(struct platform_device *pdev) +{ + int ret; + struct anarion_gmac *gmac; + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + + ret = stmmac_get_platform_resources(pdev, &stmmac_res); + if (ret) + return ret; + + gmac = anarion_config_dt(pdev); + if (IS_ERR(gmac)) + return PTR_ERR(gmac); + + plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); + if (IS_ERR(plat_dat)) + return PTR_ERR(plat_dat); + + plat_dat->init = anarion_gmac_init; + plat_dat->exit = anarion_gmac_exit; + anarion_gmac_init(pdev, gmac); + plat_dat->bsp_priv = gmac; + + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) { + stmmac_remove_config_dt(pdev, plat_dat); + return ret; + } + + return 0; +} + +static const struct of_device_id anarion_dwmac_match[] = { + { .compatible = "adaptrum,anarion-gmac" }, + { } +}; +MODULE_DEVICE_TABLE(of, anarion_dwmac_match); + +static struct platform_driver anarion_dwmac_driver = { + .probe = anarion_dwmac_probe, + .remove = stmmac_pltfr_remove, + .driver = { + .name = "anarion-dwmac", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = anarion_dwmac_match, + }, +}; +module_platform_driver(anarion_dwmac_driver); + +MODULE_DESCRIPTION("Adaptrum Anarion DWMAC specific glue layer"); +MODULE_AUTHOR("Alexandru Gagniuc "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 706b35834820715d122a984eba2369cae7bf3abd Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 6 Jun 2017 17:46:49 +0300 Subject: net/mlx5e: Rearrange netdevice ops structures Since we are going to allow building the driver without eswitch support, it would be possible to compile out the sriov netdevice ops struct such that the basic ops instance will be used for non VF devices too. Add missing udp tunnel ndos into mlx5e_netdev_ops_basic. While here, rearrange some ndos in the sriov ops struct and put vf/eswitch related ndos towards the end of it. Signed-off-by: Saeed Mahameed Reviewed-by: Or Gerlitz --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 57f31fa478ce..c2986777a1d8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3706,6 +3706,9 @@ static const struct net_device_ops mlx5e_netdev_ops_basic = { .ndo_change_mtu = mlx5e_change_mtu, .ndo_do_ioctl = mlx5e_ioctl, .ndo_set_tx_maxrate = mlx5e_set_tx_maxrate, + .ndo_udp_tunnel_add = mlx5e_add_vxlan_port, + .ndo_udp_tunnel_del = mlx5e_del_vxlan_port, + .ndo_features_check = mlx5e_features_check, #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = mlx5e_rx_flow_steer, #endif @@ -3730,13 +3733,19 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = { .ndo_set_features = mlx5e_set_features, .ndo_change_mtu = mlx5e_change_mtu, .ndo_do_ioctl = mlx5e_ioctl, + .ndo_set_tx_maxrate = mlx5e_set_tx_maxrate, .ndo_udp_tunnel_add = mlx5e_add_vxlan_port, .ndo_udp_tunnel_del = mlx5e_del_vxlan_port, - .ndo_set_tx_maxrate = mlx5e_set_tx_maxrate, .ndo_features_check = mlx5e_features_check, #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = mlx5e_rx_flow_steer, #endif + .ndo_tx_timeout = mlx5e_tx_timeout, + .ndo_xdp = mlx5e_xdp, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = mlx5e_netpoll, +#endif + /* SRIOV E-Switch NDOs */ .ndo_set_vf_mac = mlx5e_set_vf_mac, .ndo_set_vf_vlan = mlx5e_set_vf_vlan, .ndo_set_vf_spoofchk = mlx5e_set_vf_spoofchk, @@ -3745,11 +3754,6 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = { .ndo_get_vf_config = mlx5e_get_vf_config, .ndo_set_vf_link_state = mlx5e_set_vf_link_state, .ndo_get_vf_stats = mlx5e_get_vf_stats, - .ndo_tx_timeout = mlx5e_tx_timeout, - .ndo_xdp = mlx5e_xdp, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = mlx5e_netpoll, -#endif .ndo_has_offload_stats = mlx5e_has_offload_stats, .ndo_get_offload_stats = mlx5e_get_offload_stats, }; -- cgit v1.2.3 From 07c9f1e57839c678c867b89aa9dcb2220e579b13 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 6 Jun 2017 09:12:04 +0300 Subject: net/mlx5e: NIC netdev init flow cleanup Remove redundant call to unregister vport representor in mlx5e_add error flow. Hide the representor priv and eswitch internal structures from en_main.c as preparation step for downstream patches which would allow building the driver without support for representors and eswitch. Fixes: 6f08a22c5fb2 ("net/mlx5e: Register/unregister vport representors on interface attach/detach") Signed-off-by: Saeed Mahameed Reviewed-by: Or Gerlitz --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 22 ++++++---------------- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 13 +++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_rep.h | 1 + 3 files changed, 20 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index c2986777a1d8..b44d6f677845 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -4428,32 +4428,27 @@ static void mlx5e_detach(struct mlx5_core_dev *mdev, void *vpriv) static void *mlx5e_add(struct mlx5_core_dev *mdev) { - struct mlx5_eswitch *esw = mdev->priv.eswitch; - int total_vfs = MLX5_TOTAL_VPORTS(mdev); - struct mlx5e_rep_priv *rpriv = NULL; + struct net_device *netdev; + void *rpriv = NULL; void *priv; - int vport; int err; - struct net_device *netdev; err = mlx5e_check_required_hca_cap(mdev); if (err) return NULL; if (MLX5_CAP_GEN(mdev, vport_group_manager)) { - rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL); + rpriv = mlx5e_alloc_nic_rep_priv(mdev); if (!rpriv) { - mlx5_core_warn(mdev, - "Not creating net device, Failed to alloc rep priv data\n"); + mlx5_core_warn(mdev, "Failed to alloc NIC rep priv data\n"); return NULL; } - rpriv->rep = &esw->offloads.vport_reps[0]; } netdev = mlx5e_create_netdev(mdev, &mlx5e_nic_profile, rpriv); if (!netdev) { mlx5_core_err(mdev, "mlx5e_create_netdev failed\n"); - goto err_unregister_reps; + goto err_free_rpriv; } priv = netdev_priv(netdev); @@ -4474,14 +4469,9 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) err_detach: mlx5e_detach(mdev, priv); - err_destroy_netdev: mlx5e_destroy_netdev(priv); - -err_unregister_reps: - for (vport = 1; vport < total_vfs; vport++) - mlx5_eswitch_unregister_vport_rep(esw, vport); - +err_free_rpriv: kfree(rpriv); return NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 45e60be9c277..a0dd0e7e5b57 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -1099,3 +1099,16 @@ void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv) mlx5e_rep_unregister_vf_vports(priv); /* VFs vports */ mlx5_eswitch_unregister_vport_rep(esw, 0); /* UPLINK PF*/ } + +void *mlx5e_alloc_nic_rep_priv(struct mlx5_core_dev *mdev) +{ + struct mlx5_eswitch *esw = mdev->priv.eswitch; + struct mlx5e_rep_priv *rpriv; + + rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL); + if (!rpriv) + return NULL; + + rpriv->rep = &esw->offloads.vport_reps[0]; + return rpriv; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index a0a1a7a1d6c0..23e43bbf928d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -123,6 +123,7 @@ struct mlx5e_encap_entry { int encap_size; }; +void *mlx5e_alloc_nic_rep_priv(struct mlx5_core_dev *mdev); void mlx5e_register_vport_reps(struct mlx5e_priv *priv); void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv); bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv); -- cgit v1.2.3 From a9f7705ffd663ff057222e91a86d9bc1d697fd58 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sun, 11 Jun 2017 19:05:10 +0300 Subject: net/mlx5: Unify vport manager capability check Expose MLX5_VPORT_MANAGER macro to check for strict vport manager E-switch and MPFS (Multi Physical Function Switch) abilities. VPORT manager must be a PF with an ethernet link and with FW advertised vport group manager capability Replace older checks with the new macro and use it where needed in eswitch.c and mlx5e netdev eswitch related flows. The same macro will be reused in MPFS separation downstream patch. Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 18 ++++++----------- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 4 +--- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 23 ++++++++-------------- .../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 4 ++++ 4 files changed, 19 insertions(+), 30 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index b44d6f677845..e3c858c44532 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2581,12 +2581,6 @@ static void mlx5e_build_channels_tx_maps(struct mlx5e_priv *priv) } } -static bool mlx5e_is_eswitch_vport_mngr(struct mlx5_core_dev *mdev) -{ - return (MLX5_CAP_GEN(mdev, vport_group_manager) && - MLX5_CAP_GEN(mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH); -} - void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) { int num_txqs = priv->channels.num * priv->channels.params.num_tc; @@ -2600,7 +2594,7 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) mlx5e_activate_channels(&priv->channels); netif_tx_start_all_queues(priv->netdev); - if (mlx5e_is_eswitch_vport_mngr(priv->mdev)) + if (MLX5_VPORT_MANAGER(priv->mdev)) mlx5e_add_sqs_fwd_rules(priv); mlx5e_wait_channels_min_rx_wqes(&priv->channels); @@ -2611,7 +2605,7 @@ void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) { mlx5e_redirect_rqts_to_drop(priv); - if (mlx5e_is_eswitch_vport_mngr(priv->mdev)) + if (MLX5_VPORT_MANAGER(priv->mdev)) mlx5e_remove_sqs_fwd_rules(priv); /* FIXME: This is a W/A only for tx timeout watch dog false alarm when @@ -4079,7 +4073,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) mlx5e_set_netdev_dev_addr(netdev); #ifdef CONFIG_NET_SWITCHDEV - if (MLX5_CAP_GEN(mdev, vport_group_manager)) + if (MLX5_VPORT_MANAGER(mdev)) netdev->switchdev_ops = &mlx5e_switchdev_ops; #endif @@ -4221,7 +4215,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) mlx5e_enable_async_events(priv); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) + if (MLX5_VPORT_MANAGER(priv->mdev)) mlx5e_register_vport_reps(priv); if (netdev->reg_state != NETREG_REGISTERED) @@ -4255,7 +4249,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv) queue_work(priv->wq, &priv->set_rx_mode_work); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) + if (MLX5_VPORT_MANAGER(priv->mdev)) mlx5e_unregister_vport_reps(priv); mlx5e_disable_async_events(priv); @@ -4437,7 +4431,7 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) if (err) return NULL; - if (MLX5_CAP_GEN(mdev, vport_group_manager)) { + if (MLX5_VPORT_MANAGER(mdev)) { rpriv = mlx5e_alloc_nic_rep_priv(mdev); if (!rpriv) { mlx5_core_warn(mdev, "Failed to alloc NIC rep priv data\n"); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index 52b9a64cd3a2..24d2f707fdfc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -688,9 +688,7 @@ int mlx5_start_eqs(struct mlx5_core_dev *dev) u64 async_event_mask = MLX5_ASYNC_EVENT_MASK; int err; - if (MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_ETH && - MLX5_CAP_GEN(dev, vport_group_manager) && - mlx5_core_is_pf(dev)) + if (MLX5_VPORT_MANAGER(dev)) async_event_mask |= (1ull << MLX5_EVENT_TYPE_NIC_VPORT_CHANGE); if (MLX5_CAP_GEN(dev, port_module_event)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 8b18cc9ec026..5c001b61d04a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1611,13 +1611,14 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num) } /* Public E-Switch API */ +#define ESW_ALLOWED(esw) ((esw) && MLX5_VPORT_MANAGER((esw)->dev)) + int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) { int err; int i, enabled_events; - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + if (!ESW_ALLOWED(esw)) return 0; if (!MLX5_CAP_GEN(esw->dev, eswitch_flow_table) || @@ -1667,9 +1668,7 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) int nvports; int i; - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH || - esw->mode == SRIOV_NONE) + if (!ESW_ALLOWED(esw) || esw->mode == SRIOV_NONE) return; esw_info(esw->dev, "disable SRIOV: active vports(%d) mode(%d)\n", @@ -1698,8 +1697,7 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) void mlx5_eswitch_attach(struct mlx5_eswitch *esw) { - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + if (!ESW_ALLOWED(esw)) return; esw_enable_vport(esw, 0, UC_ADDR_CHANGE); @@ -1708,8 +1706,7 @@ void mlx5_eswitch_attach(struct mlx5_eswitch *esw) void mlx5_eswitch_detach(struct mlx5_eswitch *esw) { - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + if (!ESW_ALLOWED(esw)) return; esw_disable_vport(esw, 0); @@ -1723,8 +1720,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) int vport_num; int err; - if (!MLX5_CAP_GEN(dev, vport_group_manager) || - MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + if (!MLX5_VPORT_MANAGER(dev)) return 0; esw_info(dev, @@ -1806,8 +1802,7 @@ abort: void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) { - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + if (!esw || !MLX5_VPORT_MANAGER(esw->dev)) return; esw_info(esw->dev, "cleanup\n"); @@ -1838,8 +1833,6 @@ void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe) } /* Vport Administration */ -#define ESW_ALLOWED(esw) \ - (esw && MLX5_CAP_GEN(esw->dev, vport_group_manager) && mlx5_core_is_pf(esw->dev)) #define LEGAL_VPORT(esw, vport) (vport >= 0 && vport < esw->total_vports) int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 6a263e8d883a..d8da9240a00b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -43,6 +43,10 @@ #define DRIVER_VERSION "5.0-0" #define MLX5_TOTAL_VPORTS(mdev) (1 + pci_sriov_get_totalvfs(mdev->pdev)) +#define MLX5_VPORT_MANAGER(mdev) \ + (MLX5_CAP_GEN(mdev, vport_group_manager) && \ + (MLX5_CAP_GEN(mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH) && \ + mlx5_core_is_pf(mdev)) extern uint mlx5_core_debug_mask; -- cgit v1.2.3 From eeb66cdb682678bfd1f02a4547e3649b38ffea7e Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sun, 4 Jun 2017 23:11:55 +0300 Subject: net/mlx5: Separate between E-Switch and MPFS Multi-Physical Function Switch (MPFs) is required for when multi-PF configuration is enabled to allow passing user configured unicast MAC addresses to the requesting PF. Before this patch eswitch.c used to manage the HW MPFS l2 table, E-Switch always (regardless of sriov) enabled vport(0) (NIC PF) vport's contexts update on unicast mac address list changes, to populate the PF's MPFS L2 table accordingly. In downstream patch we would like to allow compiling the driver without E-Switch functionalities, for that we move MPFS l2 table logic out of eswitch.c into its own file, and provide Kconfig flag (MLX5_MPFS) to allow compiling out MPFS for those who don't want Multi-PF support. NIC PF netdevice will now directly update MPFS l2 table via the new MPFS API. VF netdevice has no access to MPFS L2 table, so E-Switch will remain responsible of updating its MPFS l2 table on behalf of its VFs. Due to this change we also don't require enabling vport(0) (PF vport) unicast mac changes events anymore, for when SRIOV is not enabled. Which means E-Switch is now activated only on SRIOV activation, and not required otherwise. Signed-off-by: Saeed Mahameed Cc: Jes Sorensen Cc: kernel-team@fb.com --- drivers/net/ethernet/mellanox/mlx5/core/Kconfig | 10 + drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 + drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 17 +- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 190 ++++--------------- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 55 +----- drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c | 201 +++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h | 95 ++++++++++ drivers/net/ethernet/mellanox/mlx5/core/main.c | 26 ++- include/linux/mlx5/driver.h | 2 + 9 files changed, 377 insertions(+), 221 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig index 5aee05992f27..d7174295b6ef 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -34,6 +34,16 @@ config MLX5_CORE_EN ---help--- Ethernet support in Mellanox Technologies ConnectX-4 NIC. +config MLX5_MPFS + bool "Mellanox Technologies MLX5 MPFS support" + depends on MLX5_CORE_EN + default y + ---help--- + Mellanox Technologies Ethernet Multi-Physical Function Switch (MPFS) + support in ConnectX NIC. MPFs is required for when multi-PF configuration + is enabled to allow passing user configured unicast MAC addresses to the + requesting PF. + config MLX5_CORE_EN_DCB bool "Data Center Bridging (DCB) Support" default y diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 9d17e4e76d3a..c867e48f8a4c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -16,6 +16,8 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += eswitch.o eswitch_offloads.o \ en_rx.o en_rx_am.o en_txrx.o en_clock.o vxlan.o \ en_tc.o en_arfs.o en_rep.o en_fs_ethtool.o en_selftest.o +mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index dfccb5305e9c..eecbc6d4f51f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -36,6 +36,7 @@ #include #include #include "en.h" +#include "lib/mpfs.h" static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv, struct mlx5e_l2_rule *ai, int type); @@ -65,6 +66,7 @@ struct mlx5e_l2_hash_node { struct hlist_node hlist; u8 action; struct mlx5e_l2_rule ai; + bool mpfs; }; static inline int mlx5e_hash_l2(u8 *addr) @@ -362,17 +364,30 @@ static void mlx5e_del_vlan_rules(struct mlx5e_priv *priv) static void mlx5e_execute_l2_action(struct mlx5e_priv *priv, struct mlx5e_l2_hash_node *hn) { - switch (hn->action) { + u8 action = hn->action; + int l2_err = 0; + + switch (action) { case MLX5E_ACTION_ADD: mlx5e_add_l2_flow_rule(priv, &hn->ai, MLX5E_FULLMATCH); + if (!is_multicast_ether_addr(hn->ai.addr)) { + l2_err = mlx5_mpfs_add_mac(priv->mdev, hn->ai.addr); + hn->mpfs = !l2_err; + } hn->action = MLX5E_ACTION_NONE; break; case MLX5E_ACTION_DEL: + if (!is_multicast_ether_addr(hn->ai.addr) && hn->mpfs) + l2_err = mlx5_mpfs_del_mac(priv->mdev, hn->ai.addr); mlx5e_del_l2_flow_rule(priv, &hn->ai); mlx5e_del_l2_from_hash(hn); break; } + + if (l2_err) + netdev_warn(priv->netdev, "MPFS, failed to %s mac %pM, err(%d)\n", + action == MLX5E_ACTION_ADD ? "add" : "del", hn->ai.addr, l2_err); } static void mlx5e_sync_netdev_addr(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 5c001b61d04a..fd51f0ea8df9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -46,19 +46,13 @@ enum { MLX5_ACTION_DEL = 2, }; -/* E-Switch UC L2 table hash node */ -struct esw_uc_addr { - struct l2addr_node node; - u32 table_index; - u32 vport; -}; - /* Vport UC/MC hash node */ struct vport_addr { struct l2addr_node node; u8 action; u32 vport; - struct mlx5_flow_handle *flow_rule; /* SRIOV only */ + struct mlx5_flow_handle *flow_rule; + bool mpfs; /* UC MAC was added to MPFs */ /* A flag indicating that mac was added due to mc promiscuous vport */ bool mc_promisc; }; @@ -154,81 +148,6 @@ static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport, return modify_esw_vport_context_cmd(dev, vport, in, sizeof(in)); } -/* HW L2 Table (MPFS) management */ -static int set_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index, - u8 *mac, u8 vlan_valid, u16 vlan) -{ - u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {0}; - u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)] = {0}; - u8 *in_mac_addr; - - MLX5_SET(set_l2_table_entry_in, in, opcode, - MLX5_CMD_OP_SET_L2_TABLE_ENTRY); - MLX5_SET(set_l2_table_entry_in, in, table_index, index); - MLX5_SET(set_l2_table_entry_in, in, vlan_valid, vlan_valid); - MLX5_SET(set_l2_table_entry_in, in, vlan, vlan); - - in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address); - ether_addr_copy(&in_mac_addr[2], mac); - - return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); -} - -static int del_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index) -{ - u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {0}; - u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)] = {0}; - - MLX5_SET(delete_l2_table_entry_in, in, opcode, - MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY); - MLX5_SET(delete_l2_table_entry_in, in, table_index, index); - return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); -} - -static int alloc_l2_table_index(struct mlx5_l2_table *l2_table, u32 *ix) -{ - int err = 0; - - *ix = find_first_zero_bit(l2_table->bitmap, l2_table->size); - if (*ix >= l2_table->size) - err = -ENOSPC; - else - __set_bit(*ix, l2_table->bitmap); - - return err; -} - -static void free_l2_table_index(struct mlx5_l2_table *l2_table, u32 ix) -{ - __clear_bit(ix, l2_table->bitmap); -} - -static int set_l2_table_entry(struct mlx5_core_dev *dev, u8 *mac, - u8 vlan_valid, u16 vlan, - u32 *index) -{ - struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table; - int err; - - err = alloc_l2_table_index(l2_table, index); - if (err) - return err; - - err = set_l2_table_entry_cmd(dev, *index, mac, vlan_valid, vlan); - if (err) - free_l2_table_index(l2_table, *index); - - return err; -} - -static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index) -{ - struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table; - - del_l2_table_entry_cmd(dev, index); - free_l2_table_index(l2_table, index); -} - /* E-Switch FDB */ static struct mlx5_flow_handle * __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule, @@ -455,65 +374,60 @@ typedef int (*vport_addr_action)(struct mlx5_eswitch *esw, static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) { - struct hlist_head *hash = esw->l2_table.l2_hash; - struct esw_uc_addr *esw_uc; u8 *mac = vaddr->node.addr; u32 vport = vaddr->vport; int err; - esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr); - if (esw_uc) { + /* Skip mlx5_mpfs_add_mac for PFs, + * it is already done by the PF netdev in mlx5e_execute_l2_action + */ + if (!vport) + goto fdb_add; + + err = mlx5_mpfs_add_mac(esw->dev, mac); + if (err) { esw_warn(esw->dev, - "Failed to set L2 mac(%pM) for vport(%d), mac is already in use by vport(%d)\n", - mac, vport, esw_uc->vport); - return -EEXIST; + "Failed to add L2 table mac(%pM) for vport(%d), err(%d)\n", + mac, vport, err); + return err; } + vaddr->mpfs = true; - esw_uc = l2addr_hash_add(hash, mac, struct esw_uc_addr, GFP_KERNEL); - if (!esw_uc) - return -ENOMEM; - esw_uc->vport = vport; - - err = set_l2_table_entry(esw->dev, mac, 0, 0, &esw_uc->table_index); - if (err) - goto abort; - +fdb_add: /* SRIOV is enabled: Forward UC MAC to vport */ if (esw->fdb_table.fdb && esw->mode == SRIOV_LEGACY) vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport); - esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM index:%d fr(%p)\n", - vport, mac, esw_uc->table_index, vaddr->flow_rule); - return err; -abort: - l2addr_hash_del(esw_uc); + esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM fr(%p)\n", + vport, mac, vaddr->flow_rule); + return err; } static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) { - struct hlist_head *hash = esw->l2_table.l2_hash; - struct esw_uc_addr *esw_uc; u8 *mac = vaddr->node.addr; u32 vport = vaddr->vport; + int err = 0; - esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr); - if (!esw_uc || esw_uc->vport != vport) { - esw_debug(esw->dev, - "MAC(%pM) doesn't belong to vport (%d)\n", - mac, vport); - return -EINVAL; - } - esw_debug(esw->dev, "\tDELETE UC MAC: vport[%d] %pM index:%d fr(%p)\n", - vport, mac, esw_uc->table_index, vaddr->flow_rule); + /* Skip mlx5_mpfs_del_mac for PFs, + * it is already done by the PF netdev in mlx5e_execute_l2_action + */ + if (!vport || !vaddr->mpfs) + goto fdb_del; - del_l2_table_entry(esw->dev, esw_uc->table_index); + err = mlx5_mpfs_del_mac(esw->dev, mac); + if (err) + esw_warn(esw->dev, + "Failed to del L2 table mac(%pM) for vport(%d), err(%d)\n", + mac, vport, err); + vaddr->mpfs = false; +fdb_del: if (vaddr->flow_rule) mlx5_del_flow_rules(vaddr->flow_rule); vaddr->flow_rule = NULL; - l2addr_hash_del(esw_uc); return 0; } @@ -1635,7 +1549,6 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d) mode (%d)\n", nvfs, mode); esw->mode = mode; - esw_disable_vport(esw, 0); if (mode == SRIOV_LEGACY) err = esw_create_legacy_fdb_table(esw, nvfs + 1); @@ -1648,7 +1561,11 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) if (err) esw_warn(esw->dev, "Failed to create eswitch TSAR"); - enabled_events = (mode == SRIOV_LEGACY) ? SRIOV_VPORT_EVENTS : UC_ADDR_CHANGE; + /* Don't enable vport events when in SRIOV_OFFLOADS mode, since: + * 1. L2 table (MPFS) is programmed by PF/VF representors netdevs set_rx_mode + * 2. FDB/Eswitch is programmed by user space tools + */ + enabled_events = (mode == SRIOV_LEGACY) ? SRIOV_VPORT_EVENTS : 0; for (i = 0; i <= nvfs; i++) esw_enable_vport(esw, i, enabled_events); @@ -1657,7 +1574,6 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) return 0; abort: - esw_enable_vport(esw, 0, UC_ADDR_CHANGE); esw->mode = SRIOV_NONE; return err; } @@ -1691,30 +1607,10 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) esw_offloads_cleanup(esw, nvports); esw->mode = SRIOV_NONE; - /* VPORT 0 (PF) must be enabled back with non-sriov configuration */ - esw_enable_vport(esw, 0, UC_ADDR_CHANGE); -} - -void mlx5_eswitch_attach(struct mlx5_eswitch *esw) -{ - if (!ESW_ALLOWED(esw)) - return; - - esw_enable_vport(esw, 0, UC_ADDR_CHANGE); - /* VF Vports will be enabled when SRIOV is enabled */ -} - -void mlx5_eswitch_detach(struct mlx5_eswitch *esw) -{ - if (!ESW_ALLOWED(esw)) - return; - - esw_disable_vport(esw, 0); } int mlx5_eswitch_init(struct mlx5_core_dev *dev) { - int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); int total_vports = MLX5_TOTAL_VPORTS(dev); struct mlx5_eswitch *esw; int vport_num; @@ -1724,8 +1620,8 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) return 0; esw_info(dev, - "Total vports %d, l2 table size(%d), per vport: max uc(%d) max mc(%d)\n", - total_vports, l2_table_size, + "Total vports %d, per vport: max uc(%d) max mc(%d)\n", + total_vports, MLX5_MAX_UC_PER_VPORT(dev), MLX5_MAX_MC_PER_VPORT(dev)); @@ -1735,14 +1631,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) esw->dev = dev; - esw->l2_table.bitmap = kcalloc(BITS_TO_LONGS(l2_table_size), - sizeof(uintptr_t), GFP_KERNEL); - if (!esw->l2_table.bitmap) { - err = -ENOMEM; - goto abort; - } - esw->l2_table.size = l2_table_size; - esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq"); if (!esw->work_queue) { err = -ENOMEM; @@ -1793,7 +1681,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) abort: if (esw->work_queue) destroy_workqueue(esw->work_queue); - kfree(esw->l2_table.bitmap); kfree(esw->vports); kfree(esw->offloads.vport_reps); kfree(esw); @@ -1809,7 +1696,6 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) esw->dev->priv.eswitch = NULL; destroy_workqueue(esw->work_queue); - kfree(esw->l2_table.bitmap); kfree(esw->offloads.vport_reps); kfree(esw->vports); kfree(esw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 834a33050969..701d228de4ad 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -37,6 +37,7 @@ #include #include #include +#include "lib/mpfs.h" #define MLX5_MAX_UC_PER_VPORT(dev) \ (1 << MLX5_CAP_GEN(dev, log_max_current_uc_list)) @@ -44,9 +45,6 @@ #define MLX5_MAX_MC_PER_VPORT(dev) \ (1 << MLX5_CAP_GEN(dev, log_max_current_mc_list)) -#define MLX5_L2_ADDR_HASH_SIZE (BIT(BITS_PER_BYTE)) -#define MLX5_L2_ADDR_HASH(addr) (addr[5]) - #define FDB_UPLINK_VPORT 0xffff #define MLX5_MIN_BW_SHARE 1 @@ -54,48 +52,6 @@ #define MLX5_RATE_TO_BW_SHARE(rate, divider, limit) \ min_t(u32, max_t(u32, (rate) / (divider), MLX5_MIN_BW_SHARE), limit) -/* L2 -mac address based- hash helpers */ -struct l2addr_node { - struct hlist_node hlist; - u8 addr[ETH_ALEN]; -}; - -#define for_each_l2hash_node(hn, tmp, hash, i) \ - for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \ - hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist) - -#define l2addr_hash_find(hash, mac, type) ({ \ - int ix = MLX5_L2_ADDR_HASH(mac); \ - bool found = false; \ - type *ptr = NULL; \ - \ - hlist_for_each_entry(ptr, &hash[ix], node.hlist) \ - if (ether_addr_equal(ptr->node.addr, mac)) {\ - found = true; \ - break; \ - } \ - if (!found) \ - ptr = NULL; \ - ptr; \ -}) - -#define l2addr_hash_add(hash, mac, type, gfp) ({ \ - int ix = MLX5_L2_ADDR_HASH(mac); \ - type *ptr = NULL; \ - \ - ptr = kzalloc(sizeof(type), gfp); \ - if (ptr) { \ - ether_addr_copy(ptr->node.addr, mac); \ - hlist_add_head(&ptr->node.hlist, &hash[ix]);\ - } \ - ptr; \ -}) - -#define l2addr_hash_del(ptr) ({ \ - hlist_del(&ptr->node.hlist); \ - kfree(ptr); \ -}) - struct vport_ingress { struct mlx5_flow_table *acl; struct mlx5_flow_group *allow_untagged_spoofchk_grp; @@ -150,12 +106,6 @@ struct mlx5_vport { u16 enabled_events; }; -struct mlx5_l2_table { - struct hlist_head l2_hash[MLX5_L2_ADDR_HASH_SIZE]; - u32 size; - unsigned long *bitmap; -}; - struct mlx5_eswitch_fdb { void *fdb; union { @@ -222,7 +172,6 @@ struct esw_mc_addr { /* SRIOV only */ struct mlx5_eswitch { struct mlx5_core_dev *dev; - struct mlx5_l2_table l2_table; struct mlx5_eswitch_fdb fdb_table; struct hlist_head mc_table[MLX5_L2_ADDR_HASH_SIZE]; struct workqueue_struct *work_queue; @@ -250,8 +199,6 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports); /* E-Switch API */ int mlx5_eswitch_init(struct mlx5_core_dev *dev); void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw); -void mlx5_eswitch_attach(struct mlx5_eswitch *esw); -void mlx5_eswitch_detach(struct mlx5_eswitch *esw); void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe); int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode); void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c new file mode 100644 index 000000000000..7cb67122e8b5 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "mlx5_core.h" +#include "lib/mpfs.h" + +/* HW L2 Table (MPFS) management */ +static int set_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index, u8 *mac) +{ + u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {0}; + u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)] = {0}; + u8 *in_mac_addr; + + MLX5_SET(set_l2_table_entry_in, in, opcode, MLX5_CMD_OP_SET_L2_TABLE_ENTRY); + MLX5_SET(set_l2_table_entry_in, in, table_index, index); + + in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address); + ether_addr_copy(&in_mac_addr[2], mac); + + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} + +static int del_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index) +{ + u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {0}; + u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)] = {0}; + + MLX5_SET(delete_l2_table_entry_in, in, opcode, MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY); + MLX5_SET(delete_l2_table_entry_in, in, table_index, index); + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} + +/* UC L2 table hash node */ +struct l2table_node { + struct l2addr_node node; + u32 index; /* index in HW l2 table */ +}; + +struct mlx5_mpfs { + struct hlist_head hash[MLX5_L2_ADDR_HASH_SIZE]; + struct mutex lock; /* Synchronize l2 table access */ + u32 size; + unsigned long *bitmap; +}; + +static int alloc_l2table_index(struct mlx5_mpfs *l2table, u32 *ix) +{ + int err = 0; + + *ix = find_first_zero_bit(l2table->bitmap, l2table->size); + if (*ix >= l2table->size) + err = -ENOSPC; + else + __set_bit(*ix, l2table->bitmap); + + return err; +} + +static void free_l2table_index(struct mlx5_mpfs *l2table, u32 ix) +{ + __clear_bit(ix, l2table->bitmap); +} + +int mlx5_mpfs_init(struct mlx5_core_dev *dev) +{ + int l2table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); + struct mlx5_mpfs *mpfs; + + if (!MLX5_VPORT_MANAGER(dev)) + return 0; + + mpfs = kzalloc(sizeof(*mpfs), GFP_KERNEL); + if (!mpfs) + return -ENOMEM; + + mutex_init(&mpfs->lock); + mpfs->size = l2table_size; + mpfs->bitmap = kcalloc(BITS_TO_LONGS(l2table_size), + sizeof(uintptr_t), GFP_KERNEL); + if (!mpfs->bitmap) { + kfree(mpfs); + return -ENOMEM; + } + + dev->priv.mpfs = mpfs; + return 0; +} + +void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev) +{ + struct mlx5_mpfs *mpfs = dev->priv.mpfs; + + if (!MLX5_VPORT_MANAGER(dev)) + return; + + WARN_ON(!hlist_empty(mpfs->hash)); + kfree(mpfs->bitmap); + kfree(mpfs); +} + +int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac) +{ + struct mlx5_mpfs *mpfs = dev->priv.mpfs; + struct l2table_node *l2addr; + u32 index; + int err; + + if (!MLX5_VPORT_MANAGER(dev)) + return 0; + + mutex_lock(&mpfs->lock); + + l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node); + if (l2addr) { + err = -EEXIST; + goto abort; + } + + err = alloc_l2table_index(mpfs, &index); + if (err) + goto abort; + + l2addr = l2addr_hash_add(mpfs->hash, mac, struct l2table_node, GFP_KERNEL); + if (!l2addr) { + free_l2table_index(mpfs, index); + err = -ENOMEM; + goto abort; + } + + l2addr->index = index; + err = set_l2table_entry_cmd(dev, index, mac); + if (err) { + l2addr_hash_del(l2addr); + free_l2table_index(mpfs, index); + } + + mlx5_core_dbg(dev, "MPFS mac added %pM, index (%d)\n", mac, index); +abort: + mutex_unlock(&mpfs->lock); + return err; +} + +int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac) +{ + struct mlx5_mpfs *mpfs = dev->priv.mpfs; + struct l2table_node *l2addr; + int err = 0; + u32 index; + + if (!MLX5_VPORT_MANAGER(dev)) + return 0; + + mutex_lock(&mpfs->lock); + + l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node); + if (!l2addr) { + err = -ENOENT; + goto unlock; + } + + index = l2addr->index; + del_l2table_entry_cmd(dev, index); + l2addr_hash_del(l2addr); + free_l2table_index(mpfs, index); + mlx5_core_dbg(dev, "MPFS mac deleted %pM, index (%d)\n", mac, index); +unlock: + mutex_unlock(&mpfs->lock); + return err; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h new file mode 100644 index 000000000000..4a7b2c3203a7 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017, Mellanox Technologies, Ltd. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __MLX5_MPFS_H__ +#define __MLX5_MPFS_H__ + +#include +#include + +/* L2 -mac address based- hash helpers */ +#define MLX5_L2_ADDR_HASH_SIZE (BIT(BITS_PER_BYTE)) +#define MLX5_L2_ADDR_HASH(addr) (addr[5]) + +struct l2addr_node { + struct hlist_node hlist; + u8 addr[ETH_ALEN]; +}; + +#define for_each_l2hash_node(hn, tmp, hash, i) \ + for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \ + hlist_for_each_entry_safe(hn, tmp, &(hash)[i], hlist) + +#define l2addr_hash_find(hash, mac, type) ({ \ + int ix = MLX5_L2_ADDR_HASH(mac); \ + bool found = false; \ + type *ptr = NULL; \ + \ + hlist_for_each_entry(ptr, &(hash)[ix], node.hlist) \ + if (ether_addr_equal(ptr->node.addr, mac)) {\ + found = true; \ + break; \ + } \ + if (!found) \ + ptr = NULL; \ + ptr; \ +}) + +#define l2addr_hash_add(hash, mac, type, gfp) ({ \ + int ix = MLX5_L2_ADDR_HASH(mac); \ + type *ptr = NULL; \ + \ + ptr = kzalloc(sizeof(type), gfp); \ + if (ptr) { \ + ether_addr_copy(ptr->node.addr, mac); \ + hlist_add_head(&ptr->node.hlist, &(hash)[ix]);\ + } \ + ptr; \ +}) + +#define l2addr_hash_del(ptr) ({ \ + hlist_del(&(ptr)->node.hlist); \ + kfree(ptr); \ +}) + +#ifdef CONFIG_MLX5_MPFS +int mlx5_mpfs_init(struct mlx5_core_dev *dev); +void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev); +int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac); +int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac); +#else /* #ifndef CONFIG_MLX5_MPFS */ +static inline int mlx5_mpfs_init(struct mlx5_core_dev *dev) { return 0; } +static inline void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev) {} +static inline int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac) { return 0; } +static inline int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac) { return 0; } +#endif +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index c065132b956d..d4a9c9b7b6a2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -53,6 +53,7 @@ #include #include "mlx5_core.h" #include "fs_core.h" +#include "lib/mpfs.h" #ifdef CONFIG_MLX5_CORE_EN #include "eswitch.h" #endif @@ -946,11 +947,17 @@ static int mlx5_init_once(struct mlx5_core_dev *dev, struct mlx5_priv *priv) goto err_tables_cleanup; } + err = mlx5_mpfs_init(dev); + if (err) { + dev_err(&pdev->dev, "Failed to init l2 table %d\n", err); + goto err_rl_cleanup; + } + #ifdef CONFIG_MLX5_CORE_EN err = mlx5_eswitch_init(dev); if (err) { dev_err(&pdev->dev, "Failed to init eswitch %d\n", err); - goto err_rl_cleanup; + goto err_mpfs_cleanup; } #endif @@ -973,11 +980,11 @@ err_sriov_cleanup: err_eswitch_cleanup: #ifdef CONFIG_MLX5_CORE_EN mlx5_eswitch_cleanup(dev->priv.eswitch); - -err_rl_cleanup: +err_mpfs_cleanup: #endif + mlx5_mpfs_cleanup(dev); +err_rl_cleanup: mlx5_cleanup_rl_table(dev); - err_tables_cleanup: mlx5_cleanup_mkey_table(dev); mlx5_cleanup_srq_table(dev); @@ -998,6 +1005,7 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev) #ifdef CONFIG_MLX5_CORE_EN mlx5_eswitch_cleanup(dev->priv.eswitch); #endif + mlx5_mpfs_cleanup(dev); mlx5_cleanup_rl_table(dev); mlx5_cleanup_reserved_gids(dev); mlx5_cleanup_mkey_table(dev); @@ -1155,10 +1163,6 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv, goto err_fs; } -#ifdef CONFIG_MLX5_CORE_EN - mlx5_eswitch_attach(dev->priv.eswitch); -#endif - err = mlx5_sriov_attach(dev); if (err) { dev_err(&pdev->dev, "sriov init failed %d\n", err); @@ -1202,9 +1206,6 @@ err_fpga_start: mlx5_sriov_detach(dev); err_sriov: -#ifdef CONFIG_MLX5_CORE_EN - mlx5_eswitch_detach(dev->priv.eswitch); -#endif mlx5_cleanup_fs(dev); err_fs: @@ -1279,9 +1280,6 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv, mlx5_fpga_device_stop(dev); mlx5_sriov_detach(dev); -#ifdef CONFIG_MLX5_CORE_EN - mlx5_eswitch_detach(dev->priv.eswitch); -#endif mlx5_cleanup_fs(dev); mlx5_irq_clear_affinity_hints(dev); free_comp_eqs(dev); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index df6ce59a1f95..88d6eb5b3a76 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -550,6 +550,7 @@ struct mlx5_fc_stats { unsigned long sampling_interval; /* jiffies */ }; +struct mlx5_mpfs; struct mlx5_eswitch; struct mlx5_lag; struct mlx5_pagefault; @@ -647,6 +648,7 @@ struct mlx5_priv { spinlock_t ctx_lock; struct mlx5_flow_steering *steering; + struct mlx5_mpfs *mpfs; struct mlx5_eswitch *eswitch; struct mlx5_core_sriov sriov; struct mlx5_lag *lag; -- cgit v1.2.3 From e80541ecabd57b69726232b89242e28d8123cccc Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 5 Jun 2017 15:17:12 +0300 Subject: net/mlx5: Add CONFIG_MLX5_ESWITCH Kconfig Allow to selectively build the driver with or without sriov eswitch, VF representors and TC offloads. Also remove the need of two ndo ops structures (sriov & basic) and keep only one unified ndo ops, compile out VF SRIOV ndos when not needed (MLX5_ESWITCH=n), and for VF netdev calling those ndos will result in returning -EPERM. Signed-off-by: Saeed Mahameed Reviewed-by: Or Gerlitz Cc: Jes Sorensen Cc: kernel-team@fb.com --- drivers/net/ethernet/mellanox/mlx5/core/Kconfig | 11 +++++ drivers/net/ethernet/mellanox/mlx5/core/Makefile | 9 ++-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 53 +++++++---------------- drivers/net/ethernet/mellanox/mlx5/core/en_rep.h | 8 ++++ drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 2 + drivers/net/ethernet/mellanox/mlx5/core/en_tc.h | 7 +++ drivers/net/ethernet/mellanox/mlx5/core/eq.c | 4 -- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 23 +++++++--- drivers/net/ethernet/mellanox/mlx5/core/main.c | 10 +---- drivers/net/ethernet/mellanox/mlx5/core/sriov.c | 14 +----- 10 files changed, 69 insertions(+), 72 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig index d7174295b6ef..fdaef00465d7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -44,6 +44,17 @@ config MLX5_MPFS is enabled to allow passing user configured unicast MAC addresses to the requesting PF. +config MLX5_ESWITCH + bool "Mellanox Technologies MLX5 SRIOV E-Switch support" + depends on MLX5_CORE_EN + default y + ---help--- + Mellanox Technologies Ethernet SRIOV E-Switch support in ConnectX NIC. + E-Switch provides internal SRIOV packet steering and switching for the + enabled VFs and PF in two available modes: + Legacy SRIOV mode (L2 mac vlan steering based). + Switchdev mode (eswitch offloads). + config MLX5_CORE_EN_DCB bool "Data Center Bridging (DCB) Support" default y diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index c867e48f8a4c..22ed657d263a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -11,13 +11,14 @@ mlx5_core-$(CONFIG_MLX5_ACCEL) += accel/ipsec.o mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ fpga/ipsec.o -mlx5_core-$(CONFIG_MLX5_CORE_EN) += eswitch.o eswitch_offloads.o \ - en_main.o en_common.o en_fs.o en_ethtool.o en_tx.o \ - en_rx.o en_rx_am.o en_txrx.o en_clock.o vxlan.o \ - en_tc.o en_arfs.o en_rep.o en_fs_ethtool.o en_selftest.o +mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_rx_am.o en_txrx.o en_clock.o vxlan.o \ + en_arfs.o en_fs_ethtool.o en_selftest.o mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o +mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o en_rep.o en_tc.o + mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index e3c858c44532..b19e9d235008 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3025,6 +3025,7 @@ static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { +#ifdef CONFIG_MLX5_ESWITCH struct mlx5e_priv *priv = netdev_priv(dev); if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) @@ -3048,6 +3049,7 @@ static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, } mqprio: +#endif if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; @@ -3350,6 +3352,7 @@ static int mlx5e_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } } +#ifdef CONFIG_MLX5_ESWITCH static int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -3452,6 +3455,7 @@ static int mlx5e_get_vf_stats(struct net_device *dev, return mlx5_eswitch_get_vport_stats(mdev->priv.eswitch, vf + 1, vf_stats); } +#endif static void mlx5e_add_vxlan_port(struct net_device *netdev, struct udp_tunnel_info *ti) @@ -3685,7 +3689,7 @@ static void mlx5e_netpoll(struct net_device *dev) } #endif -static const struct net_device_ops mlx5e_netdev_ops_basic = { +static const struct net_device_ops mlx5e_netdev_ops = { .ndo_open = mlx5e_open, .ndo_stop = mlx5e_close, .ndo_start_xmit = mlx5e_xmit, @@ -3711,34 +3715,7 @@ static const struct net_device_ops mlx5e_netdev_ops_basic = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = mlx5e_netpoll, #endif -}; - -static const struct net_device_ops mlx5e_netdev_ops_sriov = { - .ndo_open = mlx5e_open, - .ndo_stop = mlx5e_close, - .ndo_start_xmit = mlx5e_xmit, - .ndo_setup_tc = mlx5e_ndo_setup_tc, - .ndo_select_queue = mlx5e_select_queue, - .ndo_get_stats64 = mlx5e_get_stats, - .ndo_set_rx_mode = mlx5e_set_rx_mode, - .ndo_set_mac_address = mlx5e_set_mac, - .ndo_vlan_rx_add_vid = mlx5e_vlan_rx_add_vid, - .ndo_vlan_rx_kill_vid = mlx5e_vlan_rx_kill_vid, - .ndo_set_features = mlx5e_set_features, - .ndo_change_mtu = mlx5e_change_mtu, - .ndo_do_ioctl = mlx5e_ioctl, - .ndo_set_tx_maxrate = mlx5e_set_tx_maxrate, - .ndo_udp_tunnel_add = mlx5e_add_vxlan_port, - .ndo_udp_tunnel_del = mlx5e_del_vxlan_port, - .ndo_features_check = mlx5e_features_check, -#ifdef CONFIG_RFS_ACCEL - .ndo_rx_flow_steer = mlx5e_rx_flow_steer, -#endif - .ndo_tx_timeout = mlx5e_tx_timeout, - .ndo_xdp = mlx5e_xdp, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = mlx5e_netpoll, -#endif +#ifdef CONFIG_MLX5_ESWITCH /* SRIOV E-Switch NDOs */ .ndo_set_vf_mac = mlx5e_set_vf_mac, .ndo_set_vf_vlan = mlx5e_set_vf_vlan, @@ -3750,6 +3727,7 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = { .ndo_get_vf_stats = mlx5e_get_vf_stats, .ndo_has_offload_stats = mlx5e_has_offload_stats, .ndo_get_offload_stats = mlx5e_get_offload_stats, +#endif }; static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev) @@ -3979,9 +3957,11 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev) } } +#if IS_ENABLED(CONFIG_NET_SWITCHDEV) && IS_ENABLED(CONFIG_MLX5_ESWITCH) static const struct switchdev_ops mlx5e_switchdev_ops = { .switchdev_port_attr_get = mlx5e_attr_get, }; +#endif static void mlx5e_build_nic_netdev(struct net_device *netdev) { @@ -3992,15 +3972,12 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) SET_NETDEV_DEV(netdev, &mdev->pdev->dev); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) { - netdev->netdev_ops = &mlx5e_netdev_ops_sriov; + netdev->netdev_ops = &mlx5e_netdev_ops; + #ifdef CONFIG_MLX5_CORE_EN_DCB - if (MLX5_CAP_GEN(mdev, qos)) - netdev->dcbnl_ops = &mlx5e_dcbnl_ops; + if (MLX5_CAP_GEN(mdev, vport_group_manager) && MLX5_CAP_GEN(mdev, qos)) + netdev->dcbnl_ops = &mlx5e_dcbnl_ops; #endif - } else { - netdev->netdev_ops = &mlx5e_netdev_ops_basic; - } netdev->watchdog_timeo = 15 * HZ; @@ -4072,7 +4049,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) mlx5e_set_netdev_dev_addr(netdev); -#ifdef CONFIG_NET_SWITCHDEV +#if IS_ENABLED(CONFIG_NET_SWITCHDEV) && IS_ENABLED(CONFIG_MLX5_ESWITCH) if (MLX5_VPORT_MANAGER(mdev)) netdev->switchdev_ops = &mlx5e_switchdev_ops; #endif @@ -4431,6 +4408,7 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) if (err) return NULL; +#ifdef CONFIG_MLX5_ESWITCH if (MLX5_VPORT_MANAGER(mdev)) { rpriv = mlx5e_alloc_nic_rep_priv(mdev); if (!rpriv) { @@ -4438,6 +4416,7 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) return NULL; } } +#endif netdev = mlx5e_create_netdev(mdev, &mlx5e_nic_profile, rpriv); if (!netdev) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index 23e43bbf928d..5659ed9f51e6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -38,6 +38,7 @@ #include "eswitch.h" #include "en.h" +#ifdef CONFIG_MLX5_ESWITCH struct mlx5e_neigh_update_table { struct rhashtable neigh_ht; /* Save the neigh hash entries in a list in addition to the hash table @@ -142,5 +143,12 @@ void mlx5e_rep_encap_entry_detach(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e); void mlx5e_rep_queue_neigh_stats_work(struct mlx5e_priv *priv); +#else /* CONFIG_MLX5_ESWITCH */ +static inline void mlx5e_register_vport_reps(struct mlx5e_priv *priv) {} +static inline void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv) {} +static inline bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) { return false; } +static inline int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) { return 0; } +static inline void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) {} +#endif #endif /* __MLX5E_REP_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 325b2c8c1c6d..8e224bcbc6a6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -857,6 +857,7 @@ wq_ll_pop: &wqe->next.next_wqe_index); } +#ifdef CONFIG_MLX5_ESWITCH void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) { struct net_device *netdev = rq->netdev; @@ -901,6 +902,7 @@ wq_ll_pop: mlx5_wq_ll_pop(&rq->wq, wqe_counter_be, &wqe->next.next_wqe_index); } +#endif static inline void mlx5e_mpwqe_fill_rx_skb(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index ecbe30d808ae..36473ec65ce8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -35,6 +35,7 @@ #define MLX5E_TC_FLOW_ID_MASK 0x0000ffff +#ifdef CONFIG_MLX5_ESWITCH int mlx5e_tc_init(struct mlx5e_priv *priv); void mlx5e_tc_cleanup(struct mlx5e_priv *priv); @@ -60,4 +61,10 @@ static inline int mlx5e_tc_num_filters(struct mlx5e_priv *priv) return atomic_read(&priv->fs.tc.ht.nelems); } +#else /* CONFIG_MLX5_ESWITCH */ +static inline int mlx5e_tc_init(struct mlx5e_priv *priv) { return 0; } +static inline void mlx5e_tc_cleanup(struct mlx5e_priv *priv) {} +static inline int mlx5e_tc_num_filters(struct mlx5e_priv *priv) { return 0; } +#endif + #endif /* __MLX5_EN_TC_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index 24d2f707fdfc..de704ff5619a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -36,9 +36,7 @@ #include #include "mlx5_core.h" #include "fpga/core.h" -#ifdef CONFIG_MLX5_CORE_EN #include "eswitch.h" -#endif enum { MLX5_EQE_SIZE = sizeof(struct mlx5_eqe), @@ -467,11 +465,9 @@ static irqreturn_t mlx5_eq_int(int irq, void *eq_ptr) } break; -#ifdef CONFIG_MLX5_CORE_EN case MLX5_EVENT_TYPE_NIC_VPORT_CHANGE: mlx5_eswitch_vport_event(dev->priv.eswitch, eqe); break; -#endif case MLX5_EVENT_TYPE_PORT_MODULE_EVENT: mlx5_port_module_event(dev, eqe); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 701d228de4ad..565c8b7a399a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -39,6 +39,14 @@ #include #include "lib/mpfs.h" +enum { + SRIOV_NONE, + SRIOV_LEGACY, + SRIOV_OFFLOADS +}; + +#ifdef CONFIG_MLX5_ESWITCH + #define MLX5_MAX_UC_PER_VPORT(dev) \ (1 << MLX5_CAP_GEN(dev, log_max_current_uc_list)) @@ -125,12 +133,6 @@ struct mlx5_eswitch_fdb { }; }; -enum { - SRIOV_NONE, - SRIOV_LEGACY, - SRIOV_OFFLOADS -}; - struct mlx5_esw_sq { struct mlx5_flow_handle *send_to_vport_rule; struct list_head list; @@ -292,4 +294,13 @@ int __mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, #define esw_debug(dev, format, ...) \ mlx5_core_dbg_mask(dev, MLX5_DEBUG_ESWITCH_MASK, format, ##__VA_ARGS__) +#else /* CONFIG_MLX5_ESWITCH */ +/* eswitch API stubs */ +static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } +static inline void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) {} +static inline void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe) {} +static inline int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) { return 0; } +static inline void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) {} +#endif /* CONFIG_MLX5_ESWITCH */ + #endif /* __MLX5_ESWITCH_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index d4a9c9b7b6a2..124c7c3c3a00 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -54,9 +54,7 @@ #include "mlx5_core.h" #include "fs_core.h" #include "lib/mpfs.h" -#ifdef CONFIG_MLX5_CORE_EN #include "eswitch.h" -#endif #include "lib/mlx5.h" #include "fpga/core.h" #include "accel/ipsec.h" @@ -953,13 +951,11 @@ static int mlx5_init_once(struct mlx5_core_dev *dev, struct mlx5_priv *priv) goto err_rl_cleanup; } -#ifdef CONFIG_MLX5_CORE_EN err = mlx5_eswitch_init(dev); if (err) { dev_err(&pdev->dev, "Failed to init eswitch %d\n", err); goto err_mpfs_cleanup; } -#endif err = mlx5_sriov_init(dev); if (err) { @@ -978,10 +974,8 @@ static int mlx5_init_once(struct mlx5_core_dev *dev, struct mlx5_priv *priv) err_sriov_cleanup: mlx5_sriov_cleanup(dev); err_eswitch_cleanup: -#ifdef CONFIG_MLX5_CORE_EN mlx5_eswitch_cleanup(dev->priv.eswitch); err_mpfs_cleanup: -#endif mlx5_mpfs_cleanup(dev); err_rl_cleanup: mlx5_cleanup_rl_table(dev); @@ -1002,9 +996,7 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev) { mlx5_fpga_cleanup(dev); mlx5_sriov_cleanup(dev); -#ifdef CONFIG_MLX5_CORE_EN mlx5_eswitch_cleanup(dev->priv.eswitch); -#endif mlx5_mpfs_cleanup(dev); mlx5_cleanup_rl_table(dev); mlx5_cleanup_reserved_gids(dev); @@ -1311,7 +1303,7 @@ struct mlx5_core_event_handler { }; static const struct devlink_ops mlx5_devlink_ops = { -#ifdef CONFIG_MLX5_CORE_EN +#ifdef CONFIG_MLX5_ESWITCH .eswitch_mode_set = mlx5_devlink_eswitch_mode_set, .eswitch_mode_get = mlx5_devlink_eswitch_mode_get, .eswitch_inline_mode_set = mlx5_devlink_eswitch_inline_mode_set, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c index bf99d40e30b4..5e7ffc9fad78 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -33,9 +33,7 @@ #include #include #include "mlx5_core.h" -#ifdef CONFIG_MLX5_CORE_EN #include "eswitch.h" -#endif bool mlx5_sriov_is_enabled(struct mlx5_core_dev *dev) { @@ -57,14 +55,12 @@ static int mlx5_device_enable_sriov(struct mlx5_core_dev *dev, int num_vfs) return -EBUSY; } -#ifdef CONFIG_MLX5_CORE_EN err = mlx5_eswitch_enable_sriov(dev->priv.eswitch, num_vfs, SRIOV_LEGACY); if (err) { mlx5_core_warn(dev, "failed to enable eswitch SRIOV (%d)\n", err); return err; } -#endif for (vf = 0; vf < num_vfs; vf++) { err = mlx5_core_enable_hca(dev, vf + 1); @@ -88,11 +84,7 @@ static void mlx5_device_disable_sriov(struct mlx5_core_dev *dev) int vf; if (!sriov->enabled_vfs) -#ifdef CONFIG_MLX5_CORE_EN - goto disable_sriov_resources; -#else - return; -#endif + goto out; for (vf = 0; vf < sriov->num_vfs; vf++) { if (!sriov->vfs_ctx[vf].enabled) @@ -106,10 +98,8 @@ static void mlx5_device_disable_sriov(struct mlx5_core_dev *dev) sriov->enabled_vfs--; } -#ifdef CONFIG_MLX5_CORE_EN -disable_sriov_resources: +out: mlx5_eswitch_disable_sriov(dev->priv.eswitch); -#endif if (mlx5_wait_for_vf_pages(dev)) mlx5_core_warn(dev, "timeout reclaiming VFs pages\n"); -- cgit v1.2.3 From 97834eba7c194659a72c5bb0f8c19c7055bb69ea Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Wed, 7 Jun 2017 12:14:24 +0300 Subject: net/mlx5: Delay events till ib registration ends When mlx5_ib registers itself to mlx5_core as an interface, it will call mlx5_add_device which will call mlx5_ib interface add callback, in case the latter successfully returns, only then mlx5_core will add it to the interface list and async events will be forwarded to mlx5_ib. Between mlx5_ib interface add callback and mlx5_core adding the mlx5_ib interface to its devices list, arriving mlx5_core events can be missed by the new mlx5_ib registering interface. In other words: thread 1: mlx5_ib: mlx5_register_interface(dev) thread 1: mlx5_core: mlx5_add_device(dev) thread 1: mlx5_core: ctx = dev->add => (mlx5_ib)->mlx5_ib_add thread 2: mlx5_core_event: **new event arrives, forward to dev_list thread 1: mlx5_core: add_ctx_to_dev_list(ctx) /* previous event was missed by the new interface.*/ It is ok to miss events before dev->add (mlx5_ib)->mlx5_ib_add_device but not after. We fix this race by accumulating the events that come between the ib_register_device (inside mlx5_add_device->(dev->add)) till the adding to the list completes and fire them to the new registering interface after that. Fixes: f1ee87fe55c8 ("net/mlx5: Organize device list API in one place") Signed-off-by: Erez Shitrit Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/dev.c | 73 ++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/main.c | 3 ++ include/linux/mlx5/driver.h | 3 ++ 3 files changed, 79 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c index a62f4b6a21a5..ff60cf7342ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c @@ -45,11 +45,70 @@ struct mlx5_device_context { unsigned long state; }; +struct mlx5_delayed_event { + struct list_head list; + struct mlx5_core_dev *dev; + enum mlx5_dev_event event; + unsigned long param; +}; + enum { MLX5_INTERFACE_ADDED, MLX5_INTERFACE_ATTACHED, }; +static void add_delayed_event(struct mlx5_priv *priv, + struct mlx5_core_dev *dev, + enum mlx5_dev_event event, + unsigned long param) +{ + struct mlx5_delayed_event *delayed_event; + + delayed_event = kzalloc(sizeof(*delayed_event), GFP_ATOMIC); + if (!delayed_event) { + mlx5_core_err(dev, "event %d is missed\n", event); + return; + } + + mlx5_core_dbg(dev, "Accumulating event %d\n", event); + delayed_event->dev = dev; + delayed_event->event = event; + delayed_event->param = param; + list_add_tail(&delayed_event->list, &priv->waiting_events_list); +} + +static void fire_delayed_event_locked(struct mlx5_device_context *dev_ctx, + struct mlx5_core_dev *dev, + struct mlx5_priv *priv) +{ + struct mlx5_delayed_event *de; + struct mlx5_delayed_event *n; + + /* stop delaying events */ + priv->is_accum_events = false; + + /* fire all accumulated events before new event comes */ + list_for_each_entry_safe(de, n, &priv->waiting_events_list, list) { + dev_ctx->intf->event(dev, dev_ctx->context, de->event, de->param); + list_del(&de->list); + kfree(de); + } +} + +static void cleanup_delayed_evets(struct mlx5_priv *priv) +{ + struct mlx5_delayed_event *de; + struct mlx5_delayed_event *n; + + spin_lock_irq(&priv->ctx_lock); + priv->is_accum_events = false; + list_for_each_entry_safe(de, n, &priv->waiting_events_list, list) { + list_del(&de->list); + kfree(de); + } + spin_unlock_irq(&priv->ctx_lock); +} + void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) { struct mlx5_device_context *dev_ctx; @@ -63,6 +122,12 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) return; dev_ctx->intf = intf; + /* accumulating events that can come after mlx5_ib calls to + * ib_register_device, till adding that interface to the events list. + */ + + priv->is_accum_events = true; + dev_ctx->context = intf->add(dev); set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state); if (intf->attach) @@ -71,6 +136,9 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) if (dev_ctx->context) { spin_lock_irq(&priv->ctx_lock); list_add_tail(&dev_ctx->list, &priv->ctx_list); + + fire_delayed_event_locked(dev_ctx, dev, priv); + #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING if (dev_ctx->intf->pfault) { if (priv->pfault) { @@ -84,6 +152,8 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) spin_unlock_irq(&priv->ctx_lock); } else { kfree(dev_ctx); + /* delete all accumulated events */ + cleanup_delayed_evets(priv); } } @@ -341,6 +411,9 @@ void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, spin_lock_irqsave(&priv->ctx_lock, flags); + if (priv->is_accum_events) + add_delayed_event(priv, dev, event, param); + list_for_each_entry(dev_ctx, &priv->ctx_list, list) if (dev_ctx->intf->event) dev_ctx->intf->event(dev, dev_ctx->context, event, param); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 124c7c3c3a00..6dbd637b4e66 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1343,6 +1343,9 @@ static int init_one(struct pci_dev *pdev, mutex_init(&dev->pci_status_mutex); mutex_init(&dev->intf_state_mutex); + INIT_LIST_HEAD(&priv->waiting_events_list); + priv->is_accum_events = false; + #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING err = init_srcu_struct(&priv->pfault_srcu); if (err) { diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 88d6eb5b3a76..d26f18b39c4a 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -647,6 +647,9 @@ struct mlx5_priv { struct list_head ctx_list; spinlock_t ctx_lock; + struct list_head waiting_events_list; + bool is_accum_events; + struct mlx5_flow_steering *steering; struct mlx5_mpfs *mpfs; struct mlx5_eswitch *eswitch; -- cgit v1.2.3 From a8ffcc741acb3c7f3dcf4c7d001209aa0995a5f1 Mon Sep 17 00:00:00 2001 From: Rabie Loulou Date: Sun, 9 Jul 2017 13:39:30 +0300 Subject: net/mlx5: Increase the maximum flow counters supported Read new NIC capability field which represnts 16 MSBs of the max flow counters number supported (max_flow_counter_31_16). Backward compatibility with older firmware is preserved, the modified driver reads max_flow_counter_31_16 as 0 from the older firmware and uses up to 64K counters. Changed flow counter id from 16 bits to 32 bits. Backward compatibility with older firmware is preserved as we kept the 16 LSBs of the counter id in place and added 16 MSBs from reserved field. Changed the background bulk reading of flow counters to work in chunks of at most 32K counters, to make sure we don't attempt to allocate very large buffers. Signed-off-by: Rabie Loulou Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 6 ++++-- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 12 ++++++------ drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h | 10 +++++----- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c | 13 ++++++++++--- include/linux/mlx5/mlx5_ifc.h | 16 ++++++---------- 6 files changed, 32 insertions(+), 27 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 95b64025ce36..e7c186b58579 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -433,6 +433,8 @@ static int esw_create_offloads_fast_fdb_table(struct mlx5_eswitch *esw) struct mlx5_flow_table *fdb = NULL; int esw_size, err = 0; u32 flags = 0; + u32 max_flow_counter = (MLX5_CAP_GEN(dev, max_flow_counter_31_16) << 16) | + MLX5_CAP_GEN(dev, max_flow_counter_15_0); root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB); if (!root_ns) { @@ -443,9 +445,9 @@ static int esw_create_offloads_fast_fdb_table(struct mlx5_eswitch *esw) esw_debug(dev, "Create offloads FDB table, min (max esw size(2^%d), max counters(%d)*groups(%d))\n", MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size), - MLX5_CAP_GEN(dev, max_flow_counter), ESW_OFFLOADS_NUM_GROUPS); + max_flow_counter, ESW_OFFLOADS_NUM_GROUPS); - esw_size = min_t(int, MLX5_CAP_GEN(dev, max_flow_counter) * ESW_OFFLOADS_NUM_GROUPS, + esw_size = min_t(int, max_flow_counter * ESW_OFFLOADS_NUM_GROUPS, 1 << MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)); if (esw->offloads.encap != DEVLINK_ESWITCH_ENCAP_MODE_NONE) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index e750f07793b8..16b32f31d691 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -359,7 +359,7 @@ int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev, return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } -int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u16 *id) +int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u32 *id) { u32 in[MLX5_ST_SZ_DW(alloc_flow_counter_in)] = {0}; u32 out[MLX5_ST_SZ_DW(alloc_flow_counter_out)] = {0}; @@ -374,7 +374,7 @@ int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u16 *id) return err; } -int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u16 id) +int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u32 id) { u32 in[MLX5_ST_SZ_DW(dealloc_flow_counter_in)] = {0}; u32 out[MLX5_ST_SZ_DW(dealloc_flow_counter_out)] = {0}; @@ -385,7 +385,7 @@ int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u16 id) return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } -int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u16 id, +int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u32 id, u64 *packets, u64 *bytes) { u32 out[MLX5_ST_SZ_BYTES(query_flow_counter_out) + @@ -409,14 +409,14 @@ int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u16 id, } struct mlx5_cmd_fc_bulk { - u16 id; + u32 id; int num; int outlen; u32 out[0]; }; struct mlx5_cmd_fc_bulk * -mlx5_cmd_fc_bulk_alloc(struct mlx5_core_dev *dev, u16 id, int num) +mlx5_cmd_fc_bulk_alloc(struct mlx5_core_dev *dev, u32 id, int num) { struct mlx5_cmd_fc_bulk *b; int outlen = @@ -453,7 +453,7 @@ mlx5_cmd_fc_bulk_query(struct mlx5_core_dev *dev, struct mlx5_cmd_fc_bulk *b) } void mlx5_cmd_fc_bulk_get(struct mlx5_core_dev *dev, - struct mlx5_cmd_fc_bulk *b, u16 id, + struct mlx5_cmd_fc_bulk *b, u32 id, u64 *packets, u64 *bytes) { int index = id - b->id; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 0f98a7cf4877..c6d7bdf255b6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -74,20 +74,20 @@ int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft, u32 underlay_qpn); -int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u16 *id); -int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u16 id); -int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u16 id, +int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u32 *id); +int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u32 id); +int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u32 id, u64 *packets, u64 *bytes); struct mlx5_cmd_fc_bulk; struct mlx5_cmd_fc_bulk * -mlx5_cmd_fc_bulk_alloc(struct mlx5_core_dev *dev, u16 id, int num); +mlx5_cmd_fc_bulk_alloc(struct mlx5_core_dev *dev, u32 id, int num); void mlx5_cmd_fc_bulk_free(struct mlx5_cmd_fc_bulk *b); int mlx5_cmd_fc_bulk_query(struct mlx5_core_dev *dev, struct mlx5_cmd_fc_bulk *b); void mlx5_cmd_fc_bulk_get(struct mlx5_core_dev *dev, - struct mlx5_cmd_fc_bulk *b, u16 id, + struct mlx5_cmd_fc_bulk *b, u32 id, u64 *packets, u64 *bytes); #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 990acee6fb09..9fb5a333df52 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -136,7 +136,7 @@ struct mlx5_fc { u64 lastpackets; u64 lastbytes; - u16 id; + u32 id; bool deleted; bool aging; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c index 6507d8acc54d..89d1f8650033 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c @@ -38,6 +38,8 @@ #include "fs_cmd.h" #define MLX5_FC_STATS_PERIOD msecs_to_jiffies(1000) +/* Max number of counters to query in bulk read is 32K */ +#define MLX5_SW_MAX_COUNTERS_BULK BIT(15) /* locking scheme: * @@ -90,16 +92,21 @@ static void mlx5_fc_stats_insert(struct rb_root *root, struct mlx5_fc *counter) rb_insert_color(&counter->node, root); } +/* The function returns the last node that was queried so the caller + * function can continue calling it till all counters are queried. + */ static struct rb_node *mlx5_fc_stats_query(struct mlx5_core_dev *dev, struct mlx5_fc *first, - u16 last_id) + u32 last_id) { struct mlx5_cmd_fc_bulk *b; struct rb_node *node = NULL; - u16 afirst_id; + u32 afirst_id; int num; int err; - int max_bulk = 1 << MLX5_CAP_GEN(dev, log_max_flow_counter_bulk); + + int max_bulk = min_t(int, MLX5_SW_MAX_COUNTERS_BULK, + (1 << MLX5_CAP_GEN(dev, log_max_flow_counter_bulk))); /* first id must be aligned to 4 when using bulk query */ afirst_id = first->id & ~0x3; diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index f847a3a57913..c99daffc3c3c 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -963,7 +963,7 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 reserved_at_2a0[0x10]; u8 max_wqe_sz_rq[0x10]; - u8 reserved_at_2c0[0x10]; + u8 max_flow_counter_31_16[0x10]; u8 max_wqe_sz_sq_dc[0x10]; u8 reserved_at_2e0[0x7]; @@ -981,7 +981,7 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 reserved_at_340[0x8]; u8 log_max_flow_counter_bulk[0x8]; - u8 max_flow_counter[0x10]; + u8 max_flow_counter_15_0[0x10]; u8 reserved_at_360[0x3]; @@ -1071,8 +1071,7 @@ struct mlx5_ifc_dest_format_struct_bits { }; struct mlx5_ifc_flow_counter_list_bits { - u8 reserved_at_0[0x10]; - u8 flow_counter_id[0x10]; + u8 flow_counter_id[0x20]; u8 reserved_at_20[0x20]; }; @@ -4402,8 +4401,7 @@ struct mlx5_ifc_query_flow_counter_in_bits { u8 reserved_at_c1[0xf]; u8 num_of_counters[0x10]; - u8 reserved_at_e0[0x10]; - u8 flow_counter_id[0x10]; + u8 flow_counter_id[0x20]; }; struct mlx5_ifc_query_esw_vport_context_out_bits { @@ -6271,8 +6269,7 @@ struct mlx5_ifc_dealloc_flow_counter_in_bits { u8 reserved_at_20[0x10]; u8 op_mod[0x10]; - u8 reserved_at_40[0x10]; - u8 flow_counter_id[0x10]; + u8 flow_counter_id[0x20]; u8 reserved_at_60[0x20]; }; @@ -7097,8 +7094,7 @@ struct mlx5_ifc_alloc_flow_counter_out_bits { u8 syndrome[0x20]; - u8 reserved_at_40[0x10]; - u8 flow_counter_id[0x10]; + u8 flow_counter_id[0x20]; u8 reserved_at_60[0x20]; }; -- cgit v1.2.3 From 2572ac53c46f58e500b9d8d0f99785666038c590 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:17 +0200 Subject: net: sched: make type an argument for ndo_setup_tc Since the type is always present, push it to be a separate argument to ndo_setup_tc. On the way, name the type enum and use it for arg type. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 6 +++--- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 7 ++++--- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 5 +++-- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 7 ++++--- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 7 ++++--- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 7 ++++--- .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 6 +++--- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 7 ++++--- drivers/net/ethernet/intel/i40e/i40e_main.c | 6 +++--- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 9 +++++---- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 6 +++--- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 8 ++++---- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 9 +++++---- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 10 +++++----- drivers/net/ethernet/netronome/nfp/bpf/main.c | 5 +++-- drivers/net/ethernet/netronome/nfp/flower/main.h | 3 ++- drivers/net/ethernet/netronome/nfp/flower/offload.c | 5 +++-- drivers/net/ethernet/netronome/nfp/nfp_app.h | 6 ++++-- drivers/net/ethernet/netronome/nfp/nfp_port.c | 7 ++++--- drivers/net/ethernet/netronome/nfp/nfp_port.h | 5 +++-- drivers/net/ethernet/sfc/efx.h | 5 +++-- drivers/net/ethernet/sfc/falcon/efx.h | 5 +++-- drivers/net/ethernet/sfc/falcon/tx.c | 7 ++++--- drivers/net/ethernet/sfc/tx.c | 7 ++++--- drivers/net/ethernet/ti/netcp_core.c | 7 ++++--- include/linux/netdevice.h | 9 +++++---- net/dsa/slave.c | 6 +++--- net/sched/cls_bpf.c | 4 ++-- net/sched/cls_flower.c | 14 ++++++-------- net/sched/cls_matchall.c | 10 ++++------ net/sched/cls_u32.c | 20 ++++++++------------ net/sched/sch_mqprio.c | 13 ++++++------- 32 files changed, 125 insertions(+), 113 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index ecef3ee87b17..6a6ea3bdd056 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1918,14 +1918,14 @@ static void xgbe_poll_controller(struct net_device *netdev) } #endif /* End CONFIG_NET_POLL_CONTROLLER */ -static int xgbe_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, +static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc_to_netdev) { struct xgbe_prv_data *pdata = netdev_priv(netdev); u8 tc; - if (tc_to_netdev->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc_to_netdev->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 67fe3d826566..4395d1cac86f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4284,10 +4284,11 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) return 0; } -int __bnx2x_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index c26688d2f326..1ac4eb0d3413 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -486,8 +486,9 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev); /* setup_tc callback */ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc); -int __bnx2x_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc); int bnx2x_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 156fb374522b..b98d9f33d9af 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7237,10 +7237,11 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) return 0; } -static int bnxt_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *ntc) +static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *ntc) { - if (ntc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index fdf220aa08d6..89d2b0cd9869 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2889,8 +2889,9 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate) return err; } -static int cxgb_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); @@ -2906,7 +2907,7 @@ static int cxgb_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, } if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) && - tc->type == TC_SETUP_CLSU32) { + type == TC_SETUP_CLSU32) { switch (tc->cls_u32->command) { case TC_CLSU32_NEW_KNODE: case TC_CLSU32_REPLACE_KNODE: diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 550ea1ec7b6c..d86d766777c8 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -342,14 +342,15 @@ static void dpaa_get_stats64(struct net_device *net_dev, } } -static int dpaa_setup_tc(struct net_device *net_dev, u32 handle, - u32 chain_index, __be16 proto, struct tc_to_netdev *tc) +static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { struct dpaa_priv *priv = netdev_priv(net_dev); u8 num_tc; int i; - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index ad9481c7ceae..6bb1e35336cc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1219,11 +1219,11 @@ static int hns3_setup_tc(struct net_device *netdev, u8 tc) return 0; } -static int hns3_nic_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 protocol, +static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc) { - if (handle != TC_H_ROOT || tc->type != TC_SETUP_MQPRIO) + if (handle != TC_H_ROOT || type != TC_SETUP_MQPRIO) return -EINVAL; return hns3_setup_tc(dev, tc->mqprio->num_tc); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 5e37387c7082..b30190639e78 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1265,10 +1265,11 @@ err_queueing_scheme: return err; } -static int __fm10k_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int __fm10k_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4104944ea367..7d47a718f922 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5656,11 +5656,11 @@ exit: return ret; } -static int __i40e_setup_tc(struct net_device *netdev, u32 handle, - u32 chain_index, __be16 proto, +static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 091fcc7e6e43..d39db9711df6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9226,8 +9226,9 @@ free_jump: return err; } -static int __ixgbe_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { struct ixgbe_adapter *adapter = netdev_priv(dev); @@ -9235,7 +9236,7 @@ static int __ixgbe_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, return -EOPNOTSUPP; if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) && - tc->type == TC_SETUP_CLSU32) { + type == TC_SETUP_CLSU32) { switch (tc->cls_u32->command) { case TC_CLSU32_NEW_KNODE: case TC_CLSU32_REPLACE_KNODE: @@ -9255,7 +9256,7 @@ static int __ixgbe_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, } } - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 3a291fc1780a..5c33550765ed 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -130,11 +130,11 @@ out: return err; } -static int __mlx4_en_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, +static int __mlx4_en_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; if (tc->mqprio->num_tc && tc->mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 57f31fa478ce..4052e225f1dc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3027,8 +3027,8 @@ out: return err; } -static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, +static int mlx5e_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -3039,7 +3039,7 @@ static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, if (chain_index) return -EOPNOTSUPP; - switch (tc->type) { + switch (type) { case TC_SETUP_CLSFLOWER: switch (tc->cls_flower->command) { case TC_CLSFLOWER_REPLACE: @@ -3054,7 +3054,7 @@ static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, } mqprio: - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 45e60be9c277..d44049ed5371 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -651,7 +651,8 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, return 0; } -static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, u32 handle, +static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, + enum tc_setup_type type, u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { @@ -664,15 +665,15 @@ static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, u32 handle, struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct net_device *uplink_dev = mlx5_eswitch_get_uplink_netdev(esw); - return uplink_dev->netdev_ops->ndo_setup_tc(uplink_dev, handle, - chain_index, + return uplink_dev->netdev_ops->ndo_setup_tc(uplink_dev, type, + handle, chain_index, proto, tc); } if (chain_index) return -EOPNOTSUPP; - switch (tc->type) { + switch (type) { case TC_SETUP_CLSFLOWER: switch (tc->cls_flower->command) { case TC_CLSFLOWER_REPLACE: diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 66d511d45c25..155424266cbf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1693,8 +1693,8 @@ static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, kfree(mall_tc_entry); } -static int mlxsw_sp_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, +static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); @@ -1703,7 +1703,7 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, u32 handle, if (chain_index) return -EOPNOTSUPP; - switch (tc->type) { + switch (type) { case TC_SETUP_MATCHALL: switch (tc->cls_mall->command) { case TC_CLSMATCHALL_REPLACE: @@ -1733,9 +1733,9 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, u32 handle, default: return -EOPNOTSUPP; } + default: + return -EOPNOTSUPP; } - - return -EOPNOTSUPP; } static const struct net_device_ops mlxsw_sp_port_netdev_ops = { diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index afbdf5fd4e4f..788880808a6e 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -121,7 +121,8 @@ static void nfp_bpf_vnic_clean(struct nfp_app *app, struct nfp_net *nn) } static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc) + enum tc_setup_type type, u32 handle, __be16 proto, + struct tc_to_netdev *tc) { struct nfp_net *nn = netdev_priv(netdev); @@ -130,7 +131,7 @@ static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, if (proto != htons(ETH_P_ALL)) return -EOPNOTSUPP; - if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) { + if (type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) { if (!nn->dp.bpf_offload_xdp) return nfp_net_bpf_offload(nn, tc->cls_bpf); else diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 9e64c048e83f..314e6e8ba649 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -135,7 +135,8 @@ int nfp_flower_metadata_init(struct nfp_app *app); void nfp_flower_metadata_cleanup(struct nfp_app *app); int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc); + enum tc_setup_type type, u32 handle, __be16 proto, + struct tc_to_netdev *tc); int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow, struct nfp_fl_key_ls *key_ls, struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 4ad10bd5e139..d045cf8c140a 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -385,7 +385,8 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, } int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc) + enum tc_setup_type type, u32 handle, __be16 proto, + struct tc_to_netdev *tc) { if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) return -EOPNOTSUPP; @@ -393,7 +394,7 @@ int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, if (!eth_proto_is_802_3(proto)) return -EOPNOTSUPP; - if (tc->type != TC_SETUP_CLSFLOWER) + if (type != TC_SETUP_CLSFLOWER) return -EINVAL; return nfp_flower_repr_offload(app, netdev, tc->cls_flower); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index 5d714e10d9a9..b3b03bb9d907 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -109,7 +109,8 @@ struct nfp_app_type { void (*ctrl_msg_rx)(struct nfp_app *app, struct sk_buff *skb); int (*setup_tc)(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc); + enum tc_setup_type type, u32 handle, __be16 proto, + struct tc_to_netdev *tc); bool (*tc_busy)(struct nfp_app *app, struct nfp_net *nn); int (*xdp_offload)(struct nfp_app *app, struct nfp_net *nn, struct bpf_prog *prog); @@ -238,12 +239,13 @@ static inline bool nfp_app_tc_busy(struct nfp_app *app, struct nfp_net *nn) static inline int nfp_app_setup_tc(struct nfp_app *app, struct net_device *netdev, + enum tc_setup_type type, u32 handle, __be16 proto, struct tc_to_netdev *tc) { if (!app || !app->type->setup_tc) return -EOPNOTSUPP; - return app->type->setup_tc(app, netdev, handle, proto, tc); + return app->type->setup_tc(app, netdev, type, handle, proto, tc); } static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index d16a7b78ba9b..9d776f982352 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -88,8 +88,9 @@ const struct switchdev_ops nfp_port_switchdev_ops = { .switchdev_port_attr_get = nfp_port_attr_get, }; -int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { struct nfp_port *port; @@ -100,7 +101,7 @@ int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, if (!port) return -EOPNOTSUPP; - return nfp_app_setup_tc(port->app, netdev, handle, proto, tc); + return nfp_app_setup_tc(port->app, netdev, type, handle, proto, tc); } struct nfp_port * diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 56c76926c82a..239c5401000c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -109,8 +109,9 @@ struct nfp_port { extern const struct switchdev_ops nfp_port_switchdev_ops; -int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc); struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); struct nfp_port * diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index fcea9371ab7f..e41a7179bc05 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -32,8 +32,9 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); -int efx_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc); unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); extern unsigned int efx_piobuf_size; extern bool efx_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/efx.h b/drivers/net/ethernet/sfc/falcon/efx.h index e5a7a40cc8b6..f3bc67ec1f30 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.h +++ b/drivers/net/ethernet/sfc/falcon/efx.h @@ -32,8 +32,9 @@ netdev_tx_t ef4_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); netdev_tx_t ef4_enqueue_skb(struct ef4_tx_queue *tx_queue, struct sk_buff *skb); void ef4_xmit_done(struct ef4_tx_queue *tx_queue, unsigned int index); -int ef4_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc); unsigned int ef4_tx_max_skb_descs(struct ef4_nic *efx); extern bool ef4_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index f1520a404ac6..6c4752694c1f 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -425,8 +425,9 @@ void ef4_init_tx_queue_core_txq(struct ef4_tx_queue *tx_queue) efx->n_tx_channels : 0)); } -int ef4_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *ntc) +int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *ntc) { struct ef4_nic *efx = netdev_priv(net_dev); struct ef4_channel *channel; @@ -434,7 +435,7 @@ int ef4_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, unsigned tc, num_tc; int rc; - if (ntc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; num_tc = ntc->mqprio->num_tc; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 02d41eb4a8e9..0c08c10d751c 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -653,8 +653,9 @@ void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue) efx->n_tx_channels : 0)); } -int efx_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *ntc) +int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *ntc) { struct efx_nic *efx = netdev_priv(net_dev); struct efx_channel *channel; @@ -662,7 +663,7 @@ int efx_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, unsigned tc, num_tc; int rc; - if (ntc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; num_tc = ntc->mqprio->num_tc; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 9d52c3a78621..cb21742f6177 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1877,8 +1877,9 @@ static u16 netcp_select_queue(struct net_device *dev, struct sk_buff *skb, return 0; } -static int netcp_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { u8 num_tc; int i; @@ -1886,7 +1887,7 @@ static int netcp_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, /* setup tc must be called under rtnl lock */ ASSERT_RTNL(); - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3a3cdc1b1f31..e4238e540544 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -774,7 +774,7 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, /* These structures hold the attributes of qdisc and classifiers * that are being passed to the netdevice through the setup_tc op. */ -enum { +enum tc_setup_type { TC_SETUP_MQPRIO, TC_SETUP_CLSU32, TC_SETUP_CLSFLOWER, @@ -785,7 +785,6 @@ enum { struct tc_cls_u32_offload; struct tc_to_netdev { - unsigned int type; union { struct tc_cls_u32_offload *cls_u32; struct tc_cls_flower_offload *cls_flower; @@ -978,8 +977,9 @@ struct xfrmdev_ops { * with PF and querying it may introduce a theoretical security risk. * int (*ndo_set_vf_rss_query_en)(struct net_device *dev, int vf, bool setting); * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb); - * int (*ndo_setup_tc)(struct net_device *dev, u32 handle, u32 chain_index, - * __be16 protocol, struct tc_to_netdev *tc); + * int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, + * u32 handle, u32 chain_index, __be16 protocol, + * struct tc_to_netdev *tc); * Called to setup any 'tc' scheduler, classifier or action on @dev. * This is always called from the stack with the rtnl lock held and netif * tx queues stopped. This allows the netdevice to perform queue @@ -1227,6 +1227,7 @@ struct net_device_ops { struct net_device *dev, int vf, bool setting); int (*ndo_setup_tc)(struct net_device *dev, + enum tc_setup_type type, u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 75c5c5808220..b4b63c20ec80 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -863,8 +863,8 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev, kfree(mall_tc_entry); } -static int dsa_slave_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 protocol, +static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc) { bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); @@ -872,7 +872,7 @@ static int dsa_slave_setup_tc(struct net_device *dev, u32 handle, if (chain_index) return -EOPNOTSUPP; - switch (tc->type) { + switch (type) { case TC_SETUP_MATCHALL: switch (tc->cls_mall->command) { case TC_CLSMATCHALL_REPLACE: diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index cf248c3137ad..e2bf2753173d 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -151,7 +151,6 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, struct tc_to_netdev offload; int err; - offload.type = TC_SETUP_CLSBPF; offload.cls_bpf = &bpf_offload; bpf_offload.command = cmd; @@ -161,7 +160,8 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, bpf_offload.exts_integrated = prog->exts_integrated; bpf_offload.gen_flags = prog->gen_flags; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, + tp->q->handle, tp->chain->index, tp->protocol, &offload); diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 7ab524fc43f9..ddeed17d2024 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -236,11 +236,10 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) offload.prio = tp->prio; offload.cookie = (unsigned long)f; - tc->type = TC_SETUP_CLSFLOWER; tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->chain->index, - tp->protocol, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tp->q->handle, + tp->chain->index, tp->protocol, tc); } static int fl_hw_replace_filter(struct tcf_proto *tp, @@ -273,11 +272,11 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, offload.key = &f->mkey; offload.exts = &f->exts; - tc->type = TC_SETUP_CLSFLOWER; tc->cls_flower = &offload; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, - tp->chain->index, tp->protocol, tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, + tp->q->handle, tp->chain->index, + tp->protocol, tc); if (!err) f->flags |= TCA_CLS_FLAGS_IN_HW; @@ -300,10 +299,9 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) offload.cookie = (unsigned long)f; offload.exts = &f->exts; - tc->type = TC_SETUP_CLSFLOWER; tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, + dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, tp->q->handle, tp->chain->index, tp->protocol, tc); } diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index f35177b48373..6ffe0b82ab83 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -58,14 +58,13 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, struct tc_cls_matchall_offload mall_offload = {0}; int err; - offload.type = TC_SETUP_MATCHALL; offload.cls_mall = &mall_offload; offload.cls_mall->command = TC_CLSMATCHALL_REPLACE; offload.cls_mall->exts = &head->exts; offload.cls_mall->cookie = cookie; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, - tp->chain->index, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MATCHALL, + tp->q->handle, tp->chain->index, tp->protocol, &offload); if (!err) head->flags |= TCA_CLS_FLAGS_IN_HW; @@ -81,14 +80,13 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, struct tc_to_netdev offload; struct tc_cls_matchall_offload mall_offload = {0}; - offload.type = TC_SETUP_MATCHALL; offload.cls_mall = &mall_offload; offload.cls_mall->command = TC_CLSMATCHALL_DESTROY; offload.cls_mall->exts = NULL; offload.cls_mall->cookie = cookie; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->chain->index, - tp->protocol, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MATCHALL, tp->q->handle, + tp->chain->index, tp->protocol, &offload); } static void mall_destroy(struct tcf_proto *tp) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 9fd243799fe7..d1bae4cc749f 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -434,15 +434,14 @@ static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle) struct tc_cls_u32_offload u32_offload = {0}; struct tc_to_netdev offload; - offload.type = TC_SETUP_CLSU32; offload.cls_u32 = &u32_offload; if (tc_should_offload(dev, tp, 0)) { offload.cls_u32->command = TC_CLSU32_DELETE_KNODE; offload.cls_u32->knode.handle = handle; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, - tp->chain->index, tp->protocol, - &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, + tp->q->handle, tp->chain->index, + tp->protocol, &offload); } } @@ -457,7 +456,6 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, if (!tc_should_offload(dev, tp, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; - offload.type = TC_SETUP_CLSU32; offload.cls_u32 = &u32_offload; offload.cls_u32->command = TC_CLSU32_NEW_HNODE; @@ -465,7 +463,7 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, offload.cls_u32->hnode.handle = h->handle; offload.cls_u32->hnode.prio = h->prio; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, tp->q->handle, tp->chain->index, tp->protocol, &offload); if (tc_skip_sw(flags)) @@ -480,7 +478,6 @@ static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) struct tc_cls_u32_offload u32_offload = {0}; struct tc_to_netdev offload; - offload.type = TC_SETUP_CLSU32; offload.cls_u32 = &u32_offload; if (tc_should_offload(dev, tp, 0)) { @@ -489,9 +486,9 @@ static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) offload.cls_u32->hnode.handle = h->handle; offload.cls_u32->hnode.prio = h->prio; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, - tp->chain->index, tp->protocol, - &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, + tp->q->handle, tp->chain->index, + tp->protocol, &offload); } } @@ -503,7 +500,6 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, struct tc_to_netdev offload; int err; - offload.type = TC_SETUP_CLSU32; offload.cls_u32 = &u32_offload; if (!tc_should_offload(dev, tp, flags)) @@ -524,7 +520,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, if (n->ht_down) offload.cls_u32->knode.link_handle = n->ht_down->handle; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, tp->q->handle, tp->chain->index, tp->protocol, &offload); diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index e0c02725cd48..329610ce4dfe 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -40,10 +40,10 @@ static void mqprio_destroy(struct Qdisc *sch) if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) { struct tc_mqprio_qopt offload = { 0 }; - struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO, - { .mqprio = &offload } }; + struct tc_to_netdev tc = { { .mqprio = &offload } }; - dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, 0, &tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, + sch->handle, 0, 0, &tc); } else { netdev_set_num_tc(dev, 0); } @@ -149,11 +149,10 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) */ if (qopt->hw) { struct tc_mqprio_qopt offload = *qopt; - struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO, - { .mqprio = &offload } }; + struct tc_to_netdev tc = { { .mqprio = &offload } }; - err = dev->netdev_ops->ndo_setup_tc(dev, sch->handle, - 0, 0, &tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, + sch->handle, 0, 0, &tc); if (err) return err; -- cgit v1.2.3 From ade9b6588420b335851951702ab975c975b0c1b2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:18 +0200 Subject: net: sched: rename TC_SETUP_MATCHALL to TC_SETUP_CLSMATCHALL In order to be aligned with the rest of the types, rename TC_SETUP_MATCHALL to TC_SETUP_CLSMATCHALL. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- include/linux/netdevice.h | 2 +- net/dsa/slave.c | 2 +- net/sched/cls_matchall.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 155424266cbf..6438c38e7a68 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1704,7 +1704,7 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, return -EOPNOTSUPP; switch (type) { - case TC_SETUP_MATCHALL: + case TC_SETUP_CLSMATCHALL: switch (tc->cls_mall->command) { case TC_CLSMATCHALL_REPLACE: return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e4238e540544..f8051a36f900 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -778,7 +778,7 @@ enum tc_setup_type { TC_SETUP_MQPRIO, TC_SETUP_CLSU32, TC_SETUP_CLSFLOWER, - TC_SETUP_MATCHALL, + TC_SETUP_CLSMATCHALL, TC_SETUP_CLSBPF, }; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index b4b63c20ec80..453f6ddcd023 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -873,7 +873,7 @@ static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, return -EOPNOTSUPP; switch (type) { - case TC_SETUP_MATCHALL: + case TC_SETUP_CLSMATCHALL: switch (tc->cls_mall->command) { case TC_CLSMATCHALL_REPLACE: return dsa_slave_add_cls_matchall(dev, protocol, diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 6ffe0b82ab83..a8853ada22f6 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -63,7 +63,7 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, offload.cls_mall->exts = &head->exts; offload.cls_mall->cookie = cookie; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MATCHALL, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, tp->q->handle, tp->chain->index, tp->protocol, &offload); if (!err) @@ -85,7 +85,7 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, offload.cls_mall->exts = NULL; offload.cls_mall->cookie = cookie; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MATCHALL, tp->q->handle, + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, tp->q->handle, tp->chain->index, tp->protocol, &offload); } -- cgit v1.2.3 From 3e0e82664322290a59189f7c2bcb39b0de932505 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:19 +0200 Subject: net: sched: make egress_dev flag part of flower offload struct Since this is specific to flower now, make it part of the flower offload struct. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 2 +- include/linux/netdevice.h | 1 - include/net/pkt_cls.h | 1 + net/sched/cls_flower.c | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index d44049ed5371..0e6bab182071 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -661,7 +661,7 @@ static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) return -EOPNOTSUPP; - if (tc->egress_dev) { + if (type == TC_SETUP_CLSFLOWER && tc->cls_flower->egress_dev) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct net_device *uplink_dev = mlx5_eswitch_get_uplink_netdev(esw); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f8051a36f900..bd49dbaee84e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -792,7 +792,6 @@ struct tc_to_netdev { struct tc_cls_bpf_offload *cls_bpf; struct tc_mqprio_qopt *mqprio; }; - bool egress_dev; }; /* These structures hold the attributes of xdp state that are being passed diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index e0c54f111467..8213acdfdf5a 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -504,6 +504,7 @@ struct tc_cls_flower_offload { struct fl_flow_key *mask; struct fl_flow_key *key; struct tcf_exts *exts; + bool egress_dev; }; enum tc_matchall_command { diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index ddeed17d2024..52deeed2b7f5 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -259,7 +259,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, return tc_skip_sw(f->flags) ? -EINVAL : 0; } dev = f->hw_dev; - tc->egress_dev = true; + offload.egress_dev = true; } else { f->hw_dev = dev; } -- cgit v1.2.3 From f73230430ac295b14cd1ee162dbf7ccfd90cbf6b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:20 +0200 Subject: cxgb4: push cls_u32 setup_tc processing into a separate function Let cxgb_setup_tc be a splitter for specific setup_tc types and push out cls_u32 specific code into a separate function. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 42 +++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 89d2b0cd9869..651229070113 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2889,6 +2889,26 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate) return err; } +static int cxgb_setup_tc_cls_u32(struct net_device *dev, + enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_cls_u32_offload *cls_u32) +{ + if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || + chain_index) + return -EOPNOTSUPP; + + switch (cls_u32->command) { + case TC_CLSU32_NEW_KNODE: + case TC_CLSU32_REPLACE_KNODE: + return cxgb4_config_knode(dev, proto, cls_u32); + case TC_CLSU32_DELETE_KNODE: + return cxgb4_delete_knode(dev, proto, cls_u32); + default: + return -EOPNOTSUPP; + } +} + static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) @@ -2896,9 +2916,6 @@ static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); - if (chain_index) - return -EOPNOTSUPP; - if (!(adap->flags & FULL_INIT_DONE)) { dev_err(adap->pdev_dev, "Failed to setup tc on port %d. Link Down?\n", @@ -2906,20 +2923,13 @@ static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, return -EINVAL; } - if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) && - type == TC_SETUP_CLSU32) { - switch (tc->cls_u32->command) { - case TC_CLSU32_NEW_KNODE: - case TC_CLSU32_REPLACE_KNODE: - return cxgb4_config_knode(dev, proto, tc->cls_u32); - case TC_CLSU32_DELETE_KNODE: - return cxgb4_delete_knode(dev, proto, tc->cls_u32); - default: - return -EOPNOTSUPP; - } + switch (type) { + case TC_SETUP_CLSU32: + return cxgb_setup_tc_cls_u32(dev, type, handle, chain_index, + proto, tc->cls_u32); + default: + return -EOPNOTSUPP; } - - return -EOPNOTSUPP; } static netdev_features_t cxgb_fix_features(struct net_device *dev, -- cgit v1.2.3 From bc32afdb2b01ae7652cbb829475d270f7d813618 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:21 +0200 Subject: ixgbe: push cls_u32 and mqprio setup_tc processing into separate functions Let __ixgbe_setup_tc be a splitter for specific setup_tc types and push out cls_u32 and mqprio specific codes into separate functions. Also change the return values so they are the same as in the rest of the drivers. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 67 ++++++++++++++++----------- 1 file changed, 39 insertions(+), 28 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index d39db9711df6..35db198199b0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9226,42 +9226,53 @@ free_jump: return err; } -static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, - struct tc_to_netdev *tc) +static int ixgbe_setup_tc_cls_u32(struct net_device *dev, + u32 handle, u32 chain_index, __be16 proto, + struct tc_cls_u32_offload *cls_u32) { struct ixgbe_adapter *adapter = netdev_priv(dev); - if (chain_index) + if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || + chain_index) return -EOPNOTSUPP; - if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) && - type == TC_SETUP_CLSU32) { - switch (tc->cls_u32->command) { - case TC_CLSU32_NEW_KNODE: - case TC_CLSU32_REPLACE_KNODE: - return ixgbe_configure_clsu32(adapter, - proto, tc->cls_u32); - case TC_CLSU32_DELETE_KNODE: - return ixgbe_delete_clsu32(adapter, tc->cls_u32); - case TC_CLSU32_NEW_HNODE: - case TC_CLSU32_REPLACE_HNODE: - return ixgbe_configure_clsu32_add_hnode(adapter, proto, - tc->cls_u32); - case TC_CLSU32_DELETE_HNODE: - return ixgbe_configure_clsu32_del_hnode(adapter, - tc->cls_u32); - default: - return -EINVAL; - } + switch (cls_u32->command) { + case TC_CLSU32_NEW_KNODE: + case TC_CLSU32_REPLACE_KNODE: + return ixgbe_configure_clsu32(adapter, proto, cls_u32); + case TC_CLSU32_DELETE_KNODE: + return ixgbe_delete_clsu32(adapter, cls_u32); + case TC_CLSU32_NEW_HNODE: + case TC_CLSU32_REPLACE_HNODE: + return ixgbe_configure_clsu32_add_hnode(adapter, proto, + cls_u32); + case TC_CLSU32_DELETE_HNODE: + return ixgbe_configure_clsu32_del_hnode(adapter, cls_u32); + default: + return -EOPNOTSUPP; } +} - if (type != TC_SETUP_MQPRIO) - return -EINVAL; - - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; +static int ixgbe_setup_tc_mqprio(struct net_device *dev, + struct tc_mqprio_qopt *mqprio) +{ + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + return ixgbe_setup_tc(dev, mqprio->num_tc); +} - return ixgbe_setup_tc(dev, tc->mqprio->num_tc); +static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) +{ + switch (type) { + case TC_SETUP_CLSU32: + return ixgbe_setup_tc_cls_u32(dev, handle, chain_index, proto, + tc->cls_u32); + case TC_SETUP_MQPRIO: + return ixgbe_setup_tc_mqprio(dev, tc->mqprio); + default: + return -EOPNOTSUPP; + } } #ifdef CONFIG_PCI_IOV -- cgit v1.2.3 From 0cf0f6d3d39672c044393aef71ee782430ca8b13 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:22 +0200 Subject: mlx5e: push cls_flower and mqprio setup_tc processing into separate functions Let mlx5e_setup_tc (former mlx5e_ndo_setup_tc) be a splitter for specific setup_tc types and push out cls_flower and mqprio specific codes into separate functions. Also change the return values so they are the same as in the rest of the drivers. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 56 +++++++++++++---------- 1 file changed, 31 insertions(+), 25 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 4052e225f1dc..adf35da74a85 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2998,12 +2998,16 @@ static int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd) return 0; } -static int mlx5e_setup_tc(struct net_device *netdev, u8 tc) +static int mlx5e_setup_tc_mqprio(struct net_device *netdev, + struct tc_mqprio_qopt *mqprio) { struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5e_channels new_channels = {}; + u8 tc = mqprio->num_tc; int err = 0; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + if (tc && tc != MLX5E_MAX_NUM_TC) return -EINVAL; @@ -3027,39 +3031,41 @@ out: return err; } -static int mlx5e_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, - struct tc_to_netdev *tc) +static int mlx5e_setup_tc_cls_flower(struct net_device *dev, + u32 handle, u32 chain_index, __be16 proto, + struct tc_cls_flower_offload *cls_flower) { struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) - goto mqprio; + if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || + chain_index) + return -EOPNOTSUPP; - if (chain_index) + switch (cls_flower->command) { + case TC_CLSFLOWER_REPLACE: + return mlx5e_configure_flower(priv, proto, cls_flower); + case TC_CLSFLOWER_DESTROY: + return mlx5e_delete_flower(priv, cls_flower); + case TC_CLSFLOWER_STATS: + return mlx5e_stats_flower(priv, cls_flower); + default: return -EOPNOTSUPP; + } +} +static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) +{ switch (type) { case TC_SETUP_CLSFLOWER: - switch (tc->cls_flower->command) { - case TC_CLSFLOWER_REPLACE: - return mlx5e_configure_flower(priv, proto, tc->cls_flower); - case TC_CLSFLOWER_DESTROY: - return mlx5e_delete_flower(priv, tc->cls_flower); - case TC_CLSFLOWER_STATS: - return mlx5e_stats_flower(priv, tc->cls_flower); - } + return mlx5e_setup_tc_cls_flower(dev, handle, chain_index, + proto, tc->cls_flower); + case TC_SETUP_MQPRIO: + return mlx5e_setup_tc_mqprio(dev, tc->mqprio); default: return -EOPNOTSUPP; } - -mqprio: - if (type != TC_SETUP_MQPRIO) - return -EINVAL; - - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - - return mlx5e_setup_tc(dev, tc->mqprio->num_tc); } static void @@ -3695,7 +3701,7 @@ static const struct net_device_ops mlx5e_netdev_ops_basic = { .ndo_open = mlx5e_open, .ndo_stop = mlx5e_close, .ndo_start_xmit = mlx5e_xmit, - .ndo_setup_tc = mlx5e_ndo_setup_tc, + .ndo_setup_tc = mlx5e_setup_tc, .ndo_select_queue = mlx5e_select_queue, .ndo_get_stats64 = mlx5e_get_stats, .ndo_set_rx_mode = mlx5e_set_rx_mode, @@ -3720,7 +3726,7 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = { .ndo_open = mlx5e_open, .ndo_stop = mlx5e_close, .ndo_start_xmit = mlx5e_xmit, - .ndo_setup_tc = mlx5e_ndo_setup_tc, + .ndo_setup_tc = mlx5e_setup_tc, .ndo_select_queue = mlx5e_select_queue, .ndo_get_stats64 = mlx5e_get_stats, .ndo_set_rx_mode = mlx5e_set_rx_mode, -- cgit v1.2.3 From 8c818c27f37f4d847769a38b9d20c1b5ae21075d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:23 +0200 Subject: mlx5e_rep: push cls_flower setup_tc processing into a separate function Let mlx5e_rep_setup_tc (former mlx5e_rep_ndo_setup_tc) be a splitter for specific setup_tc types and push out cls_flower specific code into a separate function. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 49 ++++++++++++++---------- 1 file changed, 29 insertions(+), 20 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 0e6bab182071..e6cc642f6d8c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -651,38 +651,47 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, return 0; } -static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, - enum tc_setup_type type, u32 handle, - u32 chain_index, __be16 proto, - struct tc_to_netdev *tc) +static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, + u32 handle, u32 chain_index, + __be16 proto, + struct tc_to_netdev *tc) { + struct tc_cls_flower_offload *cls_flower = tc->cls_flower; struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) + if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || + chain_index) return -EOPNOTSUPP; - if (type == TC_SETUP_CLSFLOWER && tc->cls_flower->egress_dev) { + if (cls_flower->egress_dev) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - struct net_device *uplink_dev = mlx5_eswitch_get_uplink_netdev(esw); - return uplink_dev->netdev_ops->ndo_setup_tc(uplink_dev, type, - handle, chain_index, - proto, tc); + dev = mlx5_eswitch_get_uplink_netdev(esw); + return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, + handle, chain_index, + proto, tc); } - if (chain_index) + switch (cls_flower->command) { + case TC_CLSFLOWER_REPLACE: + return mlx5e_configure_flower(priv, proto, cls_flower); + case TC_CLSFLOWER_DESTROY: + return mlx5e_delete_flower(priv, cls_flower); + case TC_CLSFLOWER_STATS: + return mlx5e_stats_flower(priv, cls_flower); + default: return -EOPNOTSUPP; + } +} +static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) +{ switch (type) { case TC_SETUP_CLSFLOWER: - switch (tc->cls_flower->command) { - case TC_CLSFLOWER_REPLACE: - return mlx5e_configure_flower(priv, proto, tc->cls_flower); - case TC_CLSFLOWER_DESTROY: - return mlx5e_delete_flower(priv, tc->cls_flower); - case TC_CLSFLOWER_STATS: - return mlx5e_stats_flower(priv, tc->cls_flower); - } + return mlx5e_rep_setup_tc_cls_flower(dev, handle, chain_index, + proto, tc); default: return -EOPNOTSUPP; } @@ -774,7 +783,7 @@ static const struct net_device_ops mlx5e_netdev_ops_rep = { .ndo_stop = mlx5e_rep_close, .ndo_start_xmit = mlx5e_xmit, .ndo_get_phys_port_name = mlx5e_rep_get_phys_port_name, - .ndo_setup_tc = mlx5e_rep_ndo_setup_tc, + .ndo_setup_tc = mlx5e_rep_setup_tc, .ndo_get_stats64 = mlx5e_rep_get_stats, .ndo_has_offload_stats = mlx5e_has_offload_stats, .ndo_get_offload_stats = mlx5e_get_offload_stats, -- cgit v1.2.3 From fd33f1dfed6141280bc85817e6f79b87a5c7320b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:24 +0200 Subject: mlxsw: spectrum: push cls_flower and cls_matchall setup_tc processing into separate functions Let mlxsw_sp_setup_tc be a splitter for specific setup_tc types and push out cls_flower and cls_matchall specific codes into separate functions. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 81 ++++++++++++++++---------- 1 file changed, 51 insertions(+), 30 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 6438c38e7a68..9f8ba37fa7e3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1693,46 +1693,67 @@ static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, kfree(mall_tc_entry); } -static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, +static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, + u32 handle, u32 chain_index, + __be16 proto, + struct tc_cls_matchall_offload *f) +{ + bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); + + if (chain_index) + return -EOPNOTSUPP; + + switch (f->command) { + case TC_CLSMATCHALL_REPLACE: + return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, proto, f, + ingress); + case TC_CLSMATCHALL_DESTROY: + mlxsw_sp_port_del_cls_matchall(mlxsw_sp_port, f); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int +mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, u32 chain_index, __be16 proto, - struct tc_to_netdev *tc) + struct tc_cls_flower_offload *f) { - struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); if (chain_index) return -EOPNOTSUPP; + switch (f->command) { + case TC_CLSFLOWER_REPLACE: + return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, + proto, f); + case TC_CLSFLOWER_DESTROY: + mlxsw_sp_flower_destroy(mlxsw_sp_port, ingress, f); + return 0; + case TC_CLSFLOWER_STATS: + return mlxsw_sp_flower_stats(mlxsw_sp_port, ingress, f); + default: + return -EOPNOTSUPP; + } +} + +static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + switch (type) { case TC_SETUP_CLSMATCHALL: - switch (tc->cls_mall->command) { - case TC_CLSMATCHALL_REPLACE: - return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, - proto, - tc->cls_mall, - ingress); - case TC_CLSMATCHALL_DESTROY: - mlxsw_sp_port_del_cls_matchall(mlxsw_sp_port, - tc->cls_mall); - return 0; - default: - return -EOPNOTSUPP; - } + return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, handle, + chain_index, proto, + tc->cls_mall); case TC_SETUP_CLSFLOWER: - switch (tc->cls_flower->command) { - case TC_CLSFLOWER_REPLACE: - return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, - proto, tc->cls_flower); - case TC_CLSFLOWER_DESTROY: - mlxsw_sp_flower_destroy(mlxsw_sp_port, ingress, - tc->cls_flower); - return 0; - case TC_CLSFLOWER_STATS: - return mlxsw_sp_flower_stats(mlxsw_sp_port, ingress, - tc->cls_flower); - default: - return -EOPNOTSUPP; - } + return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, handle, + chain_index, proto, + tc->cls_flower); default: return -EOPNOTSUPP; } -- cgit v1.2.3 From 9cbf14ede21e1789f24e87a9ba08bbc92211fe42 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:25 +0200 Subject: mlxsw: spectrum: rename cls arg in matchall processing To sync-up with the naming in the rest of the driver, rename the cls arg. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 9f8ba37fa7e3..f333d086932d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1618,7 +1618,7 @@ mlxsw_sp_port_del_cls_matchall_sample(struct mlxsw_sp_port *mlxsw_sp_port) static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, __be16 protocol, - struct tc_cls_matchall_offload *cls, + struct tc_cls_matchall_offload *f, bool ingress) { struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry; @@ -1626,7 +1626,7 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, LIST_HEAD(actions); int err; - if (!tcf_exts_has_one_action(cls->exts)) { + if (!tcf_exts_has_one_action(f->exts)) { netdev_err(mlxsw_sp_port->dev, "only singular actions are supported\n"); return -EOPNOTSUPP; } @@ -1634,9 +1634,9 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL); if (!mall_tc_entry) return -ENOMEM; - mall_tc_entry->cookie = cls->cookie; + mall_tc_entry->cookie = f->cookie; - tcf_exts_to_list(cls->exts, &actions); + tcf_exts_to_list(f->exts, &actions); a = list_first_entry(&actions, struct tc_action, list); if (is_tcf_mirred_egress_mirror(a) && protocol == htons(ETH_P_ALL)) { @@ -1648,7 +1648,7 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, mirror, a, ingress); } else if (is_tcf_sample(a) && protocol == htons(ETH_P_ALL)) { mall_tc_entry->type = MLXSW_SP_PORT_MALL_SAMPLE; - err = mlxsw_sp_port_add_cls_matchall_sample(mlxsw_sp_port, cls, + err = mlxsw_sp_port_add_cls_matchall_sample(mlxsw_sp_port, f, a, ingress); } else { err = -EOPNOTSUPP; @@ -1666,12 +1666,12 @@ err_add_action: } static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, - struct tc_cls_matchall_offload *cls) + struct tc_cls_matchall_offload *f) { struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry; mall_tc_entry = mlxsw_sp_port_mall_tc_entry_find(mlxsw_sp_port, - cls->cookie); + f->cookie); if (!mall_tc_entry) { netdev_dbg(mlxsw_sp_port->dev, "tc entry not found on port\n"); return; -- cgit v1.2.3 From 37cba6b3f86b24d82f27713b3154657ecc95f678 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:27 +0200 Subject: nfp: change flows in apps that offload ndo_setup_tc Change the flows a bit in preparation of follow-up changes in ndo_setup_tc args. Also, change the error code to align with the rest of the drivers. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/bpf/main.c | 16 ++++++---------- drivers/net/ethernet/netronome/nfp/flower/offload.c | 10 +++------- 2 files changed, 9 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index 788880808a6e..d7975dcecb40 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -126,19 +126,15 @@ static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, { struct nfp_net *nn = netdev_priv(netdev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) - return -EOPNOTSUPP; - if (proto != htons(ETH_P_ALL)) + if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) || + TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || + proto != htons(ETH_P_ALL)) return -EOPNOTSUPP; - if (type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) { - if (!nn->dp.bpf_offload_xdp) - return nfp_net_bpf_offload(nn, tc->cls_bpf); - else - return -EBUSY; - } + if (nn->dp.bpf_offload_xdp) + return -EBUSY; - return -EINVAL; + return nfp_net_bpf_offload(nn, tc->cls_bpf); } static bool nfp_bpf_tc_busy(struct nfp_app *app, struct nfp_net *nn) diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index d045cf8c140a..58af438a95c1 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -388,14 +388,10 @@ int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, enum tc_setup_type type, u32 handle, __be16 proto, struct tc_to_netdev *tc) { - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) + if (type != TC_SETUP_CLSFLOWER || + TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || + !eth_proto_is_802_3(proto)) return -EOPNOTSUPP; - if (!eth_proto_is_802_3(proto)) - return -EOPNOTSUPP; - - if (type != TC_SETUP_CLSFLOWER) - return -EINVAL; - return nfp_flower_repr_offload(app, netdev, tc->cls_flower); } -- cgit v1.2.3 From 74897ef0a553c8376aba53d818af10afcd12d945 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:28 +0200 Subject: hns3pf: don't check handle during mqprio offload Similar to the rest offloaders of mqprio, no need to check handle. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index 6bb1e35336cc..ef5795923b0c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1223,7 +1223,7 @@ static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc) { - if (handle != TC_H_ROOT || type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; return hns3_setup_tc(dev, tc->mqprio->num_tc); -- cgit v1.2.3 From 5fd9fc4e207dba0c05cafe78417952b4c4ca02dc Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:29 +0200 Subject: net: sched: push cls related args into cls_common structure As ndo_setup_tc is generic offload op for whole tc subsystem, does not really make sense to have cls-specific args. So move them under cls_common structurure which is embedded in all cls structs. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 1 - drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 1 - drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 1 - drivers/net/ethernet/broadcom/bnxt/bnxt.c | 1 - drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 14 +++++------- drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 7 +++--- drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h | 6 ++---- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 1 - .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 1 - drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 1 - drivers/net/ethernet/intel/i40e/i40e_main.c | 1 - drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 17 ++++++--------- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 11 ++++------ drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 15 +++++-------- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_tc.h | 2 +- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 25 ++++++++-------------- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 +- .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +- drivers/net/ethernet/netronome/nfp/bpf/main.c | 10 +++++---- drivers/net/ethernet/netronome/nfp/flower/main.h | 3 +-- .../net/ethernet/netronome/nfp/flower/offload.c | 12 ++++++----- drivers/net/ethernet/netronome/nfp/nfp_app.h | 6 ++---- drivers/net/ethernet/netronome/nfp/nfp_port.c | 6 +----- drivers/net/ethernet/netronome/nfp/nfp_port.h | 1 - drivers/net/ethernet/sfc/efx.h | 1 - drivers/net/ethernet/sfc/falcon/efx.h | 1 - drivers/net/ethernet/sfc/falcon/tx.c | 1 - drivers/net/ethernet/sfc/tx.c | 1 - drivers/net/ethernet/ti/netcp_core.c | 1 - include/linux/netdevice.h | 3 --- include/net/pkt_cls.h | 19 ++++++++++++++++ net/dsa/slave.c | 14 +++++------- net/sched/cls_bpf.c | 7 ++---- net/sched/cls_flower.c | 13 ++++++----- net/sched/cls_matchall.c | 8 +++---- net/sched/cls_u32.c | 20 +++++++---------- net/sched/sch_mqprio.c | 6 ++---- 39 files changed, 101 insertions(+), 144 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 6a6ea3bdd056..bbb7bfe0be7f 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1919,7 +1919,6 @@ static void xgbe_poll_controller(struct net_device *netdev) #endif /* End CONFIG_NET_POLL_CONTROLLER */ static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc_to_netdev) { struct xgbe_prv_data *pdata = netdev_priv(netdev); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 4395d1cac86f..257cf4be0162 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4285,7 +4285,6 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) } int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 1ac4eb0d3413..04eb95043023 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -487,7 +487,6 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev); /* setup_tc callback */ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc); int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc); int bnx2x_get_vf_config(struct net_device *dev, int vf, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index b98d9f33d9af..1545b88c545d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7238,7 +7238,6 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) } static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *ntc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 651229070113..13199317c8e0 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2890,27 +2890,24 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate) } static int cxgb_setup_tc_cls_u32(struct net_device *dev, - enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_cls_u32_offload *cls_u32) { - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - chain_index) + if (TC_H_MAJ(cls_u32->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_u32->common.chain_index) return -EOPNOTSUPP; switch (cls_u32->command) { case TC_CLSU32_NEW_KNODE: case TC_CLSU32_REPLACE_KNODE: - return cxgb4_config_knode(dev, proto, cls_u32); + return cxgb4_config_knode(dev, cls_u32); case TC_CLSU32_DELETE_KNODE: - return cxgb4_delete_knode(dev, proto, cls_u32); + return cxgb4_delete_knode(dev, cls_u32); default: return -EOPNOTSUPP; } } static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct port_info *pi = netdev2pinfo(dev); @@ -2925,8 +2922,7 @@ static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, switch (type) { case TC_SETUP_CLSU32: - return cxgb_setup_tc_cls_u32(dev, type, handle, chain_index, - proto, tc->cls_u32); + return cxgb_setup_tc_cls_u32(dev, tc->cls_u32); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c index 6f734c52ef25..48970ba08bdc 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c @@ -146,11 +146,11 @@ static int fill_action_fields(struct adapter *adap, return 0; } -int cxgb4_config_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls) +int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) { const struct cxgb4_match_field *start, *link_start = NULL; struct adapter *adapter = netdev2adap(dev); + __be16 protocol = cls->common.protocol; struct ch_filter_specification fs; struct cxgb4_tc_u32_table *t; struct cxgb4_link *link; @@ -338,8 +338,7 @@ out: return ret; } -int cxgb4_delete_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls) +int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) { struct adapter *adapter = netdev2adap(dev); unsigned int filter_id, max_tids, i, j; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h index 021261a41c13..70a07b7cca56 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h @@ -44,10 +44,8 @@ static inline bool can_tc_u32_offload(struct net_device *dev) return (dev->features & NETIF_F_HW_TC) && adap->tc_u32 ? true : false; } -int cxgb4_config_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls); -int cxgb4_delete_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls); +int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls); +int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls); void cxgb4_cleanup_tc_u32(struct adapter *adapter); struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap); diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index d86d766777c8..3827608cec29 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -343,7 +343,6 @@ static void dpaa_get_stats64(struct net_device *net_dev, } static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct dpaa_priv *priv = netdev_priv(net_dev); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index ef5795923b0c..dc64d751db24 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1220,7 +1220,6 @@ static int hns3_setup_tc(struct net_device *netdev, u8 tc) } static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index b30190639e78..71004b8eff95 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1266,7 +1266,6 @@ err_queueing_scheme: } static int __fm10k_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7d47a718f922..97d8bb2e8320 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5657,7 +5657,6 @@ exit: } static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 35db198199b0..0a350314d76b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8851,7 +8851,6 @@ static int ixgbe_delete_clsu32(struct ixgbe_adapter *adapter, } static int ixgbe_configure_clsu32_add_hnode(struct ixgbe_adapter *adapter, - __be16 protocol, struct tc_cls_u32_offload *cls) { u32 uhtid = TC_U32_USERHTID(cls->hnode.handle); @@ -9037,9 +9036,9 @@ static int ixgbe_clsu32_build_input(struct ixgbe_fdir_filter *input, } static int ixgbe_configure_clsu32(struct ixgbe_adapter *adapter, - __be16 protocol, struct tc_cls_u32_offload *cls) { + __be16 protocol = cls->common.protocol; u32 loc = cls->knode.handle & 0xfffff; struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_mat_field *field_ptr; @@ -9227,25 +9226,23 @@ free_jump: } static int ixgbe_setup_tc_cls_u32(struct net_device *dev, - u32 handle, u32 chain_index, __be16 proto, struct tc_cls_u32_offload *cls_u32) { struct ixgbe_adapter *adapter = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - chain_index) + if (TC_H_MAJ(cls_u32->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_u32->common.chain_index) return -EOPNOTSUPP; switch (cls_u32->command) { case TC_CLSU32_NEW_KNODE: case TC_CLSU32_REPLACE_KNODE: - return ixgbe_configure_clsu32(adapter, proto, cls_u32); + return ixgbe_configure_clsu32(adapter, cls_u32); case TC_CLSU32_DELETE_KNODE: return ixgbe_delete_clsu32(adapter, cls_u32); case TC_CLSU32_NEW_HNODE: case TC_CLSU32_REPLACE_HNODE: - return ixgbe_configure_clsu32_add_hnode(adapter, proto, - cls_u32); + return ixgbe_configure_clsu32_add_hnode(adapter, cls_u32); case TC_CLSU32_DELETE_HNODE: return ixgbe_configure_clsu32_del_hnode(adapter, cls_u32); default: @@ -9261,13 +9258,11 @@ static int ixgbe_setup_tc_mqprio(struct net_device *dev, } static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { switch (type) { case TC_SETUP_CLSU32: - return ixgbe_setup_tc_cls_u32(dev, handle, chain_index, proto, - tc->cls_u32); + return ixgbe_setup_tc_cls_u32(dev, tc->cls_u32); case TC_SETUP_MQPRIO: return ixgbe_setup_tc_mqprio(dev, tc->mqprio); default: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 5c33550765ed..e81083e25ba6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -131,7 +131,6 @@ out: } static int __mlx4_en_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index adf35da74a85..15f2a942962a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3032,18 +3032,17 @@ out: } static int mlx5e_setup_tc_cls_flower(struct net_device *dev, - u32 handle, u32 chain_index, __be16 proto, struct tc_cls_flower_offload *cls_flower) { struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - chain_index) + if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_flower->common.chain_index) return -EOPNOTSUPP; switch (cls_flower->command) { case TC_CLSFLOWER_REPLACE: - return mlx5e_configure_flower(priv, proto, cls_flower); + return mlx5e_configure_flower(priv, cls_flower); case TC_CLSFLOWER_DESTROY: return mlx5e_delete_flower(priv, cls_flower); case TC_CLSFLOWER_STATS: @@ -3054,13 +3053,11 @@ static int mlx5e_setup_tc_cls_flower(struct net_device *dev, } static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { switch (type) { case TC_SETUP_CLSFLOWER: - return mlx5e_setup_tc_cls_flower(dev, handle, chain_index, - proto, tc->cls_flower); + return mlx5e_setup_tc_cls_flower(dev, tc->cls_flower); case TC_SETUP_MQPRIO: return mlx5e_setup_tc_mqprio(dev, tc->mqprio); default: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index e6cc642f6d8c..e5cf2e7ae052 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -652,15 +652,13 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, } static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, - u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) { struct tc_cls_flower_offload *cls_flower = tc->cls_flower; struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - chain_index) + if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_flower->common.chain_index) return -EOPNOTSUPP; if (cls_flower->egress_dev) { @@ -668,13 +666,12 @@ static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, dev = mlx5_eswitch_get_uplink_netdev(esw); return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, - handle, chain_index, - proto, tc); + tc); } switch (cls_flower->command) { case TC_CLSFLOWER_REPLACE: - return mlx5e_configure_flower(priv, proto, cls_flower); + return mlx5e_configure_flower(priv, cls_flower); case TC_CLSFLOWER_DESTROY: return mlx5e_delete_flower(priv, cls_flower); case TC_CLSFLOWER_STATS: @@ -685,13 +682,11 @@ static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, } static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { switch (type) { case TC_SETUP_CLSFLOWER: - return mlx5e_rep_setup_tc_cls_flower(dev, handle, chain_index, - proto, tc); + return mlx5e_rep_setup_tc_cls_flower(dev, tc); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 78f50d9f621d..3b10d3df7627 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1939,7 +1939,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, return err; } -int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, +int mlx5e_configure_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index ecbe30d808ae..5a0f4a487855 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -38,7 +38,7 @@ int mlx5e_tc_init(struct mlx5e_priv *priv); void mlx5e_tc_cleanup(struct mlx5e_priv *priv); -int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, +int mlx5e_configure_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f); int mlx5e_delete_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index f333d086932d..1ca3204f5543 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1617,11 +1617,11 @@ mlxsw_sp_port_del_cls_matchall_sample(struct mlxsw_sp_port *mlxsw_sp_port) } static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, - __be16 protocol, struct tc_cls_matchall_offload *f, bool ingress) { struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry; + __be16 protocol = f->common.protocol; const struct tc_action *a; LIST_HEAD(actions); int err; @@ -1694,18 +1694,16 @@ static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, - u32 handle, u32 chain_index, - __be16 proto, struct tc_cls_matchall_offload *f) { - bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress = TC_H_MAJ(f->common.handle) == TC_H_MAJ(TC_H_INGRESS); - if (chain_index) + if (f->common.chain_index) return -EOPNOTSUPP; switch (f->command) { case TC_CLSMATCHALL_REPLACE: - return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, proto, f, + return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, f, ingress); case TC_CLSMATCHALL_DESTROY: mlxsw_sp_port_del_cls_matchall(mlxsw_sp_port, f); @@ -1717,18 +1715,16 @@ static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, static int mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, - u32 handle, u32 chain_index, __be16 proto, struct tc_cls_flower_offload *f) { - bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress = TC_H_MAJ(f->common.handle) == TC_H_MAJ(TC_H_INGRESS); - if (chain_index) + if (f->common.chain_index) return -EOPNOTSUPP; switch (f->command) { case TC_CLSFLOWER_REPLACE: - return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, - proto, f); + return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, f); case TC_CLSFLOWER_DESTROY: mlxsw_sp_flower_destroy(mlxsw_sp_port, ingress, f); return 0; @@ -1740,19 +1736,16 @@ mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); switch (type) { case TC_SETUP_CLSMATCHALL: - return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, handle, - chain_index, proto, + return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, tc->cls_mall); case TC_SETUP_CLSFLOWER: - return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, handle, - chain_index, proto, + return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, tc->cls_flower); default: return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index e848f06e34e6..8452d1db2f3f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -508,7 +508,7 @@ extern const struct mlxsw_sp_acl_ops mlxsw_sp_acl_tcam_ops; /* spectrum_flower.c */ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, - __be16 protocol, struct tc_cls_flower_offload *f); + struct tc_cls_flower_offload *f); void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, struct tc_cls_flower_offload *f); int mlxsw_sp_flower_stats(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 9be48d2e43ca..021b6c0076c0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -368,7 +368,7 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, } int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, - __be16 protocol, struct tc_cls_flower_offload *f) + struct tc_cls_flower_offload *f) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct net_device *dev = mlxsw_sp_port->dev; diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index d7975dcecb40..152a7abb58ed 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -121,20 +121,22 @@ static void nfp_bpf_vnic_clean(struct nfp_app *app, struct nfp_net *nn) } static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, u32 handle, __be16 proto, + enum tc_setup_type type, struct tc_to_netdev *tc) { + struct tc_cls_bpf_offload *cls_bpf = tc->cls_bpf; struct nfp_net *nn = netdev_priv(netdev); if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) || - TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - proto != htons(ETH_P_ALL)) + TC_H_MAJ(cls_bpf->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_bpf->common.protocol != htons(ETH_P_ALL) || + cls_bpf->common.chain_index) return -EOPNOTSUPP; if (nn->dp.bpf_offload_xdp) return -EBUSY; - return nfp_net_bpf_offload(nn, tc->cls_bpf); + return nfp_net_bpf_offload(nn, cls_bpf); } static bool nfp_bpf_tc_busy(struct nfp_app *app, struct nfp_net *nn) diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 314e6e8ba649..eb94d08e35cf 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -135,8 +135,7 @@ int nfp_flower_metadata_init(struct nfp_app *app); void nfp_flower_metadata_cleanup(struct nfp_app *app); int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, u32 handle, __be16 proto, - struct tc_to_netdev *tc); + enum tc_setup_type type, struct tc_to_netdev *tc); int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow, struct nfp_fl_key_ls *key_ls, struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 58af438a95c1..8197836c650d 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -385,13 +385,15 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, } int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, u32 handle, __be16 proto, - struct tc_to_netdev *tc) + enum tc_setup_type type, struct tc_to_netdev *tc) { + struct tc_cls_flower_offload *cls_flower = tc->cls_flower; + if (type != TC_SETUP_CLSFLOWER || - TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - !eth_proto_is_802_3(proto)) + TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + !eth_proto_is_802_3(cls_flower->common.protocol) || + cls_flower->common.chain_index) return -EOPNOTSUPP; - return nfp_flower_repr_offload(app, netdev, tc->cls_flower); + return nfp_flower_repr_offload(app, netdev, cls_flower); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index b3b03bb9d907..7a2f950b149c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -109,8 +109,7 @@ struct nfp_app_type { void (*ctrl_msg_rx)(struct nfp_app *app, struct sk_buff *skb); int (*setup_tc)(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, u32 handle, __be16 proto, - struct tc_to_netdev *tc); + enum tc_setup_type type, struct tc_to_netdev *tc); bool (*tc_busy)(struct nfp_app *app, struct nfp_net *nn); int (*xdp_offload)(struct nfp_app *app, struct nfp_net *nn, struct bpf_prog *prog); @@ -240,12 +239,11 @@ static inline bool nfp_app_tc_busy(struct nfp_app *app, struct nfp_net *nn) static inline int nfp_app_setup_tc(struct nfp_app *app, struct net_device *netdev, enum tc_setup_type type, - u32 handle, __be16 proto, struct tc_to_netdev *tc) { if (!app || !app->type->setup_tc) return -EOPNOTSUPP; - return app->type->setup_tc(app, netdev, type, handle, proto, tc); + return app->type->setup_tc(app, netdev, type, tc); } static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index 9d776f982352..e8abab2b912e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -89,19 +89,15 @@ const struct switchdev_ops nfp_port_switchdev_ops = { }; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct nfp_port *port; - if (chain_index) - return -EOPNOTSUPP; - port = nfp_port_from_netdev(netdev); if (!port) return -EOPNOTSUPP; - return nfp_app_setup_tc(port->app, netdev, type, handle, proto, tc); + return nfp_app_setup_tc(port->app, netdev, type, tc); } struct nfp_port * diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 239c5401000c..252f06d4307f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -110,7 +110,6 @@ struct nfp_port { extern const struct switchdev_ops nfp_port_switchdev_ops; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc); struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index e41a7179bc05..b0c6004db138 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -33,7 +33,6 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc); unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); extern unsigned int efx_piobuf_size; diff --git a/drivers/net/ethernet/sfc/falcon/efx.h b/drivers/net/ethernet/sfc/falcon/efx.h index f3bc67ec1f30..4497511fc914 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.h +++ b/drivers/net/ethernet/sfc/falcon/efx.h @@ -33,7 +33,6 @@ netdev_tx_t ef4_hard_start_xmit(struct sk_buff *skb, netdev_tx_t ef4_enqueue_skb(struct ef4_tx_queue *tx_queue, struct sk_buff *skb); void ef4_xmit_done(struct ef4_tx_queue *tx_queue, unsigned int index); int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc); unsigned int ef4_tx_max_skb_descs(struct ef4_nic *efx); extern bool ef4_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index 6c4752694c1f..447519ac3fa4 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -426,7 +426,6 @@ void ef4_init_tx_queue_core_txq(struct ef4_tx_queue *tx_queue) } int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *ntc) { struct ef4_nic *efx = netdev_priv(net_dev); diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 0c08c10d751c..d17af918ac50 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -654,7 +654,6 @@ void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue) } int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *ntc) { struct efx_nic *efx = netdev_priv(net_dev); diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index cb21742f6177..14f91b285f00 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1878,7 +1878,6 @@ static u16 netcp_select_queue(struct net_device *dev, struct sk_buff *skb, } static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { u8 num_tc; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index bd49dbaee84e..6e2f7e38cf8e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -977,7 +977,6 @@ struct xfrmdev_ops { * int (*ndo_set_vf_rss_query_en)(struct net_device *dev, int vf, bool setting); * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb); * int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, - * u32 handle, u32 chain_index, __be16 protocol, * struct tc_to_netdev *tc); * Called to setup any 'tc' scheduler, classifier or action on @dev. * This is always called from the stack with the rtnl lock held and netif @@ -1227,8 +1226,6 @@ struct net_device_ops { int vf, bool setting); int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, - __be16 protocol, struct tc_to_netdev *tc); #if IS_ENABLED(CONFIG_FCOE) int (*ndo_fcoe_enable)(struct net_device *dev); diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 8213acdfdf5a..ffaddf72108e 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -405,6 +405,21 @@ tcf_match_indev(struct sk_buff *skb, int ifindex) } #endif /* CONFIG_NET_CLS_IND */ +struct tc_cls_common_offload { + u32 handle; + u32 chain_index; + __be16 protocol; +}; + +static inline void +tc_cls_common_offload_init(struct tc_cls_common_offload *cls_common, + const struct tcf_proto *tp) +{ + cls_common->handle = tp->q->handle; + cls_common->chain_index = tp->chain->index; + cls_common->protocol = tp->protocol; +} + struct tc_cls_u32_knode { struct tcf_exts *exts; struct tc_u32_sel *sel; @@ -431,6 +446,7 @@ enum tc_clsu32_command { }; struct tc_cls_u32_offload { + struct tc_cls_common_offload common; /* knode values */ enum tc_clsu32_command command; union { @@ -497,6 +513,7 @@ enum tc_fl_command { }; struct tc_cls_flower_offload { + struct tc_cls_common_offload common; enum tc_fl_command command; u32 prio; unsigned long cookie; @@ -513,6 +530,7 @@ enum tc_matchall_command { }; struct tc_cls_matchall_offload { + struct tc_cls_common_offload common; enum tc_matchall_command command; struct tcf_exts *exts; unsigned long cookie; @@ -526,6 +544,7 @@ enum tc_clsbpf_command { }; struct tc_cls_bpf_offload { + struct tc_cls_common_offload common; enum tc_clsbpf_command command; struct tcf_exts *exts; struct bpf_prog *prog; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index e76d576b941d..5e01e9271619 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -774,12 +774,12 @@ dsa_slave_mall_tc_entry_find(struct dsa_slave_priv *p, } static int dsa_slave_add_cls_matchall(struct net_device *dev, - __be16 protocol, struct tc_cls_matchall_offload *cls, bool ingress) { struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_mall_tc_entry *mall_tc_entry; + __be16 protocol = cls->common.protocol; struct dsa_switch *ds = p->dp->ds; struct net *net = dev_net(dev); struct dsa_slave_priv *to_p; @@ -864,18 +864,16 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev, } static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, - u32 handle, u32 chain_index, - __be16 protocol, struct tc_cls_matchall_offload *cls) { - bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress = TC_H_MAJ(cls->common.handle) == TC_H_MAJ(TC_H_INGRESS); - if (chain_index) + if (cls->common.chain_index) return -EOPNOTSUPP; switch (cls->command) { case TC_CLSMATCHALL_REPLACE: - return dsa_slave_add_cls_matchall(dev, protocol, cls, ingress); + return dsa_slave_add_cls_matchall(dev, cls, ingress); case TC_CLSMATCHALL_DESTROY: dsa_slave_del_cls_matchall(dev, cls); return 0; @@ -885,13 +883,11 @@ static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, } static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc) { switch (type) { case TC_SETUP_CLSMATCHALL: - return dsa_slave_setup_tc_cls_matchall(dev, handle, chain_index, - protocol, tc->cls_mall); + return dsa_slave_setup_tc_cls_matchall(dev, tc->cls_mall); default: return -EOPNOTSUPP; } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index e2bf2753173d..dde8efdcee3b 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -153,6 +153,7 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, offload.cls_bpf = &bpf_offload; + tc_cls_common_offload_init(&bpf_offload.common, tp); bpf_offload.command = cmd; bpf_offload.exts = &prog->exts; bpf_offload.prog = prog->filter; @@ -160,11 +161,7 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, bpf_offload.exts_integrated = prog->exts_integrated; bpf_offload.gen_flags = prog->gen_flags; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, - tp->q->handle, - tp->chain->index, - tp->protocol, &offload); - + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, &offload); if (!err && (cmd == TC_CLSBPF_ADD || cmd == TC_CLSBPF_REPLACE)) prog->gen_flags |= TCA_CLS_FLAGS_IN_HW; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 52deeed2b7f5..1fdf2889ba9f 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -232,14 +232,14 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) if (!tc_can_offload(dev, tp)) return; + tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_DESTROY; offload.prio = tp->prio; offload.cookie = (unsigned long)f; tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tp->q->handle, - tp->chain->index, tp->protocol, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tc); } static int fl_hw_replace_filter(struct tcf_proto *tp, @@ -264,6 +264,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, f->hw_dev = dev; } + tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_REPLACE; offload.prio = tp->prio; offload.cookie = (unsigned long)f; @@ -274,9 +275,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, tc->cls_flower = &offload; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, - tp->q->handle, tp->chain->index, - tp->protocol, tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tc); if (!err) f->flags |= TCA_CLS_FLAGS_IN_HW; @@ -294,6 +293,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) if (!tc_can_offload(dev, tp)) return; + tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_STATS; offload.prio = tp->prio; offload.cookie = (unsigned long)f; @@ -301,8 +301,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, tp->q->handle, - tp->chain->index, tp->protocol, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, tc); } static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index a8853ada22f6..174c700160ca 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -58,14 +58,14 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, struct tc_cls_matchall_offload mall_offload = {0}; int err; + tc_cls_common_offload_init(&mall_offload.common, tp); offload.cls_mall = &mall_offload; offload.cls_mall->command = TC_CLSMATCHALL_REPLACE; offload.cls_mall->exts = &head->exts; offload.cls_mall->cookie = cookie; err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, - tp->q->handle, tp->chain->index, - tp->protocol, &offload); + &offload); if (!err) head->flags |= TCA_CLS_FLAGS_IN_HW; @@ -80,13 +80,13 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, struct tc_to_netdev offload; struct tc_cls_matchall_offload mall_offload = {0}; + tc_cls_common_offload_init(&mall_offload.common, tp); offload.cls_mall = &mall_offload; offload.cls_mall->command = TC_CLSMATCHALL_DESTROY; offload.cls_mall->exts = NULL; offload.cls_mall->cookie = cookie; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, tp->q->handle, - tp->chain->index, tp->protocol, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, &offload); } static void mall_destroy(struct tcf_proto *tp) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index d1bae4cc749f..c0f59c471523 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -437,11 +437,10 @@ static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle) offload.cls_u32 = &u32_offload; if (tc_should_offload(dev, tp, 0)) { + tc_cls_common_offload_init(&u32_offload.common, tp); offload.cls_u32->command = TC_CLSU32_DELETE_KNODE; offload.cls_u32->knode.handle = handle; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, - tp->q->handle, tp->chain->index, - tp->protocol, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); } } @@ -458,14 +457,13 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, offload.cls_u32 = &u32_offload; + tc_cls_common_offload_init(&u32_offload.common, tp); offload.cls_u32->command = TC_CLSU32_NEW_HNODE; offload.cls_u32->hnode.divisor = h->divisor; offload.cls_u32->hnode.handle = h->handle; offload.cls_u32->hnode.prio = h->prio; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, tp->q->handle, - tp->chain->index, tp->protocol, - &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); if (tc_skip_sw(flags)) return err; @@ -481,14 +479,13 @@ static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) offload.cls_u32 = &u32_offload; if (tc_should_offload(dev, tp, 0)) { + tc_cls_common_offload_init(&u32_offload.common, tp); offload.cls_u32->command = TC_CLSU32_DELETE_HNODE; offload.cls_u32->hnode.divisor = h->divisor; offload.cls_u32->hnode.handle = h->handle; offload.cls_u32->hnode.prio = h->prio; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, - tp->q->handle, tp->chain->index, - tp->protocol, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); } } @@ -505,6 +502,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, if (!tc_should_offload(dev, tp, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; + tc_cls_common_offload_init(&u32_offload.common, tp); offload.cls_u32->command = TC_CLSU32_REPLACE_KNODE; offload.cls_u32->knode.handle = n->handle; offload.cls_u32->knode.fshift = n->fshift; @@ -520,9 +518,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, if (n->ht_down) offload.cls_u32->knode.link_handle = n->ht_down->handle; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, tp->q->handle, - tp->chain->index, tp->protocol, - &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); if (!err) n->flags |= TCA_CLS_FLAGS_IN_HW; diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 329610ce4dfe..09b577dde49c 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -42,8 +42,7 @@ static void mqprio_destroy(struct Qdisc *sch) struct tc_mqprio_qopt offload = { 0 }; struct tc_to_netdev tc = { { .mqprio = &offload } }; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, - sch->handle, 0, 0, &tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &tc); } else { netdev_set_num_tc(dev, 0); } @@ -151,8 +150,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) struct tc_mqprio_qopt offload = *qopt; struct tc_to_netdev tc = { { .mqprio = &offload } }; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, - sch->handle, 0, 0, &tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &tc); if (err) return err; -- cgit v1.2.3 From d7c1c8d2e53be974b5c72e31d7d35f6d9737fe84 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:30 +0200 Subject: net: sched: move prio into cls_common prio is not cls_flower specific, but it is meaningful for all classifiers. Seems that only mlxsw cares about the value. Obviously, cls offload in other drivers is broken. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +- include/net/pkt_cls.h | 3 ++- net/sched/cls_flower.c | 3 --- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 021b6c0076c0..95428b41c50f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -270,7 +270,7 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, return -EOPNOTSUPP; } - mlxsw_sp_acl_rulei_priority(rulei, f->prio); + mlxsw_sp_acl_rulei_priority(rulei, f->common.prio); if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) { struct flow_dissector_key_control *key = diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index ffaddf72108e..572083af02ac 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -409,6 +409,7 @@ struct tc_cls_common_offload { u32 handle; u32 chain_index; __be16 protocol; + u32 prio; }; static inline void @@ -418,6 +419,7 @@ tc_cls_common_offload_init(struct tc_cls_common_offload *cls_common, cls_common->handle = tp->q->handle; cls_common->chain_index = tp->chain->index; cls_common->protocol = tp->protocol; + cls_common->prio = tp->prio; } struct tc_cls_u32_knode { @@ -515,7 +517,6 @@ enum tc_fl_command { struct tc_cls_flower_offload { struct tc_cls_common_offload common; enum tc_fl_command command; - u32 prio; unsigned long cookie; struct flow_dissector *dissector; struct fl_flow_key *mask; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 1fdf2889ba9f..ccdf2f5014ca 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -234,7 +234,6 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_DESTROY; - offload.prio = tp->prio; offload.cookie = (unsigned long)f; tc->cls_flower = &offload; @@ -266,7 +265,6 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_REPLACE; - offload.prio = tp->prio; offload.cookie = (unsigned long)f; offload.dissector = dissector; offload.mask = mask; @@ -295,7 +293,6 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_STATS; - offload.prio = tp->prio; offload.cookie = (unsigned long)f; offload.exts = &f->exts; -- cgit v1.2.3 From 38cf0426e5178b1c3810bb88e65dd23882e40283 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:31 +0200 Subject: net: sched: change return value of ndo_setup_tc for driver supporting mqprio only Change the return value from -EINVAL to -EOPNOTSUPP. The rest of the drivers have it like that, so be aligned. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 2 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 2 +- drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +- drivers/net/ethernet/sfc/falcon/tx.c | 2 +- drivers/net/ethernet/sfc/tx.c | 2 +- drivers/net/ethernet/ti/netcp_core.c | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index bbb7bfe0be7f..37d3e5b65d94 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1925,7 +1925,7 @@ static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, u8 tc; if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; tc_to_netdev->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; tc = tc_to_netdev->mqprio->num_tc; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 257cf4be0162..8687afc24698 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4288,7 +4288,7 @@ int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 1545b88c545d..a78f72a53042 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7241,7 +7241,7 @@ static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, struct tc_to_netdev *ntc) { if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 3827608cec29..bfb44c95a7ec 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -350,7 +350,7 @@ static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type, int i; if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; num_tc = tc->mqprio->num_tc; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index dc64d751db24..aa43ebda9a00 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1223,7 +1223,7 @@ static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; return hns3_setup_tc(dev, tc->mqprio->num_tc); } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 71004b8eff95..70888129200b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1269,7 +1269,7 @@ static int __fm10k_setup_tc(struct net_device *dev, enum tc_setup_type type, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 97d8bb2e8320..1f4633830c79 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5660,7 +5660,7 @@ static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index e81083e25ba6..1667e86ac05d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -134,7 +134,7 @@ static int __mlx4_en_setup_tc(struct net_device *dev, enum tc_setup_type type, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; if (tc->mqprio->num_tc && tc->mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) return -EINVAL; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index 447519ac3fa4..0f125e15143a 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -435,7 +435,7 @@ int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, int rc; if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; num_tc = ntc->mqprio->num_tc; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index d17af918ac50..53ba30c3eb7b 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -663,7 +663,7 @@ int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, int rc; if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; num_tc = ntc->mqprio->num_tc; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 14f91b285f00..caba0abc0158 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1887,7 +1887,7 @@ static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, ASSERT_RTNL(); if (type != TC_SETUP_MQPRIO) - return -EINVAL; + return -EOPNOTSUPP; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; num_tc = tc->mqprio->num_tc; -- cgit v1.2.3 From de4784ca030fed17d527dbb2bb4e21328b12de94 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:32 +0200 Subject: net: sched: get rid of struct tc_to_netdev Get rid of struct tc_to_netdev which is now just unnecessary container and rather pass per-type structures down to drivers directly. Along with that, consolidate the naming of per-type structure variables in cls_*. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 7 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 8 ++- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 ++- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 4 +- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 7 +- .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 6 +- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 8 ++- drivers/net/ethernet/intel/i40e/i40e_main.c | 8 ++- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 6 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 10 +-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 6 +- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 12 ++-- drivers/net/ethernet/mellanox/mlx5/core/en_tc.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 8 +-- drivers/net/ethernet/netronome/nfp/bpf/main.c | 5 +- drivers/net/ethernet/netronome/nfp/flower/main.h | 4 +- .../net/ethernet/netronome/nfp/flower/offload.c | 4 +- drivers/net/ethernet/netronome/nfp/nfp_app.h | 8 +-- drivers/net/ethernet/netronome/nfp/nfp_port.c | 4 +- drivers/net/ethernet/netronome/nfp/nfp_port.h | 3 +- drivers/net/ethernet/sfc/efx.h | 2 +- drivers/net/ethernet/sfc/falcon/efx.h | 2 +- drivers/net/ethernet/sfc/falcon/tx.c | 7 +- drivers/net/ethernet/sfc/tx.c | 7 +- drivers/net/ethernet/ti/netcp_core.c | 7 +- include/linux/netdevice.h | 19 +---- net/dsa/slave.c | 4 +- net/sched/cls_bpf.c | 21 +++--- net/sched/cls_flower.c | 54 ++++++-------- net/sched/cls_matchall.c | 27 +++---- net/sched/cls_u32.c | 83 ++++++++++------------ net/sched/sch_mqprio.c | 13 ++-- 33 files changed, 174 insertions(+), 202 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 37d3e5b65d94..2fd9b80b39b0 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1919,16 +1919,17 @@ static void xgbe_poll_controller(struct net_device *netdev) #endif /* End CONFIG_NET_POLL_CONTROLLER */ static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, - struct tc_to_netdev *tc_to_netdev) + void *type_data) { struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct tc_mqprio_qopt *mqprio = type_data; u8 tc; if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc_to_netdev->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - tc = tc_to_netdev->mqprio->num_tc; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + tc = mqprio->num_tc; if (tc > pdata->hw_feat.tc_cnt) return -EINVAL; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 8687afc24698..1216c1f1e052 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4285,14 +4285,16 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) } int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return bnx2x_setup_tc(dev, tc->mqprio->num_tc); + return bnx2x_setup_tc(dev, mqprio->num_tc); } /* called with rtnl_lock */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 04eb95043023..a5265e1344f1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -487,7 +487,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev); /* setup_tc callback */ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc); int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); int bnx2x_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index a78f72a53042..6e14fc4fe2c8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7238,14 +7238,16 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) } static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *ntc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return bnxt_setup_mq_tc(dev, ntc->mqprio->num_tc); + return bnxt_setup_mq_tc(dev, mqprio->num_tc); } #ifdef CONFIG_RFS_ACCEL diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 13199317c8e0..d80b20d695e0 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2908,7 +2908,7 @@ static int cxgb_setup_tc_cls_u32(struct net_device *dev, } static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); @@ -2922,7 +2922,7 @@ static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, switch (type) { case TC_SETUP_CLSU32: - return cxgb_setup_tc_cls_u32(dev, tc->cls_u32); + return cxgb_setup_tc_cls_u32(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index bfb44c95a7ec..733d54caabb6 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -343,17 +343,18 @@ static void dpaa_get_stats64(struct net_device *net_dev, } static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { struct dpaa_priv *priv = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = type_data; u8 num_tc; int i; if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - num_tc = tc->mqprio->num_tc; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + num_tc = mqprio->num_tc; if (num_tc == priv->num_tc) return 0; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index aa43ebda9a00..069ae426aa24 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1220,12 +1220,14 @@ static int hns3_setup_tc(struct net_device *netdev, u8 tc) } static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - return hns3_setup_tc(dev, tc->mqprio->num_tc); + return hns3_setup_tc(dev, mqprio->num_tc); } static int hns3_vlan_rx_add_vid(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 70888129200b..e69d49d91d67 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1266,14 +1266,16 @@ err_queueing_scheme: } static int __fm10k_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return fm10k_setup_tc(dev, tc->mqprio->num_tc); + return fm10k_setup_tc(dev, mqprio->num_tc); } static void fm10k_assign_l2_accel(struct fm10k_intfc *interface, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1f4633830c79..a7e5a76703e7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5657,14 +5657,16 @@ exit: } static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return i40e_setup_tc(netdev, tc->mqprio->num_tc); + return i40e_setup_tc(netdev, mqprio->num_tc); } /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 0a350314d76b..c6b132476de4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9258,13 +9258,13 @@ static int ixgbe_setup_tc_mqprio(struct net_device *dev, } static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { switch (type) { case TC_SETUP_CLSU32: - return ixgbe_setup_tc_cls_u32(dev, tc->cls_u32); + return ixgbe_setup_tc_cls_u32(dev, type_data); case TC_SETUP_MQPRIO: - return ixgbe_setup_tc_mqprio(dev, tc->mqprio); + return ixgbe_setup_tc_mqprio(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 1667e86ac05d..6e67ca7aa7f5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -131,17 +131,19 @@ out: } static int __mlx4_en_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - if (tc->mqprio->num_tc && tc->mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) + if (mqprio->num_tc && mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) return -EINVAL; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return mlx4_en_alloc_tx_queue_per_tc(dev, tc->mqprio->num_tc); + return mlx4_en_alloc_tx_queue_per_tc(dev, mqprio->num_tc); } #ifdef CONFIG_RFS_ACCEL diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 15f2a942962a..ae0916238b7b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3053,13 +3053,13 @@ static int mlx5e_setup_tc_cls_flower(struct net_device *dev, } static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { switch (type) { case TC_SETUP_CLSFLOWER: - return mlx5e_setup_tc_cls_flower(dev, tc->cls_flower); + return mlx5e_setup_tc_cls_flower(dev, type_data); case TC_SETUP_MQPRIO: - return mlx5e_setup_tc_mqprio(dev, tc->mqprio); + return mlx5e_setup_tc_mqprio(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index e5cf2e7ae052..3df994d1e173 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -651,10 +651,10 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, return 0; } -static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, - struct tc_to_netdev *tc) +static int +mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, + struct tc_cls_flower_offload *cls_flower) { - struct tc_cls_flower_offload *cls_flower = tc->cls_flower; struct mlx5e_priv *priv = netdev_priv(dev); if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || @@ -666,7 +666,7 @@ static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, dev = mlx5_eswitch_get_uplink_netdev(esw); return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, - tc); + cls_flower); } switch (cls_flower->command) { @@ -682,11 +682,11 @@ static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, } static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { switch (type) { case TC_SETUP_CLSFLOWER: - return mlx5e_rep_setup_tc_cls_flower(dev, tc); + return mlx5e_rep_setup_tc_cls_flower(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 5a0f4a487855..2917d964ffc3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -33,6 +33,8 @@ #ifndef __MLX5_EN_TC_H__ #define __MLX5_EN_TC_H__ +#include + #define MLX5E_TC_FLOW_ID_MASK 0x0000ffff int mlx5e_tc_init(struct mlx5e_priv *priv); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 1ca3204f5543..eb7c4549f464 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1736,17 +1736,15 @@ mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); switch (type) { case TC_SETUP_CLSMATCHALL: - return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, - tc->cls_mall); + return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, type_data); case TC_SETUP_CLSFLOWER: - return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, - tc->cls_flower); + return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index 152a7abb58ed..f981f60ec306 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -121,10 +121,9 @@ static void nfp_bpf_vnic_clean(struct nfp_app *app, struct nfp_net *nn) } static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, - struct tc_to_netdev *tc) + enum tc_setup_type type, void *type_data) { - struct tc_cls_bpf_offload *cls_bpf = tc->cls_bpf; + struct tc_cls_bpf_offload *cls_bpf = type_data; struct nfp_net *nn = netdev_priv(netdev); if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) || diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index eb94d08e35cf..71e4f4f4e9ba 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -38,8 +38,8 @@ #include #include #include +#include -struct tc_to_netdev; struct net_device; struct nfp_app; @@ -135,7 +135,7 @@ int nfp_flower_metadata_init(struct nfp_app *app); void nfp_flower_metadata_cleanup(struct nfp_app *app); int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, struct tc_to_netdev *tc); + enum tc_setup_type type, void *type_data); int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow, struct nfp_fl_key_ls *key_ls, struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 8197836c650d..01767c7376d5 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -385,9 +385,9 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, } int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, struct tc_to_netdev *tc) + enum tc_setup_type type, void *type_data) { - struct tc_cls_flower_offload *cls_flower = tc->cls_flower; + struct tc_cls_flower_offload *cls_flower = type_data; if (type != TC_SETUP_CLSFLOWER || TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index 7a2f950b149c..f34e8778fae2 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -42,7 +42,6 @@ struct bpf_prog; struct net_device; struct pci_dev; struct sk_buff; -struct tc_to_netdev; struct sk_buff; struct nfp_app; struct nfp_cpp; @@ -109,7 +108,7 @@ struct nfp_app_type { void (*ctrl_msg_rx)(struct nfp_app *app, struct sk_buff *skb); int (*setup_tc)(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, struct tc_to_netdev *tc); + enum tc_setup_type type, void *type_data); bool (*tc_busy)(struct nfp_app *app, struct nfp_net *nn); int (*xdp_offload)(struct nfp_app *app, struct nfp_net *nn, struct bpf_prog *prog); @@ -238,12 +237,11 @@ static inline bool nfp_app_tc_busy(struct nfp_app *app, struct nfp_net *nn) static inline int nfp_app_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, - struct tc_to_netdev *tc) + enum tc_setup_type type, void *type_data) { if (!app || !app->type->setup_tc) return -EOPNOTSUPP; - return app->type->setup_tc(app, netdev, type, tc); + return app->type->setup_tc(app, netdev, type, type_data); } static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index e8abab2b912e..0cf65e57addb 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -89,7 +89,7 @@ const struct switchdev_ops nfp_port_switchdev_ops = { }; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { struct nfp_port *port; @@ -97,7 +97,7 @@ int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, if (!port) return -EOPNOTSUPP; - return nfp_app_setup_tc(port->app, netdev, type, tc); + return nfp_app_setup_tc(port->app, netdev, type, type_data); } struct nfp_port * diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 252f06d4307f..c88e376dcf0f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -36,7 +36,6 @@ #include -struct tc_to_netdev; struct net_device; struct nfp_app; struct nfp_pf; @@ -110,7 +109,7 @@ struct nfp_port { extern const struct switchdev_ops nfp_port_switchdev_ops; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); struct nfp_port * diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index b0c6004db138..d407adf59610 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -33,7 +33,7 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); extern unsigned int efx_piobuf_size; extern bool efx_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/efx.h b/drivers/net/ethernet/sfc/falcon/efx.h index 4497511fc914..4f3bb30661ea 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.h +++ b/drivers/net/ethernet/sfc/falcon/efx.h @@ -33,7 +33,7 @@ netdev_tx_t ef4_hard_start_xmit(struct sk_buff *skb, netdev_tx_t ef4_enqueue_skb(struct ef4_tx_queue *tx_queue, struct sk_buff *skb); void ef4_xmit_done(struct ef4_tx_queue *tx_queue, unsigned int index); int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); unsigned int ef4_tx_max_skb_descs(struct ef4_nic *efx); extern bool ef4_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index 0f125e15143a..6a75f4140a4b 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -426,9 +426,10 @@ void ef4_init_tx_queue_core_txq(struct ef4_tx_queue *tx_queue) } int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *ntc) + void *type_data) { struct ef4_nic *efx = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = type_data; struct ef4_channel *channel; struct ef4_tx_queue *tx_queue; unsigned tc, num_tc; @@ -437,12 +438,12 @@ int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - num_tc = ntc->mqprio->num_tc; + num_tc = mqprio->num_tc; if (ef4_nic_rev(efx) < EF4_REV_FALCON_B0 || num_tc > EF4_MAX_TX_TC) return -EINVAL; - ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; if (num_tc == net_dev->num_tc) return 0; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 53ba30c3eb7b..32bf1fecf864 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -654,9 +654,10 @@ void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue) } int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *ntc) + void *type_data) { struct efx_nic *efx = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = type_data; struct efx_channel *channel; struct efx_tx_queue *tx_queue; unsigned tc, num_tc; @@ -665,12 +666,12 @@ int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - num_tc = ntc->mqprio->num_tc; + num_tc = mqprio->num_tc; if (num_tc > EFX_MAX_TX_TC) return -EINVAL; - ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; if (num_tc == net_dev->num_tc) return 0; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index caba0abc0158..eb96a6913235 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1878,8 +1878,9 @@ static u16 netcp_select_queue(struct net_device *dev, struct sk_buff *skb, } static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; u8 num_tc; int i; @@ -1889,8 +1890,8 @@ static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - num_tc = tc->mqprio->num_tc; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + num_tc = mqprio->num_tc; /* Sanity-check the number of traffic classes requested */ if ((dev->real_num_tx_queues <= 1) || diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6e2f7e38cf8e..1d238d54c484 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -771,9 +771,6 @@ static inline bool netdev_phys_item_id_same(struct netdev_phys_item_id *a, typedef u16 (*select_queue_fallback_t)(struct net_device *dev, struct sk_buff *skb); -/* These structures hold the attributes of qdisc and classifiers - * that are being passed to the netdevice through the setup_tc op. - */ enum tc_setup_type { TC_SETUP_MQPRIO, TC_SETUP_CLSU32, @@ -782,18 +779,6 @@ enum tc_setup_type { TC_SETUP_CLSBPF, }; -struct tc_cls_u32_offload; - -struct tc_to_netdev { - union { - struct tc_cls_u32_offload *cls_u32; - struct tc_cls_flower_offload *cls_flower; - struct tc_cls_matchall_offload *cls_mall; - struct tc_cls_bpf_offload *cls_bpf; - struct tc_mqprio_qopt *mqprio; - }; -}; - /* These structures hold the attributes of xdp state that are being passed * to the netdevice through the xdp op. */ @@ -977,7 +962,7 @@ struct xfrmdev_ops { * int (*ndo_set_vf_rss_query_en)(struct net_device *dev, int vf, bool setting); * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb); * int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, - * struct tc_to_netdev *tc); + * void *type_data); * Called to setup any 'tc' scheduler, classifier or action on @dev. * This is always called from the stack with the rtnl lock held and netif * tx queues stopped. This allows the netdevice to perform queue @@ -1226,7 +1211,7 @@ struct net_device_ops { int vf, bool setting); int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); #if IS_ENABLED(CONFIG_FCOE) int (*ndo_fcoe_enable)(struct net_device *dev); int (*ndo_fcoe_disable)(struct net_device *dev); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5e01e9271619..c6b5de2fe413 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -883,11 +883,11 @@ static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, } static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { switch (type) { case TC_SETUP_CLSMATCHALL: - return dsa_slave_setup_tc_cls_matchall(dev, tc->cls_mall); + return dsa_slave_setup_tc_cls_matchall(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index dde8efdcee3b..2d4d06e41cd9 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -147,21 +147,18 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, enum tc_clsbpf_command cmd) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_bpf_offload bpf_offload = {}; - struct tc_to_netdev offload; + struct tc_cls_bpf_offload cls_bpf = {}; int err; - offload.cls_bpf = &bpf_offload; + tc_cls_common_offload_init(&cls_bpf.common, tp); + cls_bpf.command = cmd; + cls_bpf.exts = &prog->exts; + cls_bpf.prog = prog->filter; + cls_bpf.name = prog->bpf_name; + cls_bpf.exts_integrated = prog->exts_integrated; + cls_bpf.gen_flags = prog->gen_flags; - tc_cls_common_offload_init(&bpf_offload.common, tp); - bpf_offload.command = cmd; - bpf_offload.exts = &prog->exts; - bpf_offload.prog = prog->filter; - bpf_offload.name = prog->bpf_name; - bpf_offload.exts_integrated = prog->exts_integrated; - bpf_offload.gen_flags = prog->gen_flags; - - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, &cls_bpf); if (!err && (cmd == TC_CLSBPF_ADD || cmd == TC_CLSBPF_REPLACE)) prog->gen_flags |= TCA_CLS_FLAGS_IN_HW; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index ccdf2f5014ca..1474bacf4df4 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -88,7 +88,6 @@ struct cls_fl_filter { u32 handle; u32 flags; struct rcu_head rcu; - struct tc_to_netdev tc; struct net_device *hw_dev; }; @@ -225,20 +224,17 @@ static void fl_destroy_filter(struct rcu_head *head) static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) { - struct tc_cls_flower_offload offload = {0}; + struct tc_cls_flower_offload cls_flower = {}; struct net_device *dev = f->hw_dev; - struct tc_to_netdev *tc = &f->tc; if (!tc_can_offload(dev, tp)) return; - tc_cls_common_offload_init(&offload.common, tp); - offload.command = TC_CLSFLOWER_DESTROY; - offload.cookie = (unsigned long)f; + tc_cls_common_offload_init(&cls_flower.common, tp); + cls_flower.command = TC_CLSFLOWER_DESTROY; + cls_flower.cookie = (unsigned long) f; - tc->cls_flower = &offload; - - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, &cls_flower); } static int fl_hw_replace_filter(struct tcf_proto *tp, @@ -247,8 +243,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, struct cls_fl_filter *f) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_flower_offload offload = {0}; - struct tc_to_netdev *tc = &f->tc; + struct tc_cls_flower_offload cls_flower = {}; int err; if (!tc_can_offload(dev, tp)) { @@ -258,22 +253,21 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, return tc_skip_sw(f->flags) ? -EINVAL : 0; } dev = f->hw_dev; - offload.egress_dev = true; + cls_flower.egress_dev = true; } else { f->hw_dev = dev; } - tc_cls_common_offload_init(&offload.common, tp); - offload.command = TC_CLSFLOWER_REPLACE; - offload.cookie = (unsigned long)f; - offload.dissector = dissector; - offload.mask = mask; - offload.key = &f->mkey; - offload.exts = &f->exts; - - tc->cls_flower = &offload; + tc_cls_common_offload_init(&cls_flower.common, tp); + cls_flower.command = TC_CLSFLOWER_REPLACE; + cls_flower.cookie = (unsigned long) f; + cls_flower.dissector = dissector; + cls_flower.mask = mask; + cls_flower.key = &f->mkey; + cls_flower.exts = &f->exts; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, + &cls_flower); if (!err) f->flags |= TCA_CLS_FLAGS_IN_HW; @@ -284,21 +278,19 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) { - struct tc_cls_flower_offload offload = {0}; + struct tc_cls_flower_offload cls_flower = {}; struct net_device *dev = f->hw_dev; - struct tc_to_netdev *tc = &f->tc; if (!tc_can_offload(dev, tp)) return; - tc_cls_common_offload_init(&offload.common, tp); - offload.command = TC_CLSFLOWER_STATS; - offload.cookie = (unsigned long)f; - offload.exts = &f->exts; - - tc->cls_flower = &offload; + tc_cls_common_offload_init(&cls_flower.common, tp); + cls_flower.command = TC_CLSFLOWER_STATS; + cls_flower.cookie = (unsigned long) f; + cls_flower.exts = &f->exts; - dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, + &cls_flower); } static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 174c700160ca..c9f6500b8080 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -54,18 +54,16 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, unsigned long cookie) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_to_netdev offload; - struct tc_cls_matchall_offload mall_offload = {0}; + struct tc_cls_matchall_offload cls_mall = {}; int err; - tc_cls_common_offload_init(&mall_offload.common, tp); - offload.cls_mall = &mall_offload; - offload.cls_mall->command = TC_CLSMATCHALL_REPLACE; - offload.cls_mall->exts = &head->exts; - offload.cls_mall->cookie = cookie; + tc_cls_common_offload_init(&cls_mall.common, tp); + cls_mall.command = TC_CLSMATCHALL_REPLACE; + cls_mall.exts = &head->exts; + cls_mall.cookie = cookie; err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, - &offload); + &cls_mall); if (!err) head->flags |= TCA_CLS_FLAGS_IN_HW; @@ -77,16 +75,13 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, unsigned long cookie) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_to_netdev offload; - struct tc_cls_matchall_offload mall_offload = {0}; + struct tc_cls_matchall_offload cls_mall = {}; - tc_cls_common_offload_init(&mall_offload.common, tp); - offload.cls_mall = &mall_offload; - offload.cls_mall->command = TC_CLSMATCHALL_DESTROY; - offload.cls_mall->exts = NULL; - offload.cls_mall->cookie = cookie; + tc_cls_common_offload_init(&cls_mall.common, tp); + cls_mall.command = TC_CLSMATCHALL_DESTROY; + cls_mall.cookie = cookie; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, &cls_mall); } static void mall_destroy(struct tcf_proto *tp) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index c0f59c471523..4ed51d347d0a 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -431,39 +431,35 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key) static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_u32_offload u32_offload = {0}; - struct tc_to_netdev offload; + struct tc_cls_u32_offload cls_u32 = {}; - offload.cls_u32 = &u32_offload; + if (!tc_should_offload(dev, tp, 0)) + return; - if (tc_should_offload(dev, tp, 0)) { - tc_cls_common_offload_init(&u32_offload.common, tp); - offload.cls_u32->command = TC_CLSU32_DELETE_KNODE; - offload.cls_u32->knode.handle = handle; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); - } + tc_cls_common_offload_init(&cls_u32.common, tp); + cls_u32.command = TC_CLSU32_DELETE_KNODE; + cls_u32.knode.handle = handle; + + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32); } static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, u32 flags) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_u32_offload u32_offload = {0}; - struct tc_to_netdev offload; + struct tc_cls_u32_offload cls_u32 = {}; int err; if (!tc_should_offload(dev, tp, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; - offload.cls_u32 = &u32_offload; - - tc_cls_common_offload_init(&u32_offload.common, tp); - offload.cls_u32->command = TC_CLSU32_NEW_HNODE; - offload.cls_u32->hnode.divisor = h->divisor; - offload.cls_u32->hnode.handle = h->handle; - offload.cls_u32->hnode.prio = h->prio; + tc_cls_common_offload_init(&cls_u32.common, tp); + cls_u32.command = TC_CLSU32_NEW_HNODE; + cls_u32.hnode.divisor = h->divisor; + cls_u32.hnode.handle = h->handle; + cls_u32.hnode.prio = h->prio; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32); if (tc_skip_sw(flags)) return err; @@ -473,52 +469,47 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_u32_offload u32_offload = {0}; - struct tc_to_netdev offload; + struct tc_cls_u32_offload cls_u32 = {}; - offload.cls_u32 = &u32_offload; + if (!tc_should_offload(dev, tp, 0)) + return; - if (tc_should_offload(dev, tp, 0)) { - tc_cls_common_offload_init(&u32_offload.common, tp); - offload.cls_u32->command = TC_CLSU32_DELETE_HNODE; - offload.cls_u32->hnode.divisor = h->divisor; - offload.cls_u32->hnode.handle = h->handle; - offload.cls_u32->hnode.prio = h->prio; + tc_cls_common_offload_init(&cls_u32.common, tp); + cls_u32.command = TC_CLSU32_DELETE_HNODE; + cls_u32.hnode.divisor = h->divisor; + cls_u32.hnode.handle = h->handle; + cls_u32.hnode.prio = h->prio; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); - } + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32); } static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, u32 flags) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_u32_offload u32_offload = {0}; - struct tc_to_netdev offload; + struct tc_cls_u32_offload cls_u32 = {}; int err; - offload.cls_u32 = &u32_offload; - if (!tc_should_offload(dev, tp, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; - tc_cls_common_offload_init(&u32_offload.common, tp); - offload.cls_u32->command = TC_CLSU32_REPLACE_KNODE; - offload.cls_u32->knode.handle = n->handle; - offload.cls_u32->knode.fshift = n->fshift; + tc_cls_common_offload_init(&cls_u32.common, tp); + cls_u32.command = TC_CLSU32_REPLACE_KNODE; + cls_u32.knode.handle = n->handle; + cls_u32.knode.fshift = n->fshift; #ifdef CONFIG_CLS_U32_MARK - offload.cls_u32->knode.val = n->val; - offload.cls_u32->knode.mask = n->mask; + cls_u32.knode.val = n->val; + cls_u32.knode.mask = n->mask; #else - offload.cls_u32->knode.val = 0; - offload.cls_u32->knode.mask = 0; + cls_u32.knode.val = 0; + cls_u32.knode.mask = 0; #endif - offload.cls_u32->knode.sel = &n->sel; - offload.cls_u32->knode.exts = &n->exts; + cls_u32.knode.sel = &n->sel; + cls_u32.knode.exts = &n->exts; if (n->ht_down) - offload.cls_u32->knode.link_handle = n->ht_down->handle; + cls_u32.knode.link_handle = n->ht_down->handle; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32); if (!err) n->flags |= TCA_CLS_FLAGS_IN_HW; diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 09b577dde49c..2165a05994b7 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -39,10 +39,9 @@ static void mqprio_destroy(struct Qdisc *sch) } if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) { - struct tc_mqprio_qopt offload = { 0 }; - struct tc_to_netdev tc = { { .mqprio = &offload } }; + struct tc_mqprio_qopt mqprio = {}; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &mqprio); } else { netdev_set_num_tc(dev, 0); } @@ -147,14 +146,14 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) * supplied and verified mapping */ if (qopt->hw) { - struct tc_mqprio_qopt offload = *qopt; - struct tc_to_netdev tc = { { .mqprio = &offload } }; + struct tc_mqprio_qopt mqprio = *qopt; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, + &mqprio); if (err) return err; - priv->hw_offload = offload.hw; + priv->hw_offload = mqprio.hw; } else { netdev_set_num_tc(dev, qopt->num_tc); for (i = 0; i < qopt->num_tc; i++) -- cgit v1.2.3 From 2a32ca138e24836d01918797018b0fd0302c6b3f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 7 Aug 2017 12:41:53 +0200 Subject: hns3: fix unused function warning Without CONFIG_PCI_IOV, we get a harmless warning about an unused function: drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c:2273:13: error: 'hclge_disable_sriov' defined but not used [-Werror=unused-function] The #ifdefs in this driver are obviously wrong, so this just removes them and uses an IS_ENABLED() check that does the same thing correctly in a more readable way. Fixes: 46a3df9f9718 ("net: hns3: Add HNS3 Acceleration Engine & Compatibility Layer Support") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- .../ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 27 ++++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 3611991689bc..7440e85b607c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -2272,19 +2272,17 @@ static void hclge_service_task(struct work_struct *work) static void hclge_disable_sriov(struct hclge_dev *hdev) { -#ifdef CONFIG_PCI_IOV - /* If our VFs are assigned we cannot shut down SR-IOV - * without causing issues, so just leave the hardware - * available but disabled - */ - if (pci_vfs_assigned(hdev->pdev)) { - dev_warn(&hdev->pdev->dev, - "disabling driver while VFs are assigned\n"); - return; - } + /* If our VFs are assigned we cannot shut down SR-IOV + * without causing issues, so just leave the hardware + * available but disabled + */ + if (pci_vfs_assigned(hdev->pdev)) { + dev_warn(&hdev->pdev->dev, + "disabling driver while VFs are assigned\n"); + return; + } - pci_disable_sriov(hdev->pdev); -#endif + pci_disable_sriov(hdev->pdev); } struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle) @@ -4182,9 +4180,8 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev) set_bit(HCLGE_STATE_DOWN, &hdev->state); -#ifdef CONFIG_PCI_IOV - hclge_disable_sriov(hdev); -#endif + if (IS_ENABLED(CONFIG_PCI_IOV)) + hclge_disable_sriov(hdev); if (hdev->service_timer.data) del_timer_sync(&hdev->service_timer); -- cgit v1.2.3 From e1cea2e7396820730ca98d53e986bd5241ef14cc Mon Sep 17 00:00:00 2001 From: John Allen Date: Mon, 7 Aug 2017 15:42:30 -0500 Subject: ibmvnic: Report rx buffer return codes as netdev_dbg Reporting any return code for a receive buffer as an "rx error" only produces alarming noise and the only values that have been observed to be used in this field are not error conditions. Change this to a netdev_dbg with a more descriptive message. Signed-off-by: John Allen Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 5932160eb815..99576ba4187f 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1579,7 +1579,8 @@ restart_poll: rx_comp.correlator); /* do error checking */ if (next->rx_comp.rc) { - netdev_err(netdev, "rx error %x\n", next->rx_comp.rc); + netdev_dbg(netdev, "rx buffer returned with rc %x\n", + be16_to_cpu(next->rx_comp.rc)); /* free the entry */ next->rx_comp.first = 0; remove_buff_from_pool(adapter, rx_buff); -- cgit v1.2.3 From 451d3ca0a0192def659c00f024718d8b59a3ef47 Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Sat, 5 Aug 2017 13:05:46 +0200 Subject: net: dsa: lan9303: Change lan9303_xxx_packet_processing() port param. lan9303_enable_packet_processing, lan9303_disable_packet_processing() Pass port number (0,1,2) as parameter instead of port offset. Because other functions in the module pass port numbers. And to enable simplifications in following patch. Introduce lan9303_write_switch_port(). Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 8e430d1ee297..77f3cee3af0d 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -159,9 +159,7 @@ # define LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT1 (BIT(9) | BIT(8)) # define LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT0 (BIT(1) | BIT(0)) -#define LAN9303_PORT_0_OFFSET 0x400 -#define LAN9303_PORT_1_OFFSET 0x800 -#define LAN9303_PORT_2_OFFSET 0xc00 +#define LAN9303_SWITCH_PORT_REG(port, reg0) (0x400 * (port) + (reg0)) /* the built-in PHYs are of type LAN911X */ #define MII_LAN911X_SPECIAL_MODES 0x12 @@ -428,6 +426,13 @@ on_error: return ret; } +static int lan9303_write_switch_port(struct lan9303 *chip, int port, + u16 regnum, u32 val) +{ + return lan9303_write_switch_reg( + chip, LAN9303_SWITCH_PORT_REG(port, regnum), val); +} + static int lan9303_detect_phy_setup(struct lan9303 *chip) { int reg; @@ -458,22 +463,19 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip) return 0; } -#define LAN9303_MAC_RX_CFG_OFFS (LAN9303_MAC_RX_CFG_0 - LAN9303_PORT_0_OFFSET) -#define LAN9303_MAC_TX_CFG_OFFS (LAN9303_MAC_TX_CFG_0 - LAN9303_PORT_0_OFFSET) - static int lan9303_disable_packet_processing(struct lan9303 *chip, unsigned int port) { int ret; /* disable RX, but keep register reset default values else */ - ret = lan9303_write_switch_reg(chip, LAN9303_MAC_RX_CFG_OFFS + port, - LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES); + ret = lan9303_write_switch_port(chip, port, LAN9303_MAC_RX_CFG_0, + LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES); if (ret) return ret; /* disable TX, but keep register reset default values else */ - return lan9303_write_switch_reg(chip, LAN9303_MAC_TX_CFG_OFFS + port, + return lan9303_write_switch_port(chip, port, LAN9303_MAC_TX_CFG_0, LAN9303_MAC_TX_CFG_X_TX_IFG_CONFIG_DEFAULT | LAN9303_MAC_TX_CFG_X_TX_PAD_ENABLE); } @@ -484,14 +486,14 @@ static int lan9303_enable_packet_processing(struct lan9303 *chip, int ret; /* enable RX and keep register reset default values else */ - ret = lan9303_write_switch_reg(chip, LAN9303_MAC_RX_CFG_OFFS + port, - LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES | - LAN9303_MAC_RX_CFG_X_RX_ENABLE); + ret = lan9303_write_switch_port(chip, port, LAN9303_MAC_RX_CFG_0, + LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES | + LAN9303_MAC_RX_CFG_X_RX_ENABLE); if (ret) return ret; /* enable TX and keep register reset default values else */ - return lan9303_write_switch_reg(chip, LAN9303_MAC_TX_CFG_OFFS + port, + return lan9303_write_switch_port(chip, port, LAN9303_MAC_TX_CFG_0, LAN9303_MAC_TX_CFG_X_TX_IFG_CONFIG_DEFAULT | LAN9303_MAC_TX_CFG_X_TX_PAD_ENABLE | LAN9303_MAC_TX_CFG_X_TX_ENABLE); @@ -558,13 +560,13 @@ static int lan9303_disable_processing(struct lan9303 *chip) { int ret; - ret = lan9303_disable_packet_processing(chip, LAN9303_PORT_0_OFFSET); + ret = lan9303_disable_packet_processing(chip, 0); if (ret) return ret; - ret = lan9303_disable_packet_processing(chip, LAN9303_PORT_1_OFFSET); + ret = lan9303_disable_packet_processing(chip, 1); if (ret) return ret; - return lan9303_disable_packet_processing(chip, LAN9303_PORT_2_OFFSET); + return lan9303_disable_packet_processing(chip, 2); } static int lan9303_check_device(struct lan9303 *chip) @@ -634,7 +636,7 @@ static int lan9303_setup(struct dsa_switch *ds) if (ret) dev_err(chip->dev, "failed to separate ports %d\n", ret); - ret = lan9303_enable_packet_processing(chip, LAN9303_PORT_0_OFFSET); + ret = lan9303_enable_packet_processing(chip, 0); if (ret) dev_err(chip->dev, "failed to re-enable switching %d\n", ret); @@ -757,11 +759,9 @@ static int lan9303_port_enable(struct dsa_switch *ds, int port, /* enable internal packet processing */ switch (port) { case 1: - return lan9303_enable_packet_processing(chip, - LAN9303_PORT_1_OFFSET); + return lan9303_enable_packet_processing(chip, port); case 2: - return lan9303_enable_packet_processing(chip, - LAN9303_PORT_2_OFFSET); + return lan9303_enable_packet_processing(chip, port); default: dev_dbg(chip->dev, "Error: request to power up invalid port %d\n", port); @@ -778,12 +778,12 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port, /* disable internal packet processing */ switch (port) { case 1: - lan9303_disable_packet_processing(chip, LAN9303_PORT_1_OFFSET); + lan9303_disable_packet_processing(chip, port); lan9303_phy_write(ds, chip->phy_addr_sel_strap + 1, MII_BMCR, BMCR_PDOWN); break; case 2: - lan9303_disable_packet_processing(chip, LAN9303_PORT_2_OFFSET); + lan9303_disable_packet_processing(chip, port); lan9303_phy_write(ds, chip->phy_addr_sel_strap + 2, MII_BMCR, BMCR_PDOWN); break; -- cgit v1.2.3 From a368ca537889d6baff24fd05a903c3625f2906cc Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Sat, 5 Aug 2017 13:05:47 +0200 Subject: net: dsa: lan9303: define LAN9303_NUM_PORTS 3 Will be used instead of '3' in upcomming patches. Signed-off-by: Egil Hjelmeland Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 77f3cee3af0d..49e3a0cb1b5b 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -20,6 +20,8 @@ #include "lan9303.h" +#define LAN9303_NUM_PORTS 3 + /* 13.2 System Control and Status Registers * Multiply register number by 4 to get address offset. */ -- cgit v1.2.3 From b3d14a2b2f988aad27152d7d3b5e4af2f4b69914 Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Sat, 5 Aug 2017 13:05:48 +0200 Subject: net: dsa: lan9303: Simplify lan9303_xxx_packet_processing() usage Simplify usage of lan9303_enable_packet_processing, lan9303_disable_packet_processing() Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 49e3a0cb1b5b..daae267f6b05 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -560,15 +560,16 @@ static int lan9303_handle_reset(struct lan9303 *chip) /* stop processing packets for all ports */ static int lan9303_disable_processing(struct lan9303 *chip) { - int ret; + int p; - ret = lan9303_disable_packet_processing(chip, 0); - if (ret) - return ret; - ret = lan9303_disable_packet_processing(chip, 1); - if (ret) - return ret; - return lan9303_disable_packet_processing(chip, 2); + for (p = 0; p < LAN9303_NUM_PORTS; p++) { + int ret = lan9303_disable_packet_processing(chip, p); + + if (ret) + return ret; + } + + return 0; } static int lan9303_check_device(struct lan9303 *chip) @@ -761,7 +762,6 @@ static int lan9303_port_enable(struct dsa_switch *ds, int port, /* enable internal packet processing */ switch (port) { case 1: - return lan9303_enable_packet_processing(chip, port); case 2: return lan9303_enable_packet_processing(chip, port); default: @@ -780,13 +780,9 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port, /* disable internal packet processing */ switch (port) { case 1: - lan9303_disable_packet_processing(chip, port); - lan9303_phy_write(ds, chip->phy_addr_sel_strap + 1, - MII_BMCR, BMCR_PDOWN); - break; case 2: lan9303_disable_packet_processing(chip, port); - lan9303_phy_write(ds, chip->phy_addr_sel_strap + 2, + lan9303_phy_write(ds, chip->phy_addr_sel_strap + port, MII_BMCR, BMCR_PDOWN); break; default: -- cgit v1.2.3 From 9c84258ed68a1dc3c58a16741747a19df9256c5e Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Sat, 5 Aug 2017 13:05:49 +0200 Subject: net: dsa: lan9303: Rename lan9303_xxx_packet_processing() The lan9303_enable_packet_processing, lan9303_disable_packet_processing functions operate on port, so the names should reflect that. And to align with lan9303_disable_processing(), rename: lan9303_enable_packet_processing -> lan9303_enable_processing_port lan9303_disable_packet_processing -> lan9303_disable_processing_port Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index daae267f6b05..444958bb19f6 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -465,8 +465,8 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip) return 0; } -static int lan9303_disable_packet_processing(struct lan9303 *chip, - unsigned int port) +static int lan9303_disable_processing_port(struct lan9303 *chip, + unsigned int port) { int ret; @@ -482,8 +482,8 @@ static int lan9303_disable_packet_processing(struct lan9303 *chip, LAN9303_MAC_TX_CFG_X_TX_PAD_ENABLE); } -static int lan9303_enable_packet_processing(struct lan9303 *chip, - unsigned int port) +static int lan9303_enable_processing_port(struct lan9303 *chip, + unsigned int port) { int ret; @@ -563,7 +563,7 @@ static int lan9303_disable_processing(struct lan9303 *chip) int p; for (p = 0; p < LAN9303_NUM_PORTS; p++) { - int ret = lan9303_disable_packet_processing(chip, p); + int ret = lan9303_disable_processing_port(chip, p); if (ret) return ret; @@ -639,7 +639,7 @@ static int lan9303_setup(struct dsa_switch *ds) if (ret) dev_err(chip->dev, "failed to separate ports %d\n", ret); - ret = lan9303_enable_packet_processing(chip, 0); + ret = lan9303_enable_processing_port(chip, 0); if (ret) dev_err(chip->dev, "failed to re-enable switching %d\n", ret); @@ -763,7 +763,7 @@ static int lan9303_port_enable(struct dsa_switch *ds, int port, switch (port) { case 1: case 2: - return lan9303_enable_packet_processing(chip, port); + return lan9303_enable_processing_port(chip, port); default: dev_dbg(chip->dev, "Error: request to power up invalid port %d\n", port); @@ -781,7 +781,7 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port, switch (port) { case 1: case 2: - lan9303_disable_packet_processing(chip, port); + lan9303_disable_processing_port(chip, port); lan9303_phy_write(ds, chip->phy_addr_sel_strap + port, MII_BMCR, BMCR_PDOWN); break; -- cgit v1.2.3 From 0a967b4a8e4d5e4e5e50e799bb9d8aa4218c6b2f Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Sat, 5 Aug 2017 13:05:50 +0200 Subject: net: dsa: lan9303: refactor lan9303_get_ethtool_stats In lan9303_get_ethtool_stats: Get rid of 0x400 constant magic by using new lan9303_read_switch_reg() inside loop. Reduced scope of two variables. Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 444958bb19f6..15befd155251 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -435,6 +435,13 @@ static int lan9303_write_switch_port(struct lan9303 *chip, int port, chip, LAN9303_SWITCH_PORT_REG(port, regnum), val); } +static int lan9303_read_switch_port(struct lan9303 *chip, int port, + u16 regnum, u32 *val) +{ + return lan9303_read_switch_reg( + chip, LAN9303_SWITCH_PORT_REG(port, regnum), val); +} + static int lan9303_detect_phy_setup(struct lan9303 *chip) { int reg; @@ -705,19 +712,18 @@ static void lan9303_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) { struct lan9303 *chip = ds->priv; - u32 reg; - unsigned int u, poff; - int ret; - - poff = port * 0x400; + unsigned int u; for (u = 0; u < ARRAY_SIZE(lan9303_mib); u++) { - ret = lan9303_read_switch_reg(chip, - lan9303_mib[u].offset + poff, - ®); + u32 reg; + int ret; + + ret = lan9303_read_switch_port( + chip, port, lan9303_mib[u].offset, ®); + if (ret) - dev_warn(chip->dev, "Reading status reg %u failed\n", - lan9303_mib[u].offset + poff); + dev_warn(chip->dev, "Reading status port %d reg %u failed\n", + port, lan9303_mib[u].offset); data[u] = reg; } } -- cgit v1.2.3 From e00e21979dc8a821840f23a8f1d62bd823755192 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 5 Aug 2017 14:46:35 +0100 Subject: net: hns3: fix spelling mistake: "capabilty" -> "capability" Trivial fix to spelling mistake in dev_err error message and also split overly long line to avoid a checkpatch warning. Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 7440e85b607c..6fb7648bb2f2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -4089,7 +4089,8 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) ret = hclge_get_cap(hdev); if (ret) { - dev_err(&pdev->dev, "get hw capabilty error, ret = %d.\n", ret); + dev_err(&pdev->dev, "get hw capability error, ret = %d.\n", + ret); return ret; } -- cgit v1.2.3 From 511aeaf46655d595f50e5e9f113836f19a78116b Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Sun, 6 Aug 2017 14:21:45 +0530 Subject: hamradio: baycom: make hdlcdrv_ops const Make hdlcdrv_ops structures const as they are only passed to hdlcdrv_register function. The corresponding argument is of type const, so make the structures const. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- drivers/net/hamradio/baycom_par.c | 2 +- drivers/net/hamradio/baycom_ser_fdx.c | 2 +- drivers/net/hamradio/baycom_ser_hdx.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c index 92b13b39f426..e1783832d304 100644 --- a/drivers/net/hamradio/baycom_par.c +++ b/drivers/net/hamradio/baycom_par.c @@ -386,7 +386,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, /* --------------------------------------------------------------------- */ -static struct hdlcdrv_ops par96_ops = { +static const struct hdlcdrv_ops par96_ops = { .drvname = bc_drvname, .drvinfo = bc_drvinfo, .open = par96_open, diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index d9a646acca20..190f66c88479 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -508,7 +508,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, /* --------------------------------------------------------------------- */ -static struct hdlcdrv_ops ser12_ops = { +static const struct hdlcdrv_ops ser12_ops = { .drvname = bc_drvname, .drvinfo = bc_drvinfo, .open = ser12_open, diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index f1c8a9ff3891..3c823c648cf5 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -542,7 +542,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, /* --------------------------------------------------------------------- */ -static struct hdlcdrv_ops ser12_ops = { +static const struct hdlcdrv_ops ser12_ops = { .drvname = bc_drvname, .drvinfo = bc_drvinfo, .open = ser12_open, -- cgit v1.2.3 From 6c2c1dcb185f1e44e1c895781dbaba40195234f9 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:39 +0300 Subject: net: dsa: Change DSA slave FDB API to be switchdev independent In order to support FDB add/del to be on a notifier chain the slave API need to be changed to be switchdev independent. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 12 +++++------- drivers/net/dsa/b53/b53_priv.h | 8 +++----- drivers/net/dsa/microchip/ksz_common.c | 34 ++++++++++++++++------------------ drivers/net/dsa/mt7530.c | 14 ++++++-------- drivers/net/dsa/mv88e6xxx/chip.c | 12 +++++------- drivers/net/dsa/qca8k.c | 15 ++++++--------- include/net/dsa.h | 8 +++----- net/dsa/switch.c | 8 +++++--- 8 files changed, 49 insertions(+), 62 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 7f36d3e3c98b..53361796607a 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1214,8 +1214,7 @@ static int b53_arl_op(struct b53_device *dev, int op, int port, } int b53_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct b53_device *priv = ds->priv; @@ -1230,22 +1229,21 @@ int b53_fdb_prepare(struct dsa_switch *ds, int port, EXPORT_SYMBOL(b53_fdb_prepare); void b53_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct b53_device *priv = ds->priv; - if (b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, true)) + if (b53_arl_op(priv, 0, port, addr, vid, true)) pr_err("%s: failed to add MAC address\n", __func__); } EXPORT_SYMBOL(b53_fdb_add); int b53_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct b53_device *priv = ds->priv; - return b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, false); + return b53_arl_op(priv, 0, port, addr, vid, false); } EXPORT_SYMBOL(b53_fdb_del); diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 155a9c48c317..d417bcaec71d 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -397,13 +397,11 @@ int b53_vlan_dump(struct dsa_switch *ds, int port, struct switchdev_obj_port_vlan *vlan, switchdev_obj_dump_cb_t *cb); int b53_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const unsigned char *addr, u16 vid); void b53_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const unsigned char *addr, u16 vid); int b53_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb); + const unsigned char *addr, u16 vid); int b53_fdb_dump(struct dsa_switch *ds, int port, struct switchdev_obj_port_fdb *fdb, switchdev_obj_dump_cb_t *cb); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index b313ecdf2919..db828080ee93 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -679,8 +679,7 @@ static int ksz_port_vlan_dump(struct dsa_switch *ds, int port, } static int ksz_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { /* nothing needed */ @@ -707,8 +706,7 @@ struct alu_struct { }; static void ksz_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct ksz_device *dev = ds->priv; u32 alu_table[4]; @@ -717,12 +715,12 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port, mutex_lock(&dev->alu_mutex); /* find any entry with mac & vid */ - data = fdb->vid << ALU_FID_INDEX_S; - data |= ((fdb->addr[0] << 8) | fdb->addr[1]); + data = vid << ALU_FID_INDEX_S; + data |= ((addr[0] << 8) | addr[1]); ksz_write32(dev, REG_SW_ALU_INDEX_0, data); - data = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16)); - data |= ((fdb->addr[4] << 8) | fdb->addr[5]); + data = ((addr[2] << 24) | (addr[3] << 16)); + data |= ((addr[4] << 8) | addr[5]); ksz_write32(dev, REG_SW_ALU_INDEX_1, data); /* start read operation */ @@ -740,12 +738,12 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port, /* update ALU entry */ alu_table[0] = ALU_V_STATIC_VALID; alu_table[1] |= BIT(port); - if (fdb->vid) + if (vid) alu_table[1] |= ALU_V_USE_FID; - alu_table[2] = (fdb->vid << ALU_V_FID_S); - alu_table[2] |= ((fdb->addr[0] << 8) | fdb->addr[1]); - alu_table[3] = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16)); - alu_table[3] |= ((fdb->addr[4] << 8) | fdb->addr[5]); + alu_table[2] = (vid << ALU_V_FID_S); + alu_table[2] |= ((addr[0] << 8) | addr[1]); + alu_table[3] = ((addr[2] << 24) | (addr[3] << 16)); + alu_table[3] |= ((addr[4] << 8) | addr[5]); write_table(ds, alu_table); @@ -760,7 +758,7 @@ exit: } static int ksz_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct ksz_device *dev = ds->priv; u32 alu_table[4]; @@ -770,12 +768,12 @@ static int ksz_port_fdb_del(struct dsa_switch *ds, int port, mutex_lock(&dev->alu_mutex); /* read any entry with mac & vid */ - data = fdb->vid << ALU_FID_INDEX_S; - data |= ((fdb->addr[0] << 8) | fdb->addr[1]); + data = vid << ALU_FID_INDEX_S; + data |= ((addr[0] << 8) | addr[1]); ksz_write32(dev, REG_SW_ALU_INDEX_0, data); - data = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16)); - data |= ((fdb->addr[4] << 8) | fdb->addr[5]); + data = ((addr[2] << 24) | (addr[3] << 16)); + data |= ((addr[4] << 8) | addr[5]); ksz_write32(dev, REG_SW_ALU_INDEX_1, data); /* start read operation */ diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 1e46418a3b74..430e3ab65a49 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -802,8 +802,7 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, static int mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct mt7530_priv *priv = ds->priv; int ret; @@ -813,7 +812,7 @@ mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, * is called while the entry is still available. */ mutex_lock(&priv->reg_mutex); - mt7530_fdb_write(priv, fdb->vid, 0, fdb->addr, -1, STATIC_ENT); + mt7530_fdb_write(priv, vid, 0, addr, -1, STATIC_ENT); ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); mutex_unlock(&priv->reg_mutex); @@ -822,28 +821,27 @@ mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, static void mt7530_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct mt7530_priv *priv = ds->priv; u8 port_mask = BIT(port); mutex_lock(&priv->reg_mutex); - mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_ENT); + mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_ENT); mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); mutex_unlock(&priv->reg_mutex); } static int mt7530_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct mt7530_priv *priv = ds->priv; int ret; u8 port_mask = BIT(port); mutex_lock(&priv->reg_mutex); - mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_EMP); + mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_EMP); ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); mutex_unlock(&priv->reg_mutex); diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 521738c4cd17..823697526db4 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1407,8 +1407,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, } static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { /* We don't need any dynamic resource from the kernel (yet), * so skip the prepare phase. @@ -1417,13 +1416,12 @@ static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, } static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct mv88e6xxx_chip *chip = ds->priv; mutex_lock(&chip->reg_lock); - if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, + if (mv88e6xxx_port_db_load_purge(chip, port, addr, vid, MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC)) dev_err(ds->dev, "p%d: failed to load unicast MAC address\n", port); @@ -1431,13 +1429,13 @@ static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, } static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct mv88e6xxx_chip *chip = ds->priv; int err; mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, MV88E6XXX_G1_ATU_DATA_STATE_UNUSED); mutex_unlock(&chip->reg_lock); diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 36c169b0c705..2fb5df9dbd64 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -778,8 +778,7 @@ qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, static int qca8k_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; @@ -788,33 +787,31 @@ qca8k_port_fdb_prepare(struct dsa_switch *ds, int port, * when port_fdb_add is called an entry is still available. Otherwise * the last free entry might have been used up by auto learning */ - return qca8k_port_fdb_insert(priv, fdb->addr, 0, fdb->vid); + return qca8k_port_fdb_insert(priv, addr, 0, vid); } static void qca8k_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u16 port_mask = BIT(port); /* Update the FDB entry adding the port_mask */ - qca8k_port_fdb_insert(priv, fdb->addr, port_mask, fdb->vid); + qca8k_port_fdb_insert(priv, addr, port_mask, vid); } static int qca8k_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u16 port_mask = BIT(port); - u16 vid = fdb->vid; if (!vid) vid = 1; - return qca8k_fdb_del(priv, fdb->addr, port_mask, vid); + return qca8k_fdb_del(priv, addr, port_mask, vid); } static int diff --git a/include/net/dsa.h b/include/net/dsa.h index 0b1a0622b33c..ba11005dac8c 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -386,13 +386,11 @@ struct dsa_switch_ops { * Forwarding database */ int (*port_fdb_prepare)(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const unsigned char *addr, u16 vid); void (*port_fdb_add)(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const unsigned char *addr, u16 vid); int (*port_fdb_del)(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb); + const unsigned char *addr, u16 vid); int (*port_fdb_dump)(struct dsa_switch *ds, int port, struct switchdev_obj_port_fdb *fdb, switchdev_obj_dump_cb_t *cb); diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 97e2e9c8cf3f..a9edfbad3889 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -94,10 +94,11 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds, if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add) return -EOPNOTSUPP; - return ds->ops->port_fdb_prepare(ds, info->port, fdb, trans); + return ds->ops->port_fdb_prepare(ds, info->port, fdb->addr, + fdb->vid); } - ds->ops->port_fdb_add(ds, info->port, fdb, trans); + ds->ops->port_fdb_add(ds, info->port, fdb->addr, fdb->vid); return 0; } @@ -114,7 +115,8 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds, if (!ds->ops->port_fdb_del) return -EOPNOTSUPP; - return ds->ops->port_fdb_del(ds, info->port, fdb); + return ds->ops->port_fdb_del(ds, info->port, fdb->addr, + fdb->vid); } static int dsa_switch_mdb_add(struct dsa_switch *ds, -- cgit v1.2.3 From 1b6dd556c3045ca5fa31cc1e98a4a43afa680e1e Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:40 +0300 Subject: net: dsa: Remove prepare phase for FDB The prepare phase for FDB add is unneeded because most of DSA devices can have failures during bus transactions (SPI, I2C, etc.), thus, the prepare phase cannot guarantee success of the commit stage. The support for learning FDB through notification chain, which will be introduced in the following patches, will provide the ability to notify back the bridge about successful offload. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 17 +++-------------- drivers/net/dsa/b53/b53_priv.h | 6 ++---- drivers/net/dsa/bcm_sf2.c | 1 - drivers/net/dsa/microchip/ksz_common.c | 24 ++++++++++-------------- drivers/net/dsa/mt7530.c | 25 ++++--------------------- drivers/net/dsa/mv88e6xxx/chip.c | 23 +++++++---------------- drivers/net/dsa/qca8k.c | 18 +----------------- include/net/dsa.h | 4 +--- net/dsa/dsa_priv.h | 4 +--- net/dsa/port.c | 4 +--- net/dsa/slave.c | 4 +++- net/dsa/switch.c | 14 +++----------- 12 files changed, 36 insertions(+), 108 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 53361796607a..3cf4f0a04453 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1213,8 +1213,8 @@ static int b53_arl_op(struct b53_device *dev, int op, int port, return b53_arl_rw_op(dev, 0); } -int b53_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +int b53_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) { struct b53_device *priv = ds->priv; @@ -1224,17 +1224,7 @@ int b53_fdb_prepare(struct dsa_switch *ds, int port, if (is5325(priv) || is5365(priv)) return -EOPNOTSUPP; - return 0; -} -EXPORT_SYMBOL(b53_fdb_prepare); - -void b53_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - struct b53_device *priv = ds->priv; - - if (b53_arl_op(priv, 0, port, addr, vid, true)) - pr_err("%s: failed to add MAC address\n", __func__); + return b53_arl_op(priv, 0, port, addr, vid, true); } EXPORT_SYMBOL(b53_fdb_add); @@ -1563,7 +1553,6 @@ static const struct dsa_switch_ops b53_switch_ops = { .port_vlan_add = b53_vlan_add, .port_vlan_del = b53_vlan_del, .port_vlan_dump = b53_vlan_dump, - .port_fdb_prepare = b53_fdb_prepare, .port_fdb_dump = b53_fdb_dump, .port_fdb_add = b53_fdb_add, .port_fdb_del = b53_fdb_del, diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index d417bcaec71d..f29c892efa6b 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -396,10 +396,8 @@ int b53_vlan_del(struct dsa_switch *ds, int port, int b53_vlan_dump(struct dsa_switch *ds, int port, struct switchdev_obj_port_vlan *vlan, switchdev_obj_dump_cb_t *cb); -int b53_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); -void b53_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); +int b53_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); int b53_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int b53_fdb_dump(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 558667c814c9..1907b27297c3 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -1022,7 +1022,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .port_vlan_add = b53_vlan_add, .port_vlan_del = b53_vlan_del, .port_vlan_dump = b53_vlan_dump, - .port_fdb_prepare = b53_fdb_prepare, .port_fdb_dump = b53_fdb_dump, .port_fdb_add = b53_fdb_add, .port_fdb_del = b53_fdb_del, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index db828080ee93..b55f3649ff93 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -678,14 +678,6 @@ static int ksz_port_vlan_dump(struct dsa_switch *ds, int port, return err; } -static int ksz_port_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - /* nothing needed */ - - return 0; -} - struct alu_struct { /* entry 1 */ u8 is_static:1; @@ -705,12 +697,13 @@ struct alu_struct { u8 mac[ETH_ALEN]; }; -static void ksz_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +static int ksz_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) { struct ksz_device *dev = ds->priv; u32 alu_table[4]; u32 data; + int ret = 0; mutex_lock(&dev->alu_mutex); @@ -727,7 +720,8 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port, ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START); /* wait to be finished */ - if (wait_alu_ready(dev, ALU_START, 1000) < 0) { + ret = wait_alu_ready(dev, ALU_START, 1000); + if (ret < 0) { dev_dbg(dev->dev, "Failed to read ALU\n"); goto exit; } @@ -750,11 +744,14 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port, ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START); /* wait to be finished */ - if (wait_alu_ready(dev, ALU_START, 1000) < 0) - dev_dbg(dev->dev, "Failed to read ALU\n"); + ret = wait_alu_ready(dev, ALU_START, 1000); + if (ret < 0) + dev_dbg(dev->dev, "Failed to write ALU\n"); exit: mutex_unlock(&dev->alu_mutex); + + return ret; } static int ksz_port_fdb_del(struct dsa_switch *ds, int port, @@ -1128,7 +1125,6 @@ static const struct dsa_switch_ops ksz_switch_ops = { .port_vlan_add = ksz_port_vlan_add, .port_vlan_del = ksz_port_vlan_del, .port_vlan_dump = ksz_port_vlan_dump, - .port_fdb_prepare = ksz_port_fdb_prepare, .port_fdb_dump = ksz_port_fdb_dump, .port_fdb_add = ksz_port_fdb_add, .port_fdb_del = ksz_port_fdb_del, diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 430e3ab65a49..f92aae8947e6 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -801,35 +801,19 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, } static int -mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - struct mt7530_priv *priv = ds->priv; - int ret; - - /* Because auto-learned entrie shares the same FDB table. - * an entry is reserved with no port_mask to make sure fdb_add - * is called while the entry is still available. - */ - mutex_lock(&priv->reg_mutex); - mt7530_fdb_write(priv, vid, 0, addr, -1, STATIC_ENT); - ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); - mutex_unlock(&priv->reg_mutex); - - return ret; -} - -static void mt7530_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { struct mt7530_priv *priv = ds->priv; + int ret; u8 port_mask = BIT(port); mutex_lock(&priv->reg_mutex); mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_ENT); - mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); + ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); mutex_unlock(&priv->reg_mutex); + + return ret; } static int @@ -1013,7 +997,6 @@ static struct dsa_switch_ops mt7530_switch_ops = { .port_stp_state_set = mt7530_stp_state_set, .port_bridge_join = mt7530_port_bridge_join, .port_bridge_leave = mt7530_port_bridge_leave, - .port_fdb_prepare = mt7530_port_fdb_prepare, .port_fdb_add = mt7530_port_fdb_add, .port_fdb_del = mt7530_port_fdb_del, .port_fdb_dump = mt7530_port_fdb_dump, diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 823697526db4..68cc1f6fb75e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1406,26 +1406,18 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry); } -static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - /* We don't need any dynamic resource from the kernel (yet), - * so skip the prepare phase. - */ - return 0; -} - -static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) { struct mv88e6xxx_chip *chip = ds->priv; + int err; mutex_lock(&chip->reg_lock); - if (mv88e6xxx_port_db_load_purge(chip, port, addr, vid, - MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC)) - dev_err(ds->dev, "p%d: failed to load unicast MAC address\n", - port); + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, + MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC); mutex_unlock(&chip->reg_lock); + + return err; } static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, @@ -3905,7 +3897,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, .port_vlan_dump = mv88e6xxx_port_vlan_dump, - .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, .port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_dump = mv88e6xxx_port_fdb_dump, diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 2fb5df9dbd64..f8ef823349bc 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -777,28 +777,13 @@ qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, } static int -qca8k_port_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - - /* The FDB table for static and auto learned entries is the same. We - * need to reserve an entry with no port_mask set to make sure that - * when port_fdb_add is called an entry is still available. Otherwise - * the last free entry might have been used up by auto learning - */ - return qca8k_port_fdb_insert(priv, addr, 0, vid); -} - -static void qca8k_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u16 port_mask = BIT(port); - /* Update the FDB entry adding the port_mask */ - qca8k_port_fdb_insert(priv, addr, port_mask, vid); + return qca8k_port_fdb_insert(priv, addr, port_mask, vid); } static int @@ -866,7 +851,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .port_stp_state_set = qca8k_port_stp_state_set, .port_bridge_join = qca8k_port_bridge_join, .port_bridge_leave = qca8k_port_bridge_leave, - .port_fdb_prepare = qca8k_port_fdb_prepare, .port_fdb_add = qca8k_port_fdb_add, .port_fdb_del = qca8k_port_fdb_del, .port_fdb_dump = qca8k_port_fdb_dump, diff --git a/include/net/dsa.h b/include/net/dsa.h index ba11005dac8c..446fc438cb80 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -385,9 +385,7 @@ struct dsa_switch_ops { /* * Forwarding database */ - int (*port_fdb_prepare)(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); - void (*port_fdb_add)(struct dsa_switch *ds, int port, + int (*port_fdb_add)(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int (*port_fdb_del)(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 945ded148c9c..04cd711ed2fd 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -44,7 +44,6 @@ struct dsa_notifier_bridge_info { /* DSA_NOTIFIER_FDB_* */ struct dsa_notifier_fdb_info { const struct switchdev_obj_port_fdb *fdb; - struct switchdev_trans *trans; int sw_index; int port; }; @@ -122,8 +121,7 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, struct switchdev_trans *trans); int dsa_port_fdb_add(struct dsa_port *dp, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const struct switchdev_obj_port_fdb *fdb); int dsa_port_fdb_del(struct dsa_port *dp, const struct switchdev_obj_port_fdb *fdb); int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb, diff --git a/net/dsa/port.c b/net/dsa/port.c index efc3bce3a89d..bd271b9cc1f2 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -147,13 +147,11 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, } int dsa_port_fdb_add(struct dsa_port *dp, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const struct switchdev_obj_port_fdb *fdb) { struct dsa_notifier_fdb_info info = { .sw_index = dp->ds->index, .port = dp->index, - .trans = trans, .fdb = fdb, }; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 3b36c47472c6..bb7ab26ef768 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -251,7 +251,9 @@ static int dsa_slave_port_obj_add(struct net_device *dev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_FDB: - err = dsa_port_fdb_add(dp, SWITCHDEV_OBJ_PORT_FDB(obj), trans); + if (switchdev_trans_ph_prepare(trans)) + return 0; + err = dsa_port_fdb_add(dp, SWITCHDEV_OBJ_PORT_FDB(obj)); break; case SWITCHDEV_OBJ_ID_PORT_MDB: err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans); diff --git a/net/dsa/switch.c b/net/dsa/switch.c index a9edfbad3889..eb20e0fee0e1 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -84,23 +84,15 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds, struct dsa_notifier_fdb_info *info) { const struct switchdev_obj_port_fdb *fdb = info->fdb; - struct switchdev_trans *trans = info->trans; /* Do not care yet about other switch chips of the fabric */ if (ds->index != info->sw_index) return 0; - if (switchdev_trans_ph_prepare(trans)) { - if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add) - return -EOPNOTSUPP; - - return ds->ops->port_fdb_prepare(ds, info->port, fdb->addr, - fdb->vid); - } - - ds->ops->port_fdb_add(ds, info->port, fdb->addr, fdb->vid); + if (!ds->ops->port_fdb_add) + return -EOPNOTSUPP; - return 0; + return ds->ops->port_fdb_add(ds, info->port, fdb->addr, fdb->vid); } static int dsa_switch_fdb_del(struct dsa_switch *ds, -- cgit v1.2.3 From a0b6b8c9fa3c73a523735b8b5f87f59211c70a4e Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:45 +0300 Subject: net: dsa: Remove support for vlan dump from DSA's drivers This is done as a preparation before removing support for vlan dump from DSA core. The vlans are synced with the bridge and thus there is no need for special dump operation support. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 44 -------------------------- drivers/net/dsa/b53/b53_priv.h | 3 -- drivers/net/dsa/bcm_sf2.c | 1 - drivers/net/dsa/dsa_loop.c | 38 ----------------------- drivers/net/dsa/microchip/ksz_common.c | 41 ------------------------- drivers/net/dsa/mv88e6xxx/chip.c | 56 ---------------------------------- 6 files changed, 183 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 3cf4f0a04453..0176d8087344 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1053,49 +1053,6 @@ int b53_vlan_del(struct dsa_switch *ds, int port, } EXPORT_SYMBOL(b53_vlan_del); -int b53_vlan_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb) -{ - struct b53_device *dev = ds->priv; - u16 vid, vid_start = 0, pvid; - struct b53_vlan *vl; - int err = 0; - - if (is5325(dev) || is5365(dev)) - vid_start = 1; - - b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid); - - /* Use our software cache for dumps, since we do not have any HW - * operation returning only the used/valid VLANs - */ - for (vid = vid_start; vid < dev->num_vlans; vid++) { - vl = &dev->vlans[vid]; - - if (!vl->valid) - continue; - - if (!(vl->members & BIT(port))) - continue; - - vlan->vid_begin = vlan->vid_end = vid; - vlan->flags = 0; - - if (vl->untag & BIT(port)) - vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; - if (pvid == vid) - vlan->flags |= BRIDGE_VLAN_INFO_PVID; - - err = cb(&vlan->obj); - if (err) - break; - } - - return err; -} -EXPORT_SYMBOL(b53_vlan_dump); - /* Address Resolution Logic routines */ static int b53_arl_op_wait(struct b53_device *dev) { @@ -1552,7 +1509,6 @@ static const struct dsa_switch_ops b53_switch_ops = { .port_vlan_prepare = b53_vlan_prepare, .port_vlan_add = b53_vlan_add, .port_vlan_del = b53_vlan_del, - .port_vlan_dump = b53_vlan_dump, .port_fdb_dump = b53_fdb_dump, .port_fdb_add = b53_fdb_add, .port_fdb_del = b53_fdb_del, diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index f29c892efa6b..af5d6c166bff 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -393,9 +393,6 @@ void b53_vlan_add(struct dsa_switch *ds, int port, struct switchdev_trans *trans); int b53_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); -int b53_vlan_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb); int b53_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int b53_fdb_del(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 1907b27297c3..bbcb4053e04e 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -1021,7 +1021,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .port_vlan_prepare = b53_vlan_prepare, .port_vlan_add = b53_vlan_add, .port_vlan_del = b53_vlan_del, - .port_vlan_dump = b53_vlan_dump, .port_fdb_dump = b53_fdb_dump, .port_fdb_add = b53_fdb_add, .port_fdb_del = b53_fdb_del, diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index fdd8f3872102..76d66604a34e 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -257,43 +257,6 @@ static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, return 0; } -static int dsa_loop_port_vlan_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb) -{ - struct dsa_loop_priv *ps = ds->priv; - struct mii_bus *bus = ps->bus; - struct dsa_loop_vlan *vl; - u16 vid, vid_start = 0; - int err = 0; - - dev_dbg(ds->dev, "%s\n", __func__); - - /* Just do a sleeping operation to make lockdep checks effective */ - mdiobus_read(bus, ps->port_base + port, MII_BMSR); - - for (vid = vid_start; vid < DSA_LOOP_VLANS; vid++) { - vl = &ps->vlans[vid]; - - if (!(vl->members & BIT(port))) - continue; - - vlan->vid_begin = vlan->vid_end = vid; - vlan->flags = 0; - - if (vl->untagged & BIT(port)) - vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; - if (ps->pvid == vid) - vlan->flags |= BRIDGE_VLAN_INFO_PVID; - - err = cb(&vlan->obj); - if (err) - break; - } - - return err; -} - static struct dsa_switch_ops dsa_loop_driver = { .get_tag_protocol = dsa_loop_get_protocol, .setup = dsa_loop_setup, @@ -310,7 +273,6 @@ static struct dsa_switch_ops dsa_loop_driver = { .port_vlan_prepare = dsa_loop_port_vlan_prepare, .port_vlan_add = dsa_loop_port_vlan_add, .port_vlan_del = dsa_loop_port_vlan_del, - .port_vlan_dump = dsa_loop_port_vlan_dump, }; static int dsa_loop_drv_probe(struct mdio_device *mdiodev) diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index b55f3649ff93..a53ce59e0442 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -638,46 +638,6 @@ static int ksz_port_vlan_del(struct dsa_switch *ds, int port, return 0; } -static int ksz_port_vlan_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb) -{ - struct ksz_device *dev = ds->priv; - u16 vid; - u16 data; - struct vlan_table *vlan_cache; - int err = 0; - - mutex_lock(&dev->vlan_mutex); - - /* use dev->vlan_cache due to lack of searching valid vlan entry */ - for (vid = vlan->vid_begin; vid < dev->num_vlans; vid++) { - vlan_cache = &dev->vlan_cache[vid]; - - if (!(vlan_cache->table[0] & VLAN_VALID)) - continue; - - vlan->vid_begin = vid; - vlan->vid_end = vid; - vlan->flags = 0; - if (vlan_cache->table[2] & BIT(port)) { - if (vlan_cache->table[1] & BIT(port)) - vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; - ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &data); - if (vid == (data & 0xFFFFF)) - vlan->flags |= BRIDGE_VLAN_INFO_PVID; - - err = cb(&vlan->obj); - if (err) - break; - } - } - - mutex_unlock(&dev->vlan_mutex); - - return err; -} - struct alu_struct { /* entry 1 */ u8 is_static:1; @@ -1124,7 +1084,6 @@ static const struct dsa_switch_ops ksz_switch_ops = { .port_vlan_prepare = ksz_port_vlan_prepare, .port_vlan_add = ksz_port_vlan_add, .port_vlan_del = ksz_port_vlan_del, - .port_vlan_dump = ksz_port_vlan_dump, .port_fdb_dump = ksz_port_fdb_dump, .port_fdb_add = ksz_port_fdb_add, .port_fdb_del = ksz_port_fdb_del, diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 68cc1f6fb75e..5bb1138818ef 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1011,61 +1011,6 @@ static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, return chip->info->ops->vtu_loadpurge(chip, entry); } -static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb) -{ - struct mv88e6xxx_chip *chip = ds->priv; - struct mv88e6xxx_vtu_entry next = { - .vid = chip->info->max_vid, - }; - u16 pvid; - int err; - - if (!chip->info->max_vid) - return -EOPNOTSUPP; - - mutex_lock(&chip->reg_lock); - - err = mv88e6xxx_port_get_pvid(chip, port, &pvid); - if (err) - goto unlock; - - do { - err = mv88e6xxx_vtu_getnext(chip, &next); - if (err) - break; - - if (!next.valid) - break; - - if (next.member[port] == - MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) - continue; - - /* reinit and dump this VLAN obj */ - vlan->vid_begin = next.vid; - vlan->vid_end = next.vid; - vlan->flags = 0; - - if (next.member[port] == - MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED) - vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; - - if (next.vid == pvid) - vlan->flags |= BRIDGE_VLAN_INFO_PVID; - - err = cb(&vlan->obj); - if (err) - break; - } while (next.vid < chip->info->max_vid); - -unlock: - mutex_unlock(&chip->reg_lock); - - return err; -} - static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) { DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); @@ -3896,7 +3841,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, - .port_vlan_dump = mv88e6xxx_port_vlan_dump, .port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_dump = mv88e6xxx_port_fdb_dump, -- cgit v1.2.3 From 180b072eb0c8529d0fd460fd192fc6b4db6c3df9 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:47 +0300 Subject: net: dsa: Remove support for MDB dump from DSA's drivers This is done as a preparation before removing support for MDB dump from DSA core. The MDBs are synced with the bridge and thus there is no need for special dump operation support. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz_common.c | 9 --------- drivers/net/dsa/mv88e6xxx/chip.c | 24 ------------------------ 2 files changed, 33 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index a53ce59e0442..4de9d90a4bb3 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -1020,14 +1020,6 @@ exit: return ret; } -static int ksz_port_mdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_mdb *mdb, - switchdev_obj_dump_cb_t *cb) -{ - /* this is not called by switch layer */ - return 0; -} - static int ksz_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress) @@ -1090,7 +1082,6 @@ static const struct dsa_switch_ops ksz_switch_ops = { .port_mdb_prepare = ksz_port_mdb_prepare, .port_mdb_add = ksz_port_mdb_add, .port_mdb_del = ksz_port_mdb_del, - .port_mdb_dump = ksz_port_mdb_dump, .port_mirror_add = ksz_port_mirror_add, .port_mirror_del = ksz_port_mirror_del, }; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5bb1138818ef..1f5c202b974d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1414,15 +1414,6 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, fdb->ndm_state = NUD_NOARP; else fdb->ndm_state = NUD_REACHABLE; - } else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) { - struct switchdev_obj_port_mdb *mdb; - - if (!is_multicast_ether_addr(addr.mac)) - continue; - - mdb = SWITCHDEV_OBJ_PORT_MDB(obj); - mdb->vid = vid; - ether_addr_copy(mdb->addr, addr.mac); } else { return -EOPNOTSUPP; } @@ -3800,20 +3791,6 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, return err; } -static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_mdb *mdb, - switchdev_obj_dump_cb_t *cb) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb); - mutex_unlock(&chip->reg_lock); - - return err; -} - static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .probe = mv88e6xxx_drv_probe, .get_tag_protocol = mv88e6xxx_get_tag_protocol, @@ -3847,7 +3824,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_mdb_prepare = mv88e6xxx_port_mdb_prepare, .port_mdb_add = mv88e6xxx_port_mdb_add, .port_mdb_del = mv88e6xxx_port_mdb_del, - .port_mdb_dump = mv88e6xxx_port_mdb_dump, .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join, .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave, }; -- cgit v1.2.3 From 2bedde1abbef5eec211308f0293dd7681b0513ec Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:49 +0300 Subject: net: dsa: Move FDB dump implementation inside DSA >From all switchdev devices only DSA requires special FDB dump. This is due to lack of ability for syncing the hardware learned FDBs with the bridge. Due to this it is removed from switchdev and moved inside DSA. Signed-off-by: Arkadi Sharshevsky Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 16 ++---- drivers/net/dsa/b53/b53_priv.h | 3 +- drivers/net/dsa/microchip/ksz_common.c | 20 ++----- drivers/net/dsa/mt7530.c | 10 +--- drivers/net/dsa/mv88e6xxx/chip.c | 38 ++++--------- drivers/net/dsa/qca8k.c | 15 ++--- include/net/dsa.h | 5 +- include/net/switchdev.h | 12 ---- net/dsa/dsa_priv.h | 2 - net/dsa/port.c | 11 ---- net/dsa/slave.c | 100 +++++++++++++++++++++++++-------- net/switchdev/switchdev.c | 84 --------------------------- 12 files changed, 112 insertions(+), 204 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 0176d8087344..274f3679f33d 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1227,8 +1227,7 @@ static void b53_arl_search_rd(struct b53_device *dev, u8 idx, } static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { if (!ent->is_valid) return 0; @@ -1236,16 +1235,11 @@ static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, if (port != ent->port) return 0; - ether_addr_copy(fdb->addr, ent->mac); - fdb->vid = ent->vid; - fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE; - - return cb(&fdb->obj); + return cb(ent->mac, ent->vid, ent->is_static, data); } int b53_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct b53_device *priv = ds->priv; struct b53_arl_entry results[2]; @@ -1263,13 +1257,13 @@ int b53_fdb_dump(struct dsa_switch *ds, int port, return ret; b53_arl_search_rd(priv, 0, &results[0]); - ret = b53_fdb_copy(port, &results[0], fdb, cb); + ret = b53_fdb_copy(port, &results[0], cb, data); if (ret) return ret; if (priv->num_arl_entries > 2) { b53_arl_search_rd(priv, 1, &results[1]); - ret = b53_fdb_copy(port, &results[1], fdb, cb); + ret = b53_fdb_copy(port, &results[1], cb, data); if (ret) return ret; diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index af5d6c166bff..01bd8cbe9a3f 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -398,8 +398,7 @@ int b53_fdb_add(struct dsa_switch *ds, int port, int b53_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int b53_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb); + dsa_fdb_dump_cb_t *cb, void *data); int b53_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress); void b53_mirror_del(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 4de9d90a4bb3..56cd6d365352 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -805,12 +805,11 @@ static void convert_alu(struct alu_struct *alu, u32 *alu_table) } static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct ksz_device *dev = ds->priv; int ret = 0; - u32 data; + u32 ksz_data; u32 alu_table[4]; struct alu_struct alu; int timeout; @@ -823,8 +822,8 @@ static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, do { timeout = 1000; do { - ksz_read32(dev, REG_SW_ALU_CTRL__4, &data); - if ((data & ALU_VALID) || !(data & ALU_START)) + ksz_read32(dev, REG_SW_ALU_CTRL__4, &ksz_data); + if ((ksz_data & ALU_VALID) || !(ksz_data & ALU_START)) break; usleep_range(1, 10); } while (timeout-- > 0); @@ -841,18 +840,11 @@ static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, convert_alu(&alu, alu_table); if (alu.port_forward & BIT(port)) { - fdb->vid = alu.fid; - if (alu.is_static) - fdb->ndm_state = NUD_NOARP; - else - fdb->ndm_state = NUD_REACHABLE; - ether_addr_copy(fdb->addr, alu.mac); - - ret = cb(&fdb->obj); + ret = cb(alu.mac, alu.fid, alu.is_static, data); if (ret) goto exit; } - } while (data & ALU_START); + } while (ksz_data & ALU_START); exit: diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index f92aae8947e6..12700710f26d 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -834,8 +834,7 @@ mt7530_port_fdb_del(struct dsa_switch *ds, int port, static int mt7530_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct mt7530_priv *priv = ds->priv; struct mt7530_fdb _fdb = { 0 }; @@ -853,11 +852,8 @@ mt7530_port_fdb_dump(struct dsa_switch *ds, int port, if (rsp & ATC_SRCH_HIT) { mt7530_fdb_read(priv, &_fdb); if (_fdb.port_mask & BIT(port)) { - ether_addr_copy(fdb->addr, _fdb.mac); - fdb->vid = _fdb.vid; - fdb->ndm_state = _fdb.noarp ? - NUD_NOARP : NUD_REACHABLE; - ret = cb(&fdb->obj); + ret = cb(_fdb.mac, _fdb.vid, _fdb.noarp, + data); if (ret < 0) break; } diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 1f5c202b974d..918d8f0fe091 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1381,10 +1381,10 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, u16 fid, u16 vid, int port, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct mv88e6xxx_atu_entry addr; + bool is_static; int err; addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; @@ -1401,24 +1401,12 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, if (addr.trunk || (addr.portvec & BIT(port)) == 0) continue; - if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) { - struct switchdev_obj_port_fdb *fdb; - - if (!is_unicast_ether_addr(addr.mac)) - continue; - - fdb = SWITCHDEV_OBJ_PORT_FDB(obj); - fdb->vid = vid; - ether_addr_copy(fdb->addr, addr.mac); - if (addr.state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC) - fdb->ndm_state = NUD_NOARP; - else - fdb->ndm_state = NUD_REACHABLE; - } else { - return -EOPNOTSUPP; - } + if (!is_unicast_ether_addr(addr.mac)) + continue; - err = cb(obj); + is_static = (addr.state == + MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC); + err = cb(addr.mac, vid, is_static, data); if (err) return err; } while (!is_broadcast_ether_addr(addr.mac)); @@ -1427,8 +1415,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, } static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct mv88e6xxx_vtu_entry vlan = { .vid = chip->info->max_vid, @@ -1441,7 +1428,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, if (err) return err; - err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb); + err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, cb, data); if (err) return err; @@ -1455,7 +1442,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, break; err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port, - obj, cb); + cb, data); if (err) return err; } while (vlan.vid < chip->info->max_vid); @@ -1464,14 +1451,13 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, } static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct mv88e6xxx_chip *chip = ds->priv; int err; mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb); + err = mv88e6xxx_port_db_dump(chip, port, cb, data); mutex_unlock(&chip->reg_lock); return err; diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index f8ef823349bc..17977f06cb98 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -801,27 +801,20 @@ qca8k_port_fdb_del(struct dsa_switch *ds, int port, static int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; struct qca8k_fdb _fdb = { 0 }; int cnt = QCA8K_NUM_FDB_RECORDS; + bool is_static; int ret = 0; mutex_lock(&priv->reg_mutex); while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { if (!_fdb.aging) break; - - ether_addr_copy(fdb->addr, _fdb.mac); - fdb->vid = _fdb.vid; - if (_fdb.aging == QCA8K_ATU_STATUS_STATIC) - fdb->ndm_state = NUD_NOARP; - else - fdb->ndm_state = NUD_REACHABLE; - - ret = cb(&fdb->obj); + is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); + ret = cb(_fdb.mac, _fdb.vid, is_static, data); if (ret) break; } diff --git a/include/net/dsa.h b/include/net/dsa.h index 4ef185997b6f..a4f66dbb4b7c 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -272,6 +272,8 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds) return ds->rtable[dst->cpu_dp->ds->index]; } +typedef int dsa_fdb_dump_cb_t(const unsigned char *addr, u16 vid, + bool is_static, void *data); struct dsa_switch_ops { /* * Legacy probing. @@ -386,8 +388,7 @@ struct dsa_switch_ops { int (*port_fdb_del)(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int (*port_fdb_dump)(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb); + dsa_fdb_dump_cb_t *cb, void *data); /* * Multicast database diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 8ae9e3b6392e..d2637a685ca6 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -208,9 +208,6 @@ int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 vid); -int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct net_device *dev, - struct net_device *filter_dev, int *idx); void switchdev_port_fwd_mark_set(struct net_device *dev, struct net_device *group_dev, bool joining); @@ -309,15 +306,6 @@ static inline int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], return -EOPNOTSUPP; } -static inline int switchdev_port_fdb_dump(struct sk_buff *skb, - struct netlink_callback *cb, - struct net_device *dev, - struct net_device *filter_dev, - int *idx) -{ - return *idx; -} - static inline bool switchdev_port_same_parent_id(struct net_device *a, struct net_device *b) { diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 9c890de6e4dd..1debf9c42fc4 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -133,8 +133,6 @@ int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, u16 vid); int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, u16 vid); -int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb); int dsa_port_mdb_add(struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb, struct switchdev_trans *trans); diff --git a/net/dsa/port.c b/net/dsa/port.c index 73787828953a..659676ba3f8b 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -173,17 +173,6 @@ int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); } -int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) -{ - struct dsa_switch *ds = dp->ds; - - if (ds->ops->port_fdb_dump) - return ds->ops->port_fdb_dump(ds, dp->index, fdb, cb); - - return -EOPNOTSUPP; -} - int dsa_port_mdb_add(struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb, struct switchdev_trans *trans) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5807c905fd1d..8c79011c5a83 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -199,6 +199,83 @@ out: return 0; } +struct dsa_slave_dump_ctx { + struct net_device *dev; + struct sk_buff *skb; + struct netlink_callback *cb; + int idx; +}; + +static int +dsa_slave_port_fdb_do_dump(const unsigned char *addr, u16 vid, + bool is_static, void *data) +{ + struct dsa_slave_dump_ctx *dump = data; + u32 portid = NETLINK_CB(dump->cb->skb).portid; + u32 seq = dump->cb->nlh->nlmsg_seq; + struct nlmsghdr *nlh; + struct ndmsg *ndm; + + if (dump->idx < dump->cb->args[2]) + goto skip; + + nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, + sizeof(*ndm), NLM_F_MULTI); + if (!nlh) + return -EMSGSIZE; + + ndm = nlmsg_data(nlh); + ndm->ndm_family = AF_BRIDGE; + ndm->ndm_pad1 = 0; + ndm->ndm_pad2 = 0; + ndm->ndm_flags = NTF_SELF; + ndm->ndm_type = 0; + ndm->ndm_ifindex = dump->dev->ifindex; + ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; + + if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr)) + goto nla_put_failure; + + if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid)) + goto nla_put_failure; + + nlmsg_end(dump->skb, nlh); + +skip: + dump->idx++; + return 0; + +nla_put_failure: + nlmsg_cancel(dump->skb, nlh); + return -EMSGSIZE; +} + +static int +dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, + struct net_device *dev, struct net_device *filter_dev, + int *idx) +{ + struct dsa_slave_dump_ctx dump = { + .dev = dev, + .skb = skb, + .cb = cb, + .idx = *idx, + }; + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_port *dp = p->dp; + struct dsa_switch *ds = dp->ds; + int err; + + if (!ds->ops->port_fdb_dump) + return -EOPNOTSUPP; + + err = ds->ops->port_fdb_dump(ds, dp->index, + dsa_slave_port_fdb_do_dump, + &dump); + *idx = dump.idx; + return err; +} + static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct dsa_slave_priv *p = netdev_priv(dev); @@ -287,26 +364,6 @@ static int dsa_slave_port_obj_del(struct net_device *dev, return err; } -static int dsa_slave_port_obj_dump(struct net_device *dev, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) -{ - struct dsa_slave_priv *p = netdev_priv(dev); - struct dsa_port *dp = p->dp; - int err; - - switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = dsa_port_fdb_dump(dp, SWITCHDEV_OBJ_PORT_FDB(obj), cb); - break; - default: - err = -EOPNOTSUPP; - break; - } - - return err; -} - static int dsa_slave_port_attr_get(struct net_device *dev, struct switchdev_attr *attr) { @@ -974,7 +1031,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_set_mac_address = dsa_slave_set_mac_address, .ndo_fdb_add = dsa_legacy_fdb_add, .ndo_fdb_del = dsa_legacy_fdb_del, - .ndo_fdb_dump = switchdev_port_fdb_dump, + .ndo_fdb_dump = dsa_slave_fdb_dump, .ndo_do_ioctl = dsa_slave_ioctl, .ndo_get_iflink = dsa_slave_get_iflink, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -992,7 +1049,6 @@ static const struct switchdev_ops dsa_slave_switchdev_ops = { .switchdev_port_attr_set = dsa_slave_port_attr_set, .switchdev_port_obj_add = dsa_slave_port_obj_add, .switchdev_port_obj_del = dsa_slave_port_obj_del, - .switchdev_port_obj_dump = dsa_slave_port_obj_dump, }; static struct device_type dsa_type = { diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 25dc67ef9d37..3d32981b9aa1 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -1009,90 +1009,6 @@ int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], } EXPORT_SYMBOL_GPL(switchdev_port_fdb_del); -struct switchdev_fdb_dump { - struct switchdev_obj_port_fdb fdb; - struct net_device *dev; - struct sk_buff *skb; - struct netlink_callback *cb; - int idx; -}; - -static int switchdev_port_fdb_dump_cb(struct switchdev_obj *obj) -{ - struct switchdev_obj_port_fdb *fdb = SWITCHDEV_OBJ_PORT_FDB(obj); - struct switchdev_fdb_dump *dump = - container_of(fdb, struct switchdev_fdb_dump, fdb); - u32 portid = NETLINK_CB(dump->cb->skb).portid; - u32 seq = dump->cb->nlh->nlmsg_seq; - struct nlmsghdr *nlh; - struct ndmsg *ndm; - - if (dump->idx < dump->cb->args[2]) - goto skip; - - nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, - sizeof(*ndm), NLM_F_MULTI); - if (!nlh) - return -EMSGSIZE; - - ndm = nlmsg_data(nlh); - ndm->ndm_family = AF_BRIDGE; - ndm->ndm_pad1 = 0; - ndm->ndm_pad2 = 0; - ndm->ndm_flags = NTF_SELF; - ndm->ndm_type = 0; - ndm->ndm_ifindex = dump->dev->ifindex; - ndm->ndm_state = fdb->ndm_state; - - if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, fdb->addr)) - goto nla_put_failure; - - if (fdb->vid && nla_put_u16(dump->skb, NDA_VLAN, fdb->vid)) - goto nla_put_failure; - - nlmsg_end(dump->skb, nlh); - -skip: - dump->idx++; - return 0; - -nla_put_failure: - nlmsg_cancel(dump->skb, nlh); - return -EMSGSIZE; -} - -/** - * switchdev_port_fdb_dump - Dump port FDB (MAC/VLAN) entries - * - * @skb: netlink skb - * @cb: netlink callback - * @dev: port device - * @filter_dev: filter device - * @idx: - * - * Dump FDB entries from switch device. - */ -int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct net_device *dev, - struct net_device *filter_dev, int *idx) -{ - struct switchdev_fdb_dump dump = { - .fdb.obj.orig_dev = dev, - .fdb.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB, - .dev = dev, - .skb = skb, - .cb = cb, - .idx = *idx, - }; - int err; - - err = switchdev_port_obj_dump(dev, &dump.fdb.obj, - switchdev_port_fdb_dump_cb); - *idx = dump.idx; - return err; -} -EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump); - bool switchdev_port_same_parent_id(struct net_device *a, struct net_device *b) { -- cgit v1.2.3 From 53b948356554376ec6f89016376825d48bf396c3 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 7 Aug 2017 10:08:10 -0700 Subject: net: vrf: Add extack messages for newlink failures Add extack error messages for failure paths creating vrf devices. Once extack support is added to iproute2, we go from the unhelpful: $ ip li add foobar type vrf RTNETLINK answers: Invalid argument to: $ ip li add foobar type vrf Error: VRF table id is missing Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/vrf.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 8a1eaf3c302a..abd2010c48ae 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -1371,10 +1371,14 @@ static int vrf_validate(struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { if (tb[IFLA_ADDRESS]) { - if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) { + NL_SET_ERR_MSG(extack, "Invalid hardware address"); return -EINVAL; - if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + } + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) { + NL_SET_ERR_MSG(extack, "Invalid hardware address"); return -EADDRNOTAVAIL; + } } return 0; } @@ -1399,12 +1403,17 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, struct net *net; int err; - if (!data || !data[IFLA_VRF_TABLE]) + if (!data || !data[IFLA_VRF_TABLE]) { + NL_SET_ERR_MSG(extack, "VRF table id is missing"); return -EINVAL; + } vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]); - if (vrf->tb_id == RT_TABLE_UNSPEC) + if (vrf->tb_id == RT_TABLE_UNSPEC) { + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_VRF_TABLE], + "Invalid VRF table id"); return -EINVAL; + } dev->priv_flags |= IFF_L3MDEV_MASTER; -- cgit v1.2.3 From 984eb90532804176a57a18ffc1b194aee350c4d3 Mon Sep 17 00:00:00 2001 From: Gabriel Craciunescu Date: Thu, 3 Aug 2017 15:01:02 +0300 Subject: ath10k: ath10k_htt_rx_amsdu_allowed() use ath10k_dbg() Each time we get disconnected from AP we get flooded with messages like: ... ath10k_pci 0000:03:00.0: no channel configured; ignoring frame(s)! ath10k_warn: 155 callbacks suppressed ... Use ath10k_dbg() here too. Signed-off-by: Gabriel Craciunescu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 799fb7501eb5..9323be663179 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1524,7 +1524,7 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar, */ if (!rx_status->freq) { - ath10k_warn(ar, "no channel configured; ignoring frame(s)!\n"); + ath10k_dbg(ar, ATH10K_DBG_HTT, "no channel configured; ignoring frame(s)!\n"); return false; } -- cgit v1.2.3 From f35a7f91f66af528b3ee1921de16bea31d347ab0 Mon Sep 17 00:00:00 2001 From: Rakesh Pillai Date: Wed, 2 Aug 2017 16:03:37 +0530 Subject: ath10k: fix memory leak in rx ring buffer allocation The rx ring buffers are added to a hash table if firmware support full rx reorder. If the full rx reorder support flag is not set before allocating the rx ring buffers, none of the buffers are added to the hash table. There is a race condition between rx ring refill and rx buffer replenish from napi poll. The interrupts are enabled in hif start, before the rx ring is refilled during init. We replenish buffers from napi poll due to the interrupts which get enabled after hif start. Hence before the entire rx ring is refilled during the init, the napi poll replenishes a few buffers in steps of 100 buffers per attempt. During this rx ring replenish from napi poll, the rx reorder flag has not been set due to which the replenished buffers are not added to the hash table Set the rx full reorder support flag before we allocate the rx ring buffer to avoid the memory leak. Signed-off-by: Rakesh Pillai Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 3602aa462662..9b4c4facf725 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2057,6 +2057,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, goto err_wmi_detach; } + /* If firmware indicates Full Rx Reorder support it must be used in a + * slightly different manner. Let HTT code know. + */ + ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER, + ar->wmi.svc_map)); + status = ath10k_htt_rx_alloc(&ar->htt); if (status) { ath10k_err(ar, "failed to alloc htt rx: %d\n", status); @@ -2177,12 +2183,6 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, } } - /* If firmware indicates Full Rx Reorder support it must be used in a - * slightly different manner. Let HTT code know. - */ - ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER, - ar->wmi.svc_map)); - status = ath10k_htt_rx_ring_refill(ar); if (status) { ath10k_err(ar, "failed to refill htt rx ring: %d\n", status); -- cgit v1.2.3 From ab3f9c8868654adc836cd6d726e202f18776331a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 2 Aug 2017 16:57:25 +0300 Subject: ath10k: switch to use new generic UUID API There are new types and helpers that are supposed to be used in new code. As a preparation to get rid of legacy types and API functions do the conversion here. Signed-off-by: Andy Shevchenko Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 2 +- drivers/net/wireless/ath/ath10k/debug.c | 6 +++--- drivers/net/wireless/ath/ath10k/pci.c | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 34b713c5e022..949ebb3e967b 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -462,7 +462,7 @@ struct ath10k_ce_crash_hdr { struct ath10k_fw_crash_data { bool crashed_since_read; - uuid_le uuid; + guid_t guid; struct timespec timestamp; __le32 registers[REG_DUMP_COUNT_QCA988X]; struct ath10k_ce_crash_data ce_crash_data[CE_COUNT_MAX]; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 56404fe4e8f5..df514507d3f1 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -70,7 +70,7 @@ struct ath10k_dump_file_data { /* some info we can get from ath10k struct that might help */ - u8 uuid[16]; + guid_t guid; __le32 chip_id; @@ -719,7 +719,7 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar) lockdep_assert_held(&ar->data_lock); crash_data->crashed_since_read = true; - uuid_le_gen(&crash_data->uuid); + guid_gen(&crash_data->guid); getnstimeofday(&crash_data->timestamp); return crash_data; @@ -766,7 +766,7 @@ static struct ath10k_dump_file_data *ath10k_build_dump_file(struct ath10k *ar, dump_data->version = cpu_to_le32(ATH10K_FW_CRASH_DUMP_VERSION); - memcpy(dump_data->uuid, &crash_data->uuid, sizeof(dump_data->uuid)); + guid_copy(&dump_data->guid, &crash_data->guid); dump_data->chip_id = cpu_to_le32(ar->chip_id); dump_data->bus_type = cpu_to_le32(0); dump_data->target_version = cpu_to_le32(ar->target_version); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index a697caec6579..0a5b88e80214 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1463,7 +1463,7 @@ static void ath10k_pci_dump_registers(struct ath10k *ar, static void ath10k_pci_fw_crashed_dump(struct ath10k *ar) { struct ath10k_fw_crash_data *crash_data; - char uuid[50]; + char guid[UUID_STRING_LEN + 1]; spin_lock_bh(&ar->data_lock); @@ -1472,11 +1472,11 @@ static void ath10k_pci_fw_crashed_dump(struct ath10k *ar) crash_data = ath10k_debug_get_new_fw_crash_data(ar); if (crash_data) - scnprintf(uuid, sizeof(uuid), "%pUl", &crash_data->uuid); + scnprintf(guid, sizeof(guid), "%pUl", &crash_data->guid); else - scnprintf(uuid, sizeof(uuid), "n/a"); + scnprintf(guid, sizeof(guid), "n/a"); - ath10k_err(ar, "firmware crashed! (uuid %s)\n", uuid); + ath10k_err(ar, "firmware crashed! (guid %s)\n", guid); ath10k_print_driver_info(ar); ath10k_pci_dump_registers(ar, crash_data); ath10k_ce_dump_registers(ar, crash_data); -- cgit v1.2.3 From 39efc7cc7ccf82d1cd946580cdb70760f347305a Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 2 Aug 2017 18:28:00 -0700 Subject: wcn36xx: Introduce mutual exclusion of fw configuration As the association status changes the driver needs to configure the hardware. This is done based on information in the "sta" acquired by ieee80211_find_sta(), which requires the caller to ensure that the "sta" is valid while its being used; generally by entering an rcu read section. But the operations acting on the "sta" has to communicate with the firmware and may therefor sleep, resulting in the following report: [ 31.418190] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:238 [ 31.425919] in_atomic(): 0, irqs_disabled(): 0, pid: 34, name: kworker/u8:1 [ 31.434609] CPU: 0 PID: 34 Comm: kworker/u8:1 Tainted: G W 4.12.0-rc4-next-20170607+ #993 [ 31.441002] Hardware name: Qualcomm Technologies, Inc. APQ 8016 SBC (DT) [ 31.450380] Workqueue: phy0 ieee80211_iface_work [ 31.457226] Call trace: [ 31.461830] [] dump_backtrace+0x0/0x260 [ 31.464004] [] show_stack+0x14/0x20 [ 31.469557] [] dump_stack+0x98/0xb8 [ 31.474592] [] ___might_sleep+0xf0/0x118 [ 31.479626] [] __might_sleep+0x50/0x88 [ 31.485010] [] mutex_lock+0x24/0x60 [ 31.490479] [] wcn36xx_smd_set_link_st+0x30/0x130 [ 31.495428] [] wcn36xx_bss_info_changed+0x148/0x448 [ 31.501504] [] ieee80211_bss_info_change_notify+0xbc/0x118 [ 31.508102] [] ieee80211_assoc_success+0x664/0x7f8 [ 31.515220] [] ieee80211_rx_mgmt_assoc_resp+0x144/0x2d8 [ 31.521555] [] ieee80211_sta_rx_queued_mgmt+0x190/0x698 [ 31.528239] [] ieee80211_iface_work+0x234/0x368 [ 31.535011] [] process_one_work+0x1cc/0x340 [ 31.541086] [] worker_thread+0x48/0x430 [ 31.546814] [] kthread+0x108/0x138 [ 31.552195] [] ret_from_fork+0x10/0x50 In order to ensure that the "sta" remains alive (and consistent) for the duration of bss_info_changed() mutual exclusion has to be ensured with sta_remove(). This is done by introducing a mutex to cover firmware configuration changes, which is made to also ensure mutual exclusion between other operations changing the state or configuration of the firmware. With this we can drop the rcu read lock. Cc: stable@vger.kernel.org Signed-off-by: Bjorn Andersson Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/main.c | 52 ++++++++++++++++++++++++++++-- drivers/net/wireless/ath/wcn36xx/wcn36xx.h | 3 ++ 2 files changed, 53 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 517a315e259b..35bd50bcbbd5 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -372,6 +372,8 @@ static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed); + mutex_lock(&wcn->conf_mutex); + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { int ch = WCN36XX_HW_CHANNEL(wcn); wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n", @@ -382,6 +384,8 @@ static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) } } + mutex_unlock(&wcn->conf_mutex); + return 0; } @@ -396,6 +400,8 @@ static void wcn36xx_configure_filter(struct ieee80211_hw *hw, wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n"); + mutex_lock(&wcn->conf_mutex); + *total &= FIF_ALLMULTI; fp = (void *)(unsigned long)multicast; @@ -408,6 +414,8 @@ static void wcn36xx_configure_filter(struct ieee80211_hw *hw, else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc) wcn36xx_smd_set_mc_list(wcn, vif, fp); } + + mutex_unlock(&wcn->conf_mutex); kfree(fp); } @@ -471,6 +479,8 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, key_conf->key, key_conf->keylen); + mutex_lock(&wcn->conf_mutex); + switch (key_conf->cipher) { case WLAN_CIPHER_SUITE_WEP40: vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; @@ -565,6 +575,8 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } out: + mutex_unlock(&wcn->conf_mutex); + return ret; } @@ -725,6 +737,8 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n", vif, changed); + mutex_lock(&wcn->conf_mutex); + if (changed & BSS_CHANGED_BEACON_INFO) { wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed dtim period %d\n", @@ -787,7 +801,13 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, bss_conf->aid); vif_priv->sta_assoc = true; - rcu_read_lock(); + + /* + * Holding conf_mutex ensures mutal exclusion with + * wcn36xx_sta_remove() and as such ensures that sta + * won't be freed while we're operating on it. As such + * we do not need to hold the rcu_read_lock(). + */ sta = ieee80211_find_sta(vif, bss_conf->bssid); if (!sta) { wcn36xx_err("sta %pM is not found\n", @@ -811,7 +831,6 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, * place where AID is available. */ wcn36xx_smd_config_sta(wcn, vif, sta); - rcu_read_unlock(); } else { wcn36xx_dbg(WCN36XX_DBG_MAC, "disassociated bss %pM vif %pM AID=%d\n", @@ -873,6 +892,9 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, } } out: + + mutex_unlock(&wcn->conf_mutex); + return; } @@ -882,7 +904,10 @@ static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) struct wcn36xx *wcn = hw->priv; wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value); + mutex_lock(&wcn->conf_mutex); wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value); + mutex_unlock(&wcn->conf_mutex); + return 0; } @@ -893,8 +918,12 @@ static void wcn36xx_remove_interface(struct ieee80211_hw *hw, struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif); + mutex_lock(&wcn->conf_mutex); + list_del(&vif_priv->list); wcn36xx_smd_delete_sta_self(wcn, vif->addr); + + mutex_unlock(&wcn->conf_mutex); } static int wcn36xx_add_interface(struct ieee80211_hw *hw, @@ -915,9 +944,13 @@ static int wcn36xx_add_interface(struct ieee80211_hw *hw, return -EOPNOTSUPP; } + mutex_lock(&wcn->conf_mutex); + list_add(&vif_priv->list, &wcn->vif_list); wcn36xx_smd_add_sta_self(wcn, vif); + mutex_unlock(&wcn->conf_mutex); + return 0; } @@ -930,6 +963,8 @@ static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n", vif, sta->addr); + mutex_lock(&wcn->conf_mutex); + spin_lock_init(&sta_priv->ampdu_lock); sta_priv->vif = vif_priv; /* @@ -941,6 +976,9 @@ static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, sta_priv->aid = sta->aid; wcn36xx_smd_config_sta(wcn, vif, sta); } + + mutex_unlock(&wcn->conf_mutex); + return 0; } @@ -954,8 +992,13 @@ static int wcn36xx_sta_remove(struct ieee80211_hw *hw, wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n", vif, sta->addr, sta_priv->sta_index); + mutex_lock(&wcn->conf_mutex); + wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index); sta_priv->vif = NULL; + + mutex_unlock(&wcn->conf_mutex); + return 0; } @@ -999,6 +1042,8 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", action, tid); + mutex_lock(&wcn->conf_mutex); + switch (action) { case IEEE80211_AMPDU_RX_START: sta_priv->tid = tid; @@ -1038,6 +1083,8 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, wcn36xx_err("Unknown AMPDU action\n"); } + mutex_unlock(&wcn->conf_mutex); + return 0; } @@ -1216,6 +1263,7 @@ static int wcn36xx_probe(struct platform_device *pdev) wcn = hw->priv; wcn->hw = hw; wcn->dev = &pdev->dev; + mutex_init(&wcn->conf_mutex); mutex_init(&wcn->hal_mutex); mutex_init(&wcn->scan_lock); diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index b52b4da9a967..6aefba4c0cda 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -202,6 +202,9 @@ struct wcn36xx { struct qcom_smem_state *tx_rings_empty_state; unsigned tx_rings_empty_state_bit; + /* prevents concurrent FW reconfiguration */ + struct mutex conf_mutex; + /* * smd_buf must be protected with smd_mutex to garantee * that all messages are sent one after another -- cgit v1.2.3 From 3588e1110e49fdad7fc8898420a9c71b34a3ca04 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Thu, 3 Aug 2017 20:25:31 +0530 Subject: ath9k: make ath_ps_ops structures as const ath_ps_ops structures are only stored as a reference in the ps_ops field of a ath_common structure. This field is of type const, so make the structures as const. Signed-off-by: Bhumika Goyal Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 2 +- drivers/net/wireless/ath/ath9k/init.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index defacc6c9c99..da2164b0cccc 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -71,7 +71,7 @@ static void ath9k_htc_op_ps_restore(struct ath_common *common) ath9k_htc_ps_restore((struct ath9k_htc_priv *) common->priv); } -static struct ath_ps_ops ath9k_htc_ps_ops = { +static const struct ath_ps_ops ath9k_htc_ps_ops = { .wakeup = ath9k_htc_op_ps_wakeup, .restore = ath9k_htc_op_ps_restore, }; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index fd9a61834c17..bb7936090b91 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -104,7 +104,7 @@ static void ath9k_op_ps_restore(struct ath_common *common) ath9k_ps_restore((struct ath_softc *) common->priv); } -static struct ath_ps_ops ath9k_ps_ops = { +static const struct ath_ps_ops ath9k_ps_ops = { .wakeup = ath9k_op_ps_wakeup, .restore = ath9k_op_ps_restore, }; -- cgit v1.2.3 From 6345016d4bea6a82300c209d99e7f593800e3b4e Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Thu, 3 Aug 2017 19:56:39 +0530 Subject: rsi: fix uninitialized descriptor pointer issue This patch fixes the uninitialized descriptor pointer issue in function rsi_send_internal_mgmt_frame(). Descriptor should point to start of the skb data. Reported-by: Dan Carpenter Fixes: 9a629fafe7d8 ("rsi: immediate wakeup bit and priority for TX command packets") Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 1fba7bba3a10..c488f4b44576 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -274,6 +274,7 @@ static int rsi_send_internal_mgmt_frame(struct rsi_common *common, rsi_dbg(ERR_ZONE, "%s: Unable to allocate skb\n", __func__); return -ENOMEM; } + desc = (struct rsi_cmd_desc *)skb->data; desc->desc_dword0.len_qno |= cpu_to_le16(DESC_IMMEDIATE_WAKEUP); skb->priority = MGMT_SOFT_Q; tx_params = (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data; -- cgit v1.2.3 From edba3532c6522302e1cfa9625e096d9e6fbb4d36 Mon Sep 17 00:00:00 2001 From: Pavani Muthyala Date: Thu, 3 Aug 2017 19:58:57 +0530 Subject: rsi: add support for rf-kill functionality This patch implements rfkill_poll handler. Also, necessary changes are done in interface up and down handler to support rfkill functionality. Signed-off-by: Pavani Muthyala Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index c91d6efa7c84..193f9227fdb3 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -279,11 +279,12 @@ static int rsi_mac80211_start(struct ieee80211_hw *hw) struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + rsi_dbg(ERR_ZONE, "===> Interface UP <===\n"); mutex_lock(&common->mutex); common->iface_down = false; - mutex_unlock(&common->mutex); - + wiphy_rfkill_start_polling(hw->wiphy); rsi_send_rx_filter_frame(common, 0); + mutex_unlock(&common->mutex); return 0; } @@ -299,8 +300,10 @@ static void rsi_mac80211_stop(struct ieee80211_hw *hw) struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + rsi_dbg(ERR_ZONE, "===> Interface DOWN <===\n"); mutex_lock(&common->mutex); common->iface_down = true; + wiphy_rfkill_stop_polling(hw->wiphy); /* Block all rx frames */ rsi_send_rx_filter_frame(common, 0xffff); @@ -1214,6 +1217,19 @@ static void rsi_reg_notify(struct wiphy *wiphy, mutex_unlock(&common->mutex); } +static void rsi_mac80211_rfkill_poll(struct ieee80211_hw *hw) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + if (common->fsm_state != FSM_MAC_INIT_DONE) + wiphy_rfkill_set_hw_state(hw->wiphy, true); + else + wiphy_rfkill_set_hw_state(hw->wiphy, false); + mutex_unlock(&common->mutex); +} + static struct ieee80211_ops mac80211_ops = { .tx = rsi_mac80211_tx, .start = rsi_mac80211_start, @@ -1232,6 +1248,7 @@ static struct ieee80211_ops mac80211_ops = { .sta_remove = rsi_mac80211_sta_remove, .set_antenna = rsi_mac80211_set_antenna, .get_antenna = rsi_mac80211_get_antenna, + .rfkill_poll = rsi_mac80211_rfkill_poll, }; /** -- cgit v1.2.3 From 588349a1fe3b1983ecdda33a0cc1b87076eea033 Mon Sep 17 00:00:00 2001 From: Pavani Muthyala Date: Thu, 3 Aug 2017 19:58:58 +0530 Subject: rsi: update set_antenna command frame TX command frame set_antenna is modified to use common descriptor structure. Also it's subframe type is set. Signed-off-by: Pavani Muthyala Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 13 +++++++------ drivers/net/wireless/rsi/rsi_mgmt.h | 11 +++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index c488f4b44576..e00d4edec2ba 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1407,7 +1407,7 @@ int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word) */ int rsi_set_antenna(struct rsi_common *common, u8 antenna) { - struct rsi_mac_frame *cmd_frame; + struct rsi_ant_sel_frame *ant_sel_frame; struct sk_buff *skb; skb = dev_alloc_skb(FRAME_DESC_SZ); @@ -1418,12 +1418,13 @@ int rsi_set_antenna(struct rsi_common *common, u8 antenna) } memset(skb->data, 0, FRAME_DESC_SZ); - cmd_frame = (struct rsi_mac_frame *)skb->data; - - cmd_frame->desc_word[1] = cpu_to_le16(ANT_SEL_FRAME); - cmd_frame->desc_word[3] = cpu_to_le16(antenna & 0x00ff); - cmd_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + ant_sel_frame = (struct rsi_ant_sel_frame *)skb->data; + ant_sel_frame->desc_dword0.frame_type = ANT_SEL_FRAME; + ant_sel_frame->sub_frame_type = ANTENNA_SEL_TYPE; + ant_sel_frame->ant_value = cpu_to_le16(antenna & ANTENNA_MASK_VALUE); + rsi_set_len_qno(&ant_sel_frame->desc_dword0.len_qno, + 0, RSI_WIFI_MGMT_Q); skb_put(skb, FRAME_DESC_SZ); return rsi_send_internal_mgmt_frame(common, skb); diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index cb0b17ec48d0..1060edcb2a96 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -155,6 +155,8 @@ #define ANTENNA_SEL_INT 0x02 /* RF_OUT_2 / Integerated */ #define ANTENNA_SEL_UFL 0x03 /* RF_OUT_1 / U.FL */ +#define ANTENNA_MASK_VALUE 0x00ff +#define ANTENNA_SEL_TYPE 1 /* Rx filter word definitions */ #define PROMISCOUS_MODE BIT(0) @@ -348,6 +350,15 @@ struct rsi_vap_caps { __le16 beacon_miss_threshold; } __packed; +struct rsi_ant_sel_frame { + struct rsi_cmd_desc_dword0 desc_dword0; + u8 reserved; + u8 sub_frame_type; + __le16 ant_value; + __le32 reserved1; + __le32 reserved2; +} __packed; + /* Key descriptor flags */ #define RSI_KEY_TYPE_BROADCAST BIT(1) #define RSI_WEP_KEY BIT(2) -- cgit v1.2.3 From ce86893fa8d8509d69bef70170ed8c797275c411 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 3 Aug 2017 19:58:59 +0530 Subject: rsi: add support for legacy power save This patch adds support for legacy power save. Necessary configuration frames are downloaded to firmware when power save is enabled/disabled Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/Makefile | 1 + drivers/net/wireless/rsi/rsi_91x_hal.c | 7 ++ drivers/net/wireless/rsi/rsi_91x_mac80211.c | 22 +++++ drivers/net/wireless/rsi/rsi_91x_main.c | 2 + drivers/net/wireless/rsi/rsi_91x_mgmt.c | 57 +++++++++++- drivers/net/wireless/rsi/rsi_91x_ps.c | 129 ++++++++++++++++++++++++++++ drivers/net/wireless/rsi/rsi_main.h | 9 +- drivers/net/wireless/rsi/rsi_mgmt.h | 21 +++++ drivers/net/wireless/rsi/rsi_ps.h | 64 ++++++++++++++ 9 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 drivers/net/wireless/rsi/rsi_91x_ps.c create mode 100644 drivers/net/wireless/rsi/rsi_ps.h (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/Makefile b/drivers/net/wireless/rsi/Makefile index a475c813674a..ebb89965997a 100644 --- a/drivers/net/wireless/rsi/Makefile +++ b/drivers/net/wireless/rsi/Makefile @@ -3,6 +3,7 @@ rsi_91x-y += rsi_91x_core.o rsi_91x-y += rsi_91x_mac80211.o rsi_91x-y += rsi_91x_mgmt.o rsi_91x-y += rsi_91x_hal.o +rsi_91x-y += rsi_91x_ps.o rsi_91x-$(CONFIG_RSI_DEBUGFS) += rsi_91x_debugfs.o rsi_usb-y += rsi_91x_usb.o rsi_91x_usb_ops.o diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index b0a7a1511aee..4addcc0826db 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -111,6 +111,8 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) /* This function prepares descriptor for given data packet */ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) { + struct rsi_hw *adapter = common->priv; + struct ieee80211_vif *vif; struct ieee80211_hdr *wh = NULL; struct ieee80211_tx_info *info; struct skb_info *tx_params; @@ -148,6 +150,7 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) xtend_desc = (struct xtended_desc *)&skb->data[FRAME_DESC_SZ]; wh = (struct ieee80211_hdr *)&skb->data[header_size]; seq_num = (le16_to_cpu(wh->seq_ctrl) >> 4); + vif = adapter->vifs[0]; data_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; @@ -156,6 +159,10 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) data_desc->mac_flags |= cpu_to_le16(RSI_QOS_ENABLE); } + if ((vif->type == NL80211_IFTYPE_STATION) && + (adapter->ps_state == PS_ENABLED)) + wh->frame_control |= cpu_to_le16(RSI_SET_PS_ENABLE); + if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) && (common->secinfo.security_enable)) { if (rsi_is_cipher_wep(common)) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 193f9227fdb3..16a0fd0f519a 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -18,6 +18,7 @@ #include "rsi_debugfs.h" #include "rsi_mgmt.h" #include "rsi_common.h" +#include "rsi_ps.h" static const struct ieee80211_channel rsi_2ghz_channels[] = { { .band = NL80211_BAND_2GHZ, .center_freq = 2412, @@ -467,6 +468,8 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw, { struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + struct ieee80211_vif *vif = adapter->vifs[0]; + struct ieee80211_conf *conf = &hw->conf; int status = -EOPNOTSUPP; mutex_lock(&common->mutex); @@ -480,6 +483,19 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw, status = rsi_config_power(hw); } + /* Power save parameters */ + if ((changed & IEEE80211_CONF_CHANGE_PS) && + (vif->type == NL80211_IFTYPE_STATION)) { + unsigned long flags; + + spin_lock_irqsave(&adapter->ps_lock, flags); + if (conf->flags & IEEE80211_CONF_PS) + rsi_enable_ps(adapter); + else + rsi_disable_ps(adapter); + spin_unlock_irqrestore(&adapter->ps_lock, flags); + } + mutex_unlock(&common->mutex); return status; @@ -522,6 +538,8 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw, { struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + struct ieee80211_bss_conf *bss = &vif->bss_conf; + struct ieee80211_conf *conf = &hw->conf; u16 rx_filter_word = 0; mutex_lock(&common->mutex); @@ -540,6 +558,8 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw, bss_conf->bssid, bss_conf->qos, bss_conf->aid); + adapter->ps_info.dtim_interval_duration = bss->dtim_period; + adapter->ps_info.listen_interval = conf->listen_interval; } if (changed & BSS_CHANGED_CQM) { @@ -1283,6 +1303,8 @@ int rsi_mac80211_attach(struct rsi_common *common) ieee80211_hw_set(hw, SIGNAL_DBM); ieee80211_hw_set(hw, HAS_RATE_CONTROL); ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); hw->queues = MAX_HW_QUEUES; hw->extra_tx_headroom = RSI_NEEDED_HEADROOM; diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c index bb0febb17be0..3e1e80888d98 100644 --- a/drivers/net/wireless/rsi/rsi_91x_main.c +++ b/drivers/net/wireless/rsi/rsi_91x_main.c @@ -231,6 +231,8 @@ struct rsi_hw *rsi_91x_init(void) goto err; } + rsi_default_ps_params(adapter); + spin_lock_init(&adapter->ps_lock); common->init_done = true; return adapter; diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index e00d4edec2ba..f76b34679df8 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -17,6 +17,7 @@ #include #include "rsi_mgmt.h" #include "rsi_common.h" +#include "rsi_ps.h" static struct bootup_params boot_params_20 = { .magic_number = cpu_to_le16(0x5aa5), @@ -1396,6 +1397,58 @@ int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word) return rsi_send_internal_mgmt_frame(common, skb); } +int rsi_send_ps_request(struct rsi_hw *adapter, bool enable) +{ + struct rsi_common *common = adapter->priv; + struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf; + struct rsi_request_ps *ps; + struct rsi_ps_info *ps_info; + struct sk_buff *skb; + int frame_len = sizeof(*ps); + + skb = dev_alloc_skb(frame_len); + if (!skb) + return -ENOMEM; + memset(skb->data, 0, frame_len); + + ps = (struct rsi_request_ps *)skb->data; + ps_info = &adapter->ps_info; + + rsi_set_len_qno(&ps->desc.desc_dword0.len_qno, + (frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q); + ps->desc.desc_dword0.frame_type = WAKEUP_SLEEP_REQUEST; + if (enable) { + ps->ps_sleep.enable = RSI_PS_ENABLE; + ps->desc.desc_dword3.token = cpu_to_le16(RSI_SLEEP_REQUEST); + } else { + ps->ps_sleep.enable = RSI_PS_DISABLE; + ps->desc.desc_dword0.len_qno |= cpu_to_le16(RSI_PS_DISABLE_IND); + ps->desc.desc_dword3.token = cpu_to_le16(RSI_WAKEUP_REQUEST); + } + ps->ps_sleep.sleep_type = ps_info->sleep_type; + ps->ps_sleep.num_bcns_per_lis_int = + cpu_to_le16(ps_info->num_bcns_per_lis_int); + ps->ps_sleep.sleep_duration = + cpu_to_le32(ps_info->deep_sleep_wakeup_period); + + if (bss->assoc) + ps->ps_sleep.connected_sleep = RSI_CONNECTED_SLEEP; + else + ps->ps_sleep.connected_sleep = RSI_DEEP_SLEEP; + + ps->ps_listen_interval = cpu_to_le32(ps_info->listen_interval); + ps->ps_dtim_interval_duration = + cpu_to_le32(ps_info->dtim_interval_duration); + + if (ps_info->listen_interval > ps_info->dtim_interval_duration) + ps->ps_listen_interval = cpu_to_le32(RSI_PS_DISABLE); + + ps->ps_num_dtim_intervals = cpu_to_le16(ps_info->num_dtims_per_sleep); + skb_put(skb, frame_len); + + return rsi_send_internal_mgmt_frame(common, skb); +} + /** * rsi_set_antenna() - This fuction send antenna configuration request * to device @@ -1569,7 +1622,9 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common, return 0; } break; - + case WAKEUP_SLEEP_REQUEST: + rsi_dbg(INFO_ZONE, "Wakeup/Sleep confirmation.\n"); + return rsi_handle_ps_confirm(adapter, msg); default: rsi_dbg(INFO_ZONE, "%s: Invalid TA confirm pkt received\n", __func__); diff --git a/drivers/net/wireless/rsi/rsi_91x_ps.c b/drivers/net/wireless/rsi/rsi_91x_ps.c new file mode 100644 index 000000000000..25e8f853837b --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_ps.c @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include "rsi_debugfs.h" +#include "rsi_mgmt.h" +#include "rsi_common.h" +#include "rsi_ps.h" + +char *str_psstate(enum ps_state state) +{ + switch (state) { + case PS_NONE: + return "PS_NONE"; + case PS_DISABLE_REQ_SENT: + return "PS_DISABLE_REQ_SENT"; + case PS_ENABLE_REQ_SENT: + return "PS_ENABLE_REQ_SENT"; + case PS_ENABLED: + return "PS_ENABLED"; + default: + return "INVALID_STATE"; + } + return "INVALID_STATE"; +} + +static inline void rsi_modify_ps_state(struct rsi_hw *adapter, + enum ps_state nstate) +{ + rsi_dbg(INFO_ZONE, "PS state changed %s => %s\n", + str_psstate(adapter->ps_state), + str_psstate(nstate)); + + adapter->ps_state = nstate; +} + +void rsi_default_ps_params(struct rsi_hw *adapter) +{ + struct rsi_ps_info *ps_info = &adapter->ps_info; + + ps_info->enabled = true; + ps_info->sleep_type = RSI_SLEEP_TYPE_LP; + ps_info->tx_threshold = 0; + ps_info->rx_threshold = 0; + ps_info->tx_hysterisis = 0; + ps_info->rx_hysterisis = 0; + ps_info->monitor_interval = 0; + ps_info->listen_interval = RSI_DEF_LISTEN_INTERVAL; + ps_info->num_bcns_per_lis_int = 0; + ps_info->dtim_interval_duration = 0; + ps_info->num_dtims_per_sleep = 0; + ps_info->deep_sleep_wakeup_period = RSI_DEF_DS_WAKEUP_PERIOD; +} + +void rsi_enable_ps(struct rsi_hw *adapter) +{ + if (adapter->ps_state != PS_NONE) { + rsi_dbg(ERR_ZONE, + "%s: Cannot accept enable PS in %s state\n", + __func__, str_psstate(adapter->ps_state)); + return; + } + + if (rsi_send_ps_request(adapter, true)) { + rsi_dbg(ERR_ZONE, + "%s: Failed to send PS request to device\n", + __func__); + return; + } + + rsi_modify_ps_state(adapter, PS_ENABLE_REQ_SENT); +} + +void rsi_disable_ps(struct rsi_hw *adapter) +{ + if (adapter->ps_state != PS_ENABLED) { + rsi_dbg(ERR_ZONE, + "%s: Cannot accept disable PS in %s state\n", + __func__, str_psstate(adapter->ps_state)); + return; + } + + if (rsi_send_ps_request(adapter, false)) { + rsi_dbg(ERR_ZONE, + "%s: Failed to send PS request to device\n", + __func__); + return; + } + + rsi_modify_ps_state(adapter, PS_DISABLE_REQ_SENT); +} + +int rsi_handle_ps_confirm(struct rsi_hw *adapter, u8 *msg) +{ + u16 cfm_type = get_unaligned_le16(msg + PS_CONFIRM_INDEX); + + switch (cfm_type) { + case RSI_SLEEP_REQUEST: + if (adapter->ps_state == PS_ENABLE_REQ_SENT) + rsi_modify_ps_state(adapter, PS_ENABLED); + break; + case RSI_WAKEUP_REQUEST: + if (adapter->ps_state == PS_DISABLE_REQ_SENT) + rsi_modify_ps_state(adapter, PS_NONE); + break; + default: + rsi_dbg(ERR_ZONE, + "Invalid PS confirm type %x in state %s\n", + cfm_type, str_psstate(adapter->ps_state)); + return -1; + } + + return 0; +} diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 6a8e8e7ed1fb..9aada0b73108 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -21,6 +21,10 @@ #include #include +struct rsi_hw; + +#include "rsi_ps.h" + #define ERR_ZONE BIT(0) /* For Error Msgs */ #define INFO_ZONE BIT(1) /* For General Status Msgs */ #define INIT_ZONE BIT(2) /* For Driver Init Seq Msgs */ @@ -177,8 +181,6 @@ enum rsi_dfs_regions { RSI_REGION_WORLD }; -struct rsi_hw; - struct rsi_common { struct rsi_hw *priv; struct vif_priv vif_info[RSI_MAX_VIFS]; @@ -282,6 +284,9 @@ struct rsi_hw { enum host_intf rsi_host_intf; u16 block_size; + enum ps_state ps_state; + struct rsi_ps_info ps_info; + spinlock_t ps_lock; /*To protect power save config*/ u32 usb_buffer_status_reg; #ifdef CONFIG_RSI_DEBUGFS struct rsi_debugfs *dfsentry; diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 1060edcb2a96..c5d114d67c83 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -69,6 +69,7 @@ #define RSI_QOS_ENABLE BIT(12) #define RSI_REKEY_PURPOSE BIT(13) #define RSI_ENCRYPT_PKT BIT(15) +#define RSI_SET_PS_ENABLE BIT(12) #define RSI_CMDDESC_40MHZ BIT(4) #define RSI_CMDDESC_UPPER_20_ENABLE BIT(5) @@ -172,6 +173,14 @@ #define RSI_BEACON_INTERVAL 200 #define RSI_DTIM_COUNT 2 +#define RSI_PS_DISABLE_IND BIT(15) +#define RSI_PS_ENABLE 1 +#define RSI_PS_DISABLE 0 +#define RSI_DEEP_SLEEP 1 +#define RSI_CONNECTED_SLEEP 2 +#define RSI_SLEEP_REQUEST 1 +#define RSI_WAKEUP_REQUEST 2 + enum opmode { STA_OPMODE = 1, AP_OPMODE = 2 @@ -519,6 +528,18 @@ struct rsi_eeprom_read_frame { __le16 reserved3; } __packed; +struct rsi_request_ps { + struct rsi_cmd_desc desc; + struct ps_sleep_params ps_sleep; + u8 ps_mimic_support; + u8 ps_uapsd_acs; + u8 ps_uapsd_wakeup_period; + u8 reserved; + __le32 ps_listen_interval; + __le32 ps_dtim_interval_duration; + __le16 ps_num_dtim_intervals; +} __packed; + static inline u32 rsi_get_queueno(u8 *addr, u16 offset) { return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12; diff --git a/drivers/net/wireless/rsi/rsi_ps.h b/drivers/net/wireless/rsi/rsi_ps.h new file mode 100644 index 000000000000..d8475873df36 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_ps.h @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2017 Redpine Signals Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __RSI_PS_H__ +#define __RSI_PS_H__ + +#define PS_CONFIRM_INDEX 12 +#define RSI_DEF_DS_WAKEUP_PERIOD 200 +#define RSI_DEF_LISTEN_INTERVAL 200 +#define RSI_SLEEP_TYPE_LP 1 + +enum ps_state { + PS_NONE = 0, + PS_ENABLE_REQ_SENT = 1, + PS_DISABLE_REQ_SENT = 2, + PS_ENABLED = 3 +}; + +struct ps_sleep_params { + u8 enable; + u8 sleep_type; + u8 connected_sleep; + u8 reserved1; + __le16 num_bcns_per_lis_int; + __le16 wakeup_type; + __le32 sleep_duration; +} __packed; + +struct rsi_ps_info { + u8 enabled; + u8 sleep_type; + u8 tx_threshold; + u8 rx_threshold; + u8 tx_hysterisis; + u8 rx_hysterisis; + u16 monitor_interval; + u32 listen_interval; + u16 num_bcns_per_lis_int; + u32 dtim_interval_duration; + u16 num_dtims_per_sleep; + u32 deep_sleep_wakeup_period; +} __packed; + +char *str_psstate(enum ps_state state); +void rsi_enable_ps(struct rsi_hw *adapter); +void rsi_disable_ps(struct rsi_hw *adapter); +int rsi_handle_ps_confirm(struct rsi_hw *adapter, u8 *msg); +void rsi_default_ps_params(struct rsi_hw *hw); +int rsi_send_ps_request(struct rsi_hw *adapter, bool enable); +void rsi_conf_uapsd(struct rsi_hw *adapter); +#endif -- cgit v1.2.3 From db07971d085fa637816ce029a5411f2ce83ee672 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 3 Aug 2017 19:59:00 +0530 Subject: rsi: add support for U-APSD power save This patch adds support for U-APSD power save. Configuration frame is downloaded to firmware with default settings and support is advertised to mac80211 Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 19 +++++++++++++++++++ drivers/net/wireless/rsi/rsi_91x_mgmt.c | 3 +++ drivers/net/wireless/rsi/rsi_91x_ps.c | 17 +++++++++++++++++ drivers/net/wireless/rsi/rsi_main.h | 1 + drivers/net/wireless/rsi/rsi_mgmt.h | 6 ++++++ 5 files changed, 46 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 16a0fd0f519a..6b833c424b18 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -327,6 +327,7 @@ static int rsi_mac80211_add_interface(struct ieee80211_hw *hw, struct rsi_common *common = adapter->priv; int ret = -EOPNOTSUPP; + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; mutex_lock(&common->mutex); switch (vif->type) { case NL80211_IFTYPE_STATION: @@ -560,6 +561,16 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw, bss_conf->aid); adapter->ps_info.dtim_interval_duration = bss->dtim_period; adapter->ps_info.listen_interval = conf->listen_interval; + + /* If U-APSD is updated, send ps parameters to firmware */ + if (bss->assoc) { + if (common->uapsd_bitmap) { + rsi_dbg(INFO_ZONE, "Configuring UAPSD\n"); + rsi_conf_uapsd(adapter); + } + } else { + common->uapsd_bitmap = 0; + } } if (changed & BSS_CHANGED_CQM) { @@ -641,6 +652,12 @@ static int rsi_mac80211_conf_tx(struct ieee80211_hw *hw, memcpy(&common->edca_params[idx], params, sizeof(struct ieee80211_tx_queue_params)); + + if (params->uapsd) + common->uapsd_bitmap |= idx; + else + common->uapsd_bitmap &= (~idx); + mutex_unlock(&common->mutex); return 0; @@ -1311,6 +1328,8 @@ int rsi_mac80211_attach(struct rsi_common *common) hw->max_rates = 1; hw->max_rate_tries = MAX_RETRIES; + hw->uapsd_queues = RSI_IEEE80211_UAPSD_QUEUES; + hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL; hw->max_tx_aggregation_subframes = 6; rsi_register_rates_channels(adapter, NL80211_BAND_2GHZ); diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index f76b34679df8..e5fe443ddfb6 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1425,6 +1425,9 @@ int rsi_send_ps_request(struct rsi_hw *adapter, bool enable) ps->desc.desc_dword0.len_qno |= cpu_to_le16(RSI_PS_DISABLE_IND); ps->desc.desc_dword3.token = cpu_to_le16(RSI_WAKEUP_REQUEST); } + + ps->ps_uapsd_acs = common->uapsd_bitmap; + ps->ps_sleep.sleep_type = ps_info->sleep_type; ps->ps_sleep.num_bcns_per_lis_int = cpu_to_le16(ps_info->num_bcns_per_lis_int); diff --git a/drivers/net/wireless/rsi/rsi_91x_ps.c b/drivers/net/wireless/rsi/rsi_91x_ps.c index 25e8f853837b..48c79f035c59 100644 --- a/drivers/net/wireless/rsi/rsi_91x_ps.c +++ b/drivers/net/wireless/rsi/rsi_91x_ps.c @@ -105,6 +105,22 @@ void rsi_disable_ps(struct rsi_hw *adapter) rsi_modify_ps_state(adapter, PS_DISABLE_REQ_SENT); } +void rsi_conf_uapsd(struct rsi_hw *adapter) +{ + int ret; + + if (adapter->ps_state != PS_ENABLED) + return; + + ret = rsi_send_ps_request(adapter, false); + if (!ret) + ret = rsi_send_ps_request(adapter, true); + if (ret) + rsi_dbg(ERR_ZONE, + "%s: Failed to send PS request to device\n", + __func__); +} + int rsi_handle_ps_confirm(struct rsi_hw *adapter, u8 *msg) { u16 cfm_type = get_unaligned_le16(msg + PS_CONFIRM_INDEX); @@ -127,3 +143,4 @@ int rsi_handle_ps_confirm(struct rsi_hw *adapter, u8 *msg) return 0; } + diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 9aada0b73108..d2cc47e98639 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -243,6 +243,7 @@ struct rsi_common { u16 oper_mode; u8 lp_ps_handshake_mode; u8 ulp_ps_handshake_mode; + u8 uapsd_bitmap; u8 rf_power_val; u8 wlan_rf_power_mode; u8 obm_ant_sel_val; diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index c5d114d67c83..b22103fd6c81 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -181,6 +181,12 @@ #define RSI_SLEEP_REQUEST 1 #define RSI_WAKEUP_REQUEST 2 +#define RSI_IEEE80211_UAPSD_QUEUES \ + (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO | \ + IEEE80211_WMM_IE_STA_QOSINFO_AC_VI | \ + IEEE80211_WMM_IE_STA_QOSINFO_AC_BE | \ + IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + enum opmode { STA_OPMODE = 1, AP_OPMODE = 2 -- cgit v1.2.3 From 23e414cca1f7331189d0165a3874f0b0453bf308 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 3 Aug 2017 19:59:01 +0530 Subject: rsi: rename sdio_read_buffer_status_register rsi_sdio_check_buffer_status would be the appropriate name for this function as we are checking hardware buffers availability status. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_sdio.c | 2 +- drivers/net/wireless/rsi/rsi_91x_sdio_ops.c | 13 +++---------- drivers/net/wireless/rsi/rsi_sdio.h | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index 42d558b61721..742f6cd44f6c 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -860,7 +860,7 @@ static int rsi_init_sdio_interface(struct rsi_hw *adapter, sdio_release_host(pfunction); adapter->determine_event_timeout = rsi_sdio_determine_event_timeout; - adapter->check_hw_queue_status = rsi_sdio_read_buffer_status_register; + adapter->check_hw_queue_status = rsi_sdio_check_buffer_status; #ifdef CONFIG_RSI_DEBUGFS adapter->num_debugfs_entries = MAX_DEBUGFS_ENTRIES; diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index b3f7adc9d085..9b94ba780ee2 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -323,17 +323,10 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) } while (1); } -/** - * rsi_sdio_read_buffer_status_register() - This function is used to the read - * buffer status register and set - * relevant fields in - * rsi_91x_sdiodev struct. - * @adapter: Pointer to the driver hw structure. - * @q_num: The Q number whose status is to be found. - * - * Return: status: -1 on failure or else queue full/stop is indicated. +/* This function is used to read buffer status register and + * set relevant fields in rsi_91x_sdiodev struct. */ -int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num) +int rsi_sdio_check_buffer_status(struct rsi_hw *adapter, u8 q_num) { struct rsi_common *common = adapter->priv; struct rsi_91x_sdiodev *dev = diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h index 3cf67565feb1..9239fbe698f8 100644 --- a/drivers/net/wireless/rsi/rsi_sdio.h +++ b/drivers/net/wireless/rsi/rsi_sdio.h @@ -127,5 +127,5 @@ int rsi_sdio_write_register_multiple(struct rsi_hw *adapter, u32 addr, int rsi_sdio_master_access_msword(struct rsi_hw *adapter, u16 ms_word); void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit); int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter); -int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num); +int rsi_sdio_check_buffer_status(struct rsi_hw *adapter, u8 q_num); #endif -- cgit v1.2.3 From d64dd2a172d875962f8ae35af3bbd9aa1c679039 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 3 Aug 2017 19:59:02 +0530 Subject: rsi: buffer full check optimization We get buffer full event from firmware whenever Tx queue is full Host should stop writing packets after this and resume after buffer free event. Buffer status checking is optimized for once in 4 times if BUFF_FULL condition is not set, otherwise once for every packet. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_sdio_ops.c | 16 +++++++++++++++- drivers/net/wireless/rsi/rsi_sdio.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index 9b94ba780ee2..94a9fcd70dc7 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -271,6 +271,7 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) "%s: ==> BUFFER_AVAILABLE <==\n", __func__); dev->rx_info.buf_available_counter++; + dev->buff_status_updated = true; break; case FIRMWARE_ASSERT_IND: @@ -333,7 +334,14 @@ int rsi_sdio_check_buffer_status(struct rsi_hw *adapter, u8 q_num) (struct rsi_91x_sdiodev *)adapter->rsi_dev; u8 buf_status = 0; int status = 0; + static int counter = 4; + if (!dev->buff_status_updated && counter) { + counter--; + goto out; + } + + dev->buff_status_updated = false; status = rsi_sdio_read_register(common->priv, RSI_DEVICE_BUFFER_STATUS_REGISTER, &buf_status); @@ -368,10 +376,16 @@ int rsi_sdio_check_buffer_status(struct rsi_hw *adapter, u8 q_num) dev->rx_info.semi_buffer_full = false; } + if (dev->rx_info.mgmt_buffer_full || dev->rx_info.buf_full_counter) + counter = 1; + else + counter = 4; + +out: if ((q_num == MGMT_SOFT_Q) && (dev->rx_info.mgmt_buffer_full)) return QUEUE_FULL; - if (dev->rx_info.buffer_full) + if ((q_num < MGMT_SOFT_Q) && (dev->rx_info.buffer_full)) return QUEUE_FULL; return QUEUE_NOT_FULL; diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h index 9239fbe698f8..95e4bed57baf 100644 --- a/drivers/net/wireless/rsi/rsi_sdio.h +++ b/drivers/net/wireless/rsi/rsi_sdio.h @@ -114,6 +114,7 @@ struct rsi_91x_sdiodev { u8 prev_desc[16]; u16 tx_blk_size; u8 write_fail; + bool buff_status_updated; }; void rsi_interrupt_handler(struct rsi_hw *adapter); -- cgit v1.2.3 From 67c52a4dafceca8ed63b2d8dc73079059e064399 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 3 Aug 2017 19:59:03 +0530 Subject: rsi: buffer available interrupt handling BUFFER_AVAILABLE interrupt is sent by firmware to indicate change in buffer status. We should check buffer status while handling this interrupt. Currently buffer status is checked only while dequeueing packets. This patch fixes a data traffic stuck problem observed occasionally. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_sdio_ops.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index 94a9fcd70dc7..8e2a95c486b0 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -259,10 +259,12 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) switch (isr_type) { case BUFFER_AVAILABLE: - dev->rx_info.watch_bufferfull_count = 0; - dev->rx_info.buffer_full = false; - dev->rx_info.semi_buffer_full = false; - dev->rx_info.mgmt_buffer_full = false; + status = rsi_sdio_check_buffer_status(adapter, + 0); + if (status < 0) + rsi_dbg(ERR_ZONE, + "%s: Failed to check buffer status\n", + __func__); rsi_sdio_ack_intr(common->priv, (1 << PKT_BUFF_AVAILABLE)); rsi_set_event(&common->tx_thread.event); @@ -270,7 +272,6 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) rsi_dbg(ISR_ZONE, "%s: ==> BUFFER_AVAILABLE <==\n", __func__); - dev->rx_info.buf_available_counter++; dev->buff_status_updated = true; break; -- cgit v1.2.3 From 80a88ecf3bccb129146e00e429a4b8a90fb89f11 Mon Sep 17 00:00:00 2001 From: Karun Eagalapati Date: Thu, 3 Aug 2017 19:59:04 +0530 Subject: rsi: RTS threshold configuration Provision is added for configuring RTS threshold by sending vap dynamic update frame to firmware. Signed-off-by: Karun Eagalapati Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 9 +++++++++ drivers/net/wireless/rsi/rsi_91x_mgmt.c | 31 +++++++++++++++++++++++++++++ drivers/net/wireless/rsi/rsi_main.h | 1 + drivers/net/wireless/rsi/rsi_mgmt.h | 14 +++++++++++++ 4 files changed, 55 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 6b833c424b18..210ad79038ed 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -497,6 +497,15 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw, spin_unlock_irqrestore(&adapter->ps_lock, flags); } + /* RTS threshold */ + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + rsi_dbg(INFO_ZONE, "RTS threshold\n"); + if ((common->rts_threshold) <= IEEE80211_MAX_RTS_THRESHOLD) { + rsi_dbg(INFO_ZONE, + "%s: Sending vap updates....\n", __func__); + status = rsi_send_vap_dynamic_update(common); + } + } mutex_unlock(&common->mutex); return status; diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index e5fe443ddfb6..f93499d0b8fa 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1058,6 +1058,37 @@ int rsi_send_radio_params_update(struct rsi_common *common) return rsi_send_internal_mgmt_frame(common, skb); } +/* This function programs the threshold. */ +int rsi_send_vap_dynamic_update(struct rsi_common *common) +{ + struct sk_buff *skb; + struct rsi_dynamic_s *dynamic_frame; + + rsi_dbg(MGMT_TX_ZONE, + "%s: Sending vap update indication frame\n", __func__); + + skb = dev_alloc_skb(sizeof(struct rsi_dynamic_s)); + if (!skb) + return -ENOMEM; + + memset(skb->data, 0, sizeof(struct rsi_dynamic_s)); + dynamic_frame = (struct rsi_dynamic_s *)skb->data; + rsi_set_len_qno(&dynamic_frame->desc_dword0.len_qno, + sizeof(dynamic_frame->frame_body), RSI_WIFI_MGMT_Q); + + dynamic_frame->desc_dword0.frame_type = VAP_DYNAMIC_UPDATE; + dynamic_frame->desc_dword2.pkt_info = + cpu_to_le32(common->rts_threshold); + /* Beacon miss threshold */ + dynamic_frame->frame_body.keep_alive_period = + cpu_to_le16(RSI_DEF_KEEPALIVE); + dynamic_frame->desc_dword3.sta_id = 0; /* vap id */ + + skb_put(skb, sizeof(struct rsi_dynamic_s)); + + return rsi_send_internal_mgmt_frame(common, skb); +} + /** * rsi_compare() - This function is used to compare two integers * @a: pointer to the first integer diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index d2cc47e98639..d05b5e0847bc 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -58,6 +58,7 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...); #define IEEE80211_ADDR_LEN 6 #define FRAME_DESC_SZ 16 #define MIN_802_11_HDR_LEN 24 +#define RSI_DEF_KEEPALIVE 90 #define DATA_QUEUE_WATER_MARK 400 #define MIN_DATA_QUEUE_WATER_MARK 300 diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index b22103fd6c81..201a46572c69 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -239,6 +239,7 @@ enum cmd_frame_type { CW_MODE_REQ, PER_CMD_PKT, ANT_SEL_FRAME = 0x20, + VAP_DYNAMIC_UPDATE = 0x27, COMMON_DEV_CONFIG = 0x28, RADIO_PARAMS_UPDATE = 0x29 }; @@ -374,6 +375,18 @@ struct rsi_ant_sel_frame { __le32 reserved2; } __packed; +struct rsi_dynamic_s { + struct rsi_cmd_desc_dword0 desc_dword0; + struct rsi_cmd_desc_dword1 desc_dword1; + struct rsi_cmd_desc_dword2 desc_dword2; + struct rsi_cmd_desc_dword3 desc_dword3; + struct framebody { + __le16 data_rate; + __le16 mgmt_rate; + __le16 keep_alive_period; + } frame_body; +} __packed; + /* Key descriptor flags */ #define RSI_KEY_TYPE_BROADCAST BIT(1) #define RSI_WEP_KEY BIT(2) @@ -585,6 +598,7 @@ int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len, u8 key_type, u8 key_id, u32 cipher); int rsi_set_channel(struct rsi_common *common, struct ieee80211_channel *channel); +int rsi_send_vap_dynamic_update(struct rsi_common *common); int rsi_send_block_unblock_frame(struct rsi_common *common, bool event); void rsi_inform_bss_status(struct rsi_common *common, u8 status, const u8 *bssid, u8 qos_enable, u16 aid); -- cgit v1.2.3 From bd69cddcdf47919a8f7145779c5da2db3c543bca Mon Sep 17 00:00:00 2001 From: Ganapathi Bhat Date: Thu, 3 Aug 2017 01:34:46 +0530 Subject: mwifiex: replace netif_carrier_on/off by netif_device_attach/dettach Driver is doing netif_carrier_off during suspend, which will set the IFF_LOWER_UP flag to 0. As a result certain applications will think this as a real carrier down and behave accordingly. This will cause issues like loss of IP address, for example. To fix this use netif_device_dettach during suspend. Fixes: 0026b32d723e ('mwifiex: fix Tx timeout issue during suspend test') Signed-off-by: Cathy Luo Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 2be78170ec67..83605a2f1c36 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -3391,11 +3391,8 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; - if (priv && priv->netdev) { - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - } + if (priv && priv->netdev) + netif_device_detach(priv->netdev); } for (i = 0; i < retry_num; i++) { @@ -3466,11 +3463,8 @@ static int mwifiex_cfg80211_resume(struct wiphy *wiphy) for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; - if (priv && priv->netdev) { - if (!netif_carrier_ok(priv->netdev)) - netif_carrier_on(priv->netdev); - mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); - } + if (priv && priv->netdev) + netif_device_attach(priv->netdev); } if (!wiphy->wowlan_config) -- cgit v1.2.3 From 2d33140f90e805cc848dbdc0b176eb67e96b2cf5 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 3 Aug 2017 09:13:27 +0000 Subject: mwifiex: Do not change bss_num in change_virtual_intf Commit 4d7ab36f0c47 ("mwifiex: Do not change bss_type in change_virtual_intf") kept original bss_type unchanged. bss_num should keep the same style, in this way. Unique tuple (bss_type, bss_num) will be able to locate the right priv structure. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 83605a2f1c36..945d444d99a9 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -889,23 +889,15 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv, switch (type) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: - priv->bss_num = mwifiex_get_unused_bss_num(adapter, - MWIFIEX_BSS_TYPE_STA); priv->bss_role = MWIFIEX_BSS_ROLE_STA; break; case NL80211_IFTYPE_P2P_CLIENT: - priv->bss_num = mwifiex_get_unused_bss_num(adapter, - MWIFIEX_BSS_TYPE_P2P); priv->bss_role = MWIFIEX_BSS_ROLE_STA; break; case NL80211_IFTYPE_P2P_GO: - priv->bss_num = mwifiex_get_unused_bss_num(adapter, - MWIFIEX_BSS_TYPE_P2P); priv->bss_role = MWIFIEX_BSS_ROLE_UAP; break; case NL80211_IFTYPE_AP: - priv->bss_num = mwifiex_get_unused_bss_num(adapter, - MWIFIEX_BSS_TYPE_UAP); priv->bss_role = MWIFIEX_BSS_ROLE_UAP; break; default: -- cgit v1.2.3 From 20e5476d6c30e56e6bc58378ae07a36904776333 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 3 Aug 2017 09:13:28 +0000 Subject: mwifiex: wrapper wps ie in pass through tlv This patch wrapper wps ie in pass through tlv, so that firmware could parse correctly. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/join.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c index b89596c18b41..d87aeff70cef 100644 --- a/drivers/net/wireless/marvell/mwifiex/join.c +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -253,7 +253,7 @@ mwifiex_cmd_append_wps_ie(struct mwifiex_private *priv, u8 **buffer) priv->wps_ie_len, *buffer); /* Wrap the generic IE buffer with a pass through TLV type */ - ie_header.type = cpu_to_le16(TLV_TYPE_MGMT_IE); + ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); ie_header.len = cpu_to_le16(priv->wps_ie_len); memcpy(*buffer, &ie_header, sizeof(ie_header)); *buffer += sizeof(ie_header); -- cgit v1.2.3 From 4ba28f93948df5643c24402c642fb769e09fab73 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Mon, 7 Aug 2017 01:36:06 +0000 Subject: mwifiex: p2p: use separate device address Per below statement about p2p device address in WFA P2P spec $2.4.3: The P2P Device Address of a P2P Device shall be its globally administered MAC address, or its globally administered MAC address with the locally administered bit set. This patch follow above statement, using a separate device address for p2p interface Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 5 +++- drivers/net/wireless/marvell/mwifiex/main.c | 40 ++++++++++++++++--------- drivers/net/wireless/marvell/mwifiex/main.h | 4 +++ 3 files changed, 34 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 945d444d99a9..b16b19af812d 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -915,6 +915,8 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv, adapter->rx_locked = false; spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + mwifiex_set_mac_address(priv, dev); + return 0; } @@ -2955,6 +2957,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, } mwifiex_init_priv_params(priv, dev); + mwifiex_set_mac_address(priv, dev); + priv->netdev = dev; ret = mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, @@ -2982,7 +2986,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, dev_net_set(dev, wiphy_net(wiphy)); dev->ieee80211_ptr = &priv->wdev; dev->ieee80211_ptr->iftype = priv->bss_mode; - memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); dev->flags |= IFF_BROADCAST | IFF_MULTICAST; diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index d67d70002ea9..ee40b739b289 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -940,31 +940,44 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } -/* - * CFG802.11 network device handler for setting MAC address. - */ -static int -mwifiex_set_mac_address(struct net_device *dev, void *addr) +int mwifiex_set_mac_address(struct mwifiex_private *priv, + struct net_device *dev) { - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct sockaddr *hw_addr = addr; int ret; + u64 mac_addr; - memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); + if (priv->bss_type != MWIFIEX_BSS_TYPE_P2P) + goto done; + + mac_addr = ether_addr_to_u64(priv->curr_addr); + mac_addr |= BIT_ULL(MWIFIEX_MAC_LOCAL_ADMIN_BIT); + u64_to_ether_addr(mac_addr, priv->curr_addr); /* Send request to firmware */ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS, HostCmd_ACT_GEN_SET, 0, NULL, true); - if (!ret) - memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); - else + if (ret) { mwifiex_dbg(priv->adapter, ERROR, "set mac address failed: ret=%d\n", ret); + return ret; + } +done: memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); + return 0; +} - return ret; +/* CFG802.11 network device handler for setting MAC address. + */ +static int +mwifiex_ndo_set_mac_address(struct net_device *dev, void *addr) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct sockaddr *hw_addr = addr; + + memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); + return mwifiex_set_mac_address(priv, dev); } /* @@ -1257,7 +1270,7 @@ static const struct net_device_ops mwifiex_netdev_ops = { .ndo_open = mwifiex_open, .ndo_stop = mwifiex_close, .ndo_start_xmit = mwifiex_hard_start_xmit, - .ndo_set_mac_address = mwifiex_set_mac_address, + .ndo_set_mac_address = mwifiex_ndo_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = mwifiex_tx_timeout, .ndo_get_stats = mwifiex_get_stats, @@ -1301,7 +1314,6 @@ void mwifiex_init_priv_params(struct mwifiex_private *priv, priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; priv->num_tx_timeout = 0; ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr); - memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 537a0ad795ff..0aaae0878742 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -165,6 +165,8 @@ enum { /* Address alignment */ #define MWIFIEX_ALIGN_ADDR(p, a) (((long)(p) + (a) - 1) & ~((a) - 1)) +#define MWIFIEX_MAC_LOCAL_ADMIN_BIT 41 + /** *enum mwifiex_debug_level - marvell wifi debug level */ @@ -1671,6 +1673,8 @@ void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, struct sk_buff *event_skb); void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter); +int mwifiex_set_mac_address(struct mwifiex_private *priv, + struct net_device *dev); #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); -- cgit v1.2.3 From fdfb0f94bfb71ce31f5417b2f6362e43b0b847f0 Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Thu, 3 Aug 2017 17:37:57 +0800 Subject: brcmfmac: set wpa_auth to WPA_AUTH_DISABLED in AP/OPEN security mode When setting wpa_auth to WPA_AUTH_NONE(1) in AP mode with WEP security, firmware will set privacy bit and add WPA OUI in VENDOR IE in beacon and probe response. The security type in softAP beacons confuse the supplicant in client side, and the user client will see [WPA-?] in supplicant scan result. So we set WPA_AUTH_DISABLED in softAP mode with OPEN security. Signed-off-by: Wright Feng Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 7e689c86d565..579089a340f6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3940,6 +3940,7 @@ brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) { s32 err; + s32 wpa_val; /* set auth */ err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0); @@ -3954,7 +3955,11 @@ static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) return err; } /* set upper-layer auth */ - err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE); + if (brcmf_is_ibssmode(ifp->vif)) + wpa_val = WPA_AUTH_NONE; + else + wpa_val = WPA_AUTH_DISABLED; + err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_val); if (err < 0) { brcmf_err("wpa_auth error %d\n", err); return err; -- cgit v1.2.3 From 0ec9eb90feec4933637fbde9d5bfbc3b62aea218 Mon Sep 17 00:00:00 2001 From: Chi-Hsien Lin Date: Thu, 3 Aug 2017 17:37:58 +0800 Subject: brcmfmac: Add support for CYW4373 SDIO/USB chipset Add support for CYW4373 SDIO/USB chipset. CYW4373 is a 1x1 dual-band 11ac chipset with 20/40/80Mhz channel support. It's a WiFi/BT combo device. Signed-off-by: Chi-Hsien Lin Reviewed-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 2 ++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 +++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 9 ++++++++- drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 3 +++ include/linux/mmc/sdio_ids.h | 1 + 6 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 984c1d0560b1..cd587325e286 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -1105,6 +1105,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373), { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index 05f22ff81d60..c5d1a1cbf601 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -690,6 +690,8 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) case BRCM_CC_4365_CHIP_ID: case BRCM_CC_4366_CHIP_ID: return 0x200000; + case CY_CC_4373_CHIP_ID: + return 0x160000; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index f3556122c6ac..613caca7dc02 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -618,6 +618,7 @@ BRCMF_FW_NVRAM_DEF(43430A1, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt"); BRCMF_FW_NVRAM_DEF(43455, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt"); BRCMF_FW_NVRAM_DEF(4354, "brcmfmac4354-sdio.bin", "brcmfmac4354-sdio.txt"); BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-sdio.bin", "brcmfmac4356-sdio.txt"); +BRCMF_FW_NVRAM_DEF(4373, "brcmfmac4373-sdio.bin", "brcmfmac4373-sdio.txt"); static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), @@ -636,7 +637,8 @@ static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFE, 43430A1), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354), - BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356) + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), + BRCMF_FW_NVRAM_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373) }; static void pkt_align(struct sk_buff *p, int len, int align) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 0eea48e73331..8f20a4bb40d9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -50,6 +50,7 @@ BRCMF_FW_DEF(43143, "brcmfmac43143.bin"); BRCMF_FW_DEF(43236B, "brcmfmac43236b.bin"); BRCMF_FW_DEF(43242A, "brcmfmac43242a.bin"); BRCMF_FW_DEF(43569, "brcmfmac43569.bin"); +BRCMF_FW_DEF(4373, "brcmfmac4373.bin"); static struct brcmf_firmware_mapping brcmf_usb_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), @@ -58,7 +59,8 @@ static struct brcmf_firmware_mapping brcmf_usb_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43238_CHIP_ID, 0x00000008, 43236B), BRCMF_FW_ENTRY(BRCM_CC_43242_CHIP_ID, 0xFFFFFFFF, 43242A), BRCMF_FW_ENTRY(BRCM_CC_43566_CHIP_ID, 0xFFFFFFFF, 43569), - BRCMF_FW_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43569) + BRCMF_FW_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43569), + BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373) }; #define TRX_MAGIC 0x30524448 /* "HDR0" */ @@ -1463,15 +1465,20 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) #define LINKSYS_USB_DEVICE(dev_id) \ { USB_DEVICE(BRCM_USB_VENDOR_ID_LINKSYS, dev_id) } +#define CYPRESS_USB_DEVICE(dev_id) \ + { USB_DEVICE(CY_USB_VENDOR_ID_CYPRESS, dev_id) } + static struct usb_device_id brcmf_usb_devid_table[] = { BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID), BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID), BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID), BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID), LINKSYS_USB_DEVICE(BRCM_USB_43235_LINKSYS_DEVICE_ID), + CYPRESS_USB_DEVICE(CY_USB_4373_DEVICE_ID), { USB_DEVICE(BRCM_USB_VENDOR_ID_LG, BRCM_USB_43242_LG_DEVICE_ID) }, /* special entry for device with firmware loaded and running */ BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID), + CYPRESS_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID), { /* end: all zeroes */ } }; diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h index f1fb8a3c7a32..57544a3a3ce4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -23,6 +23,7 @@ #define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c #define BRCM_USB_VENDOR_ID_LG 0x043e #define BRCM_USB_VENDOR_ID_LINKSYS 0x13b1 +#define CY_USB_VENDOR_ID_CYPRESS 0x04b4 #define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM /* Chipcommon Core Chip IDs */ @@ -57,6 +58,7 @@ #define BRCM_CC_4365_CHIP_ID 0x4365 #define BRCM_CC_4366_CHIP_ID 0x4366 #define BRCM_CC_4371_CHIP_ID 0x4371 +#define CY_CC_4373_CHIP_ID 0x4373 /* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e @@ -66,6 +68,7 @@ #define BRCM_USB_43242_LG_DEVICE_ID 0x3101 #define BRCM_USB_43569_DEVICE_ID 0xbd27 #define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc +#define CY_USB_4373_DEVICE_ID 0xbd29 /* PCIE Device IDs */ #define BRCM_PCIE_4350_DEVICE_ID 0x43a3 diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index b733eb404ffc..abacd5484bc0 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -39,6 +39,7 @@ #define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 #define SDIO_DEVICE_ID_BROADCOM_4356 0x4356 +#define SDIO_DEVICE_ID_CYPRESS_4373 0x4373 #define SDIO_VENDOR_ID_INTEL 0x0089 #define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402 -- cgit v1.2.3 From 99976fc084129e07df3a066dc15651853386da19 Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Thu, 3 Aug 2017 17:37:59 +0800 Subject: brcmfmac: fix wrong num_different_channels when mchan feature enabled When the device/firmware supports multi-channel, it can have P2P connection and regular connection with AP simultaneous. In this case, the num_different_channels in wiphy info was not correct when firmware supports multi-channel (The iw wiphy# info showed "#channels <= 1" in interface combinations). It caused association failed and error message "CTRL-EVENT-FREQ-CONFLICT error" in wpa_supplicant when P2P GO interface was running at the same time. The root cause is that the num_different_channels was always overridden to 1 in brcmf_setup_ifmodes even multi-channel was enabled. We correct the logic by moving num_different_channels setting forward. Signed-off-by: Wright Feng Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 579089a340f6..65cbb3d1e509 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6461,6 +6461,8 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) if (p2p) { if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) combo[c].num_different_channels = 2; + else + combo[c].num_different_channels = 1; wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); @@ -6470,10 +6472,10 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); } else { + combo[c].num_different_channels = 1; c0_limits[i].max = 1; c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); } - combo[c].num_different_channels = 1; combo[c].max_interfaces = i; combo[c].n_limits = i; combo[c].limits = c0_limits; -- cgit v1.2.3 From 8b943e36e24a247d96f79d679ce1d97a17507106 Mon Sep 17 00:00:00 2001 From: Chung-Hsien Hsu Date: Mon, 7 Aug 2017 16:16:52 +0800 Subject: brcmfmac: add setting carrier state ON for successful roaming After association, ping is not working when sweeping the channel at the AP side. It is caused by having incorrect carrier state (OFF) for the STA in successful roaming. This patch sets the carrier state ON for the case. Signed-off-by: Chung-Hsien Hsu Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 65cbb3d1e509..aaed4ab503ad 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5698,10 +5698,13 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp, u32 status = e->status; if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) { - if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state)) { brcmf_bss_roaming_done(cfg, ifp->ndev, e); - else + } else { brcmf_bss_connect_done(cfg, ifp->ndev, e, true); + brcmf_net_setcarrier(ifp, true); + } } return 0; -- cgit v1.2.3 From 4f2949febc22260f217e5459afef6c1b90bb049c Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 2 Aug 2017 23:27:13 +0530 Subject: wlcore: add const to bin_attribute structure Add const to bin_attribute structure as it is only passed to the functions sysfs_{remove/create}_bin_file. The corresponding arguments are of type const, so declare the structure to be const. Signed-off-by: Bhumika Goyal Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wlcore/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c index a9218e5b0efc..b72e2101488b 100644 --- a/drivers/net/wireless/ti/wlcore/sysfs.c +++ b/drivers/net/wireless/ti/wlcore/sysfs.c @@ -138,7 +138,7 @@ static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, return len; } -static struct bin_attribute fwlog_attr = { +static const struct bin_attribute fwlog_attr = { .attr = {.name = "fwlog", .mode = S_IRUSR}, .read = wl1271_sysfs_read_fwlog, }; -- cgit v1.2.3 From cb1b82625de7cbf30bb62672776c466615af84d2 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Mon, 7 Aug 2017 10:30:51 +0530 Subject: rtlwifi: constify rate_control_ops structure rate_control_ops structure is only passed as an argument to the function ieee80211_rate_control_{register/unregister}. This argument is of type const, so declare the structure as const. Signed-off-by: Bhumika Goyal Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rc.c b/drivers/net/wireless/realtek/rtlwifi/rc.c index 951d257cd4c0..02811eda57cd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rc.c +++ b/drivers/net/wireless/realtek/rtlwifi/rc.c @@ -283,7 +283,7 @@ static void rtl_rate_free_sta(void *rtlpriv, kfree(rate_priv); } -static struct rate_control_ops rtl_rate_ops = { +static const struct rate_control_ops rtl_rate_ops = { .name = "rtl_rc", .alloc = rtl_rate_alloc, .free = rtl_rate_free, -- cgit v1.2.3 From 2db3aaba0a9fc769435477059c1224c439061752 Mon Sep 17 00:00:00 2001 From: Michael Skeffington Date: Mon, 7 Aug 2017 12:47:36 -0400 Subject: rt2x00: Fix MMIC Countermeasures Set RX_FLAG_DECRYPTED in case of MMIC failure so that ieee80211_rx_h_decrypt() doesnt drop the frame before getting to ieee80211_rx_h_michael_mic_verify(). Signed-off-by: Michael Skeffington Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800mmio.c | 13 +++++++++++-- drivers/net/wireless/ralink/rt2x00/rt2800usb.c | 15 ++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c index ee5276e233fa..1123e2bed803 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c @@ -136,10 +136,19 @@ void rt2800mmio_fill_rxdone(struct queue_entry *entry, */ rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; - if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) { rxdesc->flags |= RX_FLAG_DECRYPTED; - else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + } else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) { + /* + * In order to check the Michael Mic, the packet must have + * been decrypted. Mac80211 doesnt check the MMIC failure + * flag to initiate MMIC countermeasures if the decoded flag + * has not been set. + */ + rxdesc->flags |= RX_FLAG_DECRYPTED; + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } } if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c index 685b8e0cd67d..3e5d3a40d986 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -697,11 +697,20 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, * stripped it from the frame. Signal this to mac80211. */ rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; - - if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) { + rxdesc->flags |= RX_FLAG_DECRYPTED; + } else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) { + /* + * In order to check the Michael Mic, the packet must have + * been decrypted. Mac80211 doesnt check the MMIC failure + * flag to initiate MMIC countermeasures if the decoded flag + * has not been set. + */ rxdesc->flags |= RX_FLAG_DECRYPTED; - else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } } if (rt2x00_get_field32(word, RXD_W0_MY_BSS)) -- cgit v1.2.3 From 6641525ce40ef45641c8f43bb19cd4e471e4cb75 Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Tue, 8 Aug 2017 14:16:40 +0300 Subject: wil6210: protect against invalid length of tx management frame Validate buffer length has the minimum needed size when sending management frame to protect against possible buffer overrun. Signed-off-by: Hamad Kadmany Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 3 +++ drivers/net/wireless/ath/wil6210/debugfs.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 0b5383a62d42..77af7492420d 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -884,6 +884,9 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf, len, true); + if (len < sizeof(struct ieee80211_hdr_3addr)) + return -EINVAL; + cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); if (!cmd) { rc = -ENOMEM; diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index f82506d276d3..a2b5d595aa19 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -801,6 +801,9 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, int rc; void *frame; + if (!len) + return -EINVAL; + frame = memdup_user(buf, len); if (IS_ERR(frame)) return PTR_ERR(frame); -- cgit v1.2.3 From 30868f5d4413759ba82c0703290f2483402fea39 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Tue, 8 Aug 2017 14:16:43 +0300 Subject: wil6210: support FW RSSI reporting New FW supports reporting RSSI signal in dBm. Report RSSI to kernel in case FW has this capability. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 12 ++++++++---- drivers/net/wireless/ath/wil6210/debugfs.c | 2 ++ drivers/net/wireless/ath/wil6210/pcie_bus.c | 3 +++ drivers/net/wireless/ath/wil6210/wmi.c | 9 ++++++--- drivers/net/wireless/ath/wil6210/wmi.h | 6 ++++-- 5 files changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 77af7492420d..5cd91145c079 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -273,12 +273,12 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, wil_dbg_wmi(wil, "Link status for CID %d: {\n" " MCS %d TSF 0x%016llx\n" - " BF status 0x%08x SNR 0x%08x SQI %d%%\n" + " BF status 0x%08x RSSI %d SQI %d%%\n" " Tx Tpt %d goodput %d Rx goodput %d\n" " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n", cid, le16_to_cpu(reply.evt.bf_mcs), le64_to_cpu(reply.evt.tsf), reply.evt.status, - le32_to_cpu(reply.evt.snr_val), + reply.evt.rssi, reply.evt.sqi, le32_to_cpu(reply.evt.tx_tpt), le32_to_cpu(reply.evt.tx_goodput), @@ -311,7 +311,11 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, if (test_bit(wil_status_fwconnected, wil->status)) { sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); - sinfo->signal = reply.evt.sqi; + if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, + wil->fw_capabilities)) + sinfo->signal = reply.evt.rssi; + else + sinfo->signal = reply.evt.sqi; } return rc; @@ -1794,7 +1798,7 @@ static void wil_wiphy_init(struct wiphy *wiphy) wiphy->bands[NL80211_BAND_60GHZ] = &wil_band_60ghz; - /* TODO: figure this out */ + /* may change after reading FW capabilities */ wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; wiphy->cipher_suites = wil_cipher_suites; diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index a2b5d595aa19..21b661158b1a 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1016,6 +1016,7 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data) " TSF = 0x%016llx\n" " TxMCS = %2d TxTpt = %4d\n" " SQI = %4d\n" + " RSSI = %4d\n" " Status = 0x%08x %s\n" " Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n" " Goodput(rx:tx) %4d:%4d\n" @@ -1025,6 +1026,7 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data) le16_to_cpu(reply.evt.bf_mcs), le32_to_cpu(reply.evt.tx_tpt), reply.evt.sqi, + reply.evt.rssi, status, wil_bfstatus_str(status), le16_to_cpu(reply.evt.my_rx_sector), le16_to_cpu(reply.evt.my_tx_sector), diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index d571feb2370e..6a3ab4bf916d 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -84,6 +84,9 @@ void wil_set_capabilities(struct wil6210_priv *wil) /* extract FW capabilities from file without loading the FW */ wil_request_firmware(wil, wil->wil_fw_name, false); + + if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities)) + wil_to_wiphy(wil)->signal_type = CFG80211_SIGNAL_TYPE_MBM; } void wil_disable_irq(struct wil6210_priv *wil) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 65ef67321fc0..a9487f2b8d60 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -381,12 +381,15 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) ch_no = data->info.channel + 1; freq = ieee80211_channel_to_frequency(ch_no, NL80211_BAND_60GHZ); channel = ieee80211_get_channel(wiphy, freq); - signal = data->info.sqi; + if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities)) + signal = 100 * data->info.rssi; + else + signal = data->info.sqi; d_status = le16_to_cpu(data->info.status); fc = rx_mgmt_frame->frame_control; - wil_dbg_wmi(wil, "MGMT Rx: channel %d MCS %d SNR %d SQI %d%%\n", - data->info.channel, data->info.mcs, data->info.snr, + wil_dbg_wmi(wil, "MGMT Rx: channel %d MCS %d RSSI %d SQI %d%%\n", + data->info.channel, data->info.mcs, data->info.rssi, data->info.sqi); wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len, le16_to_cpu(fc)); diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 256f63c57da0..4e31c2fd1fc6 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -60,6 +60,7 @@ enum wmi_fw_capability { WMI_FW_CAPABILITY_WMI_ONLY = 5, WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7, WMI_FW_CAPABILITY_D3_SUSPEND = 8, + WMI_FW_CAPABILITY_RSSI_REPORTING = 12, WMI_FW_CAPABILITY_MAX, }; @@ -1306,7 +1307,8 @@ struct wmi_notify_req_done_event { /* beamforming status, 0: fail; 1: OK; 2: retrying */ __le32 status; __le64 tsf; - __le32 snr_val; + s8 rssi; + u8 reserved0[3]; __le32 tx_tpt; __le32 tx_goodput; __le32 rx_goodput; @@ -1602,7 +1604,7 @@ struct wmi_get_ssid_event { /* wmi_rx_mgmt_info */ struct wmi_rx_mgmt_info { u8 mcs; - s8 snr; + s8 rssi; u8 range; u8 sqi; __le16 stype; -- cgit v1.2.3 From c6622116c5ae56aec47dd3d63be49cabf591162a Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Tue, 8 Aug 2017 14:16:44 +0300 Subject: wil6210: check no_fw_recovery in resume failure recovery Reset 11ad device on resume failure only if no_fw_recovery is not set. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index ce1f384e7f8e..45488292a8fc 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -85,7 +85,9 @@ static int wil_resume_keep_radio_on(struct wil6210_priv *wil) /* Send WMI resume request to the device */ rc = wmi_resume(wil); if (rc) { - wil_err(wil, "device failed to resume (%d), resetting\n", rc); + wil_err(wil, "device failed to resume (%d)\n", rc); + if (no_fw_recovery) + goto out; rc = wil_down(wil); if (rc) { wil_err(wil, "wil_down failed (%d)\n", rc); -- cgit v1.2.3 From 262345265e599fe5ccc75ba435147d17df04bfd6 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Tue, 8 Aug 2017 14:16:45 +0300 Subject: wil6210: add statistics for suspend time Add statistics for total, min and max suspend time, that calculates the time the 11ad device was in suspend. Those statistics will help to estimate the power impact of d3hot feature. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/debugfs.c | 17 +++++++++++++++-- drivers/net/wireless/ath/wil6210/main.c | 2 ++ drivers/net/wireless/ath/wil6210/pm.c | 20 ++++++++++++++++++-- drivers/net/wireless/ath/wil6210/wil6210.h | 5 +++++ 4 files changed, 40 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 21b661158b1a..d4e88652fcc6 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1617,6 +1617,8 @@ static ssize_t wil_write_suspend_stats(struct file *file, struct wil6210_priv *wil = file->private_data; memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats)); + wil->suspend_stats.min_suspend_time = ULONG_MAX; + wil->suspend_stats.collection_start = ktime_get(); return len; } @@ -1628,18 +1630,27 @@ static ssize_t wil_read_suspend_stats(struct file *file, struct wil6210_priv *wil = file->private_data; static char text[400]; int n; + unsigned long long stats_collection_time = + ktime_to_us(ktime_sub(ktime_get(), + wil->suspend_stats.collection_start)); n = snprintf(text, sizeof(text), "Suspend statistics:\n" "successful suspends:%ld failed suspends:%ld\n" "successful resumes:%ld failed resumes:%ld\n" - "rejected by host:%ld rejected by device:%ld\n", + "rejected by host:%ld rejected by device:%ld\n" + "total suspend time:%lld min suspend time:%lld\n" + "max suspend time:%lld stats collection time: %lld\n", wil->suspend_stats.successful_suspends, wil->suspend_stats.failed_suspends, wil->suspend_stats.successful_resumes, wil->suspend_stats.failed_resumes, wil->suspend_stats.rejected_by_host, - wil->suspend_stats.rejected_by_device); + wil->suspend_stats.rejected_by_device, + wil->suspend_stats.total_suspend_time, + wil->suspend_stats.min_suspend_time, + wil->suspend_stats.max_suspend_time, + stats_collection_time); n = min_t(int, n, sizeof(text)); @@ -1795,6 +1806,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) wil6210_debugfs_create_ITR_CNT(wil, dbg); + wil->suspend_stats.collection_start = ktime_get(); + return 0; } diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index daf944a71901..8968c2c51a1b 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -578,6 +578,8 @@ int wil_priv_init(struct wil6210_priv *wil) wil->wakeup_trigger = WMI_WAKEUP_TRIGGER_UCAST | WMI_WAKEUP_TRIGGER_BCAST; + memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats)); + wil->suspend_stats.min_suspend_time = ULONG_MAX; return 0; diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index 45488292a8fc..820ed17ae2d4 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -300,6 +300,9 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime) wil_dbg_pm(wil, "suspend: %s => %d\n", is_runtime ? "runtime" : "system", rc); + if (!rc) + wil->suspend_stats.suspend_start_time = ktime_get(); + return rc; } @@ -309,6 +312,7 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime) struct net_device *ndev = wil_to_ndev(wil); bool keep_radio_on = ndev->flags & IFF_UP && wil->keep_radio_on_during_sleep; + unsigned long long suspend_time_usec = 0; wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); @@ -326,8 +330,20 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime) else rc = wil_resume_radio_off(wil); + if (rc) + goto out; + + suspend_time_usec = + ktime_to_us(ktime_sub(ktime_get(), + wil->suspend_stats.suspend_start_time)); + wil->suspend_stats.total_suspend_time += suspend_time_usec; + if (suspend_time_usec < wil->suspend_stats.min_suspend_time) + wil->suspend_stats.min_suspend_time = suspend_time_usec; + if (suspend_time_usec > wil->suspend_stats.max_suspend_time) + wil->suspend_stats.max_suspend_time = suspend_time_usec; + out: - wil_dbg_pm(wil, "resume: %s => %d\n", - is_runtime ? "runtime" : "system", rc); + wil_dbg_pm(wil, "resume: %s => %d, suspend time %lld usec\n", + is_runtime ? "runtime" : "system", rc, suspend_time_usec); return rc; } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index d085ccfc7228..45d9385e8a0f 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -90,6 +90,11 @@ struct wil_suspend_stats { unsigned long failed_resumes; unsigned long rejected_by_device; unsigned long rejected_by_host; + unsigned long long total_suspend_time; + unsigned long long min_suspend_time; + unsigned long long max_suspend_time; + ktime_t collection_start; + ktime_t suspend_start_time; }; /* Calculate MAC buffer size for the firmware. It includes all overhead, -- cgit v1.2.3 From d1fbf07540b7d35df693e2e1d7528d8ca9bdb2c2 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Tue, 8 Aug 2017 14:16:46 +0300 Subject: wil6210: notify wiphy on wowlan support Set wowlan to indicate that 11ad device can wake-up on any trigger and disconnect. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 5cd91145c079..9b529ea62bff 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -26,6 +26,12 @@ bool disable_ap_sme; module_param(disable_ap_sme, bool, 0444); MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME"); +#ifdef CONFIG_PM +static struct wiphy_wowlan_support wil_wowlan_support = { + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + #define CHAN60G(_channel, _flags) { \ .band = NL80211_BAND_60GHZ, \ .center_freq = 56160 + (2160 * (_channel)), \ @@ -1808,6 +1814,10 @@ static void wil_wiphy_init(struct wiphy *wiphy) wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands); wiphy->vendor_commands = wil_nl80211_vendor_commands; + +#ifdef CONFIG_PM + wiphy->wowlan = &wil_wowlan_support; +#endif } struct wireless_dev *wil_cfg80211_init(struct device *dev) -- cgit v1.2.3 From 9b2a4c2d534ca45c74488bace32323b26633ff66 Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Tue, 8 Aug 2017 14:16:47 +0300 Subject: wil6210: fix interface-up check While wil_open is executed, any call to netif_running would return a success. In case there are failures within wil_open, should not treat the device as if it is already opened in relevant functions (like FW recovery and runtime suspend check). Fix that by checking the device up flag instead. Signed-off-by: Hamad Kadmany Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 3 ++- drivers/net/wireless/ath/wil6210/pm.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 8968c2c51a1b..b1e281442ba3 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -394,10 +394,11 @@ static void wil_fw_error_worker(struct work_struct *work) struct wil6210_priv *wil = container_of(work, struct wil6210_priv, fw_error_worker); struct wireless_dev *wdev = wil->wdev; + struct net_device *ndev = wil_to_ndev(wil); wil_dbg_misc(wil, "fw error worker\n"); - if (!netif_running(wil_to_ndev(wil))) { + if (!(ndev->flags & IFF_UP)) { wil_info(wil, "No recovery - interface is down\n"); return; } diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index 820ed17ae2d4..8f5d1b447aaa 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -21,10 +21,11 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) { int rc = 0; struct wireless_dev *wdev = wil->wdev; + struct net_device *ndev = wil_to_ndev(wil); wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system"); - if (!netif_running(wil_to_ndev(wil))) { + if (!(ndev->flags & IFF_UP)) { /* can always sleep when down */ wil_dbg_pm(wil, "Interface is down\n"); goto out; -- cgit v1.2.3 From eb4c02155881696ee6abb090d554b765e41d46ed Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Tue, 8 Aug 2017 14:16:48 +0300 Subject: wil6210: store FW RF calibration result Store initial FW RF calibration result in driver. Set this calibration result back to FW after each FW reset in order to avoid future calibration procedures. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 6 ++++++ drivers/net/wireless/ath/wil6210/wil6210.h | 6 ++++++ drivers/net/wireless/ath/wil6210/wmi.c | 5 +++++ drivers/net/wireless/ath/wil6210/wmi.h | 2 ++ 4 files changed, 19 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index b1e281442ba3..b89d017ec847 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1034,6 +1034,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0); wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0); + if (wil->fw_calib_result > 0) { + __le32 val = cpu_to_le32(wil->fw_calib_result | + (CALIB_RESULT_SIGNATURE << 8)); + wil_w(wil, RGF_USER_FW_CALIB_RESULT, (u32 __force)val); + } + wil_release_cpu(wil); } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 45d9385e8a0f..78a9c9f8fc8f 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -171,6 +171,10 @@ struct RGF_ICR { #define RGF_USER_USER_SCRATCH_PAD (0x8802bc) #define RGF_USER_BL (0x880A3C) /* Boot Loader */ #define RGF_USER_FW_REV_ID (0x880a8c) /* chip revision */ +#define RGF_USER_FW_CALIB_RESULT (0x880a90) /* b0-7:result + * b8-15:signature + */ + #define CALIB_RESULT_SIGNATURE (0x11) #define RGF_USER_CLKS_CTL_0 (0x880abc) #define BIT_USER_CLKS_CAR_AHB_SW_SEL BIT(1) /* ref clk/PLL */ #define BIT_USER_CLKS_RST_PWGD BIT(11) /* reset on "power good" */ @@ -724,6 +728,8 @@ struct wil6210_priv { enum wmi_ps_profile_type ps_profile; + int fw_calib_result; + #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP struct notifier_block pm_notify; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index a9487f2b8d60..ffdd2fa401b1 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -344,6 +344,11 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) strlcpy(wdev->wiphy->fw_version, wil->fw_version, sizeof(wdev->wiphy->fw_version)); + if (len > offsetof(struct wmi_ready_event, rfc_read_calib_result)) { + wil_dbg_wmi(wil, "rfc calibration result %d\n", + evt->rfc_read_calib_result); + wil->fw_calib_result = evt->rfc_read_calib_result; + } wil_set_recovery_state(wil, fw_recovery_idle); set_bit(wil_status_fwready, wil->status); /* let the reset sequence continue */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 4e31c2fd1fc6..1b426d7ef81f 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -1300,6 +1300,8 @@ struct wmi_ready_event { /* enum wmi_phy_capability */ u8 phy_capability; u8 numof_additional_mids; + u8 rfc_read_calib_result; + u8 reserved[3]; } __packed; /* WMI_NOTIFY_REQ_DONE_EVENTID */ -- cgit v1.2.3 From 38d16ab2b2132beeb9777de1508ce2150b2dcd35 Mon Sep 17 00:00:00 2001 From: Gidon Studinski Date: Tue, 8 Aug 2017 14:16:50 +0300 Subject: wil6210: move vring_idle_trsh definition to wil6210_priv vring_idle_trsh is used in the operational driver, hence should not be defined as a debugfs variable. Signed-off-by: Gidon Studinski Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/debugfs.c | 5 +---- drivers/net/wireless/ath/wil6210/main.c | 1 + drivers/net/wireless/ath/wil6210/txrx.c | 6 +++--- drivers/net/wireless/ath/wil6210/wil6210.h | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index d4e88652fcc6..6db00c167d2e 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -20,7 +20,6 @@ #include #include #include - #include "wil6210.h" #include "wmi.h" #include "txrx.h" @@ -30,7 +29,6 @@ static u32 mem_addr; static u32 dbg_txdesc_index; static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */ -u32 vring_idle_trsh = 16; /* HW fetches up to 16 descriptors at once */ enum dbg_off_type { doff_u32 = 0, @@ -1763,6 +1761,7 @@ static const struct dbg_off dbg_wil_off[] = { WIL_FIELD(chip_revision, 0444, doff_u8), WIL_FIELD(abft_len, 0644, doff_u8), WIL_FIELD(wakeup_trigger, 0644, doff_u8), + WIL_FIELD(vring_idle_trsh, 0644, doff_u32), {}, }; @@ -1778,8 +1777,6 @@ static const struct dbg_off dbg_statics[] = { {"desc_index", 0644, (ulong)&dbg_txdesc_index, doff_u32}, {"vring_index", 0644, (ulong)&dbg_vring_index, doff_u32}, {"mem_addr", 0644, (ulong)&mem_addr, doff_u32}, - {"vring_idle_trsh", 0644, (ulong)&vring_idle_trsh, - doff_u32}, {"led_polarity", 0644, (ulong)&led_polarity, doff_u8}, {}, }; diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index b89d017ec847..e2ea49077b78 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -581,6 +581,7 @@ int wil_priv_init(struct wil6210_priv *wil) WMI_WAKEUP_TRIGGER_BCAST; memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats)); wil->suspend_stats.min_suspend_time = ULONG_MAX; + wil->vring_idle_trsh = 16; return 0; diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index ec57bcce9601..389c718cd257 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1666,7 +1666,7 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring, /* performance monitoring */ used = wil_vring_used_tx(vring); - if (wil_val_in_range(vring_idle_trsh, + if (wil_val_in_range(wil->vring_idle_trsh, used, used + descs_used)) { txdata->idle += get_cycles() - txdata->last_idle; wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n", @@ -1813,7 +1813,7 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, /* performance monitoring */ used = wil_vring_used_tx(vring); - if (wil_val_in_range(vring_idle_trsh, + if (wil_val_in_range(wil->vring_idle_trsh, used, used + nr_frags + 1)) { txdata->idle += get_cycles() - txdata->last_idle; wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n", @@ -2175,7 +2175,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) /* performance monitoring */ used_new = wil_vring_used_tx(vring); - if (wil_val_in_range(vring_idle_trsh, + if (wil_val_in_range(wil->vring_idle_trsh, used_new, used_before_complete)) { wil_dbg_txrx(wil, "Ring[%2d] idle %d -> %d\n", ringid, used_before_complete, used_new); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 78a9c9f8fc8f..73dbd62dfed7 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -30,7 +30,6 @@ extern bool no_fw_recovery; extern unsigned int mtu_max; extern unsigned short rx_ring_overflow_thrsh; extern int agg_wsize; -extern u32 vring_idle_trsh; extern bool rx_align_2; extern bool rx_large_buf; extern bool debug_fw; @@ -693,6 +692,7 @@ struct wil6210_priv { u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ struct wil_sta_info sta[WIL6210_MAX_CID]; int bcast_vring; + u32 vring_idle_trsh; /* HW fetches up to 16 descriptors at once */ bool use_extended_dma_addr; /* indicates whether we are using 48 bits */ /* scan */ struct cfg80211_scan_request *scan_request; -- cgit v1.2.3 From 94221ae75c869b28bceb76ae300b55d35c186406 Mon Sep 17 00:00:00 2001 From: Gangfeng Huang Date: Sat, 27 May 2017 09:17:53 +0800 Subject: igb: Fix error of RX network flow classification After add an ethertype filter, if user change the adapter speed several times, the error "ethtool -N: etype filters are all used" is reported by igb driver. In older patch, function igb_nfc_filter_exit() and igb_nfc_filter_restore() is not paried. igb_nfc_filter_restore() exist in igb_up(), but function igb_nfc_filter_exit() is exist in __igb_close(). In the process of speed changing, only igb_nfc_filter_restore() is called, it will take a position of ethertype bitmap. Reproduce steps: Step 1: Add a etype filter by ethtool $ethtool -N eth0 flow-type ether proto 0x88F8 action 1 Step 2: Change the adapter speed to 100M/full duplex $ethtool -s eth0 speed 100 duplex full Step 3: Change the adapter speed to 1000M/full duplex ethtool -s eth0 speed 1000 duplex full Repeat step2 and step3, then dmesg the system log, you can find the error message, add new ethtype filter is also failed. This fixing is move igb_nfc_filter_exit() from __igb_close() to igb_down() to make igb_nfc_filter_restore()/igb_nfc_filter_exit() is paired. Signed-off-by: Gangfeng Huang Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index ec62410b035a..6a63ea564a57 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1791,6 +1791,8 @@ void igb_down(struct igb_adapter *adapter) wr32(E1000_RCTL, rctl & ~E1000_RCTL_EN); /* flush and sleep below */ + igb_nfc_filter_exit(adapter); + netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); @@ -3317,8 +3319,6 @@ static int __igb_close(struct net_device *netdev, bool suspending) igb_down(adapter); igb_free_irq(adapter); - igb_nfc_filter_exit(adapter); - igb_free_all_tx_resources(adapter); igb_free_all_rx_resources(adapter); -- cgit v1.2.3 From 26bd4e2db06be8a367cba076c6600b4d5c3e65af Mon Sep 17 00:00:00 2001 From: Cliff Spradlin Date: Mon, 19 Jun 2017 13:30:43 -0700 Subject: igb: protect TX timestamping from API misuse HW timestamping can only be requested for a packet if the NIC is first setup via ioctl(SIOCSHWTSTAMP). If this step was skipped, then the igb driver still allowed TX packets to request HW timestamping. In this situation, the _IGB_PTP_TX_IN_PROGRESS flag was set and would never clear. This prevented any future HW timestamping requests to succeed. Fix this by checking that the NIC is configured for HW TX timestamping before accepting a HW TX timestamping request. Signed-off-by: Cliff Spradlin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 6a63ea564a57..5d0a75c1ba0c 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -5380,7 +5380,8 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb, if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { struct igb_adapter *adapter = netdev_priv(tx_ring->netdev); - if (!test_and_set_bit_lock(__IGB_PTP_TX_IN_PROGRESS, + if (adapter->tstamp_config.tx_type & HWTSTAMP_TX_ON && + !test_and_set_bit_lock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state)) { skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; tx_flags |= IGB_TX_FLAGS_TSTAMP; -- cgit v1.2.3 From d75372a2daf5dc48207ee9e5592917e893cddb87 Mon Sep 17 00:00:00 2001 From: Gustavo A R Silva Date: Tue, 20 Jun 2017 16:22:34 -0500 Subject: e1000e: add check on e1e_wphy() return value Check return value from call to e1e_wphy(). This value is being checked during previous calls to function e1e_wphy() and it seems a check was missing here. Addresses-Coverity-ID: 1226905 Signed-off-by: Gustavo A R Silva Reviewed-by: Ethan Zhao Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/ich8lan.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 68ea8b4555ab..d6d4ed7acf03 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -2437,6 +2437,8 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw) if (hw->phy.revision < 2) { e1000e_phy_sw_reset(hw); ret_val = e1e_wphy(hw, MII_BMCR, 0x3140); + if (ret_val) + return ret_val; } } -- cgit v1.2.3 From 2643e6e90210e16c978919617170089b7c2164f7 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 23 Jun 2017 14:26:30 +0200 Subject: igb: Remove incorrect "unexpected SYS WRAP" log message TSAUXC.DisableSystime is never set, so SYSTIM runs into a SYS WRAP every 1100 secs on 80580/i350/i354 (40 bit SYSTIM) and every 35000 secs on 80576 (45 bit SYSTIM). This wrap event sets the TSICR.SysWrap bit unconditionally. However, checking TSIM at interrupt time shows that this event does not actually cause the interrupt. Rather, it's just bycatch while the actual interrupt is caused by, for instance, TSICR.TXTS. The conclusion is that the SYS WRAP is actually expected, so the "unexpected SYS WRAP" message is entirely bogus and just helps to confuse users. Drop it. Signed-off-by: Corinna Vinschen Acked-by: Jacob Keller Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 5d0a75c1ba0c..1a99164d5d11 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -5746,8 +5746,6 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) event.type = PTP_CLOCK_PPS; if (adapter->ptp_caps.pps) ptp_clock_event(adapter->ptp_clock, &event); - else - dev_err(&adapter->pdev->dev, "unexpected SYS WRAP"); ack |= TSINTR_SYS_WRAP; } -- cgit v1.2.3 From 42e6b92de8f8d7c736cd819ff5af6208a6bdf8ae Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:26:40 +0530 Subject: net: irda: irda-usb: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/irda/irda-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 6f3c805f7211..723e49bc4baa 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -72,7 +72,7 @@ static int qos_mtt_bits = 0; /* These are the currently known IrDA USB dongles. Add new dongles here */ -static struct usb_device_id dongles[] = { +static const struct usb_device_id dongles[] = { /* ACTiSYS Corp., ACT-IR2000U FIR-USB Adapter */ { USB_DEVICE(0x9c4, 0x011), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, /* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */ -- cgit v1.2.3 From fa00f26ff6d53ec7874f9b737acf7ab844a0cc5c Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:26:41 +0530 Subject: net: irda: kingsun: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/irda/kingsun-sir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c index 24c0f169a7b1..4fd4ac2fe09f 100644 --- a/drivers/net/irda/kingsun-sir.c +++ b/drivers/net/irda/kingsun-sir.c @@ -85,7 +85,7 @@ #define KING_PRODUCT_ID 0x4200 /* These are the currently known USB ids */ -static struct usb_device_id dongles[] = { +static const struct usb_device_id dongles[] = { /* KingSun Co,Ltd IrDA/USB Bridge */ { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) }, { } -- cgit v1.2.3 From 10ffb422700b865c2549e8506395fc732b8bf773 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:26:42 +0530 Subject: net: irda: ks959: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/irda/ks959-sir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/irda/ks959-sir.c b/drivers/net/irda/ks959-sir.c index 3affded3e30d..8025741e7586 100644 --- a/drivers/net/irda/ks959-sir.c +++ b/drivers/net/irda/ks959-sir.c @@ -133,7 +133,7 @@ #define KS959_PRODUCT_ID 0x4959 /* These are the currently known USB ids */ -static struct usb_device_id dongles[] = { +static const struct usb_device_id dongles[] = { /* KingSun Co,Ltd IrDA/USB Bridge */ {USB_DEVICE(KS959_VENDOR_ID, KS959_PRODUCT_ID)}, {} -- cgit v1.2.3 From 7f4e87e971c8c0129c179fee053d6fb627573be8 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:26:43 +0530 Subject: net: irda: ksdazzle: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/irda/ksdazzle-sir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/irda/ksdazzle-sir.c b/drivers/net/irda/ksdazzle-sir.c index 741452c7ce35..d2a0755df596 100644 --- a/drivers/net/irda/ksdazzle-sir.c +++ b/drivers/net/irda/ksdazzle-sir.c @@ -97,7 +97,7 @@ #define KSDAZZLE_PRODUCT_ID 0x4100 /* These are the currently known USB ids */ -static struct usb_device_id dongles[] = { +static const struct usb_device_id dongles[] = { /* KingSun Co,Ltd IrDA/USB Bridge */ {USB_DEVICE(KSDAZZLE_VENDOR_ID, KSDAZZLE_PRODUCT_ID)}, {} -- cgit v1.2.3 From 4730279f10a1196ca1f26d545e1728bfb6c5c2c2 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:26:44 +0530 Subject: net: irda: mcs7780: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/irda/mcs7780.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c index 765de3bedb88..c3f0b254b344 100644 --- a/drivers/net/irda/mcs7780.c +++ b/drivers/net/irda/mcs7780.c @@ -66,7 +66,7 @@ #define MCS_VENDOR_ID 0x9710 #define MCS_PRODUCT_ID 0x7780 -static struct usb_device_id mcs_table[] = { +static const struct usb_device_id mcs_table[] = { /* MosChip Corp., MCS7780 FIR-USB Adapter */ {USB_DEVICE(MCS_VENDOR_ID, MCS_PRODUCT_ID)}, {}, -- cgit v1.2.3 From d1bb5aaa294c8f553a8825a1f20ea800369bf162 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:26:45 +0530 Subject: net: irda: stir4200: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/irda/stir4200.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 7ee514879531..ee2cb70b688d 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -183,7 +183,7 @@ struct stir_cb { /* These are the currently known USB ids */ -static struct usb_device_id dongles[] = { +static const struct usb_device_id dongles[] = { /* SigmaTel, Inc, STIr4200 IrDA/USB Bridge */ { USB_DEVICE(0x066f, 0x4200) }, { } -- cgit v1.2.3 From 2f622c4951aade21d9d8c66dc9d6276012a1d8b5 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:28:01 +0530 Subject: net: usb: catc: constify usb_device_id and fix space before '[' error usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Fix checkpatch.pl error: ERROR: space prohibited before open square bracket '['. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/usb/catc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index fce92f0e5abd..dbc90313f472 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -961,7 +961,7 @@ static void catc_disconnect(struct usb_interface *intf) * Module functions and tables. */ -static struct usb_device_id catc_id_table [] = { +static const struct usb_device_id catc_id_table[] = { { USB_DEVICE(0x0423, 0xa) }, /* CATC Netmate, Belkin F5U011 */ { USB_DEVICE(0x0423, 0xc) }, /* CATC Netmate II, Belkin F5U111 */ { USB_DEVICE(0x08d1, 0x1) }, /* smartBridges smartNIC */ -- cgit v1.2.3 From 7f04c61d91c8cec3b89d62d74c5ec4ed1b0919aa Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:28:02 +0530 Subject: net: usb: cdc-phonet: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/usb/cdc-phonet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 2952cb570996..288ecd999171 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -304,7 +304,7 @@ static void usbpn_setup(struct net_device *dev) /* * USB driver callbacks */ -static struct usb_device_id usbpn_ids[] = { +static const struct usb_device_id usbpn_ids[] = { { .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS -- cgit v1.2.3 From 9befbe13eba9a6cd8d5dda1d6716bb5163f3b1c2 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:28:03 +0530 Subject: net: usb: ipheth: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/usb/ipheth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index 0f213ea22c75..d49c7103085e 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -87,7 +87,7 @@ #define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ) #define IPHETH_CARRIER_ON 0x04 -static struct usb_device_id ipheth_table[] = { +static const struct usb_device_id ipheth_table[] = { { USB_DEVICE_AND_INTERFACE_INFO( USB_VENDOR_APPLE, USB_PRODUCT_IPHONE, IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -- cgit v1.2.3 From 06fa59a018f22a59446e54995cd84f0f7ec939af Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:28:04 +0530 Subject: net: usb: kaweth: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/usb/kaweth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index 92e4fd29ae44..f1605833c5cf 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -125,7 +125,7 @@ static int kaweth_resume(struct usb_interface *intf); /**************************************************************** * usb_device_id ****************************************************************/ -static struct usb_device_id usb_klsi_table[] = { +static const struct usb_device_id usb_klsi_table[] = { { USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */ { USB_DEVICE(0x04bb, 0x0901) }, /* I-O DATA USB-ET/T */ { USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */ -- cgit v1.2.3 From 9b4355fb039a4c4b77c3ed92c049b012b92dd36b Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:28:05 +0530 Subject: net: usb: r8152: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 6cfffeff6108..ceb78e2ea4f0 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -5303,7 +5303,7 @@ static void rtl8152_disconnect(struct usb_interface *intf) .bInterfaceProtocol = USB_CDC_PROTO_NONE /* table of devices that work with this driver */ -static struct usb_device_id rtl8152_table[] = { +static const struct usb_device_id rtl8152_table[] = { {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8050)}, {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)}, {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)}, -- cgit v1.2.3 From e1cb90f2b83b6b48deeba0ac9f1920693cbad7e1 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 8 Aug 2017 21:28:41 +0530 Subject: net: usb: rtl8150: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/usb/rtl8150.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index daaa88a66f40..5f565bd574da 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -112,7 +112,7 @@ #undef EEPROM_WRITE /* table of devices that work with this driver */ -static struct usb_device_id rtl8150_table[] = { +static const struct usb_device_id rtl8150_table[] = { {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150)}, {USB_DEVICE(VENDOR_ID_MELCO, PRODUCT_ID_LUAKTX)}, {USB_DEVICE(VENDOR_ID_MICRONET, PRODUCT_ID_SP128AR)}, -- cgit v1.2.3 From 09fc97ba3e25d851da9958671e48eee527e87909 Mon Sep 17 00:00:00 2001 From: Greg Edwards Date: Wed, 28 Jun 2017 09:22:24 -0600 Subject: igb: add argument names to mailbox op function declarations Signed-off-by: Greg Edwards Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_hw.h | 15 ++++++++------- drivers/net/ethernet/intel/igb/e1000_mbx.h | 12 ++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index 2fb2213cd562..fd7865a8d2e3 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -491,13 +491,14 @@ struct e1000_fc_info { struct e1000_mbx_operations { s32 (*init_params)(struct e1000_hw *hw); - s32 (*read)(struct e1000_hw *, u32 *, u16, u16); - s32 (*write)(struct e1000_hw *, u32 *, u16, u16); - s32 (*read_posted)(struct e1000_hw *, u32 *, u16, u16); - s32 (*write_posted)(struct e1000_hw *, u32 *, u16, u16); - s32 (*check_for_msg)(struct e1000_hw *, u16); - s32 (*check_for_ack)(struct e1000_hw *, u16); - s32 (*check_for_rst)(struct e1000_hw *, u16); + s32 (*read)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); + s32 (*write)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); + s32 (*read_posted)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); + s32 (*write_posted)(struct e1000_hw *hw, u32 *msg, u16 size, + u16 mbx_id); + s32 (*check_for_msg)(struct e1000_hw *hw, u16 mbx_id); + s32 (*check_for_ack)(struct e1000_hw *hw, u16 mbx_id); + s32 (*check_for_rst)(struct e1000_hw *hw, u16 mbx_id); }; struct e1000_mbx_stats { diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h index 3e7fed73df15..73d90aeb48b2 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.h +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h @@ -67,11 +67,11 @@ #define E1000_PF_CONTROL_MSG 0x0100 /* PF control message */ -s32 igb_read_mbx(struct e1000_hw *, u32 *, u16, u16); -s32 igb_write_mbx(struct e1000_hw *, u32 *, u16, u16); -s32 igb_check_for_msg(struct e1000_hw *, u16); -s32 igb_check_for_ack(struct e1000_hw *, u16); -s32 igb_check_for_rst(struct e1000_hw *, u16); -s32 igb_init_mbx_params_pf(struct e1000_hw *); +s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); +s32 igb_write_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); +s32 igb_check_for_msg(struct e1000_hw *hw, u16 mbx_id); +s32 igb_check_for_ack(struct e1000_hw *hw, u16 mbx_id); +s32 igb_check_for_rst(struct e1000_hw *hw, u16 mbx_id); +s32 igb_init_mbx_params_pf(struct e1000_hw *hw); #endif /* _E1000_MBX_H_ */ -- cgit v1.2.3 From 1a6c4a3b1e6f8fa1185457300514cd9f902de1a1 Mon Sep 17 00:00:00 2001 From: Greg Edwards Date: Wed, 28 Jun 2017 09:22:25 -0600 Subject: igb: expose mailbox unlock method Add a mailbox unlock method to e1000_mbx_operations, which will be used to unlock the PF/VF mailbox by the PF. Signed-off-by: Greg Edwards Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_hw.h | 1 + drivers/net/ethernet/intel/igb/e1000_mbx.c | 39 ++++++++++++++++++++++++++++++ drivers/net/ethernet/intel/igb/e1000_mbx.h | 1 + 3 files changed, 41 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index fd7865a8d2e3..6076f258a0a5 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -499,6 +499,7 @@ struct e1000_mbx_operations { s32 (*check_for_msg)(struct e1000_hw *hw, u16 mbx_id); s32 (*check_for_ack)(struct e1000_hw *hw, u16 mbx_id); s32 (*check_for_rst)(struct e1000_hw *hw, u16 mbx_id); + s32 (*unlock)(struct e1000_hw *hw, u16 mbx_id); }; struct e1000_mbx_stats { diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.c b/drivers/net/ethernet/intel/igb/e1000_mbx.c index 00e263f0c030..6aa44723507b 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.c +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.c @@ -124,6 +124,24 @@ s32 igb_check_for_rst(struct e1000_hw *hw, u16 mbx_id) return ret_val; } +/** + * igb_unlock_mbx - unlock the mailbox + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the mailbox was unlocked or else ERR_MBX + **/ +s32 igb_unlock_mbx(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + if (mbx->ops.unlock) + ret_val = mbx->ops.unlock(hw, mbx_id); + + return ret_val; +} + /** * igb_poll_for_msg - Wait for message notification * @hw: pointer to the HW structure @@ -340,6 +358,26 @@ static s32 igb_obtain_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number) return ret_val; } +/** + * igb_release_mbx_lock_pf - release mailbox lock + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * return SUCCESS if we released the mailbox lock + **/ +static s32 igb_release_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number) +{ + u32 p2v_mailbox; + + /* drop PF lock of mailbox, if set */ + p2v_mailbox = rd32(E1000_P2VMAILBOX(vf_number)); + if (p2v_mailbox & E1000_P2VMAILBOX_PFU) + wr32(E1000_P2VMAILBOX(vf_number), + p2v_mailbox & ~E1000_P2VMAILBOX_PFU); + + return 0; +} + /** * igb_write_mbx_pf - Places a message in the mailbox * @hw: pointer to the HW structure @@ -437,6 +475,7 @@ s32 igb_init_mbx_params_pf(struct e1000_hw *hw) mbx->ops.check_for_msg = igb_check_for_msg_pf; mbx->ops.check_for_ack = igb_check_for_ack_pf; mbx->ops.check_for_rst = igb_check_for_rst_pf; + mbx->ops.unlock = igb_release_mbx_lock_pf; mbx->stats.msgs_tx = 0; mbx->stats.msgs_rx = 0; diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h index 73d90aeb48b2..a98c5dc60afd 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.h +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h @@ -72,6 +72,7 @@ s32 igb_write_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); s32 igb_check_for_msg(struct e1000_hw *hw, u16 mbx_id); s32 igb_check_for_ack(struct e1000_hw *hw, u16 mbx_id); s32 igb_check_for_rst(struct e1000_hw *hw, u16 mbx_id); +s32 igb_unlock_mbx(struct e1000_hw *hw, u16 mbx_id); s32 igb_init_mbx_params_pf(struct e1000_hw *hw); #endif /* _E1000_MBX_H_ */ -- cgit v1.2.3 From f1174f77b50c94eecaa658fdc56fa69b421de4b8 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 7 Aug 2017 15:26:19 +0100 Subject: bpf/verifier: rework value tracking Unifies adjusted and unadjusted register value types (e.g. FRAME_POINTER is now just a PTR_TO_STACK with zero offset). Tracks value alignment by means of tracking known & unknown bits. This also replaces the 'reg->imm' (leading zero bits) calculations for (what were) UNKNOWN_VALUEs. If pointer leaks are allowed, and adjust_ptr_min_max_vals returns -EACCES, treat the pointer as an unknown scalar and try again, because we might be able to conclude something about the result (e.g. pointer & 0x40 is either 0 or 0x40). Verifier hooks in the netronome/nfp driver were changed to match the new data structures. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/bpf/verifier.c | 24 +- include/linux/bpf.h | 34 +- include/linux/bpf_verifier.h | 34 +- include/linux/tnum.h | 79 + kernel/bpf/Makefile | 2 +- kernel/bpf/tnum.c | 164 ++ kernel/bpf/verifier.c | 1780 +++++++++++---------- 7 files changed, 1265 insertions(+), 852 deletions(-) create mode 100644 include/linux/tnum.h create mode 100644 kernel/bpf/tnum.c (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c index d696ba46f70a..5b783a91b115 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c @@ -79,28 +79,32 @@ nfp_bpf_check_exit(struct nfp_prog *nfp_prog, const struct bpf_verifier_env *env) { const struct bpf_reg_state *reg0 = &env->cur_state.regs[0]; + u64 imm; if (nfp_prog->act == NN_ACT_XDP) return 0; - if (reg0->type != CONST_IMM) { - pr_info("unsupported exit state: %d, imm: %llx\n", - reg0->type, reg0->imm); + if (!(reg0->type == SCALAR_VALUE && tnum_is_const(reg0->var_off))) { + char tn_buf[48]; + + tnum_strn(tn_buf, sizeof(tn_buf), reg0->var_off); + pr_info("unsupported exit state: %d, var_off: %s\n", + reg0->type, tn_buf); return -EINVAL; } - if (nfp_prog->act != NN_ACT_DIRECT && - reg0->imm != 0 && (reg0->imm & ~0U) != ~0U) { + imm = reg0->var_off.value; + if (nfp_prog->act != NN_ACT_DIRECT && imm != 0 && (imm & ~0U) != ~0U) { pr_info("unsupported exit state: %d, imm: %llx\n", - reg0->type, reg0->imm); + reg0->type, imm); return -EINVAL; } - if (nfp_prog->act == NN_ACT_DIRECT && reg0->imm <= TC_ACT_REDIRECT && - reg0->imm != TC_ACT_SHOT && reg0->imm != TC_ACT_STOLEN && - reg0->imm != TC_ACT_QUEUED) { + if (nfp_prog->act == NN_ACT_DIRECT && imm <= TC_ACT_REDIRECT && + imm != TC_ACT_SHOT && imm != TC_ACT_STOLEN && + imm != TC_ACT_QUEUED) { pr_info("unsupported exit state: %d, imm: %llx\n", - reg0->type, reg0->imm); + reg0->type, imm); return -EINVAL; } diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 6353c7474dba..39229c455cba 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -117,35 +117,25 @@ enum bpf_access_type { }; /* types of values stored in eBPF registers */ +/* Pointer types represent: + * pointer + * pointer + imm + * pointer + (u16) var + * pointer + (u16) var + imm + * if (range > 0) then [ptr, ptr + range - off) is safe to access + * if (id > 0) means that some 'var' was added + * if (off > 0) means that 'imm' was added + */ enum bpf_reg_type { NOT_INIT = 0, /* nothing was written into register */ - UNKNOWN_VALUE, /* reg doesn't contain a valid pointer */ + SCALAR_VALUE, /* reg doesn't contain a valid pointer */ PTR_TO_CTX, /* reg points to bpf_context */ CONST_PTR_TO_MAP, /* reg points to struct bpf_map */ PTR_TO_MAP_VALUE, /* reg points to map element value */ PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */ - FRAME_PTR, /* reg == frame_pointer */ - PTR_TO_STACK, /* reg == frame_pointer + imm */ - CONST_IMM, /* constant integer value */ - - /* PTR_TO_PACKET represents: - * skb->data - * skb->data + imm - * skb->data + (u16) var - * skb->data + (u16) var + imm - * if (range > 0) then [ptr, ptr + range - off) is safe to access - * if (id > 0) means that some 'var' was added - * if (off > 0) menas that 'imm' was added - */ - PTR_TO_PACKET, + PTR_TO_STACK, /* reg == frame_pointer + offset */ + PTR_TO_PACKET, /* reg points to skb->data */ PTR_TO_PACKET_END, /* skb->data + headlen */ - - /* PTR_TO_MAP_VALUE_ADJ is used for doing pointer math inside of a map - * elem value. We only allow this if we can statically verify that - * access from this register are going to fall within the size of the - * map element. - */ - PTR_TO_MAP_VALUE_ADJ, }; struct bpf_prog; diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 8e5d31f6faef..85936fa92d12 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -9,6 +9,7 @@ #include /* for enum bpf_reg_type */ #include /* for MAX_BPF_STACK */ +#include /* Just some arbitrary values so we can safely do math without overflowing and * are obviously wrong for any sort of memory access. @@ -19,30 +20,37 @@ struct bpf_reg_state { enum bpf_reg_type type; union { - /* valid when type == CONST_IMM | PTR_TO_STACK | UNKNOWN_VALUE */ - s64 imm; - - /* valid when type == PTR_TO_PACKET* */ - struct { - u16 off; - u16 range; - }; + /* valid when type == PTR_TO_PACKET */ + u16 range; /* valid when type == CONST_PTR_TO_MAP | PTR_TO_MAP_VALUE | * PTR_TO_MAP_VALUE_OR_NULL */ struct bpf_map *map_ptr; }; + /* Fixed part of pointer offset, pointer types only */ + s32 off; + /* For PTR_TO_PACKET, used to find other pointers with the same variable + * offset, so they can share range knowledge. + * For PTR_TO_MAP_VALUE_OR_NULL this is used to share which map value we + * came from, when one is tested for != NULL. + */ u32 id; + /* These three fields must be last. See states_equal() */ + /* For scalar types (SCALAR_VALUE), this represents our knowledge of + * the actual value. + * For pointer types, this represents the variable part of the offset + * from the pointed-to object, and is shared with all bpf_reg_states + * with the same id as us. + */ + struct tnum var_off; /* Used to determine if any memory access using this register will - * result in a bad access. These two fields must be last. - * See states_equal() + * result in a bad access. + * These refer to the same value as var_off, not necessarily the actual + * contents of the register. */ s64 min_value; u64 max_value; - u32 min_align; - u32 aux_off; - u32 aux_off_align; bool value_from_signed; }; diff --git a/include/linux/tnum.h b/include/linux/tnum.h new file mode 100644 index 000000000000..a0b07bf1842b --- /dev/null +++ b/include/linux/tnum.h @@ -0,0 +1,79 @@ +/* tnum: tracked (or tristate) numbers + * + * A tnum tracks knowledge about the bits of a value. Each bit can be either + * known (0 or 1), or unknown (x). Arithmetic operations on tnums will + * propagate the unknown bits such that the tnum result represents all the + * possible results for possible values of the operands. + */ +#include + +struct tnum { + u64 value; + u64 mask; +}; + +/* Constructors */ +/* Represent a known constant as a tnum. */ +struct tnum tnum_const(u64 value); +/* A completely unknown value */ +extern const struct tnum tnum_unknown; + +/* Arithmetic and logical ops */ +/* Shift a tnum left (by a fixed shift) */ +struct tnum tnum_lshift(struct tnum a, u8 shift); +/* Shift a tnum right (by a fixed shift) */ +struct tnum tnum_rshift(struct tnum a, u8 shift); +/* Add two tnums, return @a + @b */ +struct tnum tnum_add(struct tnum a, struct tnum b); +/* Subtract two tnums, return @a - @b */ +struct tnum tnum_sub(struct tnum a, struct tnum b); +/* Bitwise-AND, return @a & @b */ +struct tnum tnum_and(struct tnum a, struct tnum b); +/* Bitwise-OR, return @a | @b */ +struct tnum tnum_or(struct tnum a, struct tnum b); +/* Bitwise-XOR, return @a ^ @b */ +struct tnum tnum_xor(struct tnum a, struct tnum b); +/* Multiply two tnums, return @a * @b */ +struct tnum tnum_mul(struct tnum a, struct tnum b); + +/* Return a tnum representing numbers satisfying both @a and @b */ +struct tnum tnum_intersect(struct tnum a, struct tnum b); + +/* Return @a with all but the lowest @size bytes cleared */ +struct tnum tnum_cast(struct tnum a, u8 size); + +/* Returns true if @a is a known constant */ +static inline bool tnum_is_const(struct tnum a) +{ + return !a.mask; +} + +/* Returns true if @a == tnum_const(@b) */ +static inline bool tnum_equals_const(struct tnum a, u64 b) +{ + return tnum_is_const(a) && a.value == b; +} + +/* Returns true if @a is completely unknown */ +static inline bool tnum_is_unknown(struct tnum a) +{ + return !~a.mask; +} + +/* Returns true if @a is known to be a multiple of @size. + * @size must be a power of two. + */ +bool tnum_is_aligned(struct tnum a, u64 size); + +/* Returns true if @b represents a subset of @a. */ +bool tnum_in(struct tnum a, struct tnum b); + +/* Formatting functions. These have snprintf-like semantics: they will write + * up to @size bytes (including the terminating NUL byte), and return the number + * of bytes (excluding the terminating NUL) which would have been written had + * sufficient space been available. (Thus tnum_sbin always returns 64.) + */ +/* Format a tnum as a pair of hex numbers (value; mask) */ +int tnum_strn(char *str, size_t size, struct tnum a); +/* Format a tnum as tristate binary expansion */ +int tnum_sbin(char *str, size_t size, struct tnum a); diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 48e92705be59..2f0bcda40e90 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -1,6 +1,6 @@ obj-y := core.o -obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o +obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o ifeq ($(CONFIG_NET),y) obj-$(CONFIG_BPF_SYSCALL) += devmap.o diff --git a/kernel/bpf/tnum.c b/kernel/bpf/tnum.c new file mode 100644 index 000000000000..92eeeb1974a2 --- /dev/null +++ b/kernel/bpf/tnum.c @@ -0,0 +1,164 @@ +/* tnum: tracked (or tristate) numbers + * + * A tnum tracks knowledge about the bits of a value. Each bit can be either + * known (0 or 1), or unknown (x). Arithmetic operations on tnums will + * propagate the unknown bits such that the tnum result represents all the + * possible results for possible values of the operands. + */ +#include +#include + +#define TNUM(_v, _m) (struct tnum){.value = _v, .mask = _m} +/* A completely unknown value */ +const struct tnum tnum_unknown = { .value = 0, .mask = -1 }; + +struct tnum tnum_const(u64 value) +{ + return TNUM(value, 0); +} + +struct tnum tnum_lshift(struct tnum a, u8 shift) +{ + return TNUM(a.value << shift, a.mask << shift); +} + +struct tnum tnum_rshift(struct tnum a, u8 shift) +{ + return TNUM(a.value >> shift, a.mask >> shift); +} + +struct tnum tnum_add(struct tnum a, struct tnum b) +{ + u64 sm, sv, sigma, chi, mu; + + sm = a.mask + b.mask; + sv = a.value + b.value; + sigma = sm + sv; + chi = sigma ^ sv; + mu = chi | a.mask | b.mask; + return TNUM(sv & ~mu, mu); +} + +struct tnum tnum_sub(struct tnum a, struct tnum b) +{ + u64 dv, alpha, beta, chi, mu; + + dv = a.value - b.value; + alpha = dv + a.mask; + beta = dv - b.mask; + chi = alpha ^ beta; + mu = chi | a.mask | b.mask; + return TNUM(dv & ~mu, mu); +} + +struct tnum tnum_and(struct tnum a, struct tnum b) +{ + u64 alpha, beta, v; + + alpha = a.value | a.mask; + beta = b.value | b.mask; + v = a.value & b.value; + return TNUM(v, alpha & beta & ~v); +} + +struct tnum tnum_or(struct tnum a, struct tnum b) +{ + u64 v, mu; + + v = a.value | b.value; + mu = a.mask | b.mask; + return TNUM(v, mu & ~v); +} + +struct tnum tnum_xor(struct tnum a, struct tnum b) +{ + u64 v, mu; + + v = a.value ^ b.value; + mu = a.mask | b.mask; + return TNUM(v & ~mu, mu); +} + +/* half-multiply add: acc += (unknown * mask * value). + * An intermediate step in the multiply algorithm. + */ +static struct tnum hma(struct tnum acc, u64 value, u64 mask) +{ + while (mask) { + if (mask & 1) + acc = tnum_add(acc, TNUM(0, value)); + mask >>= 1; + value <<= 1; + } + return acc; +} + +struct tnum tnum_mul(struct tnum a, struct tnum b) +{ + struct tnum acc; + u64 pi; + + pi = a.value * b.value; + acc = hma(TNUM(pi, 0), a.mask, b.mask | b.value); + return hma(acc, b.mask, a.value); +} + +/* Note that if a and b disagree - i.e. one has a 'known 1' where the other has + * a 'known 0' - this will return a 'known 1' for that bit. + */ +struct tnum tnum_intersect(struct tnum a, struct tnum b) +{ + u64 v, mu; + + v = a.value | b.value; + mu = a.mask & b.mask; + return TNUM(v & ~mu, mu); +} + +struct tnum tnum_cast(struct tnum a, u8 size) +{ + a.value &= (1ULL << (size * 8)) - 1; + a.mask &= (1ULL << (size * 8)) - 1; + return a; +} + +bool tnum_is_aligned(struct tnum a, u64 size) +{ + if (!size) + return true; + return !((a.value | a.mask) & (size - 1)); +} + +bool tnum_in(struct tnum a, struct tnum b) +{ + if (b.mask & ~a.mask) + return false; + b.value &= ~a.mask; + return a.value == b.value; +} + +int tnum_strn(char *str, size_t size, struct tnum a) +{ + return snprintf(str, size, "(%#llx; %#llx)", a.value, a.mask); +} +EXPORT_SYMBOL_GPL(tnum_strn); + +int tnum_sbin(char *str, size_t size, struct tnum a) +{ + size_t n; + + for (n = 64; n; n--) { + if (n < size) { + if (a.mask & 1) + str[n - 1] = 'x'; + else if (a.value & 1) + str[n - 1] = '1'; + else + str[n - 1] = '0'; + } + a.mask >>= 1; + a.value >>= 1; + } + str[min(size - 1, (size_t)64)] = 0; + return 64; +} diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f6e8b3887eab..c3f88b466c30 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -61,12 +61,12 @@ * (and -20 constant is saved for further stack bounds checking). * Meaning that this reg is a pointer to stack plus known immediate constant. * - * Most of the time the registers have UNKNOWN_VALUE type, which + * Most of the time the registers have SCALAR_VALUE type, which * means the register has some value, but it's not a valid pointer. - * (like pointer plus pointer becomes UNKNOWN_VALUE type) + * (like pointer plus pointer becomes SCALAR_VALUE type) * * When verifier sees load or store instructions the type of base register - * can be: PTR_TO_MAP_VALUE, PTR_TO_CTX, FRAME_PTR. These are three pointer + * can be: PTR_TO_MAP_VALUE, PTR_TO_CTX, PTR_TO_STACK. These are three pointer * types recognized by check_mem_access() function. * * PTR_TO_MAP_VALUE means that this register is pointing to 'map element value' @@ -180,15 +180,12 @@ static __printf(1, 2) void verbose(const char *fmt, ...) /* string representation of 'enum bpf_reg_type' */ static const char * const reg_type_str[] = { [NOT_INIT] = "?", - [UNKNOWN_VALUE] = "inv", + [SCALAR_VALUE] = "inv", [PTR_TO_CTX] = "ctx", [CONST_PTR_TO_MAP] = "map_ptr", [PTR_TO_MAP_VALUE] = "map_value", [PTR_TO_MAP_VALUE_OR_NULL] = "map_value_or_null", - [PTR_TO_MAP_VALUE_ADJ] = "map_value_adj", - [FRAME_PTR] = "fp", [PTR_TO_STACK] = "fp", - [CONST_IMM] = "imm", [PTR_TO_PACKET] = "pkt", [PTR_TO_PACKET_END] = "pkt_end", }; @@ -221,32 +218,36 @@ static void print_verifier_state(struct bpf_verifier_state *state) if (t == NOT_INIT) continue; verbose(" R%d=%s", i, reg_type_str[t]); - if (t == CONST_IMM || t == PTR_TO_STACK) - verbose("%lld", reg->imm); - else if (t == PTR_TO_PACKET) - verbose("(id=%d,off=%d,r=%d)", - reg->id, reg->off, reg->range); - else if (t == UNKNOWN_VALUE && reg->imm) - verbose("%lld", reg->imm); - else if (t == CONST_PTR_TO_MAP || t == PTR_TO_MAP_VALUE || - t == PTR_TO_MAP_VALUE_OR_NULL || - t == PTR_TO_MAP_VALUE_ADJ) - verbose("(ks=%d,vs=%d,id=%u)", - reg->map_ptr->key_size, - reg->map_ptr->value_size, - reg->id); - if (reg->min_value != BPF_REGISTER_MIN_RANGE) - verbose(",min_value=%lld", - (long long)reg->min_value); - if (reg->max_value != BPF_REGISTER_MAX_RANGE) - verbose(",max_value=%llu", - (unsigned long long)reg->max_value); - if (reg->min_align) - verbose(",min_align=%u", reg->min_align); - if (reg->aux_off) - verbose(",aux_off=%u", reg->aux_off); - if (reg->aux_off_align) - verbose(",aux_off_align=%u", reg->aux_off_align); + if ((t == SCALAR_VALUE || t == PTR_TO_STACK) && + tnum_is_const(reg->var_off)) { + /* reg->off should be 0 for SCALAR_VALUE */ + verbose("%lld", reg->var_off.value + reg->off); + } else { + verbose("(id=%d", reg->id); + if (t != SCALAR_VALUE) + verbose(",off=%d", reg->off); + if (t == PTR_TO_PACKET) + verbose(",r=%d", reg->range); + else if (t == CONST_PTR_TO_MAP || + t == PTR_TO_MAP_VALUE || + t == PTR_TO_MAP_VALUE_OR_NULL) + verbose(",ks=%d,vs=%d", + reg->map_ptr->key_size, + reg->map_ptr->value_size); + if (reg->min_value != BPF_REGISTER_MIN_RANGE) + verbose(",min_value=%lld", + (long long)reg->min_value); + if (reg->max_value != BPF_REGISTER_MAX_RANGE) + verbose(",max_value=%llu", + (unsigned long long)reg->max_value); + if (!tnum_is_unknown(reg->var_off)) { + char tn_buf[48]; + + tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); + verbose(",var_off=%s", tn_buf); + } + verbose(")"); + } } for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { if (state->stack_slot_type[i] == STACK_SPILL) @@ -463,14 +464,69 @@ static const int caller_saved[CALLER_SAVED_REGS] = { BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5 }; -static void mark_reg_not_init(struct bpf_reg_state *regs, u32 regno) +static void __mark_reg_not_init(struct bpf_reg_state *reg); + +/* Mark the 'variable offset' part of a register as zero. This should be + * used only on registers holding a pointer type. + */ +static void __mark_reg_known_zero(struct bpf_reg_state *reg) { - BUG_ON(regno >= MAX_BPF_REG); + reg->var_off = tnum_const(0); + reg->min_value = 0; + reg->max_value = 0; +} - memset(®s[regno], 0, sizeof(regs[regno])); - regs[regno].type = NOT_INIT; - regs[regno].min_value = BPF_REGISTER_MIN_RANGE; - regs[regno].max_value = BPF_REGISTER_MAX_RANGE; +static void mark_reg_known_zero(struct bpf_reg_state *regs, u32 regno) +{ + if (WARN_ON(regno >= MAX_BPF_REG)) { + verbose("mark_reg_known_zero(regs, %u)\n", regno); + /* Something bad happened, let's kill all regs */ + for (regno = 0; regno < MAX_BPF_REG; regno++) + __mark_reg_not_init(regs + regno); + return; + } + __mark_reg_known_zero(regs + regno); +} + +/* Mark a register as having a completely unknown (scalar) value. */ +static void __mark_reg_unknown(struct bpf_reg_state *reg) +{ + reg->type = SCALAR_VALUE; + reg->id = 0; + reg->off = 0; + reg->var_off = tnum_unknown; + reg->min_value = BPF_REGISTER_MIN_RANGE; + reg->max_value = BPF_REGISTER_MAX_RANGE; +} + +static void mark_reg_unknown(struct bpf_reg_state *regs, u32 regno) +{ + if (WARN_ON(regno >= MAX_BPF_REG)) { + verbose("mark_reg_unknown(regs, %u)\n", regno); + /* Something bad happened, let's kill all regs */ + for (regno = 0; regno < MAX_BPF_REG; regno++) + __mark_reg_not_init(regs + regno); + return; + } + __mark_reg_unknown(regs + regno); +} + +static void __mark_reg_not_init(struct bpf_reg_state *reg) +{ + __mark_reg_unknown(reg); + reg->type = NOT_INIT; +} + +static void mark_reg_not_init(struct bpf_reg_state *regs, u32 regno) +{ + if (WARN_ON(regno >= MAX_BPF_REG)) { + verbose("mark_reg_not_init(regs, %u)\n", regno); + /* Something bad happened, let's kill all regs */ + for (regno = 0; regno < MAX_BPF_REG; regno++) + __mark_reg_not_init(regs + regno); + return; + } + __mark_reg_not_init(regs + regno); } static void init_reg_state(struct bpf_reg_state *regs) @@ -481,23 +537,12 @@ static void init_reg_state(struct bpf_reg_state *regs) mark_reg_not_init(regs, i); /* frame pointer */ - regs[BPF_REG_FP].type = FRAME_PTR; + regs[BPF_REG_FP].type = PTR_TO_STACK; + mark_reg_known_zero(regs, BPF_REG_FP); /* 1st arg to a function */ regs[BPF_REG_1].type = PTR_TO_CTX; -} - -static void __mark_reg_unknown_value(struct bpf_reg_state *regs, u32 regno) -{ - regs[regno].type = UNKNOWN_VALUE; - regs[regno].id = 0; - regs[regno].imm = 0; -} - -static void mark_reg_unknown_value(struct bpf_reg_state *regs, u32 regno) -{ - BUG_ON(regno >= MAX_BPF_REG); - __mark_reg_unknown_value(regs, regno); + mark_reg_known_zero(regs, BPF_REG_1); } static void reset_reg_range_values(struct bpf_reg_state *regs, u32 regno) @@ -505,14 +550,6 @@ static void reset_reg_range_values(struct bpf_reg_state *regs, u32 regno) regs[regno].min_value = BPF_REGISTER_MIN_RANGE; regs[regno].max_value = BPF_REGISTER_MAX_RANGE; regs[regno].value_from_signed = false; - regs[regno].min_align = 0; -} - -static void mark_reg_unknown_value_and_range(struct bpf_reg_state *regs, - u32 regno) -{ - mark_reg_unknown_value(regs, regno); - reset_reg_range_values(regs, regno); } enum reg_arg_type { @@ -542,7 +579,7 @@ static int check_reg_arg(struct bpf_reg_state *regs, u32 regno, return -EACCES; } if (t == DST_OP) - mark_reg_unknown_value(regs, regno); + mark_reg_unknown(regs, regno); } return 0; } @@ -552,12 +589,10 @@ static bool is_spillable_regtype(enum bpf_reg_type type) switch (type) { case PTR_TO_MAP_VALUE: case PTR_TO_MAP_VALUE_OR_NULL: - case PTR_TO_MAP_VALUE_ADJ: case PTR_TO_STACK: case PTR_TO_CTX: case PTR_TO_PACKET: case PTR_TO_PACKET_END: - case FRAME_PTR: case CONST_PTR_TO_MAP: return true; default: @@ -637,14 +672,13 @@ static int check_stack_read(struct bpf_verifier_state *state, int off, int size, } if (value_regno >= 0) /* have read misc data from the stack */ - mark_reg_unknown_value_and_range(state->regs, - value_regno); + mark_reg_unknown(state->regs, value_regno); return 0; } } /* check read/write into map element returned by bpf_map_lookup_elem() */ -static int check_map_access(struct bpf_verifier_env *env, u32 regno, int off, +static int __check_map_access(struct bpf_verifier_env *env, u32 regno, int off, int size) { struct bpf_map *map = env->cur_state.regs[regno].map_ptr; @@ -657,22 +691,25 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, int off, return 0; } -/* check read/write into an adjusted map element */ -static int check_map_access_adj(struct bpf_verifier_env *env, u32 regno, +/* check read/write into a map element with possible variable offset */ +static int check_map_access(struct bpf_verifier_env *env, u32 regno, int off, int size) { struct bpf_verifier_state *state = &env->cur_state; struct bpf_reg_state *reg = &state->regs[regno]; int err; - /* We adjusted the register to this map value, so we - * need to change off and size to min_value and max_value - * respectively to make sure our theoretical access will be - * safe. + /* We may have adjusted the register to this map value, so we + * need to try adding each of min_value and max_value to off + * to make sure our theoretical access will be safe. */ if (log_level) print_verifier_state(state); - env->varlen_map_value_access = true; + /* If the offset is variable, we will need to be stricter in state + * pruning from now on. + */ + if (!tnum_is_const(reg->var_off)) + env->varlen_map_value_access = true; /* The minimum value is only important with signed * comparisons where we can't assume the floor of a * value is 0. If we are using signed variables for our @@ -684,10 +721,9 @@ static int check_map_access_adj(struct bpf_verifier_env *env, u32 regno, regno); return -EACCES; } - err = check_map_access(env, regno, reg->min_value + off, size); + err = __check_map_access(env, regno, reg->min_value + off, size); if (err) { - verbose("R%d min value is outside of the array range\n", - regno); + verbose("R%d min value is outside of the array range\n", regno); return err; } @@ -699,7 +735,10 @@ static int check_map_access_adj(struct bpf_verifier_env *env, u32 regno, regno); return -EACCES; } - return check_map_access(env, regno, reg->max_value + off, size); + err = __check_map_access(env, regno, reg->max_value + off, size); + if (err) + verbose("R%d max value is outside of the array range\n", regno); + return err; } #define MAX_PACKET_OFF 0xffff @@ -729,14 +768,13 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env, } } -static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, - int size) +static int __check_packet_access(struct bpf_verifier_env *env, u32 regno, + int off, int size) { struct bpf_reg_state *regs = env->cur_state.regs; struct bpf_reg_state *reg = ®s[regno]; - off += reg->off; - if (off < 0 || size <= 0 || off + size > reg->range) { + if (off < 0 || size <= 0 || (u64)off + size > reg->range) { verbose("invalid access to packet, off=%d size=%d, R%d(id=%d,off=%d,r=%d)\n", off, size, regno, reg->id, reg->off, reg->range); return -EACCES; @@ -744,7 +782,35 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, return 0; } -/* check access to 'struct bpf_context' fields */ +static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, + int size) +{ + struct bpf_reg_state *regs = env->cur_state.regs; + struct bpf_reg_state *reg = ®s[regno]; + int err; + + /* We may have added a variable offset to the packet pointer; but any + * reg->range we have comes after that. We are only checking the fixed + * offset. + */ + + /* We don't allow negative numbers, because we aren't tracking enough + * detail to prove they're safe. + */ + if (reg->min_value < 0) { + verbose("R%d min value is negative, either use unsigned index or do a if (index >=0) check.\n", + regno); + return -EACCES; + } + err = __check_packet_access(env, regno, off, size); + if (err) { + verbose("R%d offset is outside of the packet\n", regno); + return err; + } + return err; +} + +/* check access to 'struct bpf_context' fields. Supports fixed offsets only */ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size, enum bpf_access_type t, enum bpf_reg_type *reg_type) { @@ -784,13 +850,7 @@ static bool __is_pointer_value(bool allow_ptr_leaks, if (allow_ptr_leaks) return false; - switch (reg->type) { - case UNKNOWN_VALUE: - case CONST_IMM: - return false; - default: - return true; - } + return reg->type != SCALAR_VALUE; } static bool is_pointer_value(struct bpf_verifier_env *env, int regno) @@ -801,23 +861,13 @@ static bool is_pointer_value(struct bpf_verifier_env *env, int regno) static int check_pkt_ptr_alignment(const struct bpf_reg_state *reg, int off, int size, bool strict) { + struct tnum reg_off; int ip_align; - int reg_off; /* Byte size accesses are always allowed. */ if (!strict || size == 1) return 0; - reg_off = reg->off; - if (reg->id) { - if (reg->aux_off_align % size) { - verbose("Packet access is only %u byte aligned, %d byte access not allowed\n", - reg->aux_off_align, size); - return -EACCES; - } - reg_off += reg->aux_off; - } - /* For platforms that do not have a Kconfig enabling * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS the value of * NET_IP_ALIGN is universally set to '2'. And on platforms @@ -827,20 +877,37 @@ static int check_pkt_ptr_alignment(const struct bpf_reg_state *reg, * unconditional IP align value of '2'. */ ip_align = 2; - if ((ip_align + reg_off + off) % size != 0) { - verbose("misaligned packet access off %d+%d+%d size %d\n", - ip_align, reg_off, off, size); + + reg_off = tnum_add(reg->var_off, tnum_const(ip_align + reg->off + off)); + if (!tnum_is_aligned(reg_off, size)) { + char tn_buf[48]; + + tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); + verbose("misaligned packet access off %d+%s+%d+%d size %d\n", + ip_align, tn_buf, reg->off, off, size); return -EACCES; } return 0; } -static int check_val_ptr_alignment(const struct bpf_reg_state *reg, - int size, bool strict) +static int check_generic_ptr_alignment(const struct bpf_reg_state *reg, + const char *pointer_desc, + int off, int size, bool strict) { - if (strict && size != 1) { - verbose("Unknown alignment. Only byte-sized access allowed in value access.\n"); + struct tnum reg_off; + + /* Byte size accesses are always allowed. */ + if (!strict || size == 1) + return 0; + + reg_off = tnum_add(reg->var_off, tnum_const(reg->off + off)); + if (!tnum_is_aligned(reg_off, size)) { + char tn_buf[48]; + + tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); + verbose("misaligned %saccess off %s+%d+%d size %d\n", + pointer_desc, tn_buf, reg->off, off, size); return -EACCES; } @@ -852,21 +919,25 @@ static int check_ptr_alignment(struct bpf_verifier_env *env, int off, int size) { bool strict = env->strict_alignment; + const char *pointer_desc = ""; switch (reg->type) { case PTR_TO_PACKET: + /* special case, because of NET_IP_ALIGN */ return check_pkt_ptr_alignment(reg, off, size, strict); - case PTR_TO_MAP_VALUE_ADJ: - return check_val_ptr_alignment(reg, size, strict); + case PTR_TO_MAP_VALUE: + pointer_desc = "value "; + break; + case PTR_TO_CTX: + pointer_desc = "context "; + break; + case PTR_TO_STACK: + pointer_desc = "stack "; + break; default: - if (off % size != 0) { - verbose("misaligned access off %d size %d\n", - off, size); - return -EACCES; - } - - return 0; + break; } + return check_generic_ptr_alignment(reg, pointer_desc, off, size, strict); } /* check whether memory at (regno + off) is accessible for t = (read | write) @@ -883,52 +954,79 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn struct bpf_reg_state *reg = &state->regs[regno]; int size, err = 0; - if (reg->type == PTR_TO_STACK) - off += reg->imm; - size = bpf_size_to_bytes(bpf_size); if (size < 0) return size; + /* alignment checks will add in reg->off themselves */ err = check_ptr_alignment(env, reg, off, size); if (err) return err; - if (reg->type == PTR_TO_MAP_VALUE || - reg->type == PTR_TO_MAP_VALUE_ADJ) { + /* for access checks, reg->off is just part of off */ + off += reg->off; + + if (reg->type == PTR_TO_MAP_VALUE) { if (t == BPF_WRITE && value_regno >= 0 && is_pointer_value(env, value_regno)) { verbose("R%d leaks addr into map\n", value_regno); return -EACCES; } - if (reg->type == PTR_TO_MAP_VALUE_ADJ) - err = check_map_access_adj(env, regno, off, size); - else - err = check_map_access(env, regno, off, size); + err = check_map_access(env, regno, off, size); if (!err && t == BPF_READ && value_regno >= 0) - mark_reg_unknown_value_and_range(state->regs, - value_regno); + mark_reg_unknown(state->regs, value_regno); } else if (reg->type == PTR_TO_CTX) { - enum bpf_reg_type reg_type = UNKNOWN_VALUE; + enum bpf_reg_type reg_type = SCALAR_VALUE; if (t == BPF_WRITE && value_regno >= 0 && is_pointer_value(env, value_regno)) { verbose("R%d leaks addr into ctx\n", value_regno); return -EACCES; } + /* ctx accesses must be at a fixed offset, so that we can + * determine what type of data were returned. + */ + if (!tnum_is_const(reg->var_off)) { + char tn_buf[48]; + + tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); + verbose("variable ctx access var_off=%s off=%d size=%d", + tn_buf, off, size); + return -EACCES; + } + off += reg->var_off.value; err = check_ctx_access(env, insn_idx, off, size, t, ®_type); if (!err && t == BPF_READ && value_regno >= 0) { - mark_reg_unknown_value_and_range(state->regs, - value_regno); - /* note that reg.[id|off|range] == 0 */ + /* ctx access returns either a scalar, or a + * PTR_TO_PACKET[_END]. In the latter case, we know + * the offset is zero. + */ + if (reg_type == SCALAR_VALUE) + mark_reg_unknown(state->regs, value_regno); + else + mark_reg_known_zero(state->regs, value_regno); + state->regs[value_regno].id = 0; + state->regs[value_regno].off = 0; + state->regs[value_regno].range = 0; state->regs[value_regno].type = reg_type; - state->regs[value_regno].aux_off = 0; - state->regs[value_regno].aux_off_align = 0; } - } else if (reg->type == FRAME_PTR || reg->type == PTR_TO_STACK) { + } else if (reg->type == PTR_TO_STACK) { + /* stack accesses must be at a fixed offset, so that we can + * determine what type of data were returned. + * See check_stack_read(). + */ + if (!tnum_is_const(reg->var_off)) { + char tn_buf[48]; + + tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); + verbose("variable stack access var_off=%s off=%d size=%d", + tn_buf, off, size); + return -EACCES; + } + off += reg->var_off.value; if (off >= 0 || off < -MAX_BPF_STACK) { verbose("invalid stack off=%d size=%d\n", off, size); return -EACCES; @@ -948,7 +1046,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn } else { err = check_stack_read(state, off, size, value_regno); } - } else if (state->regs[regno].type == PTR_TO_PACKET) { + } else if (reg->type == PTR_TO_PACKET) { if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL, t)) { verbose("cannot write into packet\n"); return -EACCES; @@ -960,21 +1058,24 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn } err = check_packet_access(env, regno, off, size); if (!err && t == BPF_READ && value_regno >= 0) - mark_reg_unknown_value_and_range(state->regs, - value_regno); + mark_reg_unknown(state->regs, value_regno); } else { verbose("R%d invalid mem access '%s'\n", regno, reg_type_str[reg->type]); return -EACCES; } - if (!err && size <= 2 && value_regno >= 0 && env->allow_ptr_leaks && - state->regs[value_regno].type == UNKNOWN_VALUE) { - /* 1 or 2 byte load zero-extends, determine the number of - * zero upper bits. Not doing it fo 4 byte load, since - * such values cannot be added to ptr_to_packet anyway. - */ - state->regs[value_regno].imm = 64 - size * 8; + if (!err && size < BPF_REG_SIZE && value_regno >= 0 && t == BPF_READ && + state->regs[value_regno].type == SCALAR_VALUE) { + /* b/h/w load zero-extends, mark upper bits as known 0 */ + state->regs[value_regno].var_off = tnum_cast( + state->regs[value_regno].var_off, size); + /* sign bit is known zero, so we can bound the value */ + state->regs[value_regno].min_value = 0; + state->regs[value_regno].max_value = min_t(u64, + state->regs[value_regno].var_off.value | + state->regs[value_regno].var_off.mask, + BPF_REGISTER_MAX_RANGE); } return err; } @@ -1016,9 +1117,17 @@ static int check_xadd(struct bpf_verifier_env *env, int insn_idx, struct bpf_ins BPF_SIZE(insn->code), BPF_WRITE, -1); } +/* Does this register contain a constant zero? */ +static bool register_is_null(struct bpf_reg_state reg) +{ + return reg.type == SCALAR_VALUE && tnum_equals_const(reg.var_off, 0); +} + /* when register 'regno' is passed into function that will read 'access_size' * bytes from that pointer, make sure that it's within stack boundary - * and all elements of stack are initialized + * and all elements of stack are initialized. + * Unlike most pointer bounds-checking functions, this one doesn't take an + * 'off' argument, so it has to add in reg->off itself. */ static int check_stack_boundary(struct bpf_verifier_env *env, int regno, int access_size, bool zero_size_allowed, @@ -1029,9 +1138,9 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno, int off, i; if (regs[regno].type != PTR_TO_STACK) { + /* Allow zero-byte read from NULL, regardless of pointer type */ if (zero_size_allowed && access_size == 0 && - regs[regno].type == CONST_IMM && - regs[regno].imm == 0) + register_is_null(regs[regno])) return 0; verbose("R%d type=%s expected=%s\n", regno, @@ -1040,7 +1149,15 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno, return -EACCES; } - off = regs[regno].imm; + /* Only allow fixed-offset stack reads */ + if (!tnum_is_const(regs[regno].var_off)) { + char tn_buf[48]; + + tnum_strn(tn_buf, sizeof(tn_buf), regs[regno].var_off); + verbose("invalid variable stack read R%d var_off=%s\n", + regno, tn_buf); + } + off = regs[regno].off + regs[regno].var_off.value; if (off >= 0 || off < -MAX_BPF_STACK || off + access_size > 0 || access_size <= 0) { verbose("invalid stack type R%d off=%d access_size=%d\n", @@ -1071,16 +1188,14 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, int access_size, bool zero_size_allowed, struct bpf_call_arg_meta *meta) { - struct bpf_reg_state *regs = env->cur_state.regs; + struct bpf_reg_state *regs = env->cur_state.regs, *reg = ®s[regno]; - switch (regs[regno].type) { + switch (reg->type) { case PTR_TO_PACKET: - return check_packet_access(env, regno, 0, access_size); + return check_packet_access(env, regno, reg->off, access_size); case PTR_TO_MAP_VALUE: - return check_map_access(env, regno, 0, access_size); - case PTR_TO_MAP_VALUE_ADJ: - return check_map_access_adj(env, regno, 0, access_size); - default: /* const_imm|ptr_to_stack or invalid ptr */ + return check_map_access(env, regno, reg->off, access_size); + default: /* scalar_value|ptr_to_stack or invalid ptr */ return check_stack_boundary(env, regno, access_size, zero_size_allowed, meta); } @@ -1123,11 +1238,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, goto err_type; } else if (arg_type == ARG_CONST_SIZE || arg_type == ARG_CONST_SIZE_OR_ZERO) { - expected_type = CONST_IMM; - /* One exception. Allow UNKNOWN_VALUE registers when the - * boundaries are known and don't cause unsafe memory accesses - */ - if (type != UNKNOWN_VALUE && type != expected_type) + expected_type = SCALAR_VALUE; + if (type != expected_type) goto err_type; } else if (arg_type == ARG_CONST_MAP_PTR) { expected_type = CONST_PTR_TO_MAP; @@ -1141,13 +1253,13 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, arg_type == ARG_PTR_TO_UNINIT_MEM) { expected_type = PTR_TO_STACK; /* One exception here. In case function allows for NULL to be - * passed in as argument, it's a CONST_IMM type. Final test + * passed in as argument, it's a SCALAR_VALUE type. Final test * happens during stack boundary checking. */ - if (type == CONST_IMM && reg->imm == 0) + if (register_is_null(*reg)) /* final test in check_stack_boundary() */; else if (type != PTR_TO_PACKET && type != PTR_TO_MAP_VALUE && - type != PTR_TO_MAP_VALUE_ADJ && type != expected_type) + type != expected_type) goto err_type; meta->raw_mode = arg_type == ARG_PTR_TO_UNINIT_MEM; } else { @@ -1173,7 +1285,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, return -EACCES; } if (type == PTR_TO_PACKET) - err = check_packet_access(env, regno, 0, + err = check_packet_access(env, regno, reg->off, meta->map_ptr->key_size); else err = check_stack_boundary(env, regno, @@ -1189,7 +1301,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, return -EACCES; } if (type == PTR_TO_PACKET) - err = check_packet_access(env, regno, 0, + err = check_packet_access(env, regno, reg->off, meta->map_ptr->value_size); else err = check_stack_boundary(env, regno, @@ -1209,10 +1321,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, return -EACCES; } - /* If the register is UNKNOWN_VALUE, the access check happens - * using its boundaries. Otherwise, just use its imm + /* The register is SCALAR_VALUE; the access check + * happens using its boundaries. */ - if (type == UNKNOWN_VALUE) { + + if (!tnum_is_const(reg->var_off)) /* For unprivileged variable accesses, disable raw * mode so that the program is required to * initialize all the memory that the helper could @@ -1220,35 +1333,28 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, */ meta = NULL; - if (reg->min_value < 0) { - verbose("R%d min value is negative, either use unsigned or 'var &= const'\n", - regno); - return -EACCES; - } - - if (reg->min_value == 0) { - err = check_helper_mem_access(env, regno - 1, 0, - zero_size_allowed, - meta); - if (err) - return err; - } + if (reg->min_value < 0) { + verbose("R%d min value is negative, either use unsigned or 'var &= const'\n", + regno); + return -EACCES; + } - if (reg->max_value == BPF_REGISTER_MAX_RANGE) { - verbose("R%d unbounded memory access, use 'var &= const' or 'if (var < const)'\n", - regno); - return -EACCES; - } - err = check_helper_mem_access(env, regno - 1, - reg->max_value, - zero_size_allowed, meta); + if (reg->min_value == 0) { + err = check_helper_mem_access(env, regno - 1, 0, + zero_size_allowed, + meta); if (err) return err; - } else { - /* register is CONST_IMM */ - err = check_helper_mem_access(env, regno - 1, reg->imm, - zero_size_allowed, meta); } + + if (reg->max_value == BPF_REGISTER_MAX_RANGE) { + verbose("R%d unbounded memory access, use 'var &= const' or 'if (var < const)'\n", + regno); + return -EACCES; + } + err = check_helper_mem_access(env, regno - 1, + reg->max_value, + zero_size_allowed, meta); } return err; @@ -1352,6 +1458,9 @@ static int check_raw_mode(const struct bpf_func_proto *fn) return count > 1 ? -EINVAL : 0; } +/* Packet data might have moved, any old PTR_TO_PACKET[_END] are now invalid, + * so turn them into unknown SCALAR_VALUE. + */ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) { struct bpf_verifier_state *state = &env->cur_state; @@ -1361,7 +1470,7 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) for (i = 0; i < MAX_BPF_REG; i++) if (regs[i].type == PTR_TO_PACKET || regs[i].type == PTR_TO_PACKET_END) - mark_reg_unknown_value(regs, i); + mark_reg_unknown(regs, i); for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { if (state->stack_slot_type[i] != STACK_SPILL) @@ -1370,8 +1479,7 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) if (reg->type != PTR_TO_PACKET && reg->type != PTR_TO_PACKET_END) continue; - __mark_reg_unknown_value(state->spilled_regs, - i / BPF_REG_SIZE); + __mark_reg_unknown(reg); } } @@ -1451,14 +1559,17 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) /* update return register */ if (fn->ret_type == RET_INTEGER) { - regs[BPF_REG_0].type = UNKNOWN_VALUE; + /* sets type to SCALAR_VALUE */ + mark_reg_unknown(regs, BPF_REG_0); } else if (fn->ret_type == RET_VOID) { regs[BPF_REG_0].type = NOT_INIT; } else if (fn->ret_type == RET_PTR_TO_MAP_VALUE_OR_NULL) { struct bpf_insn_aux_data *insn_aux; regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL; - regs[BPF_REG_0].max_value = regs[BPF_REG_0].min_value = 0; + /* There is no offset yet applied, variable or fixed */ + mark_reg_known_zero(regs, BPF_REG_0); + regs[BPF_REG_0].off = 0; /* remember map_ptr, so that check_map_access() * can check 'value_size' boundary of memory access * to map element returned from bpf_map_lookup_elem() @@ -1489,456 +1600,337 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) return 0; } -static int check_packet_ptr_add(struct bpf_verifier_env *env, - struct bpf_insn *insn) +static void check_reg_overflow(struct bpf_reg_state *reg) { - struct bpf_reg_state *regs = env->cur_state.regs; - struct bpf_reg_state *dst_reg = ®s[insn->dst_reg]; - struct bpf_reg_state *src_reg = ®s[insn->src_reg]; - struct bpf_reg_state tmp_reg; - s32 imm; - - if (BPF_SRC(insn->code) == BPF_K) { - /* pkt_ptr += imm */ - imm = insn->imm; - -add_imm: - if (imm < 0) { - verbose("addition of negative constant to packet pointer is not allowed\n"); - return -EACCES; - } - if (imm >= MAX_PACKET_OFF || - imm + dst_reg->off >= MAX_PACKET_OFF) { - verbose("constant %d is too large to add to packet pointer\n", - imm); - return -EACCES; - } - /* a constant was added to pkt_ptr. - * Remember it while keeping the same 'id' - */ - dst_reg->off += imm; - } else { - bool had_id; - - if (src_reg->type == PTR_TO_PACKET) { - /* R6=pkt(id=0,off=0,r=62) R7=imm22; r7 += r6 */ - tmp_reg = *dst_reg; /* save r7 state */ - *dst_reg = *src_reg; /* copy pkt_ptr state r6 into r7 */ - src_reg = &tmp_reg; /* pretend it's src_reg state */ - /* if the checks below reject it, the copy won't matter, - * since we're rejecting the whole program. If all ok, - * then imm22 state will be added to r7 - * and r7 will be pkt(id=0,off=22,r=62) while - * r6 will stay as pkt(id=0,off=0,r=62) - */ - } - - if (src_reg->type == CONST_IMM) { - /* pkt_ptr += reg where reg is known constant */ - imm = src_reg->imm; - goto add_imm; - } - /* disallow pkt_ptr += reg - * if reg is not uknown_value with guaranteed zero upper bits - * otherwise pkt_ptr may overflow and addition will become - * subtraction which is not allowed - */ - if (src_reg->type != UNKNOWN_VALUE) { - verbose("cannot add '%s' to ptr_to_packet\n", - reg_type_str[src_reg->type]); - return -EACCES; - } - if (src_reg->imm < 48) { - verbose("cannot add integer value with %lld upper zero bits to ptr_to_packet\n", - src_reg->imm); - return -EACCES; - } - - had_id = (dst_reg->id != 0); + if (reg->max_value > BPF_REGISTER_MAX_RANGE) + reg->max_value = BPF_REGISTER_MAX_RANGE; + if (reg->min_value < BPF_REGISTER_MIN_RANGE || + reg->min_value > BPF_REGISTER_MAX_RANGE) + reg->min_value = BPF_REGISTER_MIN_RANGE; +} - /* dst_reg stays as pkt_ptr type and since some positive - * integer value was added to the pointer, increment its 'id' - */ - dst_reg->id = ++env->id_gen; - - /* something was added to pkt_ptr, set range to zero */ - dst_reg->aux_off += dst_reg->off; - dst_reg->off = 0; - dst_reg->range = 0; - if (had_id) - dst_reg->aux_off_align = min(dst_reg->aux_off_align, - src_reg->min_align); - else - dst_reg->aux_off_align = src_reg->min_align; +static void coerce_reg_to_32(struct bpf_reg_state *reg) +{ + /* 32-bit values can't be negative as an s64 */ + if (reg->min_value < 0) + reg->min_value = 0; + /* clear high 32 bits */ + reg->var_off = tnum_cast(reg->var_off, 4); + /* Did value become known? Then update bounds */ + if (tnum_is_const(reg->var_off)) { + if ((s64)reg->var_off.value > BPF_REGISTER_MIN_RANGE) + reg->min_value = reg->var_off.value; + if (reg->var_off.value < BPF_REGISTER_MAX_RANGE) + reg->max_value = reg->var_off.value; } - return 0; } -static int evaluate_reg_alu(struct bpf_verifier_env *env, struct bpf_insn *insn) +/* Handles arithmetic on a pointer and a scalar: computes new min/max and var_off. + * Caller must check_reg_overflow all argument regs beforehand. + * Caller should also handle BPF_MOV case separately. + * If we return -EACCES, caller may want to try again treating pointer as a + * scalar. So we only emit a diagnostic if !env->allow_ptr_leaks. + */ +static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, + struct bpf_insn *insn, + const struct bpf_reg_state *ptr_reg, + const struct bpf_reg_state *off_reg) { - struct bpf_reg_state *regs = env->cur_state.regs; - struct bpf_reg_state *dst_reg = ®s[insn->dst_reg]; + struct bpf_reg_state *regs = env->cur_state.regs, *dst_reg; + bool known = tnum_is_const(off_reg->var_off); + s64 min_val = off_reg->min_value; + u64 max_val = off_reg->max_value; u8 opcode = BPF_OP(insn->code); - s64 imm_log2; + u32 dst = insn->dst_reg; - /* for type == UNKNOWN_VALUE: - * imm > 0 -> number of zero upper bits - * imm == 0 -> don't track which is the same as all bits can be non-zero - */ + dst_reg = ®s[dst]; - if (BPF_SRC(insn->code) == BPF_X) { - struct bpf_reg_state *src_reg = ®s[insn->src_reg]; - - if (src_reg->type == UNKNOWN_VALUE && src_reg->imm > 0 && - dst_reg->imm && opcode == BPF_ADD) { - /* dreg += sreg - * where both have zero upper bits. Adding them - * can only result making one more bit non-zero - * in the larger value. - * Ex. 0xffff (imm=48) + 1 (imm=63) = 0x10000 (imm=47) - * 0xffff (imm=48) + 0xffff = 0x1fffe (imm=47) - */ - dst_reg->imm = min(dst_reg->imm, src_reg->imm); - dst_reg->imm--; - return 0; - } - if (src_reg->type == CONST_IMM && src_reg->imm > 0 && - dst_reg->imm && opcode == BPF_ADD) { - /* dreg += sreg - * where dreg has zero upper bits and sreg is const. - * Adding them can only result making one more bit - * non-zero in the larger value. - */ - imm_log2 = __ilog2_u64((long long)src_reg->imm); - dst_reg->imm = min(dst_reg->imm, 63 - imm_log2); - dst_reg->imm--; - return 0; - } - /* all other cases non supported yet, just mark dst_reg */ - dst_reg->imm = 0; - return 0; + if (WARN_ON_ONCE(known && (min_val != max_val))) { + print_verifier_state(&env->cur_state); + verbose("verifier internal error\n"); + return -EINVAL; + } + + if (BPF_CLASS(insn->code) != BPF_ALU64) { + /* 32-bit ALU ops on pointers produce (meaningless) scalars */ + if (!env->allow_ptr_leaks) + verbose("R%d 32-bit pointer arithmetic prohibited\n", + dst); + return -EACCES; } - /* sign extend 32-bit imm into 64-bit to make sure that - * negative values occupy bit 63. Note ilog2() would have - * been incorrect, since sizeof(insn->imm) == 4 + if (ptr_reg->type == PTR_TO_MAP_VALUE_OR_NULL) { + if (!env->allow_ptr_leaks) + verbose("R%d pointer arithmetic on PTR_TO_MAP_VALUE_OR_NULL prohibited, null-check it first\n", + dst); + return -EACCES; + } + if (ptr_reg->type == CONST_PTR_TO_MAP) { + if (!env->allow_ptr_leaks) + verbose("R%d pointer arithmetic on CONST_PTR_TO_MAP prohibited\n", + dst); + return -EACCES; + } + if (ptr_reg->type == PTR_TO_PACKET_END) { + if (!env->allow_ptr_leaks) + verbose("R%d pointer arithmetic on PTR_TO_PACKET_END prohibited\n", + dst); + return -EACCES; + } + + /* In case of 'scalar += pointer', dst_reg inherits pointer type and id. + * The id may be overwritten later if we create a new variable offset. */ - imm_log2 = __ilog2_u64((long long)insn->imm); + dst_reg->type = ptr_reg->type; + dst_reg->id = ptr_reg->id; - if (dst_reg->imm && opcode == BPF_LSH) { - /* reg <<= imm - * if reg was a result of 2 byte load, then its imm == 48 - * which means that upper 48 bits are zero and shifting this reg - * left by 4 would mean that upper 44 bits are still zero + switch (opcode) { + case BPF_ADD: + /* We can take a fixed offset as long as it doesn't overflow + * the s32 'off' field */ - dst_reg->imm -= insn->imm; - } else if (dst_reg->imm && opcode == BPF_MUL) { - /* reg *= imm - * if multiplying by 14 subtract 4 - * This is conservative calculation of upper zero bits. - * It's not trying to special case insn->imm == 1 or 0 cases + if (known && (ptr_reg->off + min_val == + (s64)(s32)(ptr_reg->off + min_val))) { + /* pointer += K. Accumulate it into fixed offset */ + dst_reg->min_value = ptr_reg->min_value; + dst_reg->max_value = ptr_reg->max_value; + dst_reg->var_off = ptr_reg->var_off; + dst_reg->off = ptr_reg->off + min_val; + dst_reg->range = ptr_reg->range; + break; + } + if (max_val == BPF_REGISTER_MAX_RANGE) { + if (!env->allow_ptr_leaks) + verbose("R%d tried to add unbounded value to pointer\n", + dst); + return -EACCES; + } + /* A new variable offset is created. Note that off_reg->off + * == 0, since it's a scalar. + * dst_reg gets the pointer type and since some positive + * integer value was added to the pointer, give it a new 'id' + * if it's a PTR_TO_PACKET. + * this creates a new 'base' pointer, off_reg (variable) gets + * added into the variable offset, and we copy the fixed offset + * from ptr_reg. */ - dst_reg->imm -= imm_log2 + 1; - } else if (opcode == BPF_AND) { - /* reg &= imm */ - dst_reg->imm = 63 - imm_log2; - } else if (dst_reg->imm && opcode == BPF_ADD) { - /* reg += imm */ - dst_reg->imm = min(dst_reg->imm, 63 - imm_log2); - dst_reg->imm--; - } else if (opcode == BPF_RSH) { - /* reg >>= imm - * which means that after right shift, upper bits will be zero - * note that verifier already checked that - * 0 <= imm < 64 for shift insn + if (min_val <= BPF_REGISTER_MIN_RANGE) + dst_reg->min_value = BPF_REGISTER_MIN_RANGE; + if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE) + dst_reg->min_value += min_val; + if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE) + dst_reg->max_value += max_val; + dst_reg->var_off = tnum_add(ptr_reg->var_off, off_reg->var_off); + dst_reg->off = ptr_reg->off; + if (ptr_reg->type == PTR_TO_PACKET) { + dst_reg->id = ++env->id_gen; + /* something was added to pkt_ptr, set range to zero */ + dst_reg->range = 0; + } + break; + case BPF_SUB: + if (dst_reg == off_reg) { + /* scalar -= pointer. Creates an unknown scalar */ + if (!env->allow_ptr_leaks) + verbose("R%d tried to subtract pointer from scalar\n", + dst); + return -EACCES; + } + /* We don't allow subtraction from FP, because (according to + * test_verifier.c test "invalid fp arithmetic", JITs might not + * be able to deal with it. */ - dst_reg->imm += insn->imm; - if (unlikely(dst_reg->imm > 64)) - /* some dumb code did: - * r2 = *(u32 *)mem; - * r2 >>= 32; - * and all bits are zero now */ - dst_reg->imm = 64; - } else { - /* all other alu ops, means that we don't know what will - * happen to the value, mark it with unknown number of zero bits + if (ptr_reg->type == PTR_TO_STACK) { + if (!env->allow_ptr_leaks) + verbose("R%d subtraction from stack pointer prohibited\n", + dst); + return -EACCES; + } + if (known && (ptr_reg->off - min_val == + (s64)(s32)(ptr_reg->off - min_val))) { + /* pointer -= K. Subtract it from fixed offset */ + dst_reg->min_value = ptr_reg->min_value; + dst_reg->max_value = ptr_reg->max_value; + dst_reg->var_off = ptr_reg->var_off; + dst_reg->id = ptr_reg->id; + dst_reg->off = ptr_reg->off - min_val; + dst_reg->range = ptr_reg->range; + break; + } + /* Subtracting a negative value will just confuse everything. + * This can happen if off_reg is an immediate. */ - dst_reg->imm = 0; - } - - if (dst_reg->imm < 0) { - /* all 64 bits of the register can contain non-zero bits - * and such value cannot be added to ptr_to_packet, since it - * may overflow, mark it as unknown to avoid further eval + if ((s64)max_val < 0) { + if (!env->allow_ptr_leaks) + verbose("R%d tried to subtract negative max_val %lld from pointer\n", + dst, (s64)max_val); + return -EACCES; + } + /* A new variable offset is created. If the subtrahend is known + * nonnegative, then any reg->range we had before is still good. */ - dst_reg->imm = 0; - } - return 0; -} - -static int evaluate_reg_imm_alu_unknown(struct bpf_verifier_env *env, - struct bpf_insn *insn) -{ - struct bpf_reg_state *regs = env->cur_state.regs; - struct bpf_reg_state *dst_reg = ®s[insn->dst_reg]; - struct bpf_reg_state *src_reg = ®s[insn->src_reg]; - u8 opcode = BPF_OP(insn->code); - s64 imm_log2 = __ilog2_u64((long long)dst_reg->imm); - - /* BPF_X code with src_reg->type UNKNOWN_VALUE here. */ - if (src_reg->imm > 0 && dst_reg->imm) { - switch (opcode) { - case BPF_ADD: - /* dreg += sreg - * where both have zero upper bits. Adding them - * can only result making one more bit non-zero - * in the larger value. - * Ex. 0xffff (imm=48) + 1 (imm=63) = 0x10000 (imm=47) - * 0xffff (imm=48) + 0xffff = 0x1fffe (imm=47) - */ - dst_reg->imm = min(src_reg->imm, 63 - imm_log2); - dst_reg->imm--; - break; - case BPF_AND: - /* dreg &= sreg - * AND can not extend zero bits only shrink - * Ex. 0x00..00ffffff - * & 0x0f..ffffffff - * ---------------- - * 0x00..00ffffff - */ - dst_reg->imm = max(src_reg->imm, 63 - imm_log2); - break; - case BPF_OR: - /* dreg |= sreg - * OR can only extend zero bits - * Ex. 0x00..00ffffff - * | 0x0f..ffffffff - * ---------------- - * 0x0f..00ffffff - */ - dst_reg->imm = min(src_reg->imm, 63 - imm_log2); - break; - case BPF_SUB: - case BPF_MUL: - case BPF_RSH: - case BPF_LSH: - /* These may be flushed out later */ - default: - mark_reg_unknown_value(regs, insn->dst_reg); + if (max_val >= BPF_REGISTER_MAX_RANGE) + dst_reg->min_value = BPF_REGISTER_MIN_RANGE; + if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE) + dst_reg->min_value -= max_val; + if (min_val <= BPF_REGISTER_MIN_RANGE) + dst_reg->max_value = BPF_REGISTER_MAX_RANGE; + if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE) + dst_reg->max_value -= min_val; + dst_reg->var_off = tnum_sub(ptr_reg->var_off, off_reg->var_off); + dst_reg->off = ptr_reg->off; + if (ptr_reg->type == PTR_TO_PACKET) { + dst_reg->id = ++env->id_gen; + /* something was added to pkt_ptr, set range to zero */ + if (min_val < 0) + dst_reg->range = 0; } - } else { - mark_reg_unknown_value(regs, insn->dst_reg); + break; + case BPF_AND: + case BPF_OR: + case BPF_XOR: + /* bitwise ops on pointers are troublesome, prohibit for now. + * (However, in principle we could allow some cases, e.g. + * ptr &= ~3 which would reduce min_value by 3.) + */ + if (!env->allow_ptr_leaks) + verbose("R%d bitwise operator %s on pointer prohibited\n", + dst, bpf_alu_string[opcode >> 4]); + return -EACCES; + default: + /* other operators (e.g. MUL,LSH) produce non-pointer results */ + if (!env->allow_ptr_leaks) + verbose("R%d pointer arithmetic with %s operator prohibited\n", + dst, bpf_alu_string[opcode >> 4]); + return -EACCES; } - dst_reg->type = UNKNOWN_VALUE; + check_reg_overflow(dst_reg); return 0; } -static int evaluate_reg_imm_alu(struct bpf_verifier_env *env, - struct bpf_insn *insn) +static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, + struct bpf_insn *insn, + struct bpf_reg_state *dst_reg, + struct bpf_reg_state src_reg) { struct bpf_reg_state *regs = env->cur_state.regs; - struct bpf_reg_state *dst_reg = ®s[insn->dst_reg]; - struct bpf_reg_state *src_reg = ®s[insn->src_reg]; - u8 opcode = BPF_OP(insn->code); - u64 dst_imm = dst_reg->imm; - - if (BPF_SRC(insn->code) == BPF_X && src_reg->type == UNKNOWN_VALUE) - return evaluate_reg_imm_alu_unknown(env, insn); - - /* dst_reg->type == CONST_IMM here. Simulate execution of insns - * containing ALU ops. Don't care about overflow or negative - * values, just add/sub/... them; registers are in u64. - */ - if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_K) { - dst_imm += insn->imm; - } else if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_X && - src_reg->type == CONST_IMM) { - dst_imm += src_reg->imm; - } else if (opcode == BPF_SUB && BPF_SRC(insn->code) == BPF_K) { - dst_imm -= insn->imm; - } else if (opcode == BPF_SUB && BPF_SRC(insn->code) == BPF_X && - src_reg->type == CONST_IMM) { - dst_imm -= src_reg->imm; - } else if (opcode == BPF_MUL && BPF_SRC(insn->code) == BPF_K) { - dst_imm *= insn->imm; - } else if (opcode == BPF_MUL && BPF_SRC(insn->code) == BPF_X && - src_reg->type == CONST_IMM) { - dst_imm *= src_reg->imm; - } else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_K) { - dst_imm |= insn->imm; - } else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_X && - src_reg->type == CONST_IMM) { - dst_imm |= src_reg->imm; - } else if (opcode == BPF_AND && BPF_SRC(insn->code) == BPF_K) { - dst_imm &= insn->imm; - } else if (opcode == BPF_AND && BPF_SRC(insn->code) == BPF_X && - src_reg->type == CONST_IMM) { - dst_imm &= src_reg->imm; - } else if (opcode == BPF_RSH && BPF_SRC(insn->code) == BPF_K) { - dst_imm >>= insn->imm; - } else if (opcode == BPF_RSH && BPF_SRC(insn->code) == BPF_X && - src_reg->type == CONST_IMM) { - dst_imm >>= src_reg->imm; - } else if (opcode == BPF_LSH && BPF_SRC(insn->code) == BPF_K) { - dst_imm <<= insn->imm; - } else if (opcode == BPF_LSH && BPF_SRC(insn->code) == BPF_X && - src_reg->type == CONST_IMM) { - dst_imm <<= src_reg->imm; - } else { - mark_reg_unknown_value(regs, insn->dst_reg); - goto out; - } - - dst_reg->imm = dst_imm; -out: - return 0; -} - -static void check_reg_overflow(struct bpf_reg_state *reg) -{ - if (reg->max_value > BPF_REGISTER_MAX_RANGE) - reg->max_value = BPF_REGISTER_MAX_RANGE; - if (reg->min_value < BPF_REGISTER_MIN_RANGE || - reg->min_value > BPF_REGISTER_MAX_RANGE) - reg->min_value = BPF_REGISTER_MIN_RANGE; -} - -static u32 calc_align(u32 imm) -{ - if (!imm) - return 1U << 31; - return imm - ((imm - 1) & imm); -} - -static void adjust_reg_min_max_vals(struct bpf_verifier_env *env, - struct bpf_insn *insn) -{ - struct bpf_reg_state *regs = env->cur_state.regs, *dst_reg; s64 min_val = BPF_REGISTER_MIN_RANGE; u64 max_val = BPF_REGISTER_MAX_RANGE; u8 opcode = BPF_OP(insn->code); - u32 dst_align, src_align; + bool src_known, dst_known; - dst_reg = ®s[insn->dst_reg]; - src_align = 0; - if (BPF_SRC(insn->code) == BPF_X) { - check_reg_overflow(®s[insn->src_reg]); - min_val = regs[insn->src_reg].min_value; - max_val = regs[insn->src_reg].max_value; - - /* If the source register is a random pointer then the - * min_value/max_value values represent the range of the known - * accesses into that value, not the actual min/max value of the - * register itself. In this case we have to reset the reg range - * values so we know it is not safe to look at. - */ - if (regs[insn->src_reg].type != CONST_IMM && - regs[insn->src_reg].type != UNKNOWN_VALUE) { - min_val = BPF_REGISTER_MIN_RANGE; - max_val = BPF_REGISTER_MAX_RANGE; - src_align = 0; - } else { - src_align = regs[insn->src_reg].min_align; - } - } else if (insn->imm < BPF_REGISTER_MAX_RANGE && - (s64)insn->imm > BPF_REGISTER_MIN_RANGE) { - min_val = max_val = insn->imm; - src_align = calc_align(insn->imm); - } - - dst_align = dst_reg->min_align; - - /* We don't know anything about what was done to this register, mark it - * as unknown. Also, if both derived bounds came from signed/unsigned - * mixed compares and one side is unbounded, we cannot really do anything - * with them as boundaries cannot be trusted. Thus, arithmetic of two - * regs of such kind will get invalidated bounds on the dst side. - */ - if ((min_val == BPF_REGISTER_MIN_RANGE && - max_val == BPF_REGISTER_MAX_RANGE) || - (BPF_SRC(insn->code) == BPF_X && - ((min_val != BPF_REGISTER_MIN_RANGE && - max_val == BPF_REGISTER_MAX_RANGE) || - (min_val == BPF_REGISTER_MIN_RANGE && - max_val != BPF_REGISTER_MAX_RANGE) || - (dst_reg->min_value != BPF_REGISTER_MIN_RANGE && - dst_reg->max_value == BPF_REGISTER_MAX_RANGE) || - (dst_reg->min_value == BPF_REGISTER_MIN_RANGE && - dst_reg->max_value != BPF_REGISTER_MAX_RANGE)) && - regs[insn->dst_reg].value_from_signed != - regs[insn->src_reg].value_from_signed)) { - reset_reg_range_values(regs, insn->dst_reg); - return; - } - - /* If one of our values was at the end of our ranges then we can't just - * do our normal operations to the register, we need to set the values - * to the min/max since they are undefined. - */ - if (opcode != BPF_SUB) { - if (min_val == BPF_REGISTER_MIN_RANGE) - dst_reg->min_value = BPF_REGISTER_MIN_RANGE; - if (max_val == BPF_REGISTER_MAX_RANGE) - dst_reg->max_value = BPF_REGISTER_MAX_RANGE; + if (BPF_CLASS(insn->code) != BPF_ALU64) { + /* 32-bit ALU ops are (32,32)->64 */ + coerce_reg_to_32(dst_reg); + coerce_reg_to_32(&src_reg); } + min_val = src_reg.min_value; + max_val = src_reg.max_value; + src_known = tnum_is_const(src_reg.var_off); + dst_known = tnum_is_const(dst_reg->var_off); switch (opcode) { case BPF_ADD: + if (min_val == BPF_REGISTER_MIN_RANGE) + dst_reg->min_value = BPF_REGISTER_MIN_RANGE; if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE) dst_reg->min_value += min_val; + /* if max_val is MAX_RANGE, this will saturate dst->max */ if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE) dst_reg->max_value += max_val; - dst_reg->min_align = min(src_align, dst_align); + dst_reg->var_off = tnum_add(dst_reg->var_off, src_reg.var_off); break; case BPF_SUB: - /* If one of our values was at the end of our ranges, then the - * _opposite_ value in the dst_reg goes to the end of our range. - */ - if (min_val == BPF_REGISTER_MIN_RANGE) - dst_reg->max_value = BPF_REGISTER_MAX_RANGE; if (max_val == BPF_REGISTER_MAX_RANGE) dst_reg->min_value = BPF_REGISTER_MIN_RANGE; if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE) dst_reg->min_value -= max_val; + if (min_val == BPF_REGISTER_MIN_RANGE) + dst_reg->max_value = BPF_REGISTER_MAX_RANGE; if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE) dst_reg->max_value -= min_val; - dst_reg->min_align = min(src_align, dst_align); + dst_reg->var_off = tnum_sub(dst_reg->var_off, src_reg.var_off); break; case BPF_MUL: - if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE) - dst_reg->min_value *= min_val; + if (min_val < 0 || dst_reg->min_value < 0) { + /* Ain't nobody got time to multiply that sign */ + __mark_reg_unknown(dst_reg); + break; + } + dst_reg->min_value *= min_val; + /* if max_val is MAX_RANGE, this will saturate dst->max. + * We know MAX_RANGE ** 2 won't overflow a u64, because + * MAX_RANGE itself fits in a u32. + */ + BUILD_BUG_ON(BPF_REGISTER_MAX_RANGE > (u32)-1); if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE) dst_reg->max_value *= max_val; - dst_reg->min_align = max(src_align, dst_align); + dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off); break; case BPF_AND: - /* Disallow AND'ing of negative numbers, ain't nobody got time - * for that. Otherwise the minimum is 0 and the max is the max - * value we could AND against. + if (src_known && dst_known) { + u64 value = dst_reg->var_off.value & src_reg.var_off.value; + + dst_reg->var_off = tnum_const(value); + dst_reg->min_value = dst_reg->max_value = min_t(u64, + value, BPF_REGISTER_MAX_RANGE); + break; + } + /* Lose min_value when AND'ing negative numbers, ain't nobody + * got time for that. Otherwise we get our minimum from the + * var_off, since that's inherently bitwise. + * Our maximum is the minimum of the operands' maxima. */ - if (min_val < 0) + dst_reg->var_off = tnum_and(dst_reg->var_off, src_reg.var_off); + if (min_val < 0 && dst_reg->min_value < 0) dst_reg->min_value = BPF_REGISTER_MIN_RANGE; else - dst_reg->min_value = 0; - dst_reg->max_value = max_val; - dst_reg->min_align = max(src_align, dst_align); + dst_reg->min_value = dst_reg->var_off.value; + dst_reg->max_value = min(dst_reg->max_value, max_val); + break; + case BPF_OR: + if (src_known && dst_known) { + u64 value = dst_reg->var_off.value | src_reg.var_off.value; + + dst_reg->var_off = tnum_const(value); + dst_reg->min_value = dst_reg->max_value = min_t(u64, + value, BPF_REGISTER_MAX_RANGE); + break; + } + /* Lose ranges when OR'ing negative numbers, ain't nobody got + * time for that. Otherwise we get our maximum from the var_off, + * and our minimum is the maximum of the operands' minima. + */ + dst_reg->var_off = tnum_or(dst_reg->var_off, src_reg.var_off); + if (min_val < 0 || dst_reg->min_value < 0) { + dst_reg->min_value = BPF_REGISTER_MIN_RANGE; + dst_reg->max_value = BPF_REGISTER_MAX_RANGE; + } else { + dst_reg->min_value = max(dst_reg->min_value, min_val); + dst_reg->max_value = dst_reg->var_off.value | dst_reg->var_off.mask; + } break; case BPF_LSH: + if (min_val < 0) { + /* LSH by a negative number is undefined */ + mark_reg_unknown(regs, insn->dst_reg); + break; + } /* Gotta have special overflow logic here, if we're shifting * more than MAX_RANGE then just assume we have an invalid * range. */ if (min_val > ilog2(BPF_REGISTER_MAX_RANGE)) { dst_reg->min_value = BPF_REGISTER_MIN_RANGE; - dst_reg->min_align = 1; + dst_reg->var_off = tnum_unknown; } else { if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE) dst_reg->min_value <<= min_val; - if (!dst_reg->min_align) - dst_reg->min_align = 1; - dst_reg->min_align <<= min_val; + if (src_known) + dst_reg->var_off = tnum_lshift(dst_reg->var_off, min_val); + else + dst_reg->var_off = tnum_lshift(tnum_unknown, min_val); } if (max_val > ilog2(BPF_REGISTER_MAX_RANGE)) dst_reg->max_value = BPF_REGISTER_MAX_RANGE; @@ -1946,37 +1938,139 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env, dst_reg->max_value <<= max_val; break; case BPF_RSH: - /* RSH by a negative number is undefined, and the BPF_RSH is an - * unsigned shift, so make the appropriate casts. - */ - if (min_val < 0 || dst_reg->min_value < 0) { - dst_reg->min_value = BPF_REGISTER_MIN_RANGE; + if (min_val < 0) { + /* RSH by a negative number is undefined */ + mark_reg_unknown(regs, insn->dst_reg); + break; + } + /* BPF_RSH is an unsigned shift, so make the appropriate casts */ + if (dst_reg->min_value < 0) { + if (min_val) + /* Sign bit will be cleared */ + dst_reg->min_value = 0; } else { dst_reg->min_value = (u64)(dst_reg->min_value) >> min_val; } - if (min_val < 0) { - dst_reg->min_align = 1; - } else { - dst_reg->min_align >>= (u64) min_val; - if (!dst_reg->min_align) - dst_reg->min_align = 1; - } - if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE) - dst_reg->max_value >>= max_val; + if (src_known) + dst_reg->var_off = tnum_rshift(dst_reg->var_off, min_val); + else + dst_reg->var_off = tnum_rshift(tnum_unknown, min_val); + if (dst_reg->max_value == BPF_REGISTER_MAX_RANGE) + dst_reg->max_value = ~0; + dst_reg->max_value >>= max_val; break; default: - reset_reg_range_values(regs, insn->dst_reg); + mark_reg_unknown(regs, insn->dst_reg); break; } check_reg_overflow(dst_reg); + return 0; +} + +/* Handles ALU ops other than BPF_END, BPF_NEG and BPF_MOV: computes new min/max + * and var_off. + */ +static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, + struct bpf_insn *insn) +{ + struct bpf_reg_state *regs = env->cur_state.regs, *dst_reg, *src_reg; + struct bpf_reg_state *ptr_reg = NULL, off_reg = {0}; + u8 opcode = BPF_OP(insn->code); + int rc; + + dst_reg = ®s[insn->dst_reg]; + check_reg_overflow(dst_reg); + src_reg = NULL; + if (dst_reg->type != SCALAR_VALUE) + ptr_reg = dst_reg; + if (BPF_SRC(insn->code) == BPF_X) { + src_reg = ®s[insn->src_reg]; + check_reg_overflow(src_reg); + + if (src_reg->type != SCALAR_VALUE) { + if (dst_reg->type != SCALAR_VALUE) { + /* Combining two pointers by any ALU op yields + * an arbitrary scalar. + */ + if (!env->allow_ptr_leaks) { + verbose("R%d pointer %s pointer prohibited\n", + insn->dst_reg, + bpf_alu_string[opcode >> 4]); + return -EACCES; + } + mark_reg_unknown(regs, insn->dst_reg); + return 0; + } else { + /* scalar += pointer + * This is legal, but we have to reverse our + * src/dest handling in computing the range + */ + rc = adjust_ptr_min_max_vals(env, insn, + src_reg, dst_reg); + if (rc == -EACCES && env->allow_ptr_leaks) { + /* scalar += unknown scalar */ + __mark_reg_unknown(&off_reg); + return adjust_scalar_min_max_vals( + env, insn, + dst_reg, off_reg); + } + return rc; + } + } else if (ptr_reg) { + /* pointer += scalar */ + rc = adjust_ptr_min_max_vals(env, insn, + dst_reg, src_reg); + if (rc == -EACCES && env->allow_ptr_leaks) { + /* unknown scalar += scalar */ + __mark_reg_unknown(dst_reg); + return adjust_scalar_min_max_vals( + env, insn, dst_reg, *src_reg); + } + return rc; + } + } else { + /* Pretend the src is a reg with a known value, since we only + * need to be able to read from this state. + */ + off_reg.type = SCALAR_VALUE; + off_reg.var_off = tnum_const(insn->imm); + off_reg.min_value = insn->imm; + off_reg.max_value = insn->imm; + src_reg = &off_reg; + check_reg_overflow(src_reg); + if (ptr_reg) { /* pointer += K */ + rc = adjust_ptr_min_max_vals(env, insn, + ptr_reg, src_reg); + if (rc == -EACCES && env->allow_ptr_leaks) { + /* unknown scalar += K */ + __mark_reg_unknown(dst_reg); + return adjust_scalar_min_max_vals( + env, insn, dst_reg, off_reg); + } + return rc; + } + } + + /* Got here implies adding two SCALAR_VALUEs */ + if (WARN_ON_ONCE(ptr_reg)) { + print_verifier_state(&env->cur_state); + verbose("verifier internal error: unexpected ptr_reg\n"); + return -EINVAL; + } + if (WARN_ON(!src_reg)) { + print_verifier_state(&env->cur_state); + verbose("verifier internal error: no src_reg\n"); + return -EINVAL; + } + return adjust_scalar_min_max_vals(env, insn, dst_reg, *src_reg); } /* check validity of 32-bit and 64-bit arithmetic operations */ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) { - struct bpf_reg_state *regs = env->cur_state.regs, *dst_reg; + struct bpf_reg_state *regs = env->cur_state.regs; u8 opcode = BPF_OP(insn->code); int err; @@ -2036,11 +2130,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) if (err) return err; - /* we are setting our register to something new, we need to - * reset its range values. - */ - reset_reg_range_values(regs, insn->dst_reg); - if (BPF_SRC(insn->code) == BPF_X) { if (BPF_CLASS(insn->code) == BPF_ALU64) { /* case: R1 = R2 @@ -2048,24 +2137,29 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) */ regs[insn->dst_reg] = regs[insn->src_reg]; } else { + /* R1 = (u32) R2 */ if (is_pointer_value(env, insn->src_reg)) { verbose("R%d partial copy of pointer\n", insn->src_reg); return -EACCES; } - mark_reg_unknown_value(regs, insn->dst_reg); + mark_reg_unknown(regs, insn->dst_reg); + /* high 32 bits are known zero. But this is + * still out of range for max_value, so leave + * that. + */ + regs[insn->dst_reg].var_off = tnum_cast( + regs[insn->dst_reg].var_off, 4); } } else { /* case: R = imm * remember the value we stored into this reg */ - regs[insn->dst_reg].type = CONST_IMM; - regs[insn->dst_reg].imm = insn->imm; - regs[insn->dst_reg].id = 0; + regs[insn->dst_reg].type = SCALAR_VALUE; + regs[insn->dst_reg].var_off = tnum_const(insn->imm); regs[insn->dst_reg].max_value = insn->imm; regs[insn->dst_reg].min_value = insn->imm; - regs[insn->dst_reg].min_align = calc_align(insn->imm); - regs[insn->dst_reg].value_from_signed = false; + regs[insn->dst_reg].id = 0; } } else if (opcode > BPF_END) { @@ -2116,68 +2210,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) if (err) return err; - dst_reg = ®s[insn->dst_reg]; - - /* first we want to adjust our ranges. */ - adjust_reg_min_max_vals(env, insn); - - /* pattern match 'bpf_add Rx, imm' instruction */ - if (opcode == BPF_ADD && BPF_CLASS(insn->code) == BPF_ALU64 && - dst_reg->type == FRAME_PTR && BPF_SRC(insn->code) == BPF_K) { - dst_reg->type = PTR_TO_STACK; - dst_reg->imm = insn->imm; - return 0; - } else if (opcode == BPF_ADD && - BPF_CLASS(insn->code) == BPF_ALU64 && - dst_reg->type == PTR_TO_STACK && - ((BPF_SRC(insn->code) == BPF_X && - regs[insn->src_reg].type == CONST_IMM) || - BPF_SRC(insn->code) == BPF_K)) { - if (BPF_SRC(insn->code) == BPF_X) - dst_reg->imm += regs[insn->src_reg].imm; - else - dst_reg->imm += insn->imm; - return 0; - } else if (opcode == BPF_ADD && - BPF_CLASS(insn->code) == BPF_ALU64 && - (dst_reg->type == PTR_TO_PACKET || - (BPF_SRC(insn->code) == BPF_X && - regs[insn->src_reg].type == PTR_TO_PACKET))) { - /* ptr_to_packet += K|X */ - return check_packet_ptr_add(env, insn); - } else if (BPF_CLASS(insn->code) == BPF_ALU64 && - dst_reg->type == UNKNOWN_VALUE && - env->allow_ptr_leaks) { - /* unknown += K|X */ - return evaluate_reg_alu(env, insn); - } else if (BPF_CLASS(insn->code) == BPF_ALU64 && - dst_reg->type == CONST_IMM && - env->allow_ptr_leaks) { - /* reg_imm += K|X */ - return evaluate_reg_imm_alu(env, insn); - } else if (is_pointer_value(env, insn->dst_reg)) { - verbose("R%d pointer arithmetic prohibited\n", - insn->dst_reg); - return -EACCES; - } else if (BPF_SRC(insn->code) == BPF_X && - is_pointer_value(env, insn->src_reg)) { - verbose("R%d pointer arithmetic prohibited\n", - insn->src_reg); - return -EACCES; - } - - /* If we did pointer math on a map value then just set it to our - * PTR_TO_MAP_VALUE_ADJ type so we can deal with any stores or - * loads to this register appropriately, otherwise just mark the - * register as unknown. - */ - if (env->allow_ptr_leaks && - BPF_CLASS(insn->code) == BPF_ALU64 && opcode == BPF_ADD && - (dst_reg->type == PTR_TO_MAP_VALUE || - dst_reg->type == PTR_TO_MAP_VALUE_ADJ)) - dst_reg->type = PTR_TO_MAP_VALUE_ADJ; - else - mark_reg_unknown_value(regs, insn->dst_reg); + return adjust_reg_min_max_vals(env, insn); } return 0; @@ -2189,6 +2222,17 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, struct bpf_reg_state *regs = state->regs, *reg; int i; + if (dst_reg->off < 0) + /* This doesn't give us any range */ + return; + + if (dst_reg->max_value > MAX_PACKET_OFF || + dst_reg->max_value + dst_reg->off > MAX_PACKET_OFF) + /* Risk of overflow. For instance, ptr + (1<<63) may be less + * than pkt_end, but that's because it's also less than pkt. + */ + return; + /* LLVM can generate two kind of checks: * * Type 1: @@ -2219,30 +2263,44 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, * so that range of bytes [r3, r3 + 8) is safe to access. */ + /* If our ids match, then we must have the same max_value. And we + * don't care about the other reg's fixed offset, since if it's too big + * the range won't allow anything. + * dst_reg->off is known < MAX_PACKET_OFF, therefore it fits in a u16. + */ for (i = 0; i < MAX_BPF_REG; i++) if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id) /* keep the maximum range already checked */ - regs[i].range = max(regs[i].range, dst_reg->off); + regs[i].range = max_t(u16, regs[i].range, dst_reg->off); for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { if (state->stack_slot_type[i] != STACK_SPILL) continue; reg = &state->spilled_regs[i / BPF_REG_SIZE]; if (reg->type == PTR_TO_PACKET && reg->id == dst_reg->id) - reg->range = max(reg->range, dst_reg->off); + reg->range = max_t(u16, reg->range, dst_reg->off); } } /* Adjusts the register min/max values in the case that the dst_reg is the * variable register that we are working on, and src_reg is a constant or we're * simply doing a BPF_K check. + * In JEQ/JNE cases we also adjust the var_off values. */ static void reg_set_min_max(struct bpf_reg_state *true_reg, struct bpf_reg_state *false_reg, u64 val, u8 opcode) { bool value_from_signed = true; - bool is_range = true; + + /* If the dst_reg is a pointer, we can't learn anything about its + * variable offset from the compare (unless src_reg were a pointer into + * the same object, but we don't bother with that. + * Since false_reg and true_reg have the same type by construction, we + * only need to check one of them for pointerness. + */ + if (__is_pointer_value(false, false_reg)) + return; switch (opcode) { case BPF_JEQ: @@ -2250,14 +2308,14 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg, * true then we know for sure. */ true_reg->max_value = true_reg->min_value = val; - is_range = false; + true_reg->var_off = tnum_const(val); break; case BPF_JNE: /* If this is true we know nothing Jon Snow, but if it is false * we know the value for sure; */ false_reg->max_value = false_reg->min_value = val; - is_range = false; + false_reg->var_off = tnum_const(val); break; case BPF_JGT: value_from_signed = false; @@ -2305,23 +2363,19 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg, check_reg_overflow(false_reg); check_reg_overflow(true_reg); - if (is_range) { - if (__is_pointer_value(false, false_reg)) - reset_reg_range_values(false_reg, 0); - if (__is_pointer_value(false, true_reg)) - reset_reg_range_values(true_reg, 0); - } } -/* Same as above, but for the case that dst_reg is a CONST_IMM reg and src_reg - * is the variable reg. +/* Same as above, but for the case that dst_reg holds a constant and src_reg is + * the variable reg. */ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg, struct bpf_reg_state *false_reg, u64 val, u8 opcode) { bool value_from_signed = true; - bool is_range = true; + + if (__is_pointer_value(false, false_reg)) + return; switch (opcode) { case BPF_JEQ: @@ -2329,14 +2383,14 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg, * true then we know for sure. */ true_reg->max_value = true_reg->min_value = val; - is_range = false; + true_reg->var_off = tnum_const(val); break; case BPF_JNE: /* If this is true we know nothing Jon Snow, but if it is false * we know the value for sure; */ false_reg->max_value = false_reg->min_value = val; - is_range = false; + false_reg->var_off = tnum_const(val); break; case BPF_JGT: value_from_signed = false; @@ -2385,27 +2439,60 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg, check_reg_overflow(false_reg); check_reg_overflow(true_reg); - if (is_range) { - if (__is_pointer_value(false, false_reg)) - reset_reg_range_values(false_reg, 0); - if (__is_pointer_value(false, true_reg)) - reset_reg_range_values(true_reg, 0); +} + +/* Regs are known to be equal, so intersect their min/max/var_off */ +static void __reg_combine_min_max(struct bpf_reg_state *src_reg, + struct bpf_reg_state *dst_reg) +{ + src_reg->min_value = dst_reg->min_value = max(src_reg->min_value, + dst_reg->min_value); + src_reg->max_value = dst_reg->max_value = min(src_reg->max_value, + dst_reg->max_value); + src_reg->var_off = dst_reg->var_off = tnum_intersect(src_reg->var_off, + dst_reg->var_off); + check_reg_overflow(src_reg); + check_reg_overflow(dst_reg); +} + +static void reg_combine_min_max(struct bpf_reg_state *true_src, + struct bpf_reg_state *true_dst, + struct bpf_reg_state *false_src, + struct bpf_reg_state *false_dst, + u8 opcode) +{ + switch (opcode) { + case BPF_JEQ: + __reg_combine_min_max(true_src, true_dst); + break; + case BPF_JNE: + __reg_combine_min_max(false_src, false_dst); } } static void mark_map_reg(struct bpf_reg_state *regs, u32 regno, u32 id, - enum bpf_reg_type type) + bool is_null) { struct bpf_reg_state *reg = ®s[regno]; if (reg->type == PTR_TO_MAP_VALUE_OR_NULL && reg->id == id) { - if (type == UNKNOWN_VALUE) { - __mark_reg_unknown_value(regs, regno); + /* Old offset (both fixed and variable parts) should + * have been known-zero, because we don't allow pointer + * arithmetic on pointers that might be NULL. + */ + if (WARN_ON_ONCE(reg->min_value || reg->max_value || + reg->var_off.value || reg->var_off.mask || + reg->off)) { + reg->min_value = reg->max_value = reg->off = 0; + reg->var_off = tnum_const(0); + } + if (is_null) { + reg->type = SCALAR_VALUE; } else if (reg->map_ptr->inner_map_meta) { reg->type = CONST_PTR_TO_MAP; reg->map_ptr = reg->map_ptr->inner_map_meta; } else { - reg->type = type; + reg->type = PTR_TO_MAP_VALUE; } /* We don't need id from this point onwards anymore, thus we * should better reset it, so that state pruning has chances @@ -2419,19 +2506,19 @@ static void mark_map_reg(struct bpf_reg_state *regs, u32 regno, u32 id, * be folded together at some point. */ static void mark_map_regs(struct bpf_verifier_state *state, u32 regno, - enum bpf_reg_type type) + bool is_null) { struct bpf_reg_state *regs = state->regs; u32 id = regs[regno].id; int i; for (i = 0; i < MAX_BPF_REG; i++) - mark_map_reg(regs, i, id, type); + mark_map_reg(regs, i, id, is_null); for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { if (state->stack_slot_type[i] != STACK_SPILL) continue; - mark_map_reg(state->spilled_regs, i / BPF_REG_SIZE, id, type); + mark_map_reg(state->spilled_regs, i / BPF_REG_SIZE, id, is_null); } } @@ -2481,7 +2568,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, /* detect if R == 0 where R was initialized to zero earlier */ if (BPF_SRC(insn->code) == BPF_K && (opcode == BPF_JEQ || opcode == BPF_JNE) && - dst_reg->type == CONST_IMM && dst_reg->imm == insn->imm) { + dst_reg->type == SCALAR_VALUE && + tnum_equals_const(dst_reg->var_off, insn->imm)) { if (opcode == BPF_JEQ) { /* if (imm == imm) goto pc+off; * only follow the goto, ignore fall-through @@ -2503,17 +2591,30 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, /* detect if we are comparing against a constant value so we can adjust * our min/max values for our dst register. + * this is only legit if both are scalars (or pointers to the same + * object, I suppose, but we don't support that right now), because + * otherwise the different base pointers mean the offsets aren't + * comparable. */ if (BPF_SRC(insn->code) == BPF_X) { - if (regs[insn->src_reg].type == CONST_IMM) - reg_set_min_max(&other_branch->regs[insn->dst_reg], - dst_reg, regs[insn->src_reg].imm, - opcode); - else if (dst_reg->type == CONST_IMM) - reg_set_min_max_inv(&other_branch->regs[insn->src_reg], - ®s[insn->src_reg], dst_reg->imm, - opcode); - } else { + if (dst_reg->type == SCALAR_VALUE && + regs[insn->src_reg].type == SCALAR_VALUE) { + if (tnum_is_const(regs[insn->src_reg].var_off)) + reg_set_min_max(&other_branch->regs[insn->dst_reg], + dst_reg, regs[insn->src_reg].var_off.value, + opcode); + else if (tnum_is_const(dst_reg->var_off)) + reg_set_min_max_inv(&other_branch->regs[insn->src_reg], + ®s[insn->src_reg], + dst_reg->var_off.value, opcode); + else if (opcode == BPF_JEQ || opcode == BPF_JNE) + /* Comparing for equality, we can combine knowledge */ + reg_combine_min_max(&other_branch->regs[insn->src_reg], + &other_branch->regs[insn->dst_reg], + ®s[insn->src_reg], + ®s[insn->dst_reg], opcode); + } + } else if (dst_reg->type == SCALAR_VALUE) { reg_set_min_max(&other_branch->regs[insn->dst_reg], dst_reg, insn->imm, opcode); } @@ -2525,10 +2626,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, /* Mark all identical map registers in each branch as either * safe or unknown depending R == 0 or R != 0 conditional. */ - mark_map_regs(this_branch, insn->dst_reg, - opcode == BPF_JEQ ? PTR_TO_MAP_VALUE : UNKNOWN_VALUE); - mark_map_regs(other_branch, insn->dst_reg, - opcode == BPF_JEQ ? UNKNOWN_VALUE : PTR_TO_MAP_VALUE); + mark_map_regs(this_branch, insn->dst_reg, opcode == BPF_JNE); + mark_map_regs(other_branch, insn->dst_reg, opcode == BPF_JEQ); } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT && dst_reg->type == PTR_TO_PACKET && regs[insn->src_reg].type == PTR_TO_PACKET_END) { @@ -2576,8 +2675,11 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) if (insn->src_reg == 0) { u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm; - regs[insn->dst_reg].type = CONST_IMM; - regs[insn->dst_reg].imm = imm; + regs[insn->dst_reg].type = SCALAR_VALUE; + regs[insn->dst_reg].min_value = imm; + regs[insn->dst_reg].max_value = imm; + check_reg_overflow(®s[insn->dst_reg]); + regs[insn->dst_reg].var_off = tnum_const(imm); regs[insn->dst_reg].id = 0; return 0; } @@ -2659,7 +2761,7 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) /* mark destination R0 register as readable, since it contains * the value fetched from the packet */ - regs[BPF_REG_0].type = UNKNOWN_VALUE; + mark_reg_unknown(regs, BPF_REG_0); return 0; } @@ -2862,57 +2964,145 @@ err_free: return ret; } -/* the following conditions reduce the number of explored insns - * from ~140k to ~80k for ultra large programs that use a lot of ptr_to_packet +/* check %cur's range satisfies %old's */ +static bool range_within(struct bpf_reg_state *old, + struct bpf_reg_state *cur) +{ + return old->min_value <= cur->min_value && + old->max_value >= cur->max_value; +} + +/* Maximum number of register states that can exist at once */ +#define ID_MAP_SIZE (MAX_BPF_REG + MAX_BPF_STACK / BPF_REG_SIZE) +struct idpair { + u32 old; + u32 cur; +}; + +/* If in the old state two registers had the same id, then they need to have + * the same id in the new state as well. But that id could be different from + * the old state, so we need to track the mapping from old to new ids. + * Once we have seen that, say, a reg with old id 5 had new id 9, any subsequent + * regs with old id 5 must also have new id 9 for the new state to be safe. But + * regs with a different old id could still have new id 9, we don't care about + * that. + * So we look through our idmap to see if this old id has been seen before. If + * so, we require the new id to match; otherwise, we add the id pair to the map. */ -static bool compare_ptrs_to_packet(struct bpf_verifier_env *env, - struct bpf_reg_state *old, - struct bpf_reg_state *cur) +static bool check_ids(u32 old_id, u32 cur_id, struct idpair *idmap) { - if (old->id != cur->id) - return false; + unsigned int i; + + for (i = 0; i < ID_MAP_SIZE; i++) { + if (!idmap[i].old) { + /* Reached an empty slot; haven't seen this id before */ + idmap[i].old = old_id; + idmap[i].cur = cur_id; + return true; + } + if (idmap[i].old == old_id) + return idmap[i].cur == cur_id; + } + /* We ran out of idmap slots, which should be impossible */ + WARN_ON_ONCE(1); + return false; +} - /* old ptr_to_packet is more conservative, since it allows smaller - * range. Ex: - * old(off=0,r=10) is equal to cur(off=0,r=20), because - * old(off=0,r=10) means that with range=10 the verifier proceeded - * further and found no issues with the program. Now we're in the same - * spot with cur(off=0,r=20), so we're safe too, since anything further - * will only be looking at most 10 bytes after this pointer. - */ - if (old->off == cur->off && old->range < cur->range) +/* Returns true if (rold safe implies rcur safe) */ +static bool regsafe(struct bpf_reg_state *rold, + struct bpf_reg_state *rcur, + bool varlen_map_access, struct idpair *idmap) +{ + if (memcmp(rold, rcur, sizeof(*rold)) == 0) return true; - /* old(off=20,r=10) is equal to cur(off=22,re=22 or 5 or 0) - * since both cannot be used for packet access and safe(old) - * pointer has smaller off that could be used for further - * 'if (ptr > data_end)' check - * Ex: - * old(off=20,r=10) and cur(off=22,r=22) and cur(off=22,r=0) mean - * that we cannot access the packet. - * The safe range is: - * [ptr, ptr + range - off) - * so whenever off >=range, it means no safe bytes from this pointer. - * When comparing old->off <= cur->off, it means that older code - * went with smaller offset and that offset was later - * used to figure out the safe range after 'if (ptr > data_end)' check - * Say, 'old' state was explored like: - * ... R3(off=0, r=0) - * R4 = R3 + 20 - * ... now R4(off=20,r=0) <-- here - * if (R4 > data_end) - * ... R4(off=20,r=20), R3(off=0,r=20) and R3 can be used to access. - * ... the code further went all the way to bpf_exit. - * Now the 'cur' state at the mark 'here' has R4(off=30,r=0). - * old_R4(off=20,r=0) equal to cur_R4(off=30,r=0), since if the verifier - * goes further, such cur_R4 will give larger safe packet range after - * 'if (R4 > data_end)' and all further insn were already good with r=20, - * so they will be good with r=30 and we can prune the search. - */ - if (!env->strict_alignment && old->off <= cur->off && - old->off >= old->range && cur->off >= cur->range) + if (rold->type == NOT_INIT) + /* explored state can't have used this */ return true; + if (rcur->type == NOT_INIT) + return false; + switch (rold->type) { + case SCALAR_VALUE: + if (rcur->type == SCALAR_VALUE) { + /* new val must satisfy old val knowledge */ + return range_within(rold, rcur) && + tnum_in(rold->var_off, rcur->var_off); + } else { + /* if we knew anything about the old value, we're not + * equal, because we can't know anything about the + * scalar value of the pointer in the new value. + */ + return rold->min_value == BPF_REGISTER_MIN_RANGE && + rold->max_value == BPF_REGISTER_MAX_RANGE && + tnum_is_unknown(rold->var_off); + } + case PTR_TO_MAP_VALUE: + if (varlen_map_access) { + /* If the new min/max/var_off satisfy the old ones and + * everything else matches, we are OK. + * We don't care about the 'id' value, because nothing + * uses it for PTR_TO_MAP_VALUE (only for ..._OR_NULL) + */ + return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 && + range_within(rold, rcur) && + tnum_in(rold->var_off, rcur->var_off); + } else { + /* If the ranges/var_off were not the same, but + * everything else was and we didn't do a variable + * access into a map then we are a-ok. + */ + return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0; + } + case PTR_TO_MAP_VALUE_OR_NULL: + /* a PTR_TO_MAP_VALUE could be safe to use as a + * PTR_TO_MAP_VALUE_OR_NULL into the same map. + * However, if the old PTR_TO_MAP_VALUE_OR_NULL then got NULL- + * checked, doing so could have affected others with the same + * id, and we can't check for that because we lost the id when + * we converted to a PTR_TO_MAP_VALUE. + */ + if (rcur->type != PTR_TO_MAP_VALUE_OR_NULL) + return false; + if (memcmp(rold, rcur, offsetof(struct bpf_reg_state, id))) + return false; + /* Check our ids match any regs they're supposed to */ + return check_ids(rold->id, rcur->id, idmap); + case PTR_TO_PACKET: + if (rcur->type != PTR_TO_PACKET) + return false; + /* We must have at least as much range as the old ptr + * did, so that any accesses which were safe before are + * still safe. This is true even if old range < old off, + * since someone could have accessed through (ptr - k), or + * even done ptr -= k in a register, to get a safe access. + */ + if (rold->range > rcur->range) + return false; + /* If the offsets don't match, we can't trust our alignment; + * nor can we be sure that we won't fall out of range. + */ + if (rold->off != rcur->off) + return false; + /* id relations must be preserved */ + if (rold->id && !check_ids(rold->id, rcur->id, idmap)) + return false; + /* new val must satisfy old val knowledge */ + return range_within(rold, rcur) && + tnum_in(rold->var_off, rcur->var_off); + case PTR_TO_CTX: + case CONST_PTR_TO_MAP: + case PTR_TO_STACK: + case PTR_TO_PACKET_END: + /* Only valid matches are exact, which memcmp() above + * would have accepted + */ + default: + /* Don't know what's going on, just say it's not safe */ + return false; + } + /* Shouldn't get here; if we do, say it's not safe */ + WARN_ON_ONCE(1); return false; } @@ -2947,43 +3137,19 @@ static bool states_equal(struct bpf_verifier_env *env, struct bpf_verifier_state *cur) { bool varlen_map_access = env->varlen_map_value_access; - struct bpf_reg_state *rold, *rcur; + struct idpair *idmap; + bool ret = false; int i; - for (i = 0; i < MAX_BPF_REG; i++) { - rold = &old->regs[i]; - rcur = &cur->regs[i]; - - if (memcmp(rold, rcur, sizeof(*rold)) == 0) - continue; - - /* If the ranges were not the same, but everything else was and - * we didn't do a variable access into a map then we are a-ok. - */ - if (!varlen_map_access && - memcmp(rold, rcur, offsetofend(struct bpf_reg_state, id)) == 0) - continue; - - /* If we didn't map access then again we don't care about the - * mismatched range values and it's ok if our old type was - * UNKNOWN and we didn't go to a NOT_INIT'ed reg. - */ - if (rold->type == NOT_INIT || - (!varlen_map_access && rold->type == UNKNOWN_VALUE && - rcur->type != NOT_INIT)) - continue; - - /* Don't care about the reg->id in this case. */ - if (rold->type == PTR_TO_MAP_VALUE_OR_NULL && - rcur->type == PTR_TO_MAP_VALUE_OR_NULL && - rold->map_ptr == rcur->map_ptr) - continue; - - if (rold->type == PTR_TO_PACKET && rcur->type == PTR_TO_PACKET && - compare_ptrs_to_packet(env, rold, rcur)) - continue; - + idmap = kcalloc(ID_MAP_SIZE, sizeof(struct idpair), GFP_KERNEL); + /* If we failed to allocate the idmap, just say it's not safe */ + if (!idmap) return false; + + for (i = 0; i < MAX_BPF_REG; i++) { + if (!regsafe(&old->regs[i], &cur->regs[i], varlen_map_access, + idmap)) + goto out_free; } for (i = 0; i < MAX_BPF_STACK; i++) { @@ -2995,29 +3161,32 @@ static bool states_equal(struct bpf_verifier_env *env, * this verifier states are not equivalent, * return false to continue verification of this path */ - return false; + goto out_free; if (i % BPF_REG_SIZE) continue; if (old->stack_slot_type[i] != STACK_SPILL) continue; - if (memcmp(&old->spilled_regs[i / BPF_REG_SIZE], - &cur->spilled_regs[i / BPF_REG_SIZE], - sizeof(old->spilled_regs[0]))) - /* when explored and current stack slot types are - * the same, check that stored pointers types + if (!regsafe(&old->spilled_regs[i / BPF_REG_SIZE], + &cur->spilled_regs[i / BPF_REG_SIZE], + varlen_map_access, idmap)) + /* when explored and current stack slot are both storing + * spilled registers, check that stored pointers types * are the same as well. * Ex: explored safe path could have stored - * (bpf_reg_state) {.type = PTR_TO_STACK, .imm = -8} + * (bpf_reg_state) {.type = PTR_TO_STACK, .off = -8} * but current path has stored: - * (bpf_reg_state) {.type = PTR_TO_STACK, .imm = -16} + * (bpf_reg_state) {.type = PTR_TO_STACK, .off = -16} * such verifier states are not equivalent. * return false to continue verification of this path */ - return false; + goto out_free; else continue; } - return true; + ret = true; +out_free: + kfree(idmap); + return ret; } static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) @@ -3331,7 +3500,6 @@ process_bpf_exit: verbose("invalid BPF_LD mode\n"); return -EINVAL; } - reset_reg_range_values(regs, insn->dst_reg); } else { verbose("unknown insn class %d\n", class); return -EINVAL; -- cgit v1.2.3 From 46b3bb9b47ae63632892ad19976948b290aec2c6 Mon Sep 17 00:00:00 2001 From: Greg Edwards Date: Wed, 28 Jun 2017 09:22:26 -0600 Subject: igb: do not drop PF mailbox lock after read of VF message When the PF receives a mailbox message from the VF, it grabs the mailbox lock, reads the VF message from the mailbox, ACKs the message and drops the lock. While the PF is performing the action for the VF message, nothing prevents another VF message from being posted to the mailbox. The current code handles this condition by just dropping any new VF messages without processing them. This results in a mailbox timeout in the VM for posted messages waiting for an ACK, and the VF is reset by the igbvf_watchdog_task in the VM. Given the right sequence of VF messages and mailbox timeouts, this condition can go on ad infinitum. Modify the PF mailbox read method to take an 'unlock' argument that optionally leaves the mailbox locked by the PF after reading the VF message. This ensures another VF message is not posted to the mailbox until after the PF has completed processing the VF message and written its reply. Signed-off-by: Greg Edwards Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_hw.h | 3 ++- drivers/net/ethernet/intel/igb/e1000_mbx.c | 18 ++++++++++++------ drivers/net/ethernet/intel/igb/e1000_mbx.h | 3 ++- drivers/net/ethernet/intel/igb/igb_main.c | 14 ++++++++++---- 4 files changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index 6076f258a0a5..6ea9f702ba0f 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -491,7 +491,8 @@ struct e1000_fc_info { struct e1000_mbx_operations { s32 (*init_params)(struct e1000_hw *hw); - s32 (*read)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); + s32 (*read)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id, + bool unlock); s32 (*write)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); s32 (*read_posted)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); s32 (*write_posted)(struct e1000_hw *hw, u32 *msg, u16 size, diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.c b/drivers/net/ethernet/intel/igb/e1000_mbx.c index 6aa44723507b..bffd58f7b2a1 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.c +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.c @@ -32,7 +32,8 @@ * * returns SUCCESS if it successfully read message from buffer **/ -s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id, + bool unlock) { struct e1000_mbx_info *mbx = &hw->mbx; s32 ret_val = -E1000_ERR_MBX; @@ -42,7 +43,7 @@ s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) size = mbx->size; if (mbx->ops.read) - ret_val = mbx->ops.read(hw, msg, size, mbx_id); + ret_val = mbx->ops.read(hw, msg, size, mbx_id, unlock); return ret_val; } @@ -222,7 +223,7 @@ static s32 igb_read_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, ret_val = igb_poll_for_msg(hw, mbx_id); if (!ret_val) - ret_val = mbx->ops.read(hw, msg, size, mbx_id); + ret_val = mbx->ops.read(hw, msg, size, mbx_id, true); out: return ret_val; } @@ -423,13 +424,14 @@ out_no_write: * @msg: The message buffer * @size: Length of buffer * @vf_number: the VF index + * @unlock: unlock the mailbox when done? * * This function copies a message from the mailbox buffer to the caller's * memory buffer. The presumption is that the caller knows that there was * a message due to a VF request so no polling for message is needed. **/ static s32 igb_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, - u16 vf_number) + u16 vf_number, bool unlock) { s32 ret_val; u16 i; @@ -443,8 +445,12 @@ static s32 igb_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, for (i = 0; i < size; i++) msg[i] = array_rd32(E1000_VMBMEM(vf_number), i); - /* Acknowledge the message and release buffer */ - wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_ACK); + /* Acknowledge the message and release mailbox lock (or not) */ + if (unlock) + wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_ACK); + else + wr32(E1000_P2VMAILBOX(vf_number), + E1000_P2VMAILBOX_ACK | E1000_P2VMAILBOX_PFU); /* update stats */ hw->mbx.stats.msgs_rx++; diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h index a98c5dc60afd..a62b08e1572e 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.h +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h @@ -67,7 +67,8 @@ #define E1000_PF_CONTROL_MSG 0x0100 /* PF control message */ -s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); +s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id, + bool unlock); s32 igb_write_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); s32 igb_check_for_msg(struct e1000_hw *hw, u16 mbx_id); s32 igb_check_for_ack(struct e1000_hw *hw, u16 mbx_id); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 1a99164d5d11..fd4a46b03cc8 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6675,32 +6675,33 @@ static void igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) struct vf_data_storage *vf_data = &adapter->vf_data[vf]; s32 retval; - retval = igb_read_mbx(hw, msgbuf, E1000_VFMAILBOX_SIZE, vf); + retval = igb_read_mbx(hw, msgbuf, E1000_VFMAILBOX_SIZE, vf, false); if (retval) { /* if receive failed revoke VF CTS stats and restart init */ dev_err(&pdev->dev, "Error receiving message from VF\n"); vf_data->flags &= ~IGB_VF_FLAG_CTS; if (!time_after(jiffies, vf_data->last_nack + (2 * HZ))) - return; + goto unlock; goto out; } /* this is a message we already processed, do nothing */ if (msgbuf[0] & (E1000_VT_MSGTYPE_ACK | E1000_VT_MSGTYPE_NACK)) - return; + goto unlock; /* until the vf completes a reset it should not be * allowed to start any configuration. */ if (msgbuf[0] == E1000_VF_RESET) { + /* unlocks mailbox */ igb_vf_reset_msg(adapter, vf); return; } if (!(vf_data->flags & IGB_VF_FLAG_CTS)) { if (!time_after(jiffies, vf_data->last_nack + (2 * HZ))) - return; + goto unlock; retval = -1; goto out; } @@ -6741,7 +6742,12 @@ out: else msgbuf[0] |= E1000_VT_MSGTYPE_ACK; + /* unlocks mailbox */ igb_write_mbx(hw, msgbuf, 1, vf); + return; + +unlock: + igb_unlock_mbx(hw, vf); } static void igb_msg_task(struct igb_adapter *adapter) -- cgit v1.2.3 From 48f76b68f9fca4e1d5bbb1755d14e8e8e09bdd5b Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Mon, 17 Jul 2017 15:13:39 -0700 Subject: e1000e: Initial Support for IceLake i219 (8) and i219 (9) are the next LOM generations that will be available on the next Intel Client platform (IceLake). This patch provides the initial support for these devices Signed-off-by: Sasha Neftin Reviewed-by: Raanan Avargil Reviewed-by: Dima Ruinskiy Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/hw.h | 4 ++++ drivers/net/ethernet/intel/e1000e/netdev.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index 66bd5060a65b..d803b1a12349 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -100,6 +100,10 @@ struct e1000_hw; #define E1000_DEV_ID_PCH_CNP_I219_V6 0x15BE #define E1000_DEV_ID_PCH_CNP_I219_LM7 0x15BB #define E1000_DEV_ID_PCH_CNP_I219_V7 0x15BC +#define E1000_DEV_ID_PCH_ICP_I219_LM8 0x15DF +#define E1000_DEV_ID_PCH_ICP_I219_V8 0x15E0 +#define E1000_DEV_ID_PCH_ICP_I219_LM9 0x15E1 +#define E1000_DEV_ID_PCH_ICP_I219_V9 0x15E2 #define E1000_REVISION_4 4 diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 2dcb5463d9b8..327dfe5bedc0 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -7544,6 +7544,10 @@ static const struct pci_device_id e1000_pci_tbl[] = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V6), board_pch_cnp }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM7), board_pch_cnp }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V7), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_LM8), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_V8), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_LM9), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_V9), board_pch_cnp }, { 0, 0, 0, 0, 0, 0, 0 } /* terminate list */ }; -- cgit v1.2.3 From 32652c2ac2f330d2c159d8259f359da0ee2b0aa3 Mon Sep 17 00:00:00 2001 From: Greg Edwards Date: Thu, 20 Jul 2017 10:00:57 -0600 Subject: igbvf: add lock around mailbox ops The PF driver assumes the VF will not send another mailbox message until the PF has written its reply to the previous message. If the VF does, that message will be silently dropped by the PF before it writes its reply to the mailbox. This results in a VF mailbox timeout for posted messages waiting for an ACK, and the VF is reset by the igbvf_watchdog_task in the VM. Add a lock around the VF mailbox ops to prevent the VF from sending another message while the PF is still processing the previous one. Signed-off-by: Greg Edwards Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igbvf/ethtool.c | 4 +++ drivers/net/ethernet/intel/igbvf/mbx.c | 4 +++ drivers/net/ethernet/intel/igbvf/netdev.c | 47 ++++++++++++++++++++++++++++++ drivers/net/ethernet/intel/igbvf/vf.h | 1 + 4 files changed, 56 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index 34faa113a8a0..a127688e83e6 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -296,8 +296,12 @@ static int igbvf_link_test(struct igbvf_adapter *adapter, u64 *data) struct e1000_hw *hw = &adapter->hw; *data = 0; + spin_lock_bh(&hw->mbx_lock); + hw->mac.ops.check_for_link(hw); + spin_unlock_bh(&hw->mbx_lock); + if (!(er32(STATUS) & E1000_STATUS_LU)) *data = 1; diff --git a/drivers/net/ethernet/intel/igbvf/mbx.c b/drivers/net/ethernet/intel/igbvf/mbx.c index 01752f44ace2..c9a441632e9f 100644 --- a/drivers/net/ethernet/intel/igbvf/mbx.c +++ b/drivers/net/ethernet/intel/igbvf/mbx.c @@ -264,6 +264,8 @@ static s32 e1000_write_mbx_vf(struct e1000_hw *hw, u32 *msg, u16 size) s32 err; u16 i; + WARN_ON_ONCE(!spin_is_locked(&hw->mbx_lock)); + /* lock the mailbox to prevent pf/vf race condition */ err = e1000_obtain_mbx_lock_vf(hw); if (err) @@ -300,6 +302,8 @@ static s32 e1000_read_mbx_vf(struct e1000_hw *hw, u32 *msg, u16 size) s32 err; u16 i; + WARN_ON_ONCE(!spin_is_locked(&hw->mbx_lock)); + /* lock the mailbox to prevent pf/vf race condition */ err = e1000_obtain_mbx_lock_vf(hw); if (err) diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 1b9cbbe88f6f..1ed556911b14 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1235,7 +1235,12 @@ static void igbvf_set_rlpml(struct igbvf_adapter *adapter) struct e1000_hw *hw = &adapter->hw; max_frame_size = adapter->max_frame_size + VLAN_TAG_SIZE; + + spin_lock_bh(&hw->mbx_lock); + e1000_rlpml_set_vf(hw, max_frame_size); + + spin_unlock_bh(&hw->mbx_lock); } static int igbvf_vlan_rx_add_vid(struct net_device *netdev, @@ -1244,10 +1249,16 @@ static int igbvf_vlan_rx_add_vid(struct net_device *netdev, struct igbvf_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + spin_lock_bh(&hw->mbx_lock); + if (hw->mac.ops.set_vfta(hw, vid, true)) { dev_err(&adapter->pdev->dev, "Failed to add vlan id %d\n", vid); + spin_unlock_bh(&hw->mbx_lock); return -EINVAL; } + + spin_unlock_bh(&hw->mbx_lock); + set_bit(vid, adapter->active_vlans); return 0; } @@ -1258,11 +1269,17 @@ static int igbvf_vlan_rx_kill_vid(struct net_device *netdev, struct igbvf_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + spin_lock_bh(&hw->mbx_lock); + if (hw->mac.ops.set_vfta(hw, vid, false)) { dev_err(&adapter->pdev->dev, "Failed to remove vlan id %d\n", vid); + spin_unlock_bh(&hw->mbx_lock); return -EINVAL; } + + spin_unlock_bh(&hw->mbx_lock); + clear_bit(vid, adapter->active_vlans); return 0; } @@ -1428,7 +1445,11 @@ static void igbvf_set_multi(struct net_device *netdev) netdev_for_each_mc_addr(ha, netdev) memcpy(mta_list + (i++ * ETH_ALEN), ha->addr, ETH_ALEN); + spin_lock_bh(&hw->mbx_lock); + hw->mac.ops.update_mc_addr_list(hw, mta_list, i, 0, 0); + + spin_unlock_bh(&hw->mbx_lock); kfree(mta_list); } @@ -1449,16 +1470,24 @@ static int igbvf_set_uni(struct net_device *netdev) return -ENOSPC; } + spin_lock_bh(&hw->mbx_lock); + /* Clear all unicast MAC filters */ hw->mac.ops.set_uc_addr(hw, E1000_VF_MAC_FILTER_CLR, NULL); + spin_unlock_bh(&hw->mbx_lock); + if (!netdev_uc_empty(netdev)) { struct netdev_hw_addr *ha; /* Add MAC filters one by one */ netdev_for_each_uc_addr(ha, netdev) { + spin_lock_bh(&hw->mbx_lock); + hw->mac.ops.set_uc_addr(hw, E1000_VF_MAC_FILTER_ADD, ha->addr); + + spin_unlock_bh(&hw->mbx_lock); udelay(200); } } @@ -1503,12 +1532,16 @@ static void igbvf_reset(struct igbvf_adapter *adapter) struct net_device *netdev = adapter->netdev; struct e1000_hw *hw = &adapter->hw; + spin_lock_bh(&hw->mbx_lock); + /* Allow time for pending master requests to run */ if (mac->ops.reset_hw(hw)) dev_err(&adapter->pdev->dev, "PF still resetting\n"); mac->ops.init_hw(hw); + spin_unlock_bh(&hw->mbx_lock); + if (is_valid_ether_addr(adapter->hw.mac.addr)) { memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len); @@ -1643,6 +1676,7 @@ static int igbvf_sw_init(struct igbvf_adapter *adapter) igbvf_irq_disable(adapter); spin_lock_init(&adapter->stats_lock); + spin_lock_init(&adapter->hw.mbx_lock); set_bit(__IGBVF_DOWN, &adapter->state); return 0; @@ -1786,8 +1820,12 @@ static int igbvf_set_mac(struct net_device *netdev, void *p) memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len); + spin_lock_bh(&hw->mbx_lock); + hw->mac.ops.rar_set(hw, hw->mac.addr, 0); + spin_unlock_bh(&hw->mbx_lock); + if (!ether_addr_equal(addr->sa_data, hw->mac.addr)) return -EADDRNOTAVAIL; @@ -1858,7 +1896,12 @@ static bool igbvf_has_link(struct igbvf_adapter *adapter) if (test_bit(__IGBVF_DOWN, &adapter->state)) return false; + spin_lock_bh(&hw->mbx_lock); + ret_val = hw->mac.ops.check_for_link(hw); + + spin_unlock_bh(&hw->mbx_lock); + link_active = !hw->mac.get_link_status; /* if check for link returns error we will need to reset */ @@ -2808,6 +2851,8 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->min_mtu = ETH_MIN_MTU; netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE; + spin_lock_bh(&hw->mbx_lock); + /*reset the controller to put the device in a known good state */ err = hw->mac.ops.reset_hw(hw); if (err) { @@ -2824,6 +2869,8 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->addr_len); } + spin_unlock_bh(&hw->mbx_lock); + if (!is_valid_ether_addr(netdev->dev_addr)) { dev_info(&pdev->dev, "Assigning random MAC address.\n"); eth_hw_addr_random(netdev); diff --git a/drivers/net/ethernet/intel/igbvf/vf.h b/drivers/net/ethernet/intel/igbvf/vf.h index 4cf78b0dec50..d213eefb6169 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.h +++ b/drivers/net/ethernet/intel/igbvf/vf.h @@ -245,6 +245,7 @@ struct e1000_hw { struct e1000_mac_info mac; struct e1000_mbx_info mbx; + spinlock_t mbx_lock; /* serializes mailbox ops */ union { struct e1000_dev_spec_vf vf; -- cgit v1.2.3 From 0d3ee0d9251ceb2c937e7c0c1adb366fdb0812c8 Mon Sep 17 00:00:00 2001 From: Greg Edwards Date: Thu, 20 Jul 2017 10:00:58 -0600 Subject: igbvf: after mailbox write, wait for reply Two of the VF mailbox commands were not waiting for a reply from the PF, which can result in a VF mailbox timeout in the VM for the next command. Signed-off-by: Greg Edwards Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igbvf/vf.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c index 528be116184e..1d3aa9adcaa8 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.c +++ b/drivers/net/ethernet/intel/igbvf/vf.c @@ -230,6 +230,7 @@ static void e1000_update_mc_addr_list_vf(struct e1000_hw *hw, u16 *hash_list = (u16 *)&msgbuf[1]; u32 hash_value; u32 cnt, i; + s32 ret_val; /* Each entry in the list uses 1 16 bit word. We have 30 * 16 bit words available in our HW msg buffer (minus 1 for the @@ -250,7 +251,9 @@ static void e1000_update_mc_addr_list_vf(struct e1000_hw *hw, mc_addr_list += ETH_ALEN; } - mbx->ops.write_posted(hw, msgbuf, E1000_VFMAILBOX_SIZE); + ret_val = mbx->ops.write_posted(hw, msgbuf, E1000_VFMAILBOX_SIZE); + if (!ret_val) + mbx->ops.read_posted(hw, msgbuf, 1); } /** @@ -293,11 +296,14 @@ void e1000_rlpml_set_vf(struct e1000_hw *hw, u16 max_size) { struct e1000_mbx_info *mbx = &hw->mbx; u32 msgbuf[2]; + s32 ret_val; msgbuf[0] = E1000_VF_SET_LPE; msgbuf[1] = max_size; - mbx->ops.write_posted(hw, msgbuf, 2); + ret_val = mbx->ops.write_posted(hw, msgbuf, 2); + if (!ret_val) + mbx->ops.read_posted(hw, msgbuf, 1); } /** -- cgit v1.2.3 From d466124860cf96a6d4db5af5b3500f53f0d1bc7a Mon Sep 17 00:00:00 2001 From: Greg Edwards Date: Thu, 20 Jul 2017 10:15:14 -0600 Subject: igbvf: convert msleep to mdelay in atomic context This fixes a "scheduling while atomic" splat seen with CONFIG_DEBUG_ATOMIC_SLEEP enabled. Signed-off-by: Greg Edwards Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igbvf/vf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c index 1d3aa9adcaa8..9577ccf4b26a 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.c +++ b/drivers/net/ethernet/intel/igbvf/vf.c @@ -149,7 +149,7 @@ static s32 e1000_reset_hw_vf(struct e1000_hw *hw) msgbuf[0] = E1000_VF_RESET; mbx->ops.write_posted(hw, msgbuf, 1); - msleep(10); + mdelay(10); /* set our "perm_addr" based on info provided by PF */ ret_val = mbx->ops.read_posted(hw, msgbuf, 3); -- cgit v1.2.3 From c99c287254e1d2816e2f4b01df9049b88b205bc5 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Mon, 7 Aug 2017 10:39:00 -0700 Subject: liquidio: fix wrong info about vf rx/tx ring parameters reported to ethtool Information reported to ethtool about vf rx/tx ring parameters is wrong. Fix it by adding the missing initializations. Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 30 +++++++--------------- 1 file changed, 9 insertions(+), 21 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 09e287597c74..b78e296c4cba 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -649,33 +649,21 @@ lio_ethtool_get_ringparam(struct net_device *netdev, rx_max_pending = CN6XXX_MAX_OQ_DESCRIPTORS; rx_pending = CFG_GET_NUM_RX_DESCS_NIC_IF(conf6x, lio->ifidx); tx_pending = CFG_GET_NUM_TX_DESCS_NIC_IF(conf6x, lio->ifidx); - } else if (OCTEON_CN23XX_PF(oct)) { - struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_pf); - + } else if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) { tx_max_pending = CN23XX_MAX_IQ_DESCRIPTORS; rx_max_pending = CN23XX_MAX_OQ_DESCRIPTORS; - rx_pending = CFG_GET_NUM_RX_DESCS_NIC_IF(conf23, lio->ifidx); - tx_pending = CFG_GET_NUM_TX_DESCS_NIC_IF(conf23, lio->ifidx); - } - - if (lio->mtu > OCTNET_DEFAULT_FRM_SIZE - OCTNET_FRM_HEADER_SIZE) { - ering->rx_pending = 0; - ering->rx_max_pending = 0; - ering->rx_mini_pending = 0; - ering->rx_jumbo_pending = rx_pending; - ering->rx_mini_max_pending = 0; - ering->rx_jumbo_max_pending = rx_max_pending; - } else { - ering->rx_pending = rx_pending; - ering->rx_max_pending = rx_max_pending; - ering->rx_mini_pending = 0; - ering->rx_jumbo_pending = 0; - ering->rx_mini_max_pending = 0; - ering->rx_jumbo_max_pending = 0; + rx_pending = oct->droq[0]->max_count; + tx_pending = oct->instr_queue[0]->max_count; } ering->tx_pending = tx_pending; ering->tx_max_pending = tx_max_pending; + ering->rx_pending = rx_pending; + ering->rx_max_pending = rx_max_pending; + ering->rx_mini_pending = 0; + ering->rx_jumbo_pending = 0; + ering->rx_mini_max_pending = 0; + ering->rx_jumbo_max_pending = 0; } static u32 lio_get_msglevel(struct net_device *netdev) -- cgit v1.2.3 From eeb0149660a21c61122d4937bd406aa4f334c1e4 Mon Sep 17 00:00:00 2001 From: John W Linville Date: Fri, 21 Jul 2017 14:12:24 -0400 Subject: igb: support BCM54616 PHY The management port on an Edgecore AS7712-32 switch uses an igb MAC, but it uses a BCM54616 PHY. Without a patch like this, loading the igb module produces dmesg output like this: [ 3.439125] igb: Copyright (c) 2007-2014 Intel Corporation. [ 3.439866] igb: probe of 0000:00:14.0 failed with error -2 Signed-off-by: John W Linville Cc: Jeff Kirsher Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_82575.c | 6 ++++++ drivers/net/ethernet/intel/igb/e1000_defines.h | 1 + drivers/net/ethernet/intel/igb/e1000_hw.h | 1 + 3 files changed, 8 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 4a50870e0fa7..c37cc8bccf47 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -340,6 +340,9 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw) phy->ops.set_d3_lplu_state = igb_set_d3_lplu_state_82580; phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_m88; break; + case BCM54616_E_PHY_ID: + phy->type = e1000_phy_bcm54616; + break; default: ret_val = -E1000_ERR_PHY; goto out; @@ -1659,6 +1662,9 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) case e1000_phy_82580: ret_val = igb_copper_link_setup_82580(hw); break; + case e1000_phy_bcm54616: + ret_val = 0; + break; default: ret_val = -E1000_ERR_PHY; break; diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index d8517779439b..1de82f247312 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -889,6 +889,7 @@ #define I210_I_PHY_ID 0x01410C00 #define M88E1543_E_PHY_ID 0x01410EA0 #define M88E1512_E_PHY_ID 0x01410DD0 +#define BCM54616_E_PHY_ID 0x03625D10 /* M88E1000 Specific Registers */ #define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index 6ea9f702ba0f..6c9485ab4b57 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -128,6 +128,7 @@ enum e1000_phy_type { e1000_phy_ife, e1000_phy_82580, e1000_phy_i210, + e1000_phy_bcm54616, }; enum e1000_bus_type { -- cgit v1.2.3 From 7b83f52047e8a3d551a9495b0267df5d0754c5bf Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 7 Aug 2017 11:30:00 -0700 Subject: netvsc: make sure and unregister datapath Go back to switching datapath directly in the notifier callback. Otherwise datapath might not get switched on unregister. No need for calling the NOTIFY_PEERS notifier since that is only for a gratitious ARP/ND packet; but that is not required with Hyper-V because both VF and synthetic NIC have the same MAC address. Reported-by: Vitaly Kuznetsov Fixes: 0c195567a8f6 ("netvsc: transparent VF management") Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 3 -- drivers/net/hyperv/netvsc.c | 2 -- drivers/net/hyperv/netvsc_drv.c | 71 ++++++++++++++++------------------------- 3 files changed, 28 insertions(+), 48 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index c701b059c5ac..d1ea99a12cf2 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -724,14 +724,11 @@ struct net_device_context { struct net_device __rcu *vf_netdev; struct netvsc_vf_pcpu_stats __percpu *vf_stats; struct work_struct vf_takeover; - struct work_struct vf_notify; /* 1: allocated, serial number is valid. 0: not allocated */ u32 vf_alloc; /* Serial number of the VF to team with */ u32 vf_serial; - - bool datapath; /* 0 - synthetic, 1 - VF nic */ }; /* Per channel data */ diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 9598220b3bcc..208f03aa83de 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -60,8 +60,6 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf) sizeof(struct nvsp_message), (unsigned long)init_pkt, VM_PKT_DATA_INBAND, 0); - - net_device_ctx->datapath = vf; } static struct netvsc_device *alloc_net_device(void) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index e75c0f852a63..eb0023f55fe1 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1649,55 +1649,35 @@ static int netvsc_register_vf(struct net_device *vf_netdev) return NOTIFY_OK; } -/* Change datapath */ -static void netvsc_vf_update(struct work_struct *w) +static int netvsc_vf_up(struct net_device *vf_netdev) { - struct net_device_context *ndev_ctx - = container_of(w, struct net_device_context, vf_notify); - struct net_device *ndev = hv_get_drvdata(ndev_ctx->device_ctx); + struct net_device_context *net_device_ctx; struct netvsc_device *netvsc_dev; - struct net_device *vf_netdev; - bool vf_is_up; - - if (!rtnl_trylock()) { - schedule_work(w); - return; - } + struct net_device *ndev; - vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); - if (!vf_netdev) - goto unlock; + ndev = get_netvsc_byref(vf_netdev); + if (!ndev) + return NOTIFY_DONE; - netvsc_dev = rtnl_dereference(ndev_ctx->nvdev); + net_device_ctx = netdev_priv(ndev); + netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); if (!netvsc_dev) - goto unlock; - - vf_is_up = netif_running(vf_netdev); - if (vf_is_up != ndev_ctx->datapath) { - if (vf_is_up) { - netdev_info(ndev, "VF up: %s\n", vf_netdev->name); - rndis_filter_open(netvsc_dev); - netvsc_switch_datapath(ndev, true); - netdev_info(ndev, "Data path switched to VF: %s\n", - vf_netdev->name); - } else { - netdev_info(ndev, "VF down: %s\n", vf_netdev->name); - netvsc_switch_datapath(ndev, false); - rndis_filter_close(netvsc_dev); - netdev_info(ndev, "Data path switched from VF: %s\n", - vf_netdev->name); - } + return NOTIFY_DONE; - /* Now notify peers through VF device. */ - call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, ndev); - } -unlock: - rtnl_unlock(); + /* Bump refcount when datapath is acvive - Why? */ + rndis_filter_open(netvsc_dev); + + /* notify the host to switch the data path. */ + netvsc_switch_datapath(ndev, true); + netdev_info(ndev, "Data path switched to VF: %s\n", vf_netdev->name); + + return NOTIFY_OK; } -static int netvsc_vf_notify(struct net_device *vf_netdev) +static int netvsc_vf_down(struct net_device *vf_netdev) { struct net_device_context *net_device_ctx; + struct netvsc_device *netvsc_dev; struct net_device *ndev; ndev = get_netvsc_byref(vf_netdev); @@ -1705,7 +1685,13 @@ static int netvsc_vf_notify(struct net_device *vf_netdev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); - schedule_work(&net_device_ctx->vf_notify); + netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); + if (!netvsc_dev) + return NOTIFY_DONE; + + netvsc_switch_datapath(ndev, false); + netdev_info(ndev, "Data path switched from VF: %s\n", vf_netdev->name); + rndis_filter_close(netvsc_dev); return NOTIFY_OK; } @@ -1721,7 +1707,6 @@ static int netvsc_unregister_vf(struct net_device *vf_netdev) net_device_ctx = netdev_priv(ndev); cancel_work_sync(&net_device_ctx->vf_takeover); - cancel_work_sync(&net_device_ctx->vf_notify); netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name); @@ -1764,7 +1749,6 @@ static int netvsc_probe(struct hv_device *dev, spin_lock_init(&net_device_ctx->lock); INIT_LIST_HEAD(&net_device_ctx->reconfig_events); INIT_WORK(&net_device_ctx->vf_takeover, netvsc_vf_setup); - INIT_WORK(&net_device_ctx->vf_notify, netvsc_vf_update); net_device_ctx->vf_stats = netdev_alloc_pcpu_stats(struct netvsc_vf_pcpu_stats); @@ -1915,8 +1899,9 @@ static int netvsc_netdev_event(struct notifier_block *this, case NETDEV_UNREGISTER: return netvsc_unregister_vf(event_dev); case NETDEV_UP: + return netvsc_vf_up(event_dev); case NETDEV_DOWN: - return netvsc_vf_notify(event_dev); + return netvsc_vf_down(event_dev); default: return NOTIFY_DONE; } -- cgit v1.2.3 From ea6404c841011a1ed7b6eac64621d5f96e8b7183 Mon Sep 17 00:00:00 2001 From: Derek Chickles Date: Mon, 7 Aug 2017 12:22:15 -0700 Subject: liquidio: fix misspelled firmware image filenames Fix misspelled firmware image filenames advertised via MODULE_FIRMWARE(). Signed-off-by: Derek Chickles Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 8c2cd8011bae..3ec0dd9b7201 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -39,10 +39,14 @@ MODULE_AUTHOR("Cavium Networks, "); MODULE_DESCRIPTION("Cavium LiquidIO Intelligent Server Adapter Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(LIQUIDIO_VERSION); -MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210SV_NAME LIO_FW_NAME_SUFFIX); -MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210NV_NAME LIO_FW_NAME_SUFFIX); -MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_410NV_NAME LIO_FW_NAME_SUFFIX); -MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_23XX_NAME LIO_FW_NAME_SUFFIX); +MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210SV_NAME + "_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX); +MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210NV_NAME + "_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX); +MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_410NV_NAME + "_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX); +MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_23XX_NAME + "_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX); static int ddr_timeout = 10000; module_param(ddr_timeout, int, 0644); -- cgit v1.2.3 From 274cdb46e98b965449c68d912bcc45899ba34593 Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Tue, 8 Aug 2017 00:22:21 +0200 Subject: net: dsa: lan9303: Only allocate 3 ports Save 2628 bytes on arm eabi by allocate only the required 3 ports. Now that ds->num_ports is correct: In net/dsa/tag_lan9303.c eliminate duplicate LAN9303_MAX_PORTS, use ds->num_ports. (Matching the pattern of other net/dsa/tag_xxx.c files.) Signed-off-by: Egil Hjelmeland Reviewed-by: Florian Fainelli Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 2 +- net/dsa/tag_lan9303.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 15befd155251..46fc1d5d3c9e 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -811,7 +811,7 @@ static struct dsa_switch_ops lan9303_switch_ops = { static int lan9303_register_switch(struct lan9303 *chip) { - chip->ds = dsa_switch_alloc(chip->dev, DSA_MAX_PORTS); + chip->ds = dsa_switch_alloc(chip->dev, LAN9303_NUM_PORTS); if (!chip->ds) return -ENOMEM; diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c index 247774d149f9..e23e7635fa00 100644 --- a/net/dsa/tag_lan9303.c +++ b/net/dsa/tag_lan9303.c @@ -39,7 +39,6 @@ */ #define LAN9303_TAG_LEN 4 -#define LAN9303_MAX_PORTS 3 static struct sk_buff *lan9303_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -104,7 +103,7 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev, source_port = ntohs(lan9303_tag[1]) & 0x3; - if (source_port >= LAN9303_MAX_PORTS) { + if (source_port >= ds->num_ports) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n"); return NULL; } -- cgit v1.2.3 From 4da1874190946b16d21b3c02e283b08619255c24 Mon Sep 17 00:00:00 2001 From: Arjun Vynipadath Date: Tue, 8 Aug 2017 11:20:52 +0530 Subject: cxgb4: Clear On FLASH config file after a FW upgrade Because Firmware and the Firmware Configuration File need to be in sync; clear out any On-FLASH Firmware Configuration File when new Firmware is loaded. This will avoid difficult to diagnose and fix problems with a mis-matched Firmware Configuration File which prevents the adapter from being initialized. Original work by: Casey Leedom Signed-off-by: Arjun Vynipadath Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 1 + drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 70 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 1978abbc6ceb..daa37750d152 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -1405,6 +1405,7 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, int t4_fl_pkt_align(struct adapter *adap); unsigned int t4_flash_cfg_addr(struct adapter *adapter); int t4_check_fw_version(struct adapter *adap); +int t4_load_cfg(struct adapter *adapter, const u8 *cfg_data, unsigned int size); int t4_get_fw_version(struct adapter *adapter, u32 *vers); int t4_get_bs_version(struct adapter *adapter, u32 *vers); int t4_get_tp_version(struct adapter *adapter, u32 *vers); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 24087c886974..fff8fba86f97 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -6663,6 +6663,17 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, if (ret < 0) goto out; + /* + * If there was a Firmware Configuration File stored in FLASH, + * there's a good chance that it won't be compatible with the new + * Firmware. In order to prevent difficult to diagnose adapter + * initialization issues, we clear out the Firmware Configuration File + * portion of the FLASH . The user will need to re-FLASH a new + * Firmware Configuration File which is compatible with the new + * Firmware if that's desired. + */ + (void)t4_load_cfg(adap, NULL, 0); + /* * Older versions of the firmware don't understand the new * PCIE_FW.HALT flag and so won't know to perform a RESET when they @@ -8895,6 +8906,65 @@ void t4_idma_monitor(struct adapter *adapter, } } +/** + * t4_load_cfg - download config file + * @adap: the adapter + * @cfg_data: the cfg text file to write + * @size: text file size + * + * Write the supplied config text file to the card's serial flash. + */ +int t4_load_cfg(struct adapter *adap, const u8 *cfg_data, unsigned int size) +{ + int ret, i, n, cfg_addr; + unsigned int addr; + unsigned int flash_cfg_start_sec; + unsigned int sf_sec_size = adap->params.sf_size / adap->params.sf_nsec; + + cfg_addr = t4_flash_cfg_addr(adap); + if (cfg_addr < 0) + return cfg_addr; + + addr = cfg_addr; + flash_cfg_start_sec = addr / SF_SEC_SIZE; + + if (size > FLASH_CFG_MAX_SIZE) { + dev_err(adap->pdev_dev, "cfg file too large, max is %u bytes\n", + FLASH_CFG_MAX_SIZE); + return -EFBIG; + } + + i = DIV_ROUND_UP(FLASH_CFG_MAX_SIZE, /* # of sectors spanned */ + sf_sec_size); + ret = t4_flash_erase_sectors(adap, flash_cfg_start_sec, + flash_cfg_start_sec + i - 1); + /* If size == 0 then we're simply erasing the FLASH sectors associated + * with the on-adapter Firmware Configuration File. + */ + if (ret || size == 0) + goto out; + + /* this will write to the flash up to SF_PAGE_SIZE at a time */ + for (i = 0; i < size; i += SF_PAGE_SIZE) { + if ((size - i) < SF_PAGE_SIZE) + n = size - i; + else + n = SF_PAGE_SIZE; + ret = t4_write_flash(adap, addr, n, cfg_data); + if (ret) + goto out; + + addr += SF_PAGE_SIZE; + cfg_data += SF_PAGE_SIZE; + } + +out: + if (ret) + dev_err(adap->pdev_dev, "config file %s failed %d\n", + (size == 0 ? "clear" : "download"), ret); + return ret; +} + /** * t4_set_vf_mac - Set MAC address for the specified VF * @adapter: The adapter -- cgit v1.2.3 From 51ce3e2145cc3927c9551b8c3623610114b41651 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 8 Aug 2017 10:52:32 +0100 Subject: net: phy: mdio-bcm-unimac: fix unsigned wrap-around when decrementing timeout Change post-decrement compare to pre-decrement to avoid an unsigned integer wrap-around on timeout. This leads to the following !timeout check to never to be true so -ETIMEDOUT is never returned. Detected by CoverityScan, CID#1452623 ("Logically dead code") Fixes: 69a60b0579a4 ("net: phy: mdio-bcm-unimac: factor busy polling loop") Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/phy/mdio-bcm-unimac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 89425ca48412..73c5267a11fd 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -71,7 +71,7 @@ static int unimac_mdio_poll(void *wait_func_data) return 0; usleep_range(1000, 2000); - } while (timeout--); + } while (--timeout); if (!timeout) return -ETIMEDOUT; -- cgit v1.2.3 From 3df3ba2d4dc12be2788f9d6ec6943ba3eccb7add Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Tue, 8 Aug 2017 18:02:11 +0200 Subject: qmi_wwan: fix NULL deref on disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qmi_wwan_disconnect is called twice when disconnecting devices with separate control and data interfaces. The first invocation will set the interface data to NULL for both interfaces to flag that the disconnect has been handled. But the matching NULL check was left out when qmi_wwan_disconnect was added, resulting in this oops: usb 2-1.4: USB disconnect, device number 4 qmi_wwan 2-1.4:1.6 wwp0s29u1u4i6: unregister 'qmi_wwan' usb-0000:00:1d.0-1.4, WWAN/QMI device BUG: unable to handle kernel NULL pointer dereference at 00000000000000e0 IP: qmi_wwan_disconnect+0x25/0xc0 [qmi_wwan] PGD 0 P4D 0 Oops: 0000 [#1] SMP Modules linked in: CPU: 2 PID: 33 Comm: kworker/2:1 Tainted: G E 4.12.3-nr44-normandy-r1500619820+ #1 Hardware name: LENOVO 4291LR7/4291LR7, BIOS CBET4000 4.6-810-g50522254fb 07/21/2017 Workqueue: usb_hub_wq hub_event [usbcore] task: ffff8c882b716040 task.stack: ffffb8e800d84000 RIP: 0010:qmi_wwan_disconnect+0x25/0xc0 [qmi_wwan] RSP: 0018:ffffb8e800d87b38 EFLAGS: 00010246 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 0000000000000001 RSI: ffff8c8824f3f1d0 RDI: ffff8c8824ef6400 RBP: ffff8c8824ef6400 R08: 0000000000000000 R09: 0000000000000000 R10: ffffb8e800d87780 R11: 0000000000000011 R12: ffffffffc07ea0e8 R13: ffff8c8824e2e000 R14: ffff8c8824e2e098 R15: 0000000000000000 FS: 0000000000000000(0000) GS:ffff8c8835300000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000000000e0 CR3: 0000000229ca5000 CR4: 00000000000406e0 Call Trace: ? usb_unbind_interface+0x71/0x270 [usbcore] ? device_release_driver_internal+0x154/0x210 ? qmi_wwan_unbind+0x6d/0xc0 [qmi_wwan] ? usbnet_disconnect+0x6c/0xf0 [usbnet] ? qmi_wwan_disconnect+0x87/0xc0 [qmi_wwan] ? usb_unbind_interface+0x71/0x270 [usbcore] ? device_release_driver_internal+0x154/0x210 Reported-and-tested-by: Nathaniel Roach Fixes: c6adf77953bc ("net: usb: qmi_wwan: add qmap mux protocol support") Cc: Daniele Palmas Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 5894e3c9468f..68c23b0ee40d 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1340,10 +1340,14 @@ static int qmi_wwan_probe(struct usb_interface *intf, static void qmi_wwan_disconnect(struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info = (void *)&dev->data; + struct qmi_wwan_state *info; struct list_head *iter; struct net_device *ldev; + /* called twice if separate control and data intf */ + if (!dev) + return; + info = (void *)&dev->data; if (info->flags & QMI_WWAN_FLAG_MUX) { if (!rtnl_trylock()) { restart_syscall(); -- cgit v1.2.3 From 4aa234eeec0aa9414637bd193f0745cf91bb60ef Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Jun 2017 09:14:45 +0200 Subject: iwlwifi: mvm: remove useless condition in LED code If the module parameter is set to disable the LED, we leave the initialization routine before setting the LEDS_INIT_COMPLETE status bit. Therefore, there's no need to check the parameter again on exit, just the status check is sufficient. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/led.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c index 3cac4278a5fd..95ef37df292f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c @@ -129,8 +129,7 @@ int iwl_mvm_leds_init(struct iwl_mvm *mvm) void iwl_mvm_leds_exit(struct iwl_mvm *mvm) { - if (iwlwifi_mod_params.led_mode == IWL_LED_DISABLE || - !(mvm->init_status & IWL_MVM_INIT_STATUS_LEDS_INIT_COMPLETE)) + if (!(mvm->init_status & IWL_MVM_INIT_STATUS_LEDS_INIT_COMPLETE)) return; led_classdev_unregister(&mvm->led); -- cgit v1.2.3 From 7089ae634c50544b29b31faf1a751e8765c8de3b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 28 Jun 2017 16:19:49 +0200 Subject: iwlwifi: mvm: use firmware LED command where applicable On devices starting from 8000 series, the host can no longer toggle the LED through the CSR_LED_REG register, but must do it via the firmware instead. Add support for this. Note that this means that the LED cannot be turned on while the firmware is off, so using an arbitrary LED trigger may not work as expected. Fixes: 503ab8c56ca0 ("iwlwifi: Add 8000 HW family support") Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/fw/api/commands.h | 5 ++ drivers/net/wireless/intel/iwlwifi/fw/api/led.h | 71 ++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 + drivers/net/wireless/intel/iwlwifi/mvm/led.c | 56 ++++++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 ++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 7 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/led.h (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index c7b8cffdf281..0eb35b119ae9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -287,6 +287,11 @@ enum iwl_legacy_cmds { */ NON_QOS_TX_COUNTER_CMD = 0x2d, + /** + * @LEDS_CMD: command is &struct iwl_led_cmd + */ + LEDS_CMD = 0x48, + /** * @LQ_CMD: using &struct iwl_lq_cmd */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/led.h b/drivers/net/wireless/intel/iwlwifi/fw/api/led.h new file mode 100644 index 000000000000..b30c9d229d6e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/led.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_fw_api_led_h__ +#define __iwl_fw_api_led_h__ + +/** + * struct iwl_led_cmd - LED switching command + * + * @status: LED status (on/off) + */ +struct iwl_led_cmd { + __le32 status; +} __packed; /* LEDS_CMD_API_S_VER_2 */ + +#endif /* __iwl_fw_api_led_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 69336f38ac58..e8e74dd558f7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -83,6 +83,7 @@ #include "fw/api/commands.h" #include "fw/api/d3.h" #include "fw/api/filter.h" +#include "fw/api/led.h" #include "fw/api/mac.h" #include "fw/api/nvm-reg.h" #include "fw/api/phy-ctxt.h" diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 875cf3a60adb..ec018d94a9dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1217,6 +1217,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) goto error; + iwl_mvm_leds_sync(mvm); + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c index 95ef37df292f..005e2e7278a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2017 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,26 +68,45 @@ #include "iwl-csr.h" #include "mvm.h" -/* Set led register on */ -static void iwl_mvm_led_enable(struct iwl_mvm *mvm) +static void iwl_mvm_send_led_fw_cmd(struct iwl_mvm *mvm, bool on) { - iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON); + struct iwl_led_cmd led_cmd = { + .status = cpu_to_le32(on), + }; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(LONG_GROUP, LEDS_CMD), + .len = { sizeof(led_cmd), }, + .data = { &led_cmd, }, + .flags = CMD_ASYNC, + }; + int err; + + if (!iwl_mvm_firmware_running(mvm)) + return; + + err = iwl_mvm_send_cmd(mvm, &cmd); + + if (err) + IWL_WARN(mvm, "LED command failed: %d\n", err); } -/* Set led register off */ -static void iwl_mvm_led_disable(struct iwl_mvm *mvm) +static void iwl_mvm_led_set(struct iwl_mvm *mvm, bool on) { - iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_OFF); + if (mvm->cfg->device_family >= IWL_DEVICE_FAMILY_8000) { + iwl_mvm_send_led_fw_cmd(mvm, on); + return; + } + + iwl_write32(mvm->trans, CSR_LED_REG, + on ? CSR_LED_REG_TURN_ON : CSR_LED_REG_TURN_OFF); } static void iwl_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led); - if (brightness > 0) - iwl_mvm_led_enable(mvm); - else - iwl_mvm_led_disable(mvm); + + iwl_mvm_led_set(mvm, brightness > 0); } int iwl_mvm_leds_init(struct iwl_mvm *mvm) @@ -127,6 +148,21 @@ int iwl_mvm_leds_init(struct iwl_mvm *mvm) return 0; } +void iwl_mvm_leds_sync(struct iwl_mvm *mvm) +{ + if (!(mvm->init_status & IWL_MVM_INIT_STATUS_LEDS_INIT_COMPLETE)) + return; + + /* + * if we control through the register, we're doing it + * even when the firmware isn't up, so no need to sync + */ + if (mvm->cfg->device_family < IWL_DEVICE_FAMILY_8000) + return; + + iwl_mvm_led_set(mvm, mvm->led.brightness > 0); +} + void iwl_mvm_leds_exit(struct iwl_mvm *mvm) { if (!(mvm->init_status & IWL_MVM_INIT_STATUS_LEDS_INIT_COMPLETE)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index c274fe177dfa..8b6238e1c7ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1566,6 +1566,7 @@ void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, #ifdef CONFIG_IWLWIFI_LEDS int iwl_mvm_leds_init(struct iwl_mvm *mvm); void iwl_mvm_leds_exit(struct iwl_mvm *mvm); +void iwl_mvm_leds_sync(struct iwl_mvm *mvm); #else static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm) { @@ -1574,6 +1575,9 @@ static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm) static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm) { } +static inline void iwl_mvm_leds_sync(struct iwl_mvm *mvm) +{ +} #endif /* D3 (WoWLAN, NetDetect) */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 9c9c1b4b6d48..29a21a11c7f8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -350,6 +350,7 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { HCMD_NAME(BINDING_CONTEXT_CMD), HCMD_NAME(TIME_QUOTA_CMD), HCMD_NAME(NON_QOS_TX_COUNTER_CMD), + HCMD_NAME(LEDS_CMD), HCMD_NAME(LQ_CMD), HCMD_NAME(FW_PAGING_BLOCK_CMD), HCMD_NAME(SCAN_OFFLOAD_REQUEST_CMD), -- cgit v1.2.3 From b1a1efc57665f7c70e2051e0531837cb1985f11b Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 21 Jun 2017 14:10:42 +0530 Subject: iwlwifi: mvm: add const to thermal_cooling_device_ops structure Declare thermal_cooling_device_ops structure as const as it is only passed as an argument to the function thermal_cooling_device_register and this argument is of type const. So, declare the structure as const. Signed-off-by: Bhumika Goyal Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index a638bd69a1f9..3f143402cf7f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -813,7 +813,7 @@ unlock: return ret; } -static struct thermal_cooling_device_ops tcooling_ops = { +static const struct thermal_cooling_device_ops tcooling_ops = { .get_max_state = iwl_mvm_tcool_get_max_state, .get_cur_state = iwl_mvm_tcool_get_cur_state, .set_cur_state = iwl_mvm_tcool_set_cur_state, -- cgit v1.2.3 From 61d8c626468a8193a5e890e9f633edc10aa05c1e Mon Sep 17 00:00:00 2001 From: Chaya Rachel Ivgi Date: Tue, 27 Jun 2017 14:13:02 +0300 Subject: iwlwifi: mvm: add debugfs to force CT-kill CT-kill is a thermal-based "RF-kill", which disables the NIC completely if the temperature gets too high, in order to avoid damage. Add a debugfs entry to simulate high temperatures, in order to test CT-kill flows in the driver without having to physically heat the device up. Signed-off-by: Chaya Rachel Ivgi Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 14 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 29f1d1807415..0b5cae54b86b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -114,6 +114,18 @@ static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mvm *mvm, char *buf, return ret ?: count; } +static ssize_t iwl_dbgfs_force_ctkill_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + if (!iwl_mvm_firmware_running(mvm) || + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) + return -EIO; + + iwl_mvm_enter_ctkill(mvm); + + return count; +} + static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { @@ -1641,6 +1653,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); /* Device wide debugfs entries */ MVM_DEBUGFS_READ_FILE_OPS(ctdp_budget); MVM_DEBUGFS_WRITE_FILE_OPS(stop_ctdp, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(force_ctkill, 8); MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8); @@ -1828,6 +1841,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(ctdp_budget, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(stop_ctdp, dbgfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(force_ctkill, dbgfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 8b6238e1c7ea..a6983042d3b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1755,6 +1755,7 @@ void iwl_mvm_thermal_exit(struct iwl_mvm *mvm); void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp); void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm); int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm); int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 budget); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 3f143402cf7f..8876c2abc440 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -71,7 +71,7 @@ #define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ -static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) +void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) { struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; u32 duration = tt->params.ct_kill_duration; -- cgit v1.2.3 From a4380b4eb3da772473efb38bfc1aaad8ddad2b84 Mon Sep 17 00:00:00 2001 From: Sharon Dvir Date: Mon, 12 Jun 2017 11:28:21 +0300 Subject: iwlwifi: change functions that can only return 0 to void Change iwl_set_ucode_api_flags(), iwl_set_ucode_capabilities() to be void. No need to check returned values. Found by Klocwork. Signed-off-by: Sharon Dvir Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 6fdb5921e17f..cdb765656115 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -478,8 +478,8 @@ static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data) return 0; } -static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, - struct iwl_ucode_capabilities *capa) +static void iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, + struct iwl_ucode_capabilities *capa) { const struct iwl_ucode_api *ucode_api = (void *)data; u32 api_index = le32_to_cpu(ucode_api->api_index); @@ -490,20 +490,17 @@ static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, IWL_ERR(drv, "api flags index %d larger than supported by driver\n", api_index); - /* don't return an error so we can load FW that has more bits */ - return 0; + return; } for (i = 0; i < 32; i++) { if (api_flags & BIT(i)) __set_bit(i + 32 * api_index, capa->_api); } - - return 0; } -static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, - struct iwl_ucode_capabilities *capa) +static void iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, + struct iwl_ucode_capabilities *capa) { const struct iwl_ucode_capa *ucode_capa = (void *)data; u32 api_index = le32_to_cpu(ucode_capa->api_index); @@ -514,16 +511,13 @@ static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, IWL_ERR(drv, "capa flags index %d larger than supported by driver\n", api_index); - /* don't return an error so we can load FW that has more bits */ - return 0; + return; } for (i = 0; i < 32; i++) { if (api_flags & BIT(i)) __set_bit(i + 32 * api_index, capa->_capa); } - - return 0; } static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv, @@ -765,14 +759,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, case IWL_UCODE_TLV_API_CHANGES_SET: if (tlv_len != sizeof(struct iwl_ucode_api)) goto invalid_tlv_len; - if (iwl_set_ucode_api_flags(drv, tlv_data, capa)) - goto tlv_error; + iwl_set_ucode_api_flags(drv, tlv_data, capa); break; case IWL_UCODE_TLV_ENABLED_CAPABILITIES: if (tlv_len != sizeof(struct iwl_ucode_capa)) goto invalid_tlv_len; - if (iwl_set_ucode_capabilities(drv, tlv_data, capa)) - goto tlv_error; + iwl_set_ucode_capabilities(drv, tlv_data, capa); break; case IWL_UCODE_TLV_INIT_EVTLOG_PTR: if (tlv_len != sizeof(u32)) -- cgit v1.2.3 From cbeb58ef36a01f190a1b61e52a60d4264d420c6b Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 28 Jun 2017 14:08:53 +0300 Subject: iwlwifi: fix a few instances of misaligned kerneldoc parameters There are a few places where we don't have a space between the * and the @ in the parameter description. Also, in one case, the @ had trailing space before the parameter name. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/tx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index 95dbed609f3e..2909d1ed89cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -421,7 +421,7 @@ enum iwl_tx_status { * occur if tx failed for this frame when it was a member of a previous * aggregation block). If rate scaling is used, retry count indicates the * rate table entry used for all frames in the new agg. - *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for + * @AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for * this frame * * TODO: complete documentation -- cgit v1.2.3 From f6586b69b29ef2e4f3c606c3d2da731a5ff4f16c Mon Sep 17 00:00:00 2001 From: Tzipi Peres Date: Wed, 14 Jun 2017 10:02:47 +0300 Subject: iwlwifi: add support of FPGA fw Load FW according to NIC type, taking into account simulation, if exists. This is determined by a prph register. Signed-off-by: Tzipi Peres Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/cfg/a000.c | 14 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-config.h | 1 + drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 6 ++++++ drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 11 +++++++++++ 4 files changed, 32 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/a000.c b/drivers/net/wireless/intel/iwlwifi/cfg/a000.c index 98f24cd1b44f..40d67a5a2635 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/a000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/a000.c @@ -75,11 +75,14 @@ #define IWL_A000_JF_FW_PRE "iwlwifi-Qu-a0-jf-b0-" #define IWL_A000_HR_FW_PRE "iwlwifi-Qu-a0-hr-a0-" #define IWL_A000_HR_CDB_FW_PRE "iwlwifi-QuIcp-z0-hrcdb-a0-" +#define IWL_A000_HR_F0_FW_PRE "iwlwifi-QuQnj-f0-hr-a0-" #define IWL_A000_HR_MODULE_FIRMWARE(api) \ IWL_A000_HR_FW_PRE "-" __stringify(api) ".ucode" #define IWL_A000_JF_MODULE_FIRMWARE(api) \ IWL_A000_JF_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_A000_HR_QNJ_MODULE_FIRMWARE(api) \ + IWL_A000_HR_F0_FW_PRE "-" __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_A000 10 @@ -168,5 +171,16 @@ const struct iwl_cfg iwla000_2ax_cfg_hr = { .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, }; +const struct iwl_cfg iwla000_2ax_cfg_qnj_hr = { + .name = "Intel(R) Dual Band Wireless AX a000", + .fw_name_pre = IWL_A000_HR_F0_FW_PRE, + IWL_DEVICE_A000, + .ht_params = &iwl_a000_ht_params, + .nvm_ver = IWL_A000_NVM_VERSION, + .nvm_calib_ver = IWL_A000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + MODULE_FIRMWARE(IWL_A000_HR_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_A000_JF_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_A000_HR_QNJ_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index c52623cb7c2a..573dbeed3fbf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -463,6 +463,7 @@ extern const struct iwl_cfg iwla000_2ac_cfg_hr; extern const struct iwl_cfg iwla000_2ac_cfg_hr_cdb; extern const struct iwl_cfg iwla000_2ac_cfg_jf; extern const struct iwl_cfg iwla000_2ax_cfg_hr; +extern const struct iwl_cfg iwla000_2ax_cfg_qnj_hr; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 6772c59b7764..fbce97ed4ecd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -404,6 +404,12 @@ enum aux_misc_master1_en { #define SB_CPU_2_STATUS 0xA01E34 #define UMAG_SB_CPU_1_STATUS 0xA038C0 #define UMAG_SB_CPU_2_STATUS 0xA038C4 +#define UMAG_GEN_HW_STATUS 0xA038C8 + +/* For UMAG_GEN_HW_STATUS reg check */ +enum { + UMAG_GEN_HW_IS_FPGA = BIT(1), +}; /* FW chicken bits */ #define LMPM_CHICK 0xA01FF8 diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 32f06f14328c..439cf424e058 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -3137,7 +3137,18 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, iwl_set_bit(trans, CSR_HOST_CHICKEN, CSR_HOST_CHICKEN_PM_IDLE_SRC_DIS_SB_PME); +#if IS_ENABLED(CONFIG_IWLMVM) trans->hw_rf_id = iwl_read32(trans, CSR_HW_RF_ID); + if (trans->hw_rf_id == CSR_HW_RF_ID_TYPE_HR) { + u32 hw_status; + + hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS); + if (hw_status & UMAG_GEN_HW_IS_FPGA) + trans->cfg = &iwla000_2ax_cfg_qnj_hr; + else + trans->cfg = &iwla000_2ac_cfg_hr; + } +#endif iwl_pcie_set_interrupt_capa(pdev, trans); trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; -- cgit v1.2.3 From 36ae4f3aafe7768d00510a18e5ad17a55a4849a5 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 2 Jul 2017 10:32:34 +0300 Subject: iwlwifi: fix a000 RF_ID define One of the defines has a wrong value. Fixes: 1afb0ae42174 ("iwlwifi: allow combining different phy images with mac images") Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index c6c1876c1ad4..560de9de5232 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -356,7 +356,7 @@ enum { #define CSR_HW_REV_TYPE_NONE (0x00001F0) /* RF_ID value */ -#define CSR_HW_RF_ID_TYPE_JF (0x00105000) +#define CSR_HW_RF_ID_TYPE_JF (0x00105100) #define CSR_HW_RF_ID_TYPE_HR (0x0010A000) #define CSR_HW_RF_ID_TYPE_HRCDB (0x00109000) -- cgit v1.2.3 From ce27f005c9e39f2e4081b0a8f6a5f395fc8eaa61 Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Sun, 25 Jun 2017 17:23:23 +0300 Subject: iwlwifi: dump smem configuration when firmware crashes Add the smem configuration to the fw data dump, once the firmware crashes. This is useful mainly for later parsing of the smem. Signed-off-by: Golan Ben-Ami Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 38 +++++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/fw/error-dump.h | 30 +++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/smem.c | 3 ++ 3 files changed, 66 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 77245fcba996..6afc7a799892 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -545,11 +545,13 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_info *dump_info; struct iwl_fw_error_dump_mem *dump_mem; + struct iwl_fw_error_dump_smem_cfg *dump_smem_cfg; struct iwl_fw_error_dump_trigger_desc *dump_trig; struct iwl_fw_dump_ptrs *fw_error_dump; struct scatterlist *sg_dump_data; u32 sram_len, sram_ofs; const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem = fwrt->fw->dbg_mem_tlv; + struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg; u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0; u32 smem_len = fwrt->fw->n_dbg_mem_tlv ? 0 : fwrt->trans->cfg->smem_len; u32 sram2_len = fwrt->fw->n_dbg_mem_tlv ? @@ -585,8 +587,6 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) /* reading RXF/TXF sizes */ if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) { - struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg; - fifo_data_len = 0; /* Count RXF2 size */ @@ -675,7 +675,8 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) } file_len = sizeof(*dump_file) + - sizeof(*dump_data) * 2 + + sizeof(*dump_data) * 3 + + sizeof(*dump_smem_cfg) + fifo_data_len + prph_len + radio_len + @@ -706,8 +707,8 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) /* If we only want a monitor dump, reset the file length */ if (monitor_dump_only) { - file_len = sizeof(*dump_file) + sizeof(*dump_data) + - sizeof(*dump_info); + file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 + + sizeof(*dump_info) + sizeof(*dump_smem_cfg); } if (fwrt->dump.desc) @@ -744,6 +745,33 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) sizeof(dump_info->bus_human_readable)); dump_data = iwl_fw_error_next_data(dump_data); + + /* Dump shared memory configuration */ + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_CFG); + dump_data->len = cpu_to_le32(sizeof(*dump_smem_cfg)); + dump_smem_cfg = (void *)dump_data->data; + dump_smem_cfg->num_lmacs = cpu_to_le32(mem_cfg->num_lmacs); + dump_smem_cfg->num_txfifo_entries = + cpu_to_le32(mem_cfg->num_txfifo_entries); + for (i = 0; i < MAX_NUM_LMAC; i++) { + int j; + + for (j = 0; j < TX_FIFO_MAX_NUM; j++) + dump_smem_cfg->lmac[i].txfifo_size[j] = + cpu_to_le32(mem_cfg->lmac[i].txfifo_size[j]); + dump_smem_cfg->lmac[i].rxfifo1_size = + cpu_to_le32(mem_cfg->lmac[i].rxfifo1_size); + } + dump_smem_cfg->rxfifo2_size = cpu_to_le32(mem_cfg->rxfifo2_size); + dump_smem_cfg->internal_txfifo_addr = + cpu_to_le32(mem_cfg->internal_txfifo_addr); + for (i = 0; i < TX_FIFO_INTERNAL_MAX_NUM; i++) { + dump_smem_cfg->internal_txfifo_size[i] = + cpu_to_le32(mem_cfg->internal_txfifo_size[i]); + } + + dump_data = iwl_fw_error_next_data(dump_data); + /* We only dump the FIFOs if the FW is in error state */ if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) { iwl_fw_dump_fifos(fwrt, &dump_data); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index cfebde68a391..ed7beca8817e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -7,6 +7,7 @@ * * Copyright(c) 2014 Intel Corporation. All rights reserved. * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2014 Intel Corporation. All rights reserved. * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -92,6 +94,9 @@ * @IWL_FW_ERROR_DUMP_EXTERNAL: used only by external code utilities, and * for that reason is not in use in any other place in the Linux Wi-Fi * stack. + * @IWL_FW_ERROR_DUMP_MEM_CFG: the addresses and sizes of fifos in the smem, + * which we get from the fw after ALIVE. The content is structured as + * &struct iwl_fw_error_dump_smem_cfg. */ enum iwl_fw_error_dump_type { /* 0 is deprecated */ @@ -110,6 +115,7 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_RADIO_REG = 13, IWL_FW_ERROR_DUMP_INTERNAL_TXF = 14, IWL_FW_ERROR_DUMP_EXTERNAL = 15, /* Do not move */ + IWL_FW_ERROR_DUMP_MEM_CFG = 16, IWL_FW_ERROR_DUMP_MAX, }; @@ -208,6 +214,30 @@ struct iwl_fw_error_dump_fw_mon { u8 data[]; } __packed; +#define MAX_NUM_LMAC 2 +#define TX_FIFO_INTERNAL_MAX_NUM 6 +#define TX_FIFO_MAX_NUM 15 +/** + * struct iwl_fw_error_dump_smem_cfg - Dump SMEM configuration + * This must follow &struct iwl_fwrt_shared_mem_cfg. + * @num_lmacs: number of lmacs + * @num_txfifo_entries: number of tx fifos + * @lmac: sizes of lmacs txfifos and rxfifo1 + * @rxfifo2_size: size of rxfifo2 + * @internal_txfifo_addr: address of internal tx fifo + * @internal_txfifo_size: size of internal tx fifo + */ +struct iwl_fw_error_dump_smem_cfg { + __le32 num_lmacs; + __le32 num_txfifo_entries; + struct { + __le32 txfifo_size[TX_FIFO_MAX_NUM]; + __le32 rxfifo1_size; + } lmac[MAX_NUM_LMAC]; + __le32 rxfifo2_size; + __le32 internal_txfifo_addr; + __le32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; +} __packed; /** * struct iwl_fw_error_dump_prph - periphery registers data * @prph_start: address of the first register in this chunk diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c index 065a951cefba..76675736ba4f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/smem.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c @@ -113,6 +113,9 @@ static void iwl_parse_shared_mem(struct iwl_fw_runtime *fwrt, BUILD_BUG_ON(sizeof(fwrt->smem_cfg.internal_txfifo_size) != sizeof(mem_cfg->internal_txfifo_size)); + fwrt->smem_cfg.internal_txfifo_addr = + le32_to_cpu(mem_cfg->internal_txfifo_addr); + for (i = 0; i < ARRAY_SIZE(fwrt->smem_cfg.internal_txfifo_size); i++) -- cgit v1.2.3 From c135cb564c6d4d7758fa68e5d0b37021398f4057 Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Thu, 22 Jun 2017 17:09:08 +0300 Subject: iwlwifi: mvm: move a000 device NVM retrieval to a common place Getting the NVM data in a000 devices should be shared across operation mode. Signed-off-by: Shaul Triebitz Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/fw/nvm.c | 162 ++++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 2 + drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 6 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 91 ------------- 6 files changed, 169 insertions(+), 95 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/nvm.c (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index 5dcb4a848dba..35a32a3ec882 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -12,7 +12,7 @@ iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/a000.o iwlwifi-objs += iwl-trans.o iwlwifi-objs += fw/notif-wait.o iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o fw/dbg.o -iwlwifi-$(CONFIG_IWLMVM) += fw/common_rx.o +iwlwifi-$(CONFIG_IWLMVM) += fw/common_rx.o fw/nvm.o iwlwifi-objs += $(iwlwifi-m) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/nvm.c b/drivers/net/wireless/intel/iwlwifi/fw/nvm.c new file mode 100644 index 000000000000..ae03d0f5564f --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/nvm.c @@ -0,0 +1,162 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include "iwl-drv.h" +#include "runtime.h" +#include "fw/api/nvm-reg.h" +#include "fw/api/commands.h" +#include "iwl-nvm-parse.h" + +struct iwl_nvm_data *iwl_fw_get_nvm(struct iwl_fw_runtime *fwrt) +{ + struct iwl_nvm_get_info cmd = {}; + struct iwl_nvm_get_info_rsp *rsp; + struct iwl_trans *trans = fwrt->trans; + struct iwl_nvm_data *nvm; + struct iwl_host_cmd hcmd = { + .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, + .data = { &cmd, }, + .len = { sizeof(cmd) }, + .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO) + }; + int ret; + bool lar_fw_supported = !iwlwifi_mod_params.lar_disable && + fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT); + + ret = iwl_trans_send_cmd(trans, &hcmd); + if (ret) + return ERR_PTR(ret); + + if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp), + "Invalid payload len in NVM response from FW %d", + iwl_rx_packet_payload_len(hcmd.resp_pkt))) { + ret = -EINVAL; + goto out; + } + + rsp = (void *)hcmd.resp_pkt->data; + if (le32_to_cpu(rsp->general.flags) & NVM_GENERAL_FLAGS_EMPTY_OTP) + IWL_INFO(fwrt, "OTP is empty\n"); + + nvm = kzalloc(sizeof(*nvm) + + sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, + GFP_KERNEL); + if (!nvm) { + ret = -ENOMEM; + goto out; + } + + iwl_set_hw_address_from_csr(trans, nvm); + /* TODO: if platform NVM has MAC address - override it here */ + + if (!is_valid_ether_addr(nvm->hw_addr)) { + IWL_ERR(fwrt, "no valid mac address was found\n"); + ret = -EINVAL; + goto err_free; + } + + IWL_INFO(trans, "base HW address: %pM\n", nvm->hw_addr); + + /* Initialize general data */ + nvm->nvm_version = le16_to_cpu(rsp->general.nvm_version); + + /* Initialize MAC sku data */ + nvm->sku_cap_11ac_enable = + le32_to_cpu(rsp->mac_sku.enable_11ac); + nvm->sku_cap_11n_enable = + le32_to_cpu(rsp->mac_sku.enable_11n); + nvm->sku_cap_band_24GHz_enable = + le32_to_cpu(rsp->mac_sku.enable_24g); + nvm->sku_cap_band_52GHz_enable = + le32_to_cpu(rsp->mac_sku.enable_5g); + nvm->sku_cap_mimo_disabled = + le32_to_cpu(rsp->mac_sku.mimo_disable); + + /* Initialize PHY sku data */ + nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains); + nvm->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains); + + /* Initialize regulatory data */ + nvm->lar_enabled = + le32_to_cpu(rsp->regulatory.lar_enabled) && lar_fw_supported; + + iwl_init_sbands(trans->dev, trans->cfg, nvm, + rsp->regulatory.channel_profile, + nvm->valid_tx_ant & fwrt->fw->valid_tx_ant, + nvm->valid_rx_ant & fwrt->fw->valid_rx_ant, + rsp->regulatory.lar_enabled && lar_fw_supported); + + iwl_free_resp(&hcmd); + return nvm; + +err_free: + kfree(nvm); +out: + iwl_free_resp(&hcmd); + return ERR_PTR(ret); +} +IWL_EXPORT_SYMBOL(iwl_fw_get_nvm); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 66bea6545690..50cfb6d795a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -63,6 +63,7 @@ #include "img.h" #include "fw/api/debug.h" #include "fw/api/paging.h" +#include "iwl-eeprom-parse.h" struct iwl_fw_runtime_ops { int (*dump_start)(void *ctx); @@ -152,5 +153,6 @@ void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt); void iwl_fwrt_handle_notification(struct iwl_fw_runtime *fwrt, struct iwl_rx_cmd_buffer *rxb); +struct iwl_nvm_data *iwl_fw_get_nvm(struct iwl_fw_runtime *fwrt); #endif /* __iwl_fw_runtime_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index ec018d94a9dd..ac782383443e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -412,8 +412,10 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) /* Read the NVM only at driver load time, no need to do this twice */ if (!IWL_MVM_PARSE_NVM && read_nvm) { - ret = iwl_mvm_nvm_get_from_fw(mvm); - if (ret) { + mvm->nvm_data = iwl_fw_get_nvm(&mvm->fwrt); + if (IS_ERR(mvm->nvm_data)) { + ret = PTR_ERR(mvm->nvm_data); + mvm->nvm_data = NULL; IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index a6983042d3b8..8ff74fbb2562 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1377,7 +1377,6 @@ void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm); /* NVM */ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); -int iwl_mvm_nvm_get_from_fw(struct iwl_mvm *mvm); int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 5cc749261ce3..08020386c3d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -546,97 +546,6 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) return ret; } -int iwl_mvm_nvm_get_from_fw(struct iwl_mvm *mvm) -{ - struct iwl_nvm_get_info cmd = {}; - struct iwl_nvm_get_info_rsp *rsp; - struct iwl_trans *trans = mvm->trans; - struct iwl_host_cmd hcmd = { - .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, - .data = { &cmd, }, - .len = { sizeof(cmd) }, - .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO) - }; - int ret; - bool lar_fw_supported = !iwlwifi_mod_params.lar_disable && - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_SUPPORT); - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_send_cmd(mvm, &hcmd); - if (ret) - return ret; - - if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp), - "Invalid payload len in NVM response from FW %d", - iwl_rx_packet_payload_len(hcmd.resp_pkt))) { - ret = -EINVAL; - goto out; - } - - rsp = (void *)hcmd.resp_pkt->data; - if (le32_to_cpu(rsp->general.flags) & NVM_GENERAL_FLAGS_EMPTY_OTP) - IWL_INFO(mvm, "OTP is empty\n"); - - mvm->nvm_data = kzalloc(sizeof(*mvm->nvm_data) + - sizeof(struct ieee80211_channel) * - IWL_NUM_CHANNELS, GFP_KERNEL); - if (!mvm->nvm_data) { - ret = -ENOMEM; - goto out; - } - - iwl_set_hw_address_from_csr(trans, mvm->nvm_data); - /* TODO: if platform NVM has MAC address - override it here */ - - if (!is_valid_ether_addr(mvm->nvm_data->hw_addr)) { - IWL_ERR(trans, "no valid mac address was found\n"); - ret = -EINVAL; - goto err_free; - } - - IWL_INFO(trans, "base HW address: %pM\n", mvm->nvm_data->hw_addr); - - /* Initialize general data */ - mvm->nvm_data->nvm_version = le16_to_cpu(rsp->general.nvm_version); - - /* Initialize MAC sku data */ - mvm->nvm_data->sku_cap_11ac_enable = - le32_to_cpu(rsp->mac_sku.enable_11ac); - mvm->nvm_data->sku_cap_11n_enable = - le32_to_cpu(rsp->mac_sku.enable_11n); - mvm->nvm_data->sku_cap_band_24GHz_enable = - le32_to_cpu(rsp->mac_sku.enable_24g); - mvm->nvm_data->sku_cap_band_52GHz_enable = - le32_to_cpu(rsp->mac_sku.enable_5g); - mvm->nvm_data->sku_cap_mimo_disabled = - le32_to_cpu(rsp->mac_sku.mimo_disable); - - /* Initialize PHY sku data */ - mvm->nvm_data->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains); - mvm->nvm_data->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains); - - /* Initialize regulatory data */ - mvm->nvm_data->lar_enabled = - le32_to_cpu(rsp->regulatory.lar_enabled) && lar_fw_supported; - - iwl_init_sbands(trans->dev, trans->cfg, mvm->nvm_data, - rsp->regulatory.channel_profile, - mvm->nvm_data->valid_tx_ant & mvm->fw->valid_tx_ant, - mvm->nvm_data->valid_rx_ant & mvm->fw->valid_rx_ant, - rsp->regulatory.lar_enabled && lar_fw_supported); - - iwl_free_resp(&hcmd); - return 0; - -err_free: - kfree(mvm->nvm_data); -out: - iwl_free_resp(&hcmd); - return ret; -} - int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) { int ret, section; -- cgit v1.2.3 From 944eafc255500102b69940f425eb073bfab933db Mon Sep 17 00:00:00 2001 From: Chaya Rachel Ivgi Date: Sun, 9 Jul 2017 16:35:14 +0300 Subject: iwlwifi: mvm: set the default cTDP budget In case there is no value received from BIOS for cTDP budget, the default should be 2000 mWatt. Signed-off-by: Chaya Rachel Ivgi Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index ac782383443e..0099050f6e2b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1173,7 +1173,12 @@ int iwl_mvm_up(struct iwl_mvm *mvm) } /* TODO: read the budget from BIOS / Platform NVM */ - if (iwl_mvm_is_ctdp_supported(mvm) && mvm->cooling_dev.cur_state > 0) { + + /* + * In case there is no budget from BIOS / Platform NVM the default + * budget should be 2000mW (cooling state 0). + */ + if (iwl_mvm_is_ctdp_supported(mvm)) { ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START, mvm->cooling_dev.cur_state); if (ret) -- cgit v1.2.3 From 6ca33f8bae01bd3f1949287a0339c3e6f9e96aca Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Mon, 12 Jun 2017 14:59:02 +0300 Subject: iwlwifi: mvm: support new beacon template command Support a new version of the beacon template command. This replaces v8 of the command, which was missing the rate code. Also, export rate decision logic to a separate function. Signed-off-by: Haim Dreyfuss Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/tx.h | 17 +++++--- drivers/net/wireless/intel/iwlwifi/fw/file.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 47 ++++++++++++++++------- 3 files changed, 47 insertions(+), 18 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index 2909d1ed89cf..4928310ddd31 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -786,13 +786,20 @@ struct iwl_mac_beacon_cmd_v7 { struct ieee80211_hdr frame[0]; } __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_7 */ +enum iwl_mac_beacon_flags { + IWL_MAC_BEACON_CCK = BIT(8), + IWL_MAC_BEACON_ANT_A = BIT(9), + IWL_MAC_BEACON_ANT_B = BIT(10), + IWL_MAC_BEACON_ANT_C = BIT(11), +}; + /** * struct iwl_mac_beacon_cmd - beacon template command with offloaded CSA - * @byte_cnt: byte count of the beacon frame - * @flags: for future use + * @byte_cnt: byte count of the beacon frame. + * @flags: least significant byte for rate code. The most significant byte + * is &enum iwl_mac_beacon_flags. * @reserved: reserved - * @template_id: currently equal to the mac context id of the coresponding - * mac. + * @template_id: currently equal to the mac context id of the coresponding mac. * @tim_idx: the offset of the tim IE in the beacon * @tim_size: the length of the tim IE * @ecsa_offset: offset to the ECSA IE if present @@ -809,7 +816,7 @@ struct iwl_mac_beacon_cmd { __le32 ecsa_offset; __le32 csa_offset; struct ieee80211_hdr frame[0]; -} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_8 */ +} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_9 */ struct iwl_beacon_notif { struct iwl_mvm_tx_resp beacon_notify_hdr; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 0fa8c473f1e2..d933aa324ffe 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -260,6 +260,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_STA_TYPE = (__force iwl_ucode_tlv_api_t)30, IWL_UCODE_TLV_API_NAN2_VER2 = (__force iwl_ucode_tlv_api_t)31, /* API Set 1 */ + IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE = (__force iwl_ucode_tlv_api_t)34, IWL_UCODE_TLV_API_NEW_RX_STATS = (__force iwl_ucode_tlv_api_t)35, NUM_IWL_UCODE_TLV_API diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 8fe955d58c6e..a2bf530eeae4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -923,6 +923,19 @@ static u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) return ie - beacon; } +static u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct ieee80211_tx_info *info, + struct ieee80211_vif *vif) +{ + u8 rate; + + if (info->band == NL80211_BAND_5GHZ || vif->p2p) + rate = IWL_FIRST_OFDM_RATE; + else + rate = IWL_FIRST_CCK_RATE; + + return rate; +} + static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct sk_buff *beacon, @@ -930,7 +943,8 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct ieee80211_tx_info *info; - u32 rate, tx_flags; + u8 rate; + u32 tx_flags; info = IEEE80211_SKB_CB(beacon); @@ -955,14 +969,12 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS); - if (info->band == NL80211_BAND_5GHZ || vif->p2p) { - rate = IWL_FIRST_OFDM_RATE; - } else { - rate = IWL_FIRST_CCK_RATE; - tx->rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK); - } + rate = iwl_mvm_mac_ctxt_get_lowest_rate(info, vif); tx->rate_n_flags |= cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); + if (rate == IWL_FIRST_CCK_RATE) + tx->rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK); + } static int iwl_mvm_mac_ctxt_send_beacon_cmd(struct iwl_mvm *mvm, @@ -1033,19 +1045,27 @@ static int iwl_mvm_mac_ctxt_send_beacon_v7(struct iwl_mvm *mvm, sizeof(beacon_cmd)); } -static int iwl_mvm_mac_ctxt_send_beacon_v8(struct iwl_mvm *mvm, +static int iwl_mvm_mac_ctxt_send_beacon_v9(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct sk_buff *beacon) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(beacon); struct iwl_mac_beacon_cmd beacon_cmd = {}; + u8 rate = iwl_mvm_mac_ctxt_get_lowest_rate(info, vif); + u16 flags; + + flags = iwl_mvm_mac80211_idx_to_hwrate(rate); + if (rate == IWL_FIRST_CCK_RATE) + flags |= IWL_MAC_BEACON_CCK; + + beacon_cmd.flags = cpu_to_le16(flags); beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len); beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); if (vif->type == NL80211_IFTYPE_AP) - iwl_mvm_mac_ctxt_set_tim(mvm, - &beacon_cmd.tim_idx, + iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd.tim_idx, &beacon_cmd.tim_size, beacon->data, beacon->len); @@ -1073,10 +1093,11 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD)) return iwl_mvm_mac_ctxt_send_beacon_v6(mvm, vif, beacon); - if (!iwl_mvm_has_new_tx_api(mvm)) - return iwl_mvm_mac_ctxt_send_beacon_v7(mvm, vif, beacon); + if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE)) + return iwl_mvm_mac_ctxt_send_beacon_v9(mvm, vif, beacon); - return iwl_mvm_mac_ctxt_send_beacon_v8(mvm, vif, beacon); + return iwl_mvm_mac_ctxt_send_beacon_v7(mvm, vif, beacon); } /* The beacon template for the AP/GO/IBSS has changed and needs update */ -- cgit v1.2.3 From 0bef1b83d35566e9bc757469c823c126862a65d3 Mon Sep 17 00:00:00 2001 From: Matt Chen Date: Fri, 23 Jun 2017 17:50:18 +0800 Subject: iwlwifi: mvm: don't send CTDP commands via debugfs if not supported Fix this issue if it is not supported by the firmware. Fixes: 00f481bd895a ("iwlwifi: mvm: add ctdp operations to debugfs") Signed-off-by: Matt Chen Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 0b5cae54b86b..ceb486610a56 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -82,6 +82,9 @@ static ssize_t iwl_dbgfs_ctdp_budget_read(struct file *file, char buf[16]; int pos, budget; + if (!iwl_mvm_is_ctdp_supported(mvm)) + return -EOPNOTSUPP; + if (!iwl_mvm_firmware_running(mvm) || mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) return -EIO; @@ -103,6 +106,9 @@ static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mvm *mvm, char *buf, { int ret; + if (!iwl_mvm_is_ctdp_supported(mvm)) + return -EOPNOTSUPP; + if (!iwl_mvm_firmware_running(mvm) || mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) return -EIO; -- cgit v1.2.3 From 732d06e9d9cf96c39cd1c0cd16472930a5583bb7 Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Mon, 10 Jul 2017 19:58:10 +0300 Subject: iwlwifi: mvm: add station before allocating a queue One of the queue config params is the associated station id. Hence the FW must know about the station prior to the queue allocation. In a000 devices, allocating a queue without a valid station results with assert 0x2B00. In FW restart flow the queues are allocated before adding the station so first add the station. Signed-off-by: Shaul Triebitz Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 108 ++++++++++++++++----------- 1 file changed, 63 insertions(+), 45 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 922cd5379841..f88202c38d4c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1277,6 +1277,50 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, } } +static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta, + const u8 *addr, + u16 mac_id, u16 color) +{ + struct iwl_mvm_add_sta_cmd cmd; + int ret; + u32 status; + + lockdep_assert_held(&mvm->mutex); + + memset(&cmd, 0, sizeof(cmd)); + cmd.sta_id = sta->sta_id; + cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, + color)); + if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) + cmd.station_type = sta->type; + + if (!iwl_mvm_has_new_tx_api(mvm)) + cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); + cmd.tid_disable_tx = cpu_to_le16(0xffff); + + if (addr) + memcpy(cmd.addr, addr, ETH_ALEN); + + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, + iwl_mvm_add_sta_cmd_size(mvm), + &cmd, &status); + if (ret) + return ret; + + switch (status & IWL_ADD_STA_STATUS_MASK) { + case ADD_STA_SUCCESS: + IWL_DEBUG_INFO(mvm, "Internal station added.\n"); + return 0; + default: + ret = -EIO; + IWL_ERR(mvm, "Add internal station failed, status=0x%x\n", + status); + break; + } + return ret; +} + int iwl_mvm_add_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta) @@ -1285,6 +1329,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_rxq_dup_data *dup_data; int i, ret, sta_id; + bool sta_update = false; + unsigned int sta_flags = 0; lockdep_assert_held(&mvm->mutex); @@ -1301,7 +1347,23 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, /* if this is a HW restart re-alloc existing queues */ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + struct iwl_mvm_int_sta tmp_sta = { + .sta_id = sta_id, + .type = mvm_sta->sta_type, + }; + + /* + * First add an empty station since allocating + * a queue requires a valid station + */ + ret = iwl_mvm_add_int_sta_common(mvm, &tmp_sta, sta->addr, + mvmvif->id, mvmvif->color); + if (ret) + goto err; + iwl_mvm_realloc_queues_after_restart(mvm, mvm_sta); + sta_update = true; + sta_flags = iwl_mvm_has_new_tx_api(mvm) ? 0 : STA_MODIFY_QUEUES; goto update_fw; } @@ -1368,7 +1430,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, } update_fw: - ret = iwl_mvm_sta_send_to_fw(mvm, sta, false, 0); + ret = iwl_mvm_sta_send_to_fw(mvm, sta, sta_update, sta_flags); if (ret) goto err; @@ -1637,50 +1699,6 @@ void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta) sta->sta_id = IWL_MVM_INVALID_STA; } -static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta, - const u8 *addr, - u16 mac_id, u16 color) -{ - struct iwl_mvm_add_sta_cmd cmd; - int ret; - u32 status; - - lockdep_assert_held(&mvm->mutex); - - memset(&cmd, 0, sizeof(cmd)); - cmd.sta_id = sta->sta_id; - cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, - color)); - if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) - cmd.station_type = sta->type; - - if (!iwl_mvm_has_new_tx_api(mvm)) - cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); - cmd.tid_disable_tx = cpu_to_le16(0xffff); - - if (addr) - memcpy(cmd.addr, addr, ETH_ALEN); - - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, - iwl_mvm_add_sta_cmd_size(mvm), - &cmd, &status); - if (ret) - return ret; - - switch (status & IWL_ADD_STA_STATUS_MASK) { - case ADD_STA_SUCCESS: - IWL_DEBUG_INFO(mvm, "Internal station added.\n"); - return 0; - default: - ret = -EIO; - IWL_ERR(mvm, "Add internal station failed, status=0x%x\n", - status); - break; - } - return ret; -} - static void iwl_mvm_enable_aux_queue(struct iwl_mvm *mvm) { unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? -- cgit v1.2.3 From 39fff7599397e1c9fdf54093f1f4c3146066c24b Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 13 Jul 2017 09:49:32 +0300 Subject: iwlwifi: pcie: don't init a Tx queue with an SSN > size of the queue The TVQM tells us the initial write pointer for a queue, but that write pointer is in WiFi sequence number unit and not in TFD index unit. Which means that the write pointer in the TVQM's response can be bigger than the Tx queue ring size. Fix that by modulo'ing the write pointer from the TVQM with the Tx queue size. Fixes: 66128fa08806 ("iwlwifi: move to TVQM mode") Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 5dc785d4c167..83a28892dc4f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -1033,6 +1033,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, .flags = CMD_WANT_SKB, }; int ret, qid; + u32 wr_ptr; txq = kzalloc(sizeof(*txq), GFP_KERNEL); if (!txq) @@ -1073,6 +1074,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, rsp = (void *)hcmd.resp_pkt->data; qid = le16_to_cpu(rsp->queue_number); + wr_ptr = le16_to_cpu(rsp->write_pointer); if (qid >= ARRAY_SIZE(trans_pcie->txq)) { WARN_ONCE(1, "queue index %d unsupported", qid); @@ -1088,10 +1090,11 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, txq->id = qid; trans_pcie->txq[qid] = txq; + wr_ptr &= (TFD_QUEUE_SIZE_MAX - 1); /* Place first TFD at index corresponding to start sequence number */ - txq->read_ptr = le16_to_cpu(rsp->write_pointer); - txq->write_ptr = le16_to_cpu(rsp->write_pointer); + txq->read_ptr = wr_ptr; + txq->write_ptr = wr_ptr; iwl_write_direct32(trans, HBUS_TARG_WRPTR, (txq->write_ptr) | (qid << 16)); IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d\n", qid); -- cgit v1.2.3 From f4ca70ef931fc7191d6de2feedffb81eccec1982 Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Wed, 28 Jun 2017 13:03:58 +0300 Subject: iwlwifi: fix nmi triggering from host Although nmi was triggered fine till now, it appears that the driver didn't write the exact correct values to the correct addresses for each HW. Fix the nmi triggering by setting the correct addresses and values. Fixes: 4c9706dc2f29 ("iwlwifi: update nmi register") Signed-off-by: Golan Ben-Ami Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 14 +++----------- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 7 +++---- 2 files changed, 6 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index c527b8c10370..efb1998dcabd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -241,20 +241,12 @@ IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); void iwl_force_nmi(struct iwl_trans *trans) { - if (trans->cfg->device_family < IWL_DEVICE_FAMILY_8000) { + if (trans->cfg->device_family < IWL_DEVICE_FAMILY_9000) iwl_write_prph(trans, DEVICE_SET_NMI_REG, DEVICE_SET_NMI_VAL_DRV); - iwl_write_prph(trans, DEVICE_SET_NMI_REG, - DEVICE_SET_NMI_VAL_HW); - } else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_A000) { + else iwl_write_prph(trans, UREG_NIC_SET_NMI_DRIVER, - DEVICE_SET_NMI_8000_VAL); - } else { - iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG, - DEVICE_SET_NMI_8000_VAL); - iwl_write_prph(trans, DEVICE_SET_NMI_REG, - DEVICE_SET_NMI_VAL_DRV); - } + UREG_NIC_SET_NMI_DRIVER_NMI_FROM_DRIVER_MSK); } IWL_EXPORT_SYMBOL(iwl_force_nmi); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index fbce97ed4ecd..421a869633a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -109,13 +109,12 @@ /* Device system time */ #define DEVICE_SYSTEM_TIME_REG 0xA0206C -/* Device NMI register */ +/* Device NMI register and value for 8000 family and lower hw's */ #define DEVICE_SET_NMI_REG 0x00a01c30 -#define DEVICE_SET_NMI_VAL_HW BIT(0) #define DEVICE_SET_NMI_VAL_DRV BIT(7) -#define DEVICE_SET_NMI_8000_REG 0x00a01c24 -#define DEVICE_SET_NMI_8000_VAL 0x1000000 +/* Device NMI register and value for 9000 family and above hw's */ #define UREG_NIC_SET_NMI_DRIVER 0x00a05c10 +#define UREG_NIC_SET_NMI_DRIVER_NMI_FROM_DRIVER_MSK 0xff000000 /* Shared registers (0x0..0x3ff, via target indirect or periphery */ #define SHR_BASE 0x00a10000 -- cgit v1.2.3 From fb70d49f2afec87fc570852c2251ffd3117c7373 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 17 Jul 2017 09:15:34 +0300 Subject: iwlwifi: remove references to unsupported HW There are still some references to 3945 and 4965 HW, which were never supported in iwlwifi. These references were inherited from a previous project and are irrelevant here. Additionally, remove some irrelevant references to 5100 HW. Remove all these. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/dvm/commands.h | 8 +------- drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 9 +++++---- 3 files changed, 7 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h index ede47e3c5971..f89736d60a3d 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h @@ -311,11 +311,6 @@ enum { /** * rate_n_flags Tx antenna masks - * 4965 has 2 transmitters - * 5100 has 1 transmitter B - * 5150 has 1 transmitter A - * 5300 has 3 transmitters - * 5350 has 3 transmitters * bit14:16 */ #define RATE_MCS_ANT_POS 14 @@ -1230,7 +1225,6 @@ struct iwl_rx_mpdu_res_start { */ /* - * 4965 uCode updates these Tx attempt count values in host DRAM. * Used for managing Tx retries when expecting block-acks. * Driver should set these fields to 0. */ @@ -1540,7 +1534,7 @@ struct iwl_link_qual_general_params { /* Best single antenna to use for single stream (legacy, SISO). */ u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ - /* Best antennas to use for MIMO (unused for 4965, assumes both). */ + /* Best antennas to use for MIMO */ u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ /* diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 560de9de5232..7d468ad7cb6a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -169,7 +169,7 @@ /* * CSR Hardware Revision Workaround Register. Indicates hardware rev; - * "step" determines CCK backoff for txpower calculation. Used for 4965 only. + * "step" determines CCK backoff for txpower calculation. * See also CSR_HW_REV register. * Bit fields: * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 439cf424e058..382d7c251066 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1842,8 +1842,8 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, * These bits say the device is running, and should keep running for * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), * but they do not indicate that embedded SRAM is restored yet; - * 3945 and 4965 have volatile SRAM, and must save/restore contents - * to/from host DRAM when sleeping/waking for power-saving. + * HW with volatile SRAM must save/restore contents to/from + * host DRAM when sleeping/waking for power-saving. * Each direction takes approximately 1/4 millisecond; with this * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a * series of register accesses are expected (e.g. reading Event Log), @@ -1851,8 +1851,9 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, * * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that * SRAM is okay/restored. We don't check that here because this call - * is just for hardware register access; but GP1 MAC_SLEEP check is a - * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). + * is just for hardware register access; but GP1 MAC_SLEEP + * check is a good idea before accessing the SRAM of HW with + * volatile SRAM (e.g. reading Event Log). * * 5000 series and later (including 1000 series) have non-volatile SRAM, * and do not save/restore SRAM when power cycling. -- cgit v1.2.3 From 9bb3d5a003b1740a8252c59f6c82dd75b3c0fe83 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 16 Jul 2017 12:45:12 +0300 Subject: iwlwifi: pcie: free the TSO page when a Tx queue is unmapped on A000 devices When we unmap a non-empty Tx queue, we need to free the pages that we allocated for the headers in TSO flows. This code existed for the 9000 device family, but somehow it got left out when the new Tx path for the A000 devices was written. Fixes: 2b0c5946d9ed ("iwlwifi: pcie: introduce a000 TX queues management") Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 2 ++ drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 9 +++++++++ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 4 ++-- 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index a8ffd4ca8cd8..f46871840fd2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -806,6 +806,8 @@ int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr, size_t size); void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr); void iwl_pcie_apply_destination(struct iwl_trans *trans); +void iwl_pcie_free_tso_page(struct iwl_trans_pcie *trans_pcie, + struct sk_buff *skb); #ifdef CONFIG_INET struct iwl_tso_hdr_page *get_page_hdr(struct iwl_trans *trans, size_t len); #endif diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 83a28892dc4f..4db45e56b6ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -937,6 +937,15 @@ void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", txq_id, txq->read_ptr); + if (txq_id != trans_pcie->cmd_queue) { + int idx = get_cmd_index(txq, txq->read_ptr); + struct sk_buff *skb = txq->entries[idx].skb; + + if (WARN_ON_ONCE(!skb)) + continue; + + iwl_pcie_free_tso_page(trans_pcie, skb); + } iwl_pcie_gen2_free_tfd(trans, txq); txq->read_ptr = iwl_queue_inc_wrap(txq->read_ptr); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 6f5894545f4f..c893f9088f9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -577,8 +577,8 @@ int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, return 0; } -static void iwl_pcie_free_tso_page(struct iwl_trans_pcie *trans_pcie, - struct sk_buff *skb) +void iwl_pcie_free_tso_page(struct iwl_trans_pcie *trans_pcie, + struct sk_buff *skb) { struct page **page_ptr; -- cgit v1.2.3 From 88c5f476bac964eb96e751b004f5684c90629eab Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 23 Jul 2017 12:53:46 +0300 Subject: iwlwifi: mvm: fix the coex firmware API The firmware API defined in the header files didn't match the structure that are actually passed by the firmware. The impact could be a decision for MIMO in Tx or Rx in coex scenarios. Fixes: 430a3bbafdc7 ("iwlwifi: mvm: BT Coex - new API") Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/coex.h | 14 +++++--------- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 7 +++---- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 4 ++-- 3 files changed, 10 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h index 583f4189f55e..df4ecec59b40 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h @@ -220,12 +220,6 @@ enum iwl_bt_ci_compliance { BT_CI_COMPLIANCE_BOTH = 3, }; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */ -#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \ - (_ttc_rrc_status & BIT(_phy_id)) - -#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \ - ((_ttc_rrc_status >> 4) & BIT(_phy_id)) - /** * struct iwl_bt_coex_profile_notif - notification about BT coex * @mbox_msg: message from BT to WiFi @@ -234,7 +228,8 @@ enum iwl_bt_ci_compliance { * @primary_ch_lut: LUT used for primary channel &enum iwl_bt_coex_lut_type * @secondary_ch_lut: LUT used for secondary channel &enum iwl_bt_coex_lut_type * @bt_activity_grading: the activity of BT &enum iwl_bt_activity_grading - * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY + * @ttc_status: is TTC enabled - one bit per PHY + * @rrc_status: is RRC enabled - one bit per PHY * @reserved: reserved */ struct iwl_bt_coex_profile_notif { @@ -245,8 +240,9 @@ struct iwl_bt_coex_profile_notif { __le32 primary_ch_lut; __le32 secondary_ch_lut; __le32 bt_activity_grading; - u8 ttc_rrc_status; - u8 reserved[3]; + u8 ttc_status; + u8 rrc_status; + __le16 reserved; } __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ #endif /* __iwl_fw_api_coex_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 6c5c6510428a..0b4486114ddc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -560,8 +560,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, smps_mode = IEEE80211_SMPS_AUTOMATIC; if (mvmvif->phy_ctxt && - IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status, - mvmvif->phy_ctxt->id)) + (mvm->last_bt_notif.rrc_status & BIT(mvmvif->phy_ctxt->id))) smps_mode = IEEE80211_SMPS_AUTOMATIC; IWL_DEBUG_COEX(data->mvm, @@ -792,7 +791,7 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; enum iwl_bt_coex_lut_type lut_type; - if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) + if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) return LINK_QUAL_AGG_TIME_LIMIT_DEF; if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < @@ -816,7 +815,7 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; enum iwl_bt_coex_lut_type lut_type; - if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) + if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) return true; if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index ceb486610a56..ba2745a3b537 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -569,9 +569,9 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, "antenna isolation = %d CORUN LUT index = %d\n", mvm->last_ant_isol, mvm->last_corun_lut); pos += scnprintf(buf + pos, bufsz - pos, "bt_rrc = %d\n", - (notif->ttc_rrc_status >> 4) & 0xF); + notif->rrc_status & 0xF); pos += scnprintf(buf + pos, bufsz - pos, "bt_ttc = %d\n", - notif->ttc_rrc_status & 0xF); + notif->ttc_status & 0xF); pos += scnprintf(buf + pos, bufsz - pos, "sync_sco = %d\n", IWL_MVM_BT_COEX_SYNC2SCO); -- cgit v1.2.3 From 5dd294d4eb634a5f4c877dc487e83f975aa05c11 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 10 Aug 2017 01:40:01 +0200 Subject: bpf, nfp: implement jiting of BPF_J{LT,LE} This work implements jiting of BPF_J{LT,LE} instructions with BPF_X/BPF_K variants for the nfp eBPF JIT. The two BPF_J{SLT,SLE} instructions have not been added yet given BPF_J{SGT,SGE} are not supported yet either. Signed-off-by: Daniel Borkmann Acked-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/bpf/jit.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c index 8e57fda6b8b5..239dfbe8a0a1 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c @@ -1238,6 +1238,16 @@ static int jge_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) return wrp_cmp_imm(nfp_prog, meta, BR_BHS, true); } +static int jlt_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + return wrp_cmp_imm(nfp_prog, meta, BR_BHS, false); +} + +static int jle_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + return wrp_cmp_imm(nfp_prog, meta, BR_BLO, true); +} + static int jset_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { const struct bpf_insn *insn = &meta->insn; @@ -1325,6 +1335,16 @@ static int jge_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) return wrp_cmp_reg(nfp_prog, meta, BR_BHS, true); } +static int jlt_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + return wrp_cmp_reg(nfp_prog, meta, BR_BHS, false); +} + +static int jle_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + return wrp_cmp_reg(nfp_prog, meta, BR_BLO, true); +} + static int jset_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { return wrp_test_reg(nfp_prog, meta, ALU_OP_AND, BR_BNE); @@ -1383,11 +1403,15 @@ static const instr_cb_t instr_cb[256] = { [BPF_JMP | BPF_JEQ | BPF_K] = jeq_imm, [BPF_JMP | BPF_JGT | BPF_K] = jgt_imm, [BPF_JMP | BPF_JGE | BPF_K] = jge_imm, + [BPF_JMP | BPF_JLT | BPF_K] = jlt_imm, + [BPF_JMP | BPF_JLE | BPF_K] = jle_imm, [BPF_JMP | BPF_JSET | BPF_K] = jset_imm, [BPF_JMP | BPF_JNE | BPF_K] = jne_imm, [BPF_JMP | BPF_JEQ | BPF_X] = jeq_reg, [BPF_JMP | BPF_JGT | BPF_X] = jgt_reg, [BPF_JMP | BPF_JGE | BPF_X] = jge_reg, + [BPF_JMP | BPF_JLT | BPF_X] = jlt_reg, + [BPF_JMP | BPF_JLE | BPF_X] = jle_reg, [BPF_JMP | BPF_JSET | BPF_X] = jset_reg, [BPF_JMP | BPF_JNE | BPF_X] = jne_reg, [BPF_JMP | BPF_EXIT] = goto_out, -- cgit v1.2.3 From 7c1885ae9aba0d6b3f854dfee099a3c1c796755f Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Tue, 8 Aug 2017 14:28:45 -0500 Subject: ibmvnic: Clean up resources on probe failure Ensure that any resources allocated during probe are released if the probe of the driver fails. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 32c116652755..cf3cf921eb35 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -3841,31 +3841,35 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) do { rc = ibmvnic_init(adapter); - if (rc && rc != EAGAIN) { - free_netdev(netdev); - return rc; - } + if (rc && rc != EAGAIN) + goto ibmvnic_init_fail; } while (rc == EAGAIN); netdev->mtu = adapter->req_mtu - ETH_HLEN; rc = device_create_file(&dev->dev, &dev_attr_failover); - if (rc) { - free_netdev(netdev); - return rc; - } + if (rc) + goto ibmvnic_init_fail; rc = register_netdev(netdev); if (rc) { dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc); - device_remove_file(&dev->dev, &dev_attr_failover); - free_netdev(netdev); - return rc; + goto ibmvnic_register_fail; } dev_info(&dev->dev, "ibmvnic registered\n"); adapter->state = VNIC_PROBED; return 0; + +ibmvnic_register_fail: + device_remove_file(&dev->dev, &dev_attr_failover); + +ibmvnic_init_fail: + release_sub_crqs(adapter); + release_crq_queue(adapter); + free_netdev(netdev); + + return rc; } static int ibmvnic_remove(struct vio_dev *dev) -- cgit v1.2.3 From d1cf33d93166f146484659448bda54f1f651379b Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Tue, 8 Aug 2017 15:24:05 -0500 Subject: ibmvnic: Add netdev_dbg output for debugging To ease debugging of the ibmvnic driver add a series of netdev_dbg() statements to track driver status, especially during initialization, removal, and resetting of the driver. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 62 +++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index cf3cf921eb35..6b7d6b8aeb7a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -399,6 +399,7 @@ static int init_stats_token(struct ibmvnic_adapter *adapter) } adapter->stats_token = stok; + netdev_dbg(adapter->netdev, "Stats token initialized (%llx)\n", stok); return 0; } @@ -412,6 +413,8 @@ static int reset_rx_pools(struct ibmvnic_adapter *adapter) for (i = 0; i < rx_scrqs; i++) { rx_pool = &adapter->rx_pool[i]; + netdev_dbg(adapter->netdev, "Re-setting rx_pool[%d]\n", i); + rc = reset_long_term_buff(adapter, &rx_pool->long_term_buff); if (rc) return rc; @@ -444,6 +447,8 @@ static void release_rx_pools(struct ibmvnic_adapter *adapter) for (i = 0; i < rx_scrqs; i++) { rx_pool = &adapter->rx_pool[i]; + netdev_dbg(adapter->netdev, "Releasing rx_pool[%d]\n", i); + kfree(rx_pool->free_map); free_long_term_buff(adapter, &rx_pool->long_term_buff); @@ -490,7 +495,7 @@ static int init_rx_pools(struct net_device *netdev) rx_pool = &adapter->rx_pool[i]; netdev_dbg(adapter->netdev, - "Initializing rx_pool %d, %lld buffs, %lld bytes each\n", + "Initializing rx_pool[%d], %lld buffs, %lld bytes each\n", i, adapter->req_rx_add_entries_per_subcrq, be64_to_cpu(size_array[i])); @@ -540,6 +545,8 @@ static int reset_tx_pools(struct ibmvnic_adapter *adapter) tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); for (i = 0; i < tx_scrqs; i++) { + netdev_dbg(adapter->netdev, "Re-setting tx_pool[%d]\n", i); + tx_pool = &adapter->tx_pool[i]; rc = reset_long_term_buff(adapter, &tx_pool->long_term_buff); @@ -570,6 +577,7 @@ static void release_tx_pools(struct ibmvnic_adapter *adapter) tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); for (i = 0; i < tx_scrqs; i++) { + netdev_dbg(adapter->netdev, "Releasing tx_pool[%d]\n", i); tx_pool = &adapter->tx_pool[i]; kfree(tx_pool->tx_buff); free_long_term_buff(adapter, &tx_pool->long_term_buff); @@ -596,6 +604,11 @@ static int init_tx_pools(struct net_device *netdev) for (i = 0; i < tx_subcrqs; i++) { tx_pool = &adapter->tx_pool[i]; + + netdev_dbg(adapter->netdev, + "Initializing tx_pool[%d], %lld buffs\n", + i, adapter->req_tx_entries_per_subcrq); + tx_pool->tx_buff = kcalloc(adapter->req_tx_entries_per_subcrq, sizeof(struct ibmvnic_tx_buff), GFP_KERNEL); @@ -666,8 +679,10 @@ static void ibmvnic_napi_disable(struct ibmvnic_adapter *adapter) if (!adapter->napi_enabled) return; - for (i = 0; i < adapter->req_rx_queues; i++) + for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(adapter->netdev, "Disabling napi[%d]\n", i); napi_disable(&adapter->napi[i]); + } adapter->napi_enabled = false; } @@ -730,8 +745,11 @@ static void release_resources(struct ibmvnic_adapter *adapter) if (adapter->napi) { for (i = 0; i < adapter->req_rx_queues; i++) { - if (&adapter->napi[i]) + if (&adapter->napi[i]) { + netdev_dbg(adapter->netdev, + "Releasing napi[%d]\n", i); netif_napi_del(&adapter->napi[i]); + } } } } @@ -744,7 +762,8 @@ static int set_link_state(struct ibmvnic_adapter *adapter, u8 link_state) bool resend; int rc; - netdev_err(netdev, "setting link state %d\n", link_state); + netdev_dbg(netdev, "setting link state %d\n", link_state); + memset(&crq, 0, sizeof(crq)); crq.logical_link_state.first = IBMVNIC_CRQ_CMD; crq.logical_link_state.cmd = LOGICAL_LINK_STATE; @@ -781,6 +800,9 @@ static int set_real_num_queues(struct net_device *netdev) struct ibmvnic_adapter *adapter = netdev_priv(netdev); int rc; + netdev_dbg(netdev, "Setting real tx/rx queues (%llx/%llx)\n", + adapter->req_tx_queues, adapter->req_rx_queues); + rc = netif_set_real_num_tx_queues(netdev, adapter->req_tx_queues); if (rc) { netdev_err(netdev, "failed to set the number of tx queues\n"); @@ -818,6 +840,7 @@ static int init_resources(struct ibmvnic_adapter *adapter) return -ENOMEM; for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(netdev, "Adding napi[%d]\n", i); netif_napi_add(netdev, &adapter->napi[i], ibmvnic_poll, NAPI_POLL_WEIGHT); } @@ -846,6 +869,7 @@ static int __ibmvnic_open(struct net_device *netdev) * set the logical link state to up */ for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(netdev, "Enabling rx_scrq[%d] irq\n", i); if (prev_state == VNIC_CLOSED) enable_irq(adapter->rx_scrq[i]->irq); else @@ -853,6 +877,7 @@ static int __ibmvnic_open(struct net_device *netdev) } for (i = 0; i < adapter->req_tx_queues; i++) { + netdev_dbg(netdev, "Enabling tx_scrq[%d] irq\n", i); if (prev_state == VNIC_CLOSED) enable_irq(adapter->tx_scrq[i]->irq); else @@ -926,6 +951,7 @@ static void clean_tx_pools(struct ibmvnic_adapter *adapter) if (!tx_pool) continue; + netdev_dbg(adapter->netdev, "Cleaning tx_pool[%d]\n", i); for (j = 0; j < tx_entries; j++) { if (tx_pool->tx_buff[j].skb) { dev_kfree_skb_any(tx_pool->tx_buff[j].skb); @@ -953,8 +979,11 @@ static int __ibmvnic_close(struct net_device *netdev) if (adapter->tx_scrq) { for (i = 0; i < adapter->req_tx_queues; i++) - if (adapter->tx_scrq[i]->irq) + if (adapter->tx_scrq[i]->irq) { + netdev_dbg(adapter->netdev, + "Disabling tx_scrq[%d] irq\n", i); disable_irq(adapter->tx_scrq[i]->irq); + } } rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); @@ -973,8 +1002,11 @@ static int __ibmvnic_close(struct net_device *netdev) break; } - if (adapter->rx_scrq[i]->irq) + if (adapter->rx_scrq[i]->irq) { + netdev_dbg(adapter->netdev, + "Disabling rx_scrq[%d] irq\n", i); disable_irq(adapter->rx_scrq[i]->irq); + } } } @@ -1367,6 +1399,9 @@ static int do_reset(struct ibmvnic_adapter *adapter, struct net_device *netdev = adapter->netdev; int i, rc; + netdev_dbg(adapter->netdev, "Re-setting driver (%d)\n", + rwi->reset_reason); + netif_carrier_off(netdev); adapter->reset_reason = rwi->reset_reason; @@ -1491,6 +1526,7 @@ static void __ibmvnic_reset(struct work_struct *work) } if (rc) { + netdev_dbg(adapter->netdev, "Reset failed\n"); free_all_rwi(adapter); mutex_unlock(&adapter->reset_lock); return; @@ -1524,7 +1560,7 @@ static void ibmvnic_reset(struct ibmvnic_adapter *adapter, list_for_each(entry, &adapter->rwi_list) { tmp = list_entry(entry, struct ibmvnic_rwi, list); if (tmp->reset_reason == reason) { - netdev_err(netdev, "Matching reset found, skipping\n"); + netdev_dbg(netdev, "Skipping matching reset\n"); mutex_unlock(&adapter->rwi_lock); return; } @@ -1540,6 +1576,8 @@ static void ibmvnic_reset(struct ibmvnic_adapter *adapter, rwi->reset_reason = reason; list_add_tail(&rwi->list, &adapter->rwi_list); mutex_unlock(&adapter->rwi_lock); + + netdev_dbg(adapter->netdev, "Scheduling reset (reason %d)\n", reason); schedule_work(&adapter->ibmvnic_reset); } @@ -1900,12 +1938,14 @@ static int reset_sub_crq_queues(struct ibmvnic_adapter *adapter) int i, rc; for (i = 0; i < adapter->req_tx_queues; i++) { + netdev_dbg(adapter->netdev, "Re-setting tx_scrq[%d]\n", i); rc = reset_one_sub_crq_queue(adapter, adapter->tx_scrq[i]); if (rc) return rc; } for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(adapter->netdev, "Re-setting rx_scrq[%d]\n", i); rc = reset_one_sub_crq_queue(adapter, adapter->rx_scrq[i]); if (rc) return rc; @@ -2009,6 +2049,8 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter) if (!adapter->tx_scrq[i]) continue; + netdev_dbg(adapter->netdev, "Releasing tx_scrq[%d]\n", + i); if (adapter->tx_scrq[i]->irq) { free_irq(adapter->tx_scrq[i]->irq, adapter->tx_scrq[i]); @@ -2028,6 +2070,8 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter) if (!adapter->rx_scrq[i]) continue; + netdev_dbg(adapter->netdev, "Releasing rx_scrq[%d]\n", + i); if (adapter->rx_scrq[i]->irq) { free_irq(adapter->rx_scrq[i]->irq, adapter->rx_scrq[i]); @@ -2182,6 +2226,8 @@ static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter) int rc = 0; for (i = 0; i < adapter->req_tx_queues; i++) { + netdev_dbg(adapter->netdev, "Initializing tx_scrq[%d] irq\n", + i); scrq = adapter->tx_scrq[i]; scrq->irq = irq_create_mapping(NULL, scrq->hw_irq); @@ -2203,6 +2249,8 @@ static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter) } for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(adapter->netdev, "Initializing rx_scrq[%d] irq\n", + i); scrq = adapter->rx_scrq[i]; scrq->irq = irq_create_mapping(NULL, scrq->hw_irq); if (!scrq->irq) { -- cgit v1.2.3 From 16587c210cc58c2571ee3905a1c1213ca2642506 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Tue, 8 Aug 2017 15:26:18 -0500 Subject: ibmvnic: Correct 'unused variable' warning in build. Commit a248878d7a1d ("ibmvnic: Check for transport event on driver resume") removed the loop to kick irqs on driver resume but didn't remove the now unused loop variable 'i'. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 6b7d6b8aeb7a..5ac873173b2e 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -4013,7 +4013,6 @@ static int ibmvnic_resume(struct device *dev) { struct net_device *netdev = dev_get_drvdata(dev); struct ibmvnic_adapter *adapter = netdev_priv(netdev); - int i; if (adapter->state != VNIC_OPEN) return 0; -- cgit v1.2.3 From 42013e9038225aeaed98abf7ac4973c2a6f4ffc6 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Tue, 8 Aug 2017 19:34:28 -0700 Subject: liquidio: napi cleanup Disable napi when interface is going down. Delete napi when destroying the interface. Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 15 +++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 14 ++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 3ec0dd9b7201..cbd6287e578e 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1736,6 +1736,10 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) oct->droq[0]->ops.poll_mode = 0; } + /* Delete NAPI */ + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + netif_napi_del(napi); + if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) unregister_netdev(netdev); @@ -2770,6 +2774,17 @@ static int liquidio_stop(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; + struct napi_struct *napi, *n; + + if (oct->props[lio->ifidx].napi_enabled) { + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + napi_disable(napi); + + oct->props[lio->ifidx].napi_enabled = 0; + + if (OCTEON_CN23XX_PF(oct)) + oct->droq[0]->ops.poll_mode = 0; + } ifstate_reset(lio, LIO_IFSTATE_RUNNING); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 935ff299cdd9..c6f52f235647 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1137,6 +1137,10 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) oct->droq[0]->ops.poll_mode = 0; } + /* Delete NAPI */ + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + netif_napi_del(napi); + if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) unregister_netdev(netdev); @@ -1784,6 +1788,16 @@ static int liquidio_stop(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; + struct napi_struct *napi, *n; + + if (oct->props[lio->ifidx].napi_enabled) { + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + napi_disable(napi); + + oct->props[lio->ifidx].napi_enabled = 0; + + oct->droq[0]->ops.poll_mode = 0; + } netif_info(lio, ifdown, lio->netdev, "Stopping interface!\n"); /* Inform that netif carrier is down */ -- cgit v1.2.3 From d78d6776bc958b16b9a8883278f36d62e3145409 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 9 Aug 2017 10:34:15 +0530 Subject: net: dsa: make dsa_switch_ops const Make these structures const as they are only stored in the ops field of a dsa_switch structure, which is const. Done using Coccinelle. Signed-off-by: Bhumika Goyal Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/dsa_loop.c | 2 +- drivers/net/dsa/lan9303-core.c | 2 +- drivers/net/dsa/mt7530.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index 76d66604a34e..7819a9fe8321 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -257,7 +257,7 @@ static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, return 0; } -static struct dsa_switch_ops dsa_loop_driver = { +static const struct dsa_switch_ops dsa_loop_driver = { .get_tag_protocol = dsa_loop_get_protocol, .setup = dsa_loop_setup, .get_strings = dsa_loop_get_strings, diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 46fc1d5d3c9e..b471413d3df9 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -797,7 +797,7 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port, } } -static struct dsa_switch_ops lan9303_switch_ops = { +static const struct dsa_switch_ops lan9303_switch_ops = { .get_tag_protocol = lan9303_get_tag_protocol, .setup = lan9303_setup, .get_strings = lan9303_get_strings, diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 8faa796a115f..c142b97add2c 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1017,7 +1017,7 @@ mt7530_setup(struct dsa_switch *ds) return 0; } -static struct dsa_switch_ops mt7530_switch_ops = { +static const struct dsa_switch_ops mt7530_switch_ops = { .get_tag_protocol = mtk_get_tag_protocol, .setup = mt7530_setup, .get_strings = mt7530_get_strings, -- cgit v1.2.3 From 0c07ce7f1a4c2f64bc9c8a67397010772a78647a Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 9 Aug 2017 12:09:31 +0200 Subject: net-next: mediatek: fix typos inside the header file Trivial patch fixing 2 typos. Signed-off-by: John Crispin Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 4594862e5a9b..940517af8039 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -599,8 +599,8 @@ struct mtk_soc_data { * @pctl: The register map pointing at the range used to setup * GMAC port drive/slew values * @dma_refcnt: track how many netdevs are using the DMA engine - * @tx_ring: Pointer to the memore holding info about the TX ring - * @rx_ring: Pointer to the memore holding info about the RX ring + * @tx_ring: Pointer to the memory holding info about the TX ring + * @rx_ring: Pointer to the memory holding info about the RX ring * @tx_napi: The TX NAPI struct * @rx_napi: The RX NAPI struct * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring -- cgit v1.2.3 From 6427dc1da51dfc47d65caf86e45e8338d1a3905c Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 9 Aug 2017 12:09:32 +0200 Subject: net-next: mediatek: bring up QDMA RX ring 0 This patch is in preparation for adding HW flow and QoS offloading. For those features to work, the driver needs to bring up the first QDMA RX ring. This ring is used by the PPE offloading HW. Signed-off-by: John Crisp in Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 36 +++++++++++++++++++++-------- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 3 +++ 2 files changed, 29 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index acf2b3b8009c..5e81a7263654 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1285,9 +1285,19 @@ static void mtk_tx_clean(struct mtk_eth *eth) static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) { - struct mtk_rx_ring *ring = ð->rx_ring[ring_no]; + struct mtk_rx_ring *ring; int rx_data_len, rx_dma_size; int i; + u32 offset = 0; + + if (rx_flag == MTK_RX_FLAGS_QDMA) { + if (ring_no) + return -EINVAL; + ring = ð->rx_ring_qdma; + offset = 0x1000; + } else { + ring = ð->rx_ring[ring_no]; + } if (rx_flag == MTK_RX_FLAGS_HWLRO) { rx_data_len = MTK_MAX_LRO_RX_LENGTH; @@ -1337,17 +1347,16 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) */ wmb(); - mtk_w32(eth, ring->phys, MTK_PRX_BASE_PTR_CFG(ring_no)); - mtk_w32(eth, rx_dma_size, MTK_PRX_MAX_CNT_CFG(ring_no)); - mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg); - mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), MTK_PDMA_RST_IDX); + mtk_w32(eth, ring->phys, MTK_PRX_BASE_PTR_CFG(ring_no) + offset); + mtk_w32(eth, rx_dma_size, MTK_PRX_MAX_CNT_CFG(ring_no) + offset); + mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg + offset); + mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), MTK_PDMA_RST_IDX + offset); return 0; } -static void mtk_rx_clean(struct mtk_eth *eth, int ring_no) +static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring) { - struct mtk_rx_ring *ring = ð->rx_ring[ring_no]; int i; if (ring->data && ring->dma) { @@ -1673,6 +1682,10 @@ static int mtk_dma_init(struct mtk_eth *eth) if (err) return err; + err = mtk_rx_alloc(eth, 0, MTK_RX_FLAGS_QDMA); + if (err) + return err; + err = mtk_rx_alloc(eth, 0, MTK_RX_FLAGS_NORMAL); if (err) return err; @@ -1712,12 +1725,13 @@ static void mtk_dma_free(struct mtk_eth *eth) eth->phy_scratch_ring = 0; } mtk_tx_clean(eth); - mtk_rx_clean(eth, 0); + mtk_rx_clean(eth, ð->rx_ring[0]); + mtk_rx_clean(eth, ð->rx_ring_qdma); if (eth->hwlro) { mtk_hwlro_rx_uninit(eth); for (i = 1; i < MTK_MAX_RX_RING_NUM; i++) - mtk_rx_clean(eth, i); + mtk_rx_clean(eth, ð->rx_ring[i]); } kfree(eth->scratch_head); @@ -1784,7 +1798,9 @@ static int mtk_start_dma(struct mtk_eth *eth) mtk_w32(eth, MTK_TX_WB_DDONE | MTK_TX_DMA_EN | - MTK_DMA_SIZE_16DWORDS | MTK_NDP_CO_PRO, + MTK_DMA_SIZE_16DWORDS | MTK_NDP_CO_PRO | + MTK_RX_DMA_EN | MTK_RX_2B_OFFSET | + MTK_RX_BT_32DWORDS, MTK_QDMA_GLO_CFG); mtk_w32(eth, diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 940517af8039..3d3c24a28112 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -532,6 +532,7 @@ struct mtk_tx_ring { enum mtk_rx_flags { MTK_RX_FLAGS_NORMAL = 0, MTK_RX_FLAGS_HWLRO, + MTK_RX_FLAGS_QDMA, }; /* struct mtk_rx_ring - This struct holds info describing a RX ring @@ -601,6 +602,7 @@ struct mtk_soc_data { * @dma_refcnt: track how many netdevs are using the DMA engine * @tx_ring: Pointer to the memory holding info about the TX ring * @rx_ring: Pointer to the memory holding info about the RX ring + * @rx_ring_qdma: Pointer to the memory holding info about the QDMA RX ring * @tx_napi: The TX NAPI struct * @rx_napi: The RX NAPI struct * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring @@ -633,6 +635,7 @@ struct mtk_eth { atomic_t dma_refcnt; struct mtk_tx_ring tx_ring; struct mtk_rx_ring rx_ring[MTK_MAX_RX_RING_NUM]; + struct mtk_rx_ring rx_ring_qdma; struct napi_struct tx_napi; struct napi_struct rx_napi; struct mtk_tx_dma *scratch_ring; -- cgit v1.2.3 From c5c60b2d577e88c50beae6ecf469b7a78643be4f Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:53:42 +0530 Subject: ar5523: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ar5523/ar5523.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 106d6f8d471a..68f0463ed8df 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1749,7 +1749,7 @@ static void ar5523_disconnect(struct usb_interface *intf) { USB_DEVICE((vendor), (device) + 1), \ .driver_info = AR5523_FLAG_ABG|AR5523_FLAG_PRE_FIRMWARE } -static struct usb_device_id ar5523_id_table[] = { +static const struct usb_device_id ar5523_id_table[] = { AR5523_DEVICE_UG(0x168c, 0x0001), /* Atheros / AR5523 */ AR5523_DEVICE_UG(0x0cf3, 0x0001), /* Atheros2 / AR5523_1 */ AR5523_DEVICE_UG(0x0cf3, 0x0003), /* Atheros2 / AR5523_2 */ -- cgit v1.2.3 From 76f6a5c80a4b9e2d9ae2fe53c6ee6be0f630ea88 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:54:41 +0530 Subject: carl9170: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/carl9170/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index 99ab20334d21..e7c3f3b8457d 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -64,7 +64,7 @@ MODULE_ALIAS("arusb_lnx"); * http://wireless.kernel.org/en/users/Drivers/ar9170/devices ), * whenever you add a new device. */ -static struct usb_device_id carl9170_usb_ids[] = { +static const struct usb_device_id carl9170_usb_ids[] = { /* Atheros 9170 */ { USB_DEVICE(0x0cf3, 0x9170) }, /* Atheros TG121N */ -- cgit v1.2.3 From 22796d149229ceadd95abe7cf90331d2c9db7bcf Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:55:03 +0530 Subject: at76c50x: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/atmel/at76c50x-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 09defbcedd5e..94bf01f8b2a8 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -130,7 +130,7 @@ MODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin"); #define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) -static struct usb_device_id dev_table[] = { +static const struct usb_device_id dev_table[] = { /* * at76c503-i3861 */ -- cgit v1.2.3 From c4291319b6fe43b82f4ffb4b0b9fdc711c88264b Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:56:28 +0530 Subject: orinoco: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/orinoco/orinoco_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c index c84fd8490601..56f6e3b71f48 100644 --- a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c @@ -210,7 +210,7 @@ struct ezusb_packet { } __packed; /* Table of devices that work or may work with this driver */ -static struct usb_device_id ezusb_table[] = { +static const struct usb_device_id ezusb_table[] = { {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_WL215_ID)}, {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_HP_WL215_ID)}, {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_W200_ID)}, -- cgit v1.2.3 From 54c9f21605d8e9e76303b1f03d1ef93fc897a824 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:56:43 +0530 Subject: p54: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/p54/p54usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intersil/p54/p54usb.c b/drivers/net/wireless/intersil/p54/p54usb.c index 043bd1c23c19..b0b86f701061 100644 --- a/drivers/net/wireless/intersil/p54/p54usb.c +++ b/drivers/net/wireless/intersil/p54/p54usb.c @@ -41,7 +41,7 @@ MODULE_FIRMWARE("isl3887usb"); * whenever you add a new device. */ -static struct usb_device_id p54u_table[] = { +static const struct usb_device_id p54u_table[] = { /* Version 1 devices (pci chip + net2280) */ {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */ {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */ -- cgit v1.2.3 From 3673c417f5091caa29221d000855eeebb25bcfa4 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:57:28 +0530 Subject: libertas: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/libertas/if_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c index e53025ea6689..16e54c757dd0 100644 --- a/drivers/net/wireless/marvell/libertas/if_usb.c +++ b/drivers/net/wireless/marvell/libertas/if_usb.c @@ -52,7 +52,7 @@ static const struct lbs_fw_table fw_table[] = { { MODEL_8682, "libertas/usb8682.bin", NULL } }; -static struct usb_device_id if_usb_table[] = { +static const struct usb_device_id if_usb_table[] = { /* Enter the device signature inside */ { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 }, { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 }, -- cgit v1.2.3 From 8a3132fa6bbb8bc25b7c8f2c8dcac34beb398625 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:57:29 +0530 Subject: libertas_tf: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/libertas_tf/if_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c index e0ade40d9497..e9104eca327b 100644 --- a/drivers/net/wireless/marvell/libertas_tf/if_usb.c +++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c @@ -31,7 +31,7 @@ module_param_named(fw_name, lbtf_fw_name, charp, 0644); MODULE_FIRMWARE("lbtf_usb.bin"); -static struct usb_device_id if_usb_table[] = { +static const struct usb_device_id if_usb_table[] = { /* Enter the device signature inside */ { USB_DEVICE(0x1286, 0x2001) }, { USB_DEVICE(0x05a3, 0x8388) }, -- cgit v1.2.3 From 7516dbd470e32fe93cc12b632156029f19a30178 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:57:59 +0530 Subject: mwifiex: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 880ef1cb4088..f4f2b9b27e32 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -24,7 +24,7 @@ static struct mwifiex_if_ops usb_ops; -static struct usb_device_id mwifiex_usb_table[] = { +static const struct usb_device_id mwifiex_usb_table[] = { /* 8766 */ {USB_DEVICE(USB8XXX_VID, USB8766_PID_1)}, {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8766_PID_2, -- cgit v1.2.3 From e2717b3127502a3c3344f93f0562b60b88ccd4ef Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:58:24 +0530 Subject: mt7601u: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/mediatek/mt7601u/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c index 416c6045ff31..b9e4f6793138 100644 --- a/drivers/net/wireless/mediatek/mt7601u/usb.c +++ b/drivers/net/wireless/mediatek/mt7601u/usb.c @@ -19,7 +19,7 @@ #include "usb.h" #include "trace.h" -static struct usb_device_id mt7601u_device_table[] = { +static const struct usb_device_id mt7601u_device_table[] = { { USB_DEVICE(0x0b05, 0x17d3) }, { USB_DEVICE(0x0e8d, 0x760a) }, { USB_DEVICE(0x0e8d, 0x760b) }, -- cgit v1.2.3 From 88ee79a1a11c52a9eb96109220a4533c805848df Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:59:14 +0530 Subject: rt2500usb: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2500usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c index 529e05999abb..f4b48b77c491 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c @@ -1911,7 +1911,7 @@ static const struct rt2x00_ops rt2500usb_ops = { /* * rt2500usb module information. */ -static struct usb_device_id rt2500usb_device_table[] = { +static const struct usb_device_id rt2500usb_device_table[] = { /* ASUS */ { USB_DEVICE(0x0b05, 0x1706) }, { USB_DEVICE(0x0b05, 0x1707) }, -- cgit v1.2.3 From c7bb7d79cf4f2ee11ff8d5e0f1883c81c8ab9c0f Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:59:15 +0530 Subject: rt2800usb: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c index 3e5d3a40d986..24fc6d2045ef 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -924,7 +924,7 @@ static const struct rt2x00_ops rt2800usb_ops = { /* * rt2800usb module information. */ -static struct usb_device_id rt2800usb_device_table[] = { +static const struct usb_device_id rt2800usb_device_table[] = { /* Abocom */ { USB_DEVICE(0x07b8, 0x2870) }, { USB_DEVICE(0x07b8, 0x2770) }, -- cgit v1.2.3 From 543e4f87508b612c81dff44037c071450bcc5ddc Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:59:48 +0530 Subject: rt73usb: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt73usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ralink/rt2x00/rt73usb.c b/drivers/net/wireless/ralink/rt2x00/rt73usb.c index fd913222abd1..9a212823f42c 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c @@ -2408,7 +2408,7 @@ static const struct rt2x00_ops rt73usb_ops = { /* * rt73usb module information. */ -static struct usb_device_id rt73usb_device_table[] = { +static const struct usb_device_id rt73usb_device_table[] = { /* AboCom */ { USB_DEVICE(0x07b8, 0xb21b) }, { USB_DEVICE(0x07b8, 0xb21c) }, -- cgit v1.2.3 From e0b081b20523a960f2f3faf074922f4c3f81004c Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 22:00:13 +0530 Subject: rtl8187: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 55198ac2b755..121b94f09714 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -43,7 +43,7 @@ MODULE_AUTHOR("Larry Finger "); MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver"); MODULE_LICENSE("GPL"); -static struct usb_device_id rtl8187_table[] = { +static const struct usb_device_id rtl8187_table[] = { /* Asus */ {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187}, /* Belkin */ -- cgit v1.2.3 From 5033d70de16916fe8962c3fdc2ac70ba589dc395 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 22:00:39 +0530 Subject: rtl8xxxu: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 21e5ef021260..7806a4d2b1fc 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -6190,7 +6190,7 @@ static void rtl8xxxu_disconnect(struct usb_interface *interface) ieee80211_free_hw(hw); } -static struct usb_device_id dev_table[] = { +static const struct usb_device_id dev_table[] = { {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8724, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8723au_fops}, {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x1724, 0xff, 0xff, 0xff), -- cgit v1.2.3 From 3a55a4afd187b57b5205bb0736e8b9c7b422647c Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 22:01:05 +0530 Subject: rtl8192cu: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c index dfbbd35bb966..43e021b49260 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c @@ -279,7 +279,7 @@ static struct rtl_hal_cfg rtl92cu_hal_cfg = { #define USB_VENDER_ID_REALTEK 0x0bda /* 2010-10-19 DID_USB_V3.4 */ -static struct usb_device_id rtl8192c_usb_ids[] = { +static const struct usb_device_id rtl8192c_usb_ids[] = { /*=== Realtek demoboard ===*/ /* Default ID */ -- cgit v1.2.3 From b924ffd7a4cd2a34b8ad5456ea552be838ddf9af Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 22:01:24 +0530 Subject: zd1201: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/zydas/zd1201.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/zydas/zd1201.c b/drivers/net/wireless/zydas/zd1201.c index 7f586d76cf17..581e8577a221 100644 --- a/drivers/net/wireless/zydas/zd1201.c +++ b/drivers/net/wireless/zydas/zd1201.c @@ -25,7 +25,7 @@ #include #include "zd1201.h" -static struct usb_device_id zd1201_table[] = { +static const struct usb_device_id zd1201_table[] = { {USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */ {USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */ {USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb adapter */ -- cgit v1.2.3 From ecf23a788e13a00b1f02400dfa6876850ed21397 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 22:01:42 +0530 Subject: zd1211rw: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/zydas/zd1211rw/zd_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c index 01ca1d57b3d9..c30bf118c67d 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c @@ -35,7 +35,7 @@ #include "zd_mac.h" #include "zd_usb.h" -static struct usb_device_id usb_ids[] = { +static const struct usb_device_id usb_ids[] = { /* ZD1211 */ { USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, -- cgit v1.2.3 From f18bbe5c86fde8ed1a0fdab079b57c3a909b0aeb Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Wed, 9 Aug 2017 02:31:43 +0000 Subject: mwifiex: uap: enable 11d based on userspace configruation This patch check whether userspace beacon data include country ie, if so then download command to enable 11d setup in firmeare accordingly. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 ++ drivers/net/wireless/marvell/mwifiex/main.h | 3 +++ drivers/net/wireless/marvell/mwifiex/uap_cmd.c | 34 ++++++++++++++++--------- 3 files changed, 27 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index b16b19af812d..32c5074da84c 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -2006,6 +2006,8 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, priv->state_11h.is_11h_active = false; } + mwifiex_config_uap_11d(priv, ¶ms->beacon); + if (mwifiex_config_start_uap(priv, bss_cfg)) { mwifiex_dbg(priv->adapter, ERROR, "Failed to start AP\n"); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 0aaae0878742..a76bd797e454 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1564,6 +1564,9 @@ int mwifiex_config_start_uap(struct mwifiex_private *priv, void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, struct mwifiex_sta_node *node); +void mwifiex_config_uap_11d(struct mwifiex_private *priv, + struct cfg80211_beacon_data *beacon_data); + void mwifiex_init_11h_params(struct mwifiex_private *priv); int mwifiex_is_11h_active(struct mwifiex_private *priv); int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag); diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c index 477c29c9f5d9..18f7d9bf30b2 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c @@ -444,6 +444,28 @@ mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size) return; } +/* This function enable 11D if userspace set the country IE. + */ +void mwifiex_config_uap_11d(struct mwifiex_private *priv, + struct cfg80211_beacon_data *beacon_data) +{ + enum state_11d_t state_11d; + const u8 *country_ie; + + country_ie = cfg80211_find_ie(WLAN_EID_COUNTRY, beacon_data->tail, + beacon_data->tail_len); + if (country_ie) { + /* Send cmd to FW to enable 11D function */ + state_11d = ENABLE_11D; + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11D_I, + &state_11d, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "11D: failed to enable 11D\n"); + } + } +} + /* This function parses BSS related parameters from structure * and prepares TLVs. These TLVs are appended to command buffer. */ @@ -848,8 +870,6 @@ void mwifiex_uap_set_channel(struct mwifiex_private *priv, int mwifiex_config_start_uap(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_cfg) { - enum state_11d_t state_11d; - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, HostCmd_ACT_GEN_SET, UAP_BSS_PARAMS_I, bss_cfg, true)) { @@ -858,16 +878,6 @@ int mwifiex_config_start_uap(struct mwifiex_private *priv, return -1; } - /* Send cmd to FW to enable 11D function */ - state_11d = ENABLE_11D; - if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11D_I, - &state_11d, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "11D: failed to enable 11D\n"); - return -1; - } - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, HostCmd_ACT_GEN_SET, 0, NULL, true)) { mwifiex_dbg(priv->adapter, ERROR, -- cgit v1.2.3 From a67b133b9da539ba30b50a0bb4c333d4e38407e7 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Fri, 11 Aug 2017 10:53:50 +0530 Subject: brcm80211: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 8f20a4bb40d9..11ffaa01599e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1468,7 +1468,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) #define CYPRESS_USB_DEVICE(dev_id) \ { USB_DEVICE(CY_USB_VENDOR_ID_CYPRESS, dev_id) } -static struct usb_device_id brcmf_usb_devid_table[] = { +static const struct usb_device_id brcmf_usb_devid_table[] = { BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID), BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID), BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID), -- cgit v1.2.3 From e881a6584941c89ae64f6b6568cae35f9cc9f3d9 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:53:43 +0530 Subject: ath6kl: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Reviewed-by: Steve deRosier Tested-by: Steve deRosier Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 9da3594fd010..4defb7a0330f 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -1201,7 +1201,7 @@ static int ath6kl_usb_pm_resume(struct usb_interface *interface) #endif /* table of devices that work with this driver */ -static struct usb_device_id ath6kl_usb_ids[] = { +static const struct usb_device_id ath6kl_usb_ids[] = { {USB_DEVICE(0x0cf3, 0x9375)}, {USB_DEVICE(0x0cf3, 0x9374)}, { /* Terminating entry */ }, -- cgit v1.2.3 From 76b07b30c46bcc6a03ff12d45e525794b8e8c14a Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 9 Aug 2017 21:54:11 +0530 Subject: ath9k: constify usb_device_id usb_device_id are not supposed to change at runtime. All functions working with usb_device_id provided by work with const usb_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hif_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 0d9687a2aa98..c5f4dd808745 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -20,7 +20,7 @@ MODULE_FIRMWARE(HTC_7010_MODULE_FW); MODULE_FIRMWARE(HTC_9271_MODULE_FW); -static struct usb_device_id ath9k_hif_usb_ids[] = { +static const struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x0cf3, 0x9271) }, /* Atheros */ { USB_DEVICE(0x0cf3, 0x1006) }, /* Atheros */ { USB_DEVICE(0x0846, 0x9030) }, /* Netgear N150 */ -- cgit v1.2.3 From c5ebc4409f2bb2c0b053c204ba197c6b71527eed Mon Sep 17 00:00:00 2001 From: Girish Moodalbail Date: Wed, 9 Aug 2017 01:09:28 -0700 Subject: geneve: use netlink_ext_ack for error reporting in rtnl operations Add extack error messages for failure paths while creating/modifying geneve devices. Once extack support is added to iproute2, more meaningful and helpful error messages will be displayed making it easy for users to discern what went wrong. Before: ======= $ ip link add gen1 address 0:1:2:3:4:5:6 type geneve id 200 \ remote 192.168.13.2 RTNETLINK answers: Invalid argument After: ====== $ ip link add gen1 address 0:1:2:3:4:5:6 type geneve id 200 \ remote 192.168.13.2 Error: Provided link layer address is not Ethernet Also, netdev_dbg() calls used to log errors associated with Netlink request have been removed. Signed-off-by: Girish Moodalbail Signed-off-by: David S. Miller --- drivers/net/geneve.c | 128 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 36 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 8b8565dd2afb..f6404074b7b0 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1086,21 +1086,33 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { if (tb[IFLA_ADDRESS]) { - if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) { + NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_ADDRESS], + "Provided link layer address is not Ethernet"); return -EINVAL; + } - if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) { + NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_ADDRESS], + "Provided Ethernet address is not unicast"); return -EADDRNOTAVAIL; + } } - if (!data) + if (!data) { + NL_SET_ERR_MSG(extack, + "Not enough attributes provided to perform the operation"); return -EINVAL; + } if (data[IFLA_GENEVE_ID]) { __u32 vni = nla_get_u32(data[IFLA_GENEVE_ID]); - if (vni >= GENEVE_N_VID) + if (vni >= GENEVE_N_VID) { + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_ID], + "Geneve ID must be lower than 16777216"); return -ERANGE; + } } return 0; @@ -1158,6 +1170,7 @@ static bool geneve_dst_addr_equal(struct ip_tunnel_info *a, } static int geneve_configure(struct net *net, struct net_device *dev, + struct netlink_ext_ack *extack, const struct ip_tunnel_info *info, bool metadata, bool ipv6_rx_csum) { @@ -1166,8 +1179,11 @@ static int geneve_configure(struct net *net, struct net_device *dev, bool tun_collect_md, tun_on_same_port; int err, encap_len; - if (metadata && !is_tnl_info_zero(info)) + if (metadata && !is_tnl_info_zero(info)) { + NL_SET_ERR_MSG(extack, + "Device is externally controlled, so attributes (VNI, Port, and so on) must not be specified"); return -EINVAL; + } geneve->net = net; geneve->dev = dev; @@ -1188,11 +1204,17 @@ static int geneve_configure(struct net *net, struct net_device *dev, dev->needed_headroom = encap_len + ETH_HLEN; if (metadata) { - if (tun_on_same_port) + if (tun_on_same_port) { + NL_SET_ERR_MSG(extack, + "There can be only one externally controlled device on a destination port"); return -EPERM; + } } else { - if (tun_collect_md) + if (tun_collect_md) { + NL_SET_ERR_MSG(extack, + "There already exists an externally controlled device on this destination port"); return -EPERM; + } } dst_cache_reset(&geneve->info.dst_cache); @@ -1214,31 +1236,41 @@ static void init_tnl_info(struct ip_tunnel_info *info, __u16 dst_port) info->key.tp_dst = htons(dst_port); } -static int geneve_nl2info(struct net_device *dev, struct nlattr *tb[], - struct nlattr *data[], struct ip_tunnel_info *info, - bool *metadata, bool *use_udp6_rx_checksums, - bool changelink) +static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack, + struct ip_tunnel_info *info, bool *metadata, + bool *use_udp6_rx_checksums, bool changelink) { - if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) + int attrtype; + + if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) { + NL_SET_ERR_MSG(extack, + "Cannot specify both IPv4 and IPv6 Remote addresses"); return -EINVAL; + } if (data[IFLA_GENEVE_REMOTE]) { - if (changelink && (ip_tunnel_info_af(info) == AF_INET6)) - return -EOPNOTSUPP; + if (changelink && (ip_tunnel_info_af(info) == AF_INET6)) { + attrtype = IFLA_GENEVE_REMOTE; + goto change_notsup; + } info->key.u.ipv4.dst = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]); if (IN_MULTICAST(ntohl(info->key.u.ipv4.dst))) { - netdev_dbg(dev, "multicast remote is unsupported\n"); + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE], + "Remote IPv4 address cannot be Multicast"); return -EINVAL; } } if (data[IFLA_GENEVE_REMOTE6]) { #if IS_ENABLED(CONFIG_IPV6) - if (changelink && (ip_tunnel_info_af(info) == AF_INET)) - return -EOPNOTSUPP; + if (changelink && (ip_tunnel_info_af(info) == AF_INET)) { + attrtype = IFLA_GENEVE_REMOTE6; + goto change_notsup; + } info->mode = IP_TUNNEL_INFO_IPV6; info->key.u.ipv6.dst = @@ -1246,16 +1278,20 @@ static int geneve_nl2info(struct net_device *dev, struct nlattr *tb[], if (ipv6_addr_type(&info->key.u.ipv6.dst) & IPV6_ADDR_LINKLOCAL) { - netdev_dbg(dev, "link-local remote is unsupported\n"); + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE6], + "Remote IPv6 address cannot be link-local"); return -EINVAL; } if (ipv6_addr_is_multicast(&info->key.u.ipv6.dst)) { - netdev_dbg(dev, "multicast remote is unsupported\n"); + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE6], + "Remote IPv6 address cannot be Multicast"); return -EINVAL; } info->key.tun_flags |= TUNNEL_CSUM; *use_udp6_rx_checksums = true; #else + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE6], + "IPv6 support not enabled in the kernel"); return -EPFNOSUPPORT; #endif } @@ -1271,8 +1307,10 @@ static int geneve_nl2info(struct net_device *dev, struct nlattr *tb[], tvni[2] = vni & 0x000000ff; tunid = vni_to_tunnel_id(tvni); - if (changelink && (tunid != info->key.tun_id)) - return -EOPNOTSUPP; + if (changelink && (tunid != info->key.tun_id)) { + attrtype = IFLA_GENEVE_ID; + goto change_notsup; + } info->key.tun_id = tunid; } @@ -1285,44 +1323,61 @@ static int geneve_nl2info(struct net_device *dev, struct nlattr *tb[], if (data[IFLA_GENEVE_LABEL]) { info->key.label = nla_get_be32(data[IFLA_GENEVE_LABEL]) & IPV6_FLOWLABEL_MASK; - if (info->key.label && (!(info->mode & IP_TUNNEL_INFO_IPV6))) + if (info->key.label && (!(info->mode & IP_TUNNEL_INFO_IPV6))) { + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LABEL], + "Label attribute only applies for IPv6 Geneve devices"); return -EINVAL; + } } if (data[IFLA_GENEVE_PORT]) { - if (changelink) - return -EOPNOTSUPP; + if (changelink) { + attrtype = IFLA_GENEVE_PORT; + goto change_notsup; + } info->key.tp_dst = nla_get_be16(data[IFLA_GENEVE_PORT]); } if (data[IFLA_GENEVE_COLLECT_METADATA]) { - if (changelink) - return -EOPNOTSUPP; + if (changelink) { + attrtype = IFLA_GENEVE_COLLECT_METADATA; + goto change_notsup; + } *metadata = true; } if (data[IFLA_GENEVE_UDP_CSUM]) { - if (changelink) - return -EOPNOTSUPP; + if (changelink) { + attrtype = IFLA_GENEVE_UDP_CSUM; + goto change_notsup; + } if (nla_get_u8(data[IFLA_GENEVE_UDP_CSUM])) info->key.tun_flags |= TUNNEL_CSUM; } if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { - if (changelink) - return -EOPNOTSUPP; + if (changelink) { + attrtype = IFLA_GENEVE_UDP_ZERO_CSUM6_TX; + goto change_notsup; + } if (nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) info->key.tun_flags &= ~TUNNEL_CSUM; } if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { - if (changelink) - return -EOPNOTSUPP; + if (changelink) { + attrtype = IFLA_GENEVE_UDP_ZERO_CSUM6_RX; + goto change_notsup; + } if (nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) *use_udp6_rx_checksums = false; } return 0; +change_notsup: + NL_SET_ERR_MSG_ATTR(extack, data[attrtype], + "Changing VNI, Port, endpoint IP address family, external, and UDP checksum attributes are not supported"); + return -EOPNOTSUPP; } static int geneve_newlink(struct net *net, struct net_device *dev, @@ -1335,12 +1390,13 @@ static int geneve_newlink(struct net *net, struct net_device *dev, int err; init_tnl_info(&info, GENEVE_UDP_PORT); - err = geneve_nl2info(dev, tb, data, &info, &metadata, + err = geneve_nl2info(tb, data, extack, &info, &metadata, &use_udp6_rx_checksums, false); if (err) return err; - return geneve_configure(net, dev, &info, metadata, use_udp6_rx_checksums); + return geneve_configure(net, dev, extack, &info, metadata, + use_udp6_rx_checksums); } /* Quiesces the geneve device data path for both TX and RX. @@ -1409,7 +1465,7 @@ static int geneve_changelink(struct net_device *dev, struct nlattr *tb[], memcpy(&info, &geneve->info, sizeof(info)); metadata = geneve->collect_md; use_udp6_rx_checksums = geneve->use_udp6_rx_checksums; - err = geneve_nl2info(dev, tb, data, &info, &metadata, + err = geneve_nl2info(tb, data, extack, &info, &metadata, &use_udp6_rx_checksums, true); if (err) return err; @@ -1536,7 +1592,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, return dev; init_tnl_info(&info, dst_port); - err = geneve_configure(net, dev, &info, true, true); + err = geneve_configure(net, dev, NULL, &info, true, true); if (err) { free_netdev(dev); return ERR_PTR(err); -- cgit v1.2.3 From a2e8da9378cc09e2e922a0b3d481bd9d07c3d245 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 9 Aug 2017 14:30:33 +0200 Subject: net: sched: use newly added classid identity helpers Instead of checking handle, which does not have the inner class information and drivers wrongly assume clsact->egress as ingress, use the newly introduced classid identification helpers. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 18 ++++++++++++++++-- drivers/net/ethernet/netronome/nfp/bpf/main.c | 2 +- drivers/net/ethernet/netronome/nfp/flower/offload.c | 2 +- net/dsa/slave.c | 9 ++++++++- 8 files changed, 30 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index d80b20d695e0..afa6fd688fac 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2892,7 +2892,7 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate) static int cxgb_setup_tc_cls_u32(struct net_device *dev, struct tc_cls_u32_offload *cls_u32) { - if (TC_H_MAJ(cls_u32->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + if (is_classid_clsact_ingress(cls_u32->common.classid) || cls_u32->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c6b132476de4..f9fd8d8f1bef 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9230,7 +9230,7 @@ static int ixgbe_setup_tc_cls_u32(struct net_device *dev, { struct ixgbe_adapter *adapter = netdev_priv(dev); - if (TC_H_MAJ(cls_u32->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + if (is_classid_clsact_ingress(cls_u32->common.classid) || cls_u32->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 981f8415b546..8633ca5af6ed 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3031,7 +3031,7 @@ static int mlx5e_setup_tc_cls_flower(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + if (is_classid_clsact_ingress(cls_flower->common.classid) || cls_flower->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index f3c494a4ecdf..f34c00fbf78c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -657,7 +657,7 @@ mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + if (is_classid_clsact_ingress(cls_flower->common.classid) || cls_flower->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index eb7c4549f464..a99600333a49 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1696,7 +1696,14 @@ static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_cls_matchall_offload *f) { - bool ingress = TC_H_MAJ(f->common.handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress; + + if (is_classid_clsact_ingress(f->common.classid)) + ingress = true; + else if (is_classid_clsact_egress(f->common.classid)) + ingress = false; + else + return -EOPNOTSUPP; if (f->common.chain_index) return -EOPNOTSUPP; @@ -1717,7 +1724,14 @@ static int mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_cls_flower_offload *f) { - bool ingress = TC_H_MAJ(f->common.handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress; + + if (is_classid_clsact_ingress(f->common.classid)) + ingress = true; + else if (is_classid_clsact_egress(f->common.classid)) + ingress = false; + else + return -EOPNOTSUPP; if (f->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index f981f60ec306..0e6864922d5c 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -127,7 +127,7 @@ static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, struct nfp_net *nn = netdev_priv(netdev); if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) || - TC_H_MAJ(cls_bpf->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + is_classid_clsact_ingress(cls_bpf->common.classid) || cls_bpf->common.protocol != htons(ETH_P_ALL) || cls_bpf->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 01767c7376d5..3ad5aaa210a4 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -390,7 +390,7 @@ int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, struct tc_cls_flower_offload *cls_flower = type_data; if (type != TC_SETUP_CLSFLOWER || - TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + is_classid_clsact_ingress(cls_flower->common.classid) || !eth_proto_is_802_3(cls_flower->common.protocol) || cls_flower->common.chain_index) return -EOPNOTSUPP; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 8c79011c5a83..78e78a6e6833 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -914,7 +914,14 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev, static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, struct tc_cls_matchall_offload *cls) { - bool ingress = TC_H_MAJ(cls->common.handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress; + + if (is_classid_clsact_ingress(cls->common.classid)) + ingress = true; + else if (is_classid_clsact_egress(cls->common.classid)) + ingress = false; + else + return -EOPNOTSUPP; if (cls->common.chain_index) return -EOPNOTSUPP; -- cgit v1.2.3 From c1550fde51482feebeb8e27742672a2614cb1f12 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Wed, 9 Aug 2017 12:07:08 -0700 Subject: liquidio: rx/tx queue cleanup When deleting a queue, clear its corresponding bit in the qmask, vfree its memory, clear out the pointer that's pointing to it, and decrement the queue count. Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 4 ++++ drivers/net/ethernet/cavium/liquidio/request_manager.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index f7b5d68eb4cf..9372d4ce9954 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -209,6 +209,10 @@ int octeon_delete_droq(struct octeon_device *oct, u32 q_no) droq->desc_ring, droq->desc_ring_dma); memset(droq, 0, OCT_DROQ_SIZE); + oct->io_qmask.oq &= ~(1ULL << q_no); + vfree(oct->droq[q_no]); + oct->droq[q_no] = NULL; + oct->num_oqs--; return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 7b297f1f6dbe..20a96bab3d0d 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -190,6 +190,10 @@ int octeon_delete_instr_queue(struct octeon_device *oct, u32 iq_no) q_size = iq->max_count * desc_size; lio_dma_free(oct, (u32)q_size, iq->base_addr, iq->base_addr_dma); + oct->io_qmask.iq &= ~(1ULL << iq_no); + vfree(oct->instr_queue[iq_no]); + oct->instr_queue[iq_no] = NULL; + oct->num_iqs--; return 0; } return 1; -- cgit v1.2.3 From 35c7ad3567b06593adbddb59674737fd29d388ad Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Wed, 9 Aug 2017 13:28:04 -0700 Subject: liquidio: removed check for queue size alignment There is no restriction on queue size alignment. Hence removing check for valid queue size. Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/request_manager.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 20a96bab3d0d..1e0fbce86d60 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -77,13 +77,6 @@ int octeon_init_instr_queue(struct octeon_device *oct, return 1; } - if (num_descs & (num_descs - 1)) { - dev_err(&oct->pci_dev->dev, - "Number of descriptors for instr queue %d not in power of 2.\n", - iq_no); - return 1; - } - q_size = (u32)conf->instr_type * num_descs; iq = oct->instr_queue[iq_no]; -- cgit v1.2.3 From d18c2a1b159d107e341206500747dceabe8501fb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 10 Aug 2017 00:35:50 +0300 Subject: phylink: Fix an uninitialized variable bug "ret" isn't necessarily initialized here. Fixes: 9525ae83959b ("phylink: add phylink infrastructure") Signed-off-by: Dan Carpenter Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 32917bdd1432..bcb4755bcd95 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -958,7 +958,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, } mutex_unlock(&pl->state_mutex); - return ret; + return 0; } EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_set); -- cgit v1.2.3 From 6123c66854c174e4982f98195100c1d990f9e5e6 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:03 -0700 Subject: netvsc: delay setup of VF device When VF device is discovered, delay bring it automatically up in order to allow userspace to some simple changes (like renaming). Reported-by: Vitaly Kuznetsov Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 2 +- drivers/net/hyperv/netvsc_drv.c | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 98b25f6900c8..a57e37641dc5 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -723,7 +723,7 @@ struct net_device_context { /* State to manage the associated VF interface. */ struct net_device __rcu *vf_netdev; struct netvsc_vf_pcpu_stats __percpu *vf_stats; - struct work_struct vf_takeover; + struct delayed_work vf_takeover; /* 1: allocated, serial number is valid. 0: not allocated */ u32 vf_alloc; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index eb0023f55fe1..e059375a6d8c 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -47,6 +47,7 @@ #define RING_SIZE_MIN 64 #define LINKCHANGE_INT (2 * HZ) +#define VF_TAKEOVER_INT (HZ / 10) static int ring_size = 128; module_param(ring_size, int, S_IRUGO); @@ -1559,7 +1560,9 @@ static int netvsc_vf_join(struct net_device *vf_netdev, /* set slave flag before open to prevent IPv6 addrconf */ vf_netdev->flags |= IFF_SLAVE; - schedule_work(&ndev_ctx->vf_takeover); + schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); + + call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); netdev_info(vf_netdev, "joined to %s\n", ndev->name); return 0; @@ -1575,8 +1578,6 @@ static void __netvsc_vf_setup(struct net_device *ndev, { int ret; - call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); - /* Align MTU of VF with master */ ret = dev_set_mtu(vf_netdev, ndev->mtu); if (ret) @@ -1597,12 +1598,12 @@ static void __netvsc_vf_setup(struct net_device *ndev, static void netvsc_vf_setup(struct work_struct *w) { struct net_device_context *ndev_ctx - = container_of(w, struct net_device_context, vf_takeover); + = container_of(w, struct net_device_context, vf_takeover.work); struct net_device *ndev = hv_get_drvdata(ndev_ctx->device_ctx); struct net_device *vf_netdev; if (!rtnl_trylock()) { - schedule_work(w); + schedule_delayed_work(&ndev_ctx->vf_takeover, 0); return; } @@ -1706,7 +1707,7 @@ static int netvsc_unregister_vf(struct net_device *vf_netdev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); - cancel_work_sync(&net_device_ctx->vf_takeover); + cancel_delayed_work_sync(&net_device_ctx->vf_takeover); netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name); @@ -1748,7 +1749,7 @@ static int netvsc_probe(struct hv_device *dev, spin_lock_init(&net_device_ctx->lock); INIT_LIST_HEAD(&net_device_ctx->reconfig_events); - INIT_WORK(&net_device_ctx->vf_takeover, netvsc_vf_setup); + INIT_DELAYED_WORK(&net_device_ctx->vf_takeover, netvsc_vf_setup); net_device_ctx->vf_stats = netdev_alloc_pcpu_stats(struct netvsc_vf_pcpu_stats); -- cgit v1.2.3 From 5e20d55a23a76a876396ba1235bdf019e74d0c6f Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:04 -0700 Subject: netvsc: don't signal host twice if empty When hv_pkt_iter_next() returns NULL, it has already called hv_pkt_iter_close(). Calling it twice can lead to extra host signal. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index bffaf93d3cb0..b5fc8fb56b88 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1191,10 +1191,6 @@ int netvsc_poll(struct napi_struct *napi, int budget) nvchan->desc = hv_pkt_iter_next(channel, nvchan->desc); } - /* if ring is empty, signal host */ - if (!nvchan->desc) - hv_pkt_iter_close(channel); - /* If send of pending receive completions suceeded * and did not exhaust NAPI budget this time * and not doing busy poll -- cgit v1.2.3 From 16ba3266006be10dc7ec25dd1442f74cef89cb95 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:05 -0700 Subject: netvsc: propagate MAC address change to VF slave If VF is slaved to synthetic device, then any change to netvsc MAC address should be propagated to the slave device. If slave device doesn't support MAC address change then it should also be an error to attempt to change synthetic NIC MAC address. It also fixes the error unwind in the original code. If give a bad address, the old code would change the device MAC address anyway. Reviewed-by: Haiyang Zhang Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index e059375a6d8c..07015b1c42c6 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1053,27 +1053,31 @@ static void netvsc_get_stats64(struct net_device *net, static int netvsc_set_mac_addr(struct net_device *ndev, void *p) { struct net_device_context *ndc = netdev_priv(ndev); + struct net_device *vf_netdev = rtnl_dereference(ndc->vf_netdev); struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev); struct sockaddr *addr = p; - char save_adr[ETH_ALEN]; - unsigned char save_aatype; int err; - memcpy(save_adr, ndev->dev_addr, ETH_ALEN); - save_aatype = ndev->addr_assign_type; - - err = eth_mac_addr(ndev, p); - if (err != 0) + err = eth_prepare_mac_addr_change(ndev, p); + if (err) return err; if (!nvdev) return -ENODEV; + if (vf_netdev) { + err = dev_set_mac_address(vf_netdev, addr); + if (err) + return err; + } + err = rndis_filter_set_device_mac(nvdev, addr->sa_data); - if (err != 0) { - /* roll back to saved MAC */ - memcpy(ndev->dev_addr, save_adr, ETH_ALEN); - ndev->addr_assign_type = save_aatype; + if (!err) { + eth_commit_mac_addr_change(ndev, p); + } else if (vf_netdev) { + /* rollback change on VF */ + memcpy(addr->sa_data, ndev->dev_addr, ETH_ALEN); + dev_set_mac_address(vf_netdev, addr); } return err; -- cgit v1.2.3 From 68d715f68541d58033199eea80991394a6886eb7 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:06 -0700 Subject: netvsc: check error return when restoring channels and mtu If setting new values fails, and the attempt to restore original settings fails. Then log an error and leave device down. This should never happen, but if it does don't go down in flames. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 07015b1c42c6..c7391889938b 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -845,7 +845,13 @@ static int netvsc_set_channels(struct net_device *net, } else { ret = PTR_ERR(nvdev); device_info.num_chn = orig; - rndis_filter_device_add(dev, &device_info); + nvdev = rndis_filter_device_add(dev, &device_info); + + if (IS_ERR(nvdev)) { + netdev_err(net, "restoring channel setting failed: %ld\n", + PTR_ERR(nvdev)); + return ret; + } } if (was_opened) @@ -953,10 +959,16 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) /* Attempt rollback to original MTU */ ndev->mtu = orig_mtu; - rndis_filter_device_add(hdev, &device_info); + nvdev = rndis_filter_device_add(hdev, &device_info); if (vf_netdev) dev_set_mtu(vf_netdev, orig_mtu); + + if (IS_ERR(nvdev)) { + netdev_err(ndev, "restoring mtu failed: %ld\n", + PTR_ERR(nvdev)); + return ret; + } } if (was_opened) -- cgit v1.2.3 From 958333708f2877d3855e3bc31dad428e2f2c8096 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:07 -0700 Subject: netvsc: no need to allocate send/receive on numa node The send and receive buffers are both per-device (not per-channel). The associated NUMA node is a property of the CPU which is per-channel therefore it makes no sense to force the receive/send buffer to be allocated on a particular node (since it is a shared resource). Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index b5fc8fb56b88..7407006f4e22 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -246,20 +246,13 @@ int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx) static int netvsc_init_buf(struct hv_device *device, struct netvsc_device *net_device) { - int ret = 0; - struct nvsp_message *init_packet; struct nvsp_1_message_send_receive_buffer_complete *resp; - struct net_device *ndev; + struct net_device *ndev = hv_get_drvdata(device); + struct nvsp_message *init_packet; size_t map_words; - int node; - - ndev = hv_get_drvdata(device); - - node = cpu_to_node(device->channel->target_cpu); - net_device->recv_buf = vzalloc_node(net_device->recv_buf_size, node); - if (!net_device->recv_buf) - net_device->recv_buf = vzalloc(net_device->recv_buf_size); + int ret = 0; + net_device->recv_buf = vzalloc(net_device->recv_buf_size); if (!net_device->recv_buf) { netdev_err(ndev, "unable to allocate receive " "buffer of size %d\n", net_device->recv_buf_size); @@ -340,9 +333,7 @@ static int netvsc_init_buf(struct hv_device *device, goto cleanup; /* Now setup the send buffer. */ - net_device->send_buf = vzalloc_node(net_device->send_buf_size, node); - if (!net_device->send_buf) - net_device->send_buf = vzalloc(net_device->send_buf_size); + net_device->send_buf = vzalloc(net_device->send_buf_size); if (!net_device->send_buf) { netdev_err(ndev, "unable to allocate send " "buffer of size %d\n", net_device->send_buf_size); -- cgit v1.2.3 From 89bb42b11370c2daf19d8820398f7255f8499ab7 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:08 -0700 Subject: netvsc: whitespace cleanup Fix some minor indentation issues. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index c7391889938b..3219d2e8918f 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -165,7 +165,7 @@ static int netvsc_close(struct net_device *net) } static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, - int pkt_type) + int pkt_type) { struct rndis_packet *rndis_pkt; struct rndis_per_packet_info *ppi; @@ -286,7 +286,7 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, } static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, - struct hv_page_buffer *pb) + struct hv_page_buffer *pb) { int j = 0; @@ -626,6 +626,7 @@ no_memory: ++net_device_ctx->eth_stats.tx_no_memory; goto drop; } + /* * netvsc_linkstatus_callback - Link up/down notification */ @@ -649,8 +650,8 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, if (indicate->status == RNDIS_STATUS_LINK_SPEED_CHANGE) { u32 speed; - speed = *(u32 *)((void *)indicate + indicate-> - status_buf_offset) / 10000; + speed = *(u32 *)((void *)indicate + + indicate->status_buf_offset) / 10000; ndev_ctx->speed = speed; return; } @@ -1018,7 +1019,7 @@ static void netvsc_get_stats64(struct net_device *net, struct net_device_context *ndev_ctx = netdev_priv(net); struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev); struct netvsc_vf_pcpu_stats vf_tot; - int i; + int i; if (!nvdev) return; -- cgit v1.2.3 From 00f5024e821e60c0d1d7df44f2577a2c277b5cc9 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:09 -0700 Subject: netvsc: remove unnecessary cast of void pointer Assignment to a typed pointer is sufficient in C. No cast is needed. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 3219d2e8918f..9f89de17b5fa 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -523,9 +523,9 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) rndis_msg_size += NDIS_VLAN_PPI_SIZE; ppi = init_ppi_data(rndis_msg, NDIS_VLAN_PPI_SIZE, - IEEE_8021Q_INFO); - vlan = (struct ndis_pkt_8021q_info *)((void *)ppi + - ppi->ppi_offset); + IEEE_8021Q_INFO); + + vlan = (void *)ppi + ppi->ppi_offset; vlan->vlanid = skb->vlan_tci & VLAN_VID_MASK; vlan->pri = (skb->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; @@ -538,8 +538,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ppi = init_ppi_data(rndis_msg, NDIS_LSO_PPI_SIZE, TCP_LARGESEND_PKTINFO); - lso_info = (struct ndis_tcp_lso_info *)((void *)ppi + - ppi->ppi_offset); + lso_info = (void *)ppi + ppi->ppi_offset; lso_info->lso_v2_transmit.type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE; if (skb->protocol == htons(ETH_P_IP)) { -- cgit v1.2.3 From ea5a32c00bcacce1d8ac834a70a82f95a1c79425 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:10 -0700 Subject: netvsc: remove unnecessary check for NULL hdr The function init_page_array is always called with a valid pointer to RNDIS header. No check for NULL is needed. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 9f89de17b5fa..7b465e40869b 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -333,10 +333,9 @@ static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, * 2. skb linear data * 3. skb fragment data */ - if (hdr != NULL) - slots_used += fill_pg_buf(virt_to_page(hdr), - offset_in_page(hdr), - len, &pb[slots_used]); + slots_used += fill_pg_buf(virt_to_page(hdr), + offset_in_page(hdr), + len, &pb[slots_used]); packet->rmsg_size = len; packet->rmsg_pgcnt = slots_used; -- cgit v1.2.3 From 8b5327975ae171ca54dfd93e6c042d1292945867 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:11 -0700 Subject: netvsc: allow controlling send/recv buffer size Control the size of the buffer areas via ethtool ring settings. They aren't really traditional hardware rings, but host API breaks receive and send buffer into chunks. The final size of the chunks are controlled by the host. The default value of send and receive buffer area for host DMA is much larger than it needs to be. Experimentation shows that 4M receive and 1M send is sufficient. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 9 ++-- drivers/net/hyperv/netvsc.c | 70 +++++++++++++----------- drivers/net/hyperv/netvsc_drv.c | 117 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 157 insertions(+), 39 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index a57e37641dc5..30326373e46f 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -148,6 +148,8 @@ struct netvsc_device_info { unsigned char mac_adr[ETH_ALEN]; int ring_size; u32 num_chn; + u32 send_sections; + u32 recv_sections; }; enum rndis_device_state { @@ -634,12 +636,12 @@ struct nvsp_message { #define NETVSC_SEND_BUFFER_SIZE (1024 * 1024 * 15) /* 15MB */ #define NETVSC_INVALID_INDEX -1 +#define NETVSC_SEND_SECTION_SIZE 6144 +#define NETVSC_RECV_SECTION_SIZE 1728 #define NETVSC_RECEIVE_BUFFER_ID 0xcafe #define NETVSC_SEND_BUFFER_ID 0 -#define NETVSC_PACKET_SIZE 4096 - #define VRSS_SEND_TAB_SIZE 16 /* must be power of 2 */ #define VRSS_CHANNEL_MAX 64 #define VRSS_CHANNEL_DEFAULT 8 @@ -754,14 +756,13 @@ struct netvsc_device { /* Receive buffer allocated by us but manages by NetVSP */ void *recv_buf; - u32 recv_buf_size; u32 recv_buf_gpadl_handle; u32 recv_section_cnt; + u32 recv_section_size; u32 recv_completion_cnt; /* Send buffer allocated by us */ void *send_buf; - u32 send_buf_size; u32 send_buf_gpadl_handle; u32 send_section_cnt; u32 send_section_size; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 7407006f4e22..d9d7555148eb 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -75,6 +75,10 @@ static struct netvsc_device *alloc_net_device(void) atomic_set(&net_device->open_cnt, 0); net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT; net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT; + + net_device->recv_section_size = NETVSC_RECV_SECTION_SIZE; + net_device->send_section_size = NETVSC_SEND_SECTION_SIZE; + init_completion(&net_device->channel_init_wait); init_waitqueue_head(&net_device->subchan_open); @@ -143,6 +147,7 @@ static void netvsc_destroy_buf(struct hv_device *device) "revoke receive buffer to netvsp\n"); return; } + net_device->recv_section_cnt = 0; } /* Teardown the gpadl on the vsp end */ @@ -173,7 +178,7 @@ static void netvsc_destroy_buf(struct hv_device *device) * NVSP_MSG1_TYPE_SEND_SEND_BUF msg) therefore, we need * to send a revoke msg here */ - if (net_device->send_section_size) { + if (net_device->send_section_cnt) { /* Send the revoke receive buffer */ revoke_packet = &net_device->revoke_packet; memset(revoke_packet, 0, sizeof(struct nvsp_message)); @@ -205,6 +210,7 @@ static void netvsc_destroy_buf(struct hv_device *device) "revoke send buffer to netvsp\n"); return; } + net_device->send_section_cnt = 0; } /* Teardown the gpadl on the vsp end */ if (net_device->send_buf_gpadl_handle) { @@ -244,18 +250,25 @@ int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx) } static int netvsc_init_buf(struct hv_device *device, - struct netvsc_device *net_device) + struct netvsc_device *net_device, + const struct netvsc_device_info *device_info) { struct nvsp_1_message_send_receive_buffer_complete *resp; struct net_device *ndev = hv_get_drvdata(device); struct nvsp_message *init_packet; + unsigned int buf_size; size_t map_words; int ret = 0; - net_device->recv_buf = vzalloc(net_device->recv_buf_size); + /* Get receive buffer area. */ + buf_size = device_info->recv_sections * net_device->recv_section_size; + buf_size = roundup(buf_size, PAGE_SIZE); + + net_device->recv_buf = vzalloc(buf_size); if (!net_device->recv_buf) { - netdev_err(ndev, "unable to allocate receive " - "buffer of size %d\n", net_device->recv_buf_size); + netdev_err(ndev, + "unable to allocate receive buffer of size %u\n", + buf_size); ret = -ENOMEM; goto cleanup; } @@ -266,7 +279,7 @@ static int netvsc_init_buf(struct hv_device *device, * than the channel to establish the gpadl handle. */ ret = vmbus_establish_gpadl(device->channel, net_device->recv_buf, - net_device->recv_buf_size, + buf_size, &net_device->recv_buf_gpadl_handle); if (ret != 0) { netdev_err(ndev, @@ -312,31 +325,31 @@ static int netvsc_init_buf(struct hv_device *device, resp->num_sections, resp->sections[0].sub_alloc_size, resp->sections[0].num_sub_allocs); - net_device->recv_section_cnt = resp->num_sections; - - /* - * For 1st release, there should only be 1 section that represents the - * entire receive buffer - */ - if (net_device->recv_section_cnt != 1 || - resp->sections[0].offset != 0) { + /* There should only be one section for the entire receive buffer */ + if (resp->num_sections != 1 || resp->sections[0].offset != 0) { ret = -EINVAL; goto cleanup; } + net_device->recv_section_size = resp->sections[0].sub_alloc_size; + net_device->recv_section_cnt = resp->sections[0].num_sub_allocs; + /* Setup receive completion ring */ net_device->recv_completion_cnt - = round_up(resp->sections[0].num_sub_allocs + 1, + = round_up(net_device->recv_section_cnt + 1, PAGE_SIZE / sizeof(u64)); ret = netvsc_alloc_recv_comp_ring(net_device, 0); if (ret) goto cleanup; /* Now setup the send buffer. */ - net_device->send_buf = vzalloc(net_device->send_buf_size); + buf_size = device_info->send_sections * net_device->send_section_size; + buf_size = round_up(buf_size, PAGE_SIZE); + + net_device->send_buf = vzalloc(buf_size); if (!net_device->send_buf) { - netdev_err(ndev, "unable to allocate send " - "buffer of size %d\n", net_device->send_buf_size); + netdev_err(ndev, "unable to allocate send buffer of size %u\n", + buf_size); ret = -ENOMEM; goto cleanup; } @@ -346,7 +359,7 @@ static int netvsc_init_buf(struct hv_device *device, * than the channel to establish the gpadl handle. */ ret = vmbus_establish_gpadl(device->channel, net_device->send_buf, - net_device->send_buf_size, + buf_size, &net_device->send_buf_gpadl_handle); if (ret != 0) { netdev_err(ndev, @@ -391,10 +404,8 @@ static int netvsc_init_buf(struct hv_device *device, net_device->send_section_size = init_packet->msg. v1_msg.send_send_buf_complete.section_size; - /* Section count is simply the size divided by the section size. - */ - net_device->send_section_cnt = - net_device->send_buf_size / net_device->send_section_size; + /* Section count is simply the size divided by the section size. */ + net_device->send_section_cnt = buf_size / net_device->send_section_size; netdev_dbg(ndev, "Send section size: %d, Section count:%d\n", net_device->send_section_size, net_device->send_section_cnt); @@ -472,7 +483,8 @@ static int negotiate_nvsp_ver(struct hv_device *device, } static int netvsc_connect_vsp(struct hv_device *device, - struct netvsc_device *net_device) + struct netvsc_device *net_device, + const struct netvsc_device_info *device_info) { const u32 ver_list[] = { NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2, @@ -522,14 +534,8 @@ static int netvsc_connect_vsp(struct hv_device *device, if (ret != 0) goto cleanup; - /* Post the big receive buffer to NetVSP */ - if (net_device->nvsp_version <= NVSP_PROTOCOL_VERSION_2) - net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY; - else - net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; - net_device->send_buf_size = NETVSC_SEND_BUFFER_SIZE; - ret = netvsc_init_buf(device, net_device); + ret = netvsc_init_buf(device, net_device, device_info); cleanup: return ret; @@ -1287,7 +1293,7 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device, rcu_assign_pointer(net_device_ctx->nvdev, net_device); /* Connect with the NetVsp */ - ret = netvsc_connect_vsp(device, net_device); + ret = netvsc_connect_vsp(device, net_device, device_info); if (ret != 0) { netdev_err(ndev, "unable to connect to NetVSP - %d\n", ret); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 7b465e40869b..873c83a66cc2 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -45,7 +45,12 @@ #include "hyperv_net.h" -#define RING_SIZE_MIN 64 +#define RING_SIZE_MIN 64 +#define NETVSC_MIN_TX_SECTIONS 10 +#define NETVSC_DEFAULT_TX 192 /* ~1M */ +#define NETVSC_MIN_RX_SECTIONS 10 /* ~64K */ +#define NETVSC_DEFAULT_RX 2048 /* ~4M */ + #define LINKCHANGE_INT (2 * HZ) #define VF_TAKEOVER_INT (HZ / 10) @@ -831,11 +836,13 @@ static int netvsc_set_channels(struct net_device *net, if (was_opened) rndis_filter_close(nvdev); - rndis_filter_device_remove(dev, nvdev); - memset(&device_info, 0, sizeof(device_info)); device_info.num_chn = count; device_info.ring_size = ring_size; + device_info.send_sections = nvdev->send_section_cnt; + device_info.recv_sections = nvdev->recv_section_cnt; + + rndis_filter_device_remove(dev, nvdev); nvdev = rndis_filter_device_add(dev, &device_info); if (!IS_ERR(nvdev)) { @@ -947,6 +954,8 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; device_info.num_chn = nvdev->num_chn; + device_info.send_sections = nvdev->send_section_cnt; + device_info.recv_sections = nvdev->recv_section_cnt; rndis_filter_device_remove(hdev, nvdev); @@ -1351,6 +1360,104 @@ static int netvsc_set_rxfh(struct net_device *dev, const u32 *indir, return rndis_filter_set_rss_param(rndis_dev, key, ndev->num_chn); } +/* Hyper-V RNDIS protocol does not have ring in the HW sense. + * It does have pre-allocated receive area which is divided into sections. + */ +static void __netvsc_get_ringparam(struct netvsc_device *nvdev, + struct ethtool_ringparam *ring) +{ + u32 max_buf_size; + + ring->rx_pending = nvdev->recv_section_cnt; + ring->tx_pending = nvdev->send_section_cnt; + + if (nvdev->nvsp_version <= NVSP_PROTOCOL_VERSION_2) + max_buf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY; + else + max_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; + + ring->rx_max_pending = max_buf_size / nvdev->recv_section_size; + ring->tx_max_pending = NETVSC_SEND_BUFFER_SIZE + / nvdev->send_section_size; +} + +static void netvsc_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring) +{ + struct net_device_context *ndevctx = netdev_priv(ndev); + struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev); + + if (!nvdev) + return; + + __netvsc_get_ringparam(nvdev, ring); +} + +static int netvsc_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring) +{ + struct net_device_context *ndevctx = netdev_priv(ndev); + struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev); + struct hv_device *hdev = ndevctx->device_ctx; + struct netvsc_device_info device_info; + struct ethtool_ringparam orig; + u32 new_tx, new_rx; + bool was_opened; + int ret = 0; + + if (!nvdev || nvdev->destroy) + return -ENODEV; + + memset(&orig, 0, sizeof(orig)); + __netvsc_get_ringparam(nvdev, &orig); + + new_tx = clamp_t(u32, ring->tx_pending, + NETVSC_MIN_TX_SECTIONS, orig.tx_max_pending); + new_rx = clamp_t(u32, ring->rx_pending, + NETVSC_MIN_RX_SECTIONS, orig.rx_max_pending); + + if (new_tx == orig.tx_pending && + new_rx == orig.rx_pending) + return 0; /* no change */ + + memset(&device_info, 0, sizeof(device_info)); + device_info.num_chn = nvdev->num_chn; + device_info.ring_size = ring_size; + device_info.send_sections = new_tx; + device_info.recv_sections = new_rx; + + netif_device_detach(ndev); + was_opened = rndis_filter_opened(nvdev); + if (was_opened) + rndis_filter_close(nvdev); + + rndis_filter_device_remove(hdev, nvdev); + + nvdev = rndis_filter_device_add(hdev, &device_info); + if (IS_ERR(nvdev)) { + ret = PTR_ERR(nvdev); + + device_info.send_sections = orig.tx_pending; + device_info.recv_sections = orig.rx_pending; + nvdev = rndis_filter_device_add(hdev, &device_info); + if (IS_ERR(nvdev)) { + netdev_err(ndev, "restoring ringparam failed: %ld\n", + PTR_ERR(nvdev)); + return ret; + } + } + + if (was_opened) + rndis_filter_open(nvdev); + netif_device_attach(ndev); + + /* We may have missed link change notifications */ + ndevctx->last_reconfig = 0; + schedule_delayed_work(&ndevctx->dwork, 0); + + return ret; +} + static const struct ethtool_ops ethtool_ops = { .get_drvinfo = netvsc_get_drvinfo, .get_link = ethtool_op_get_link, @@ -1367,6 +1474,8 @@ static const struct ethtool_ops ethtool_ops = { .set_rxfh = netvsc_set_rxfh, .get_link_ksettings = netvsc_get_link_ksettings, .set_link_ksettings = netvsc_set_link_ksettings, + .get_ringparam = netvsc_get_ringparam, + .set_ringparam = netvsc_set_ringparam, }; static const struct net_device_ops device_ops = { @@ -1782,6 +1891,8 @@ static int netvsc_probe(struct hv_device *dev, memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; device_info.num_chn = VRSS_CHANNEL_DEFAULT; + device_info.send_sections = NETVSC_DEFAULT_TX; + device_info.recv_sections = NETVSC_DEFAULT_RX; nvdev = rndis_filter_device_add(dev, &device_info); if (IS_ERR(nvdev)) { -- cgit v1.2.3 From cad5c197704d82faf33ffdbef414f15db08d9ef9 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 9 Aug 2017 17:46:12 -0700 Subject: netvsc: keep track of some non-fatal overload conditions Add ethtool statistics for case where send chimmeny buffer is exhausted and driver has to fall back to doing scatter/gather send. Also, add statistic for case where ring buffer is full and receive completions are delayed. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 2 ++ drivers/net/hyperv/netvsc.c | 19 +++++++++++++------ drivers/net/hyperv/netvsc_drv.c | 2 ++ 3 files changed, 17 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 30326373e46f..9198dd1240ed 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -680,6 +680,8 @@ struct netvsc_ethtool_stats { unsigned long tx_no_space; unsigned long tx_too_big; unsigned long tx_busy; + unsigned long tx_send_full; + unsigned long rx_comp_busy; }; struct netvsc_vf_pcpu_stats { diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index d9d7555148eb..0530e7d729e1 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -883,7 +883,9 @@ int netvsc_send(struct net_device_context *ndev_ctx, } else if (pktlen + net_device->pkt_align < net_device->send_section_size) { section_index = netvsc_get_next_send_section(net_device); - if (section_index != NETVSC_INVALID_INDEX) { + if (unlikely(section_index == NETVSC_INVALID_INDEX)) { + ++ndev_ctx->eth_stats.tx_send_full; + } else { move_pkt_msd(&msd_send, &msd_skb, msdp); msd_len = 0; } @@ -949,9 +951,10 @@ send_now: } /* Send pending recv completions */ -static int send_recv_completions(struct netvsc_channel *nvchan) +static int send_recv_completions(struct net_device *ndev, + struct netvsc_device *nvdev, + struct netvsc_channel *nvchan) { - struct netvsc_device *nvdev = nvchan->net_device; struct multi_recv_comp *mrc = &nvchan->mrc; struct recv_comp_msg { struct nvsp_message_header hdr; @@ -969,8 +972,12 @@ static int send_recv_completions(struct netvsc_channel *nvchan) msg.status = rcd->status; ret = vmbus_sendpacket(nvchan->channel, &msg, sizeof(msg), rcd->tid, VM_PKT_COMP, 0); - if (unlikely(ret)) + if (unlikely(ret)) { + struct net_device_context *ndev_ctx = netdev_priv(ndev); + + ++ndev_ctx->eth_stats.rx_comp_busy; return ret; + } if (++mrc->first == nvdev->recv_completion_cnt) mrc->first = 0; @@ -1011,7 +1018,7 @@ static void enq_receive_complete(struct net_device *ndev, recv_comp_slot_avail(nvdev, mrc, &filled, &avail); if (unlikely(filled > NAPI_POLL_WEIGHT)) { - send_recv_completions(nvchan); + send_recv_completions(ndev, nvdev, nvchan); recv_comp_slot_avail(nvdev, mrc, &filled, &avail); } @@ -1194,7 +1201,7 @@ int netvsc_poll(struct napi_struct *napi, int budget) * then re-enable host interrupts * and reschedule if ring is not empty. */ - if (send_recv_completions(nvchan) == 0 && + if (send_recv_completions(ndev, net_device, nvchan) == 0 && work_done < budget && napi_complete_done(napi, work_done) && hv_end_read(&channel->inbound)) { diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 873c83a66cc2..b33f0507c373 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1112,6 +1112,8 @@ static const struct { { "tx_no_space", offsetof(struct netvsc_ethtool_stats, tx_no_space) }, { "tx_too_big", offsetof(struct netvsc_ethtool_stats, tx_too_big) }, { "tx_busy", offsetof(struct netvsc_ethtool_stats, tx_busy) }, + { "tx_send_full", offsetof(struct netvsc_ethtool_stats, tx_send_full) }, + { "rx_comp_busy", offsetof(struct netvsc_ethtool_stats, rx_comp_busy) }, }, vf_stats[] = { { "vf_rx_packets", offsetof(struct netvsc_vf_pcpu_stats, rx_packets) }, { "vf_rx_bytes", offsetof(struct netvsc_vf_pcpu_stats, rx_bytes) }, -- cgit v1.2.3 From 2e1c80847b9983a8c93a678f6de2dc3c42f19661 Mon Sep 17 00:00:00 2001 From: Maxim Uvarov Date: Thu, 10 Aug 2017 10:47:46 +0300 Subject: drivers: net: davinci_mdio: remove busy loop on wait user access Polling 14 mdio devices on single mdio bus eats 30% of 1Ghz cpu time due to busy loop in wait(). Add small delay to relax cpu. Signed-off-by: Max Uvarov Reviewed-by: Andrew Lunn Reviewed-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_mdio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 33df340db1f1..611e7cc5f07b 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -198,8 +198,10 @@ static inline int wait_for_user_access(struct davinci_mdio_data *data) return 0; reg = __raw_readl(®s->control); - if ((reg & CONTROL_IDLE) == 0) + if ((reg & CONTROL_IDLE) == 0) { + usleep_range(100, 200); continue; + } /* * An emac soft_reset may have clobbered the mdio controller's -- cgit v1.2.3 From cc147a0dc312e5c85fe083b1712913e4496c11e5 Mon Sep 17 00:00:00 2001 From: Maxim Uvarov Date: Thu, 10 Aug 2017 10:47:47 +0300 Subject: drivers: net: davinci_mdio: print bus frequency Frequency can be adjusted in DT it make sense to print current used value on driver init. Signed-off-by: Max Uvarov Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_mdio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 611e7cc5f07b..3c33f4504d8e 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -159,8 +159,10 @@ static int davinci_mdio_reset(struct mii_bus *bus) /* dump hardware version info */ ver = __raw_readl(&data->regs->version); - dev_info(data->dev, "davinci mdio revision %d.%d\n", - (ver >> 8) & 0xff, ver & 0xff); + dev_info(data->dev, + "davinci mdio revision %d.%d, bus freq %ld\n", + (ver >> 8) & 0xff, ver & 0xff, + data->pdata.bus_freq); if (data->skip_scan) goto done; -- cgit v1.2.3 From d24d39d906fa8944024f154e0ea3f031252c06c1 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Thu, 10 Aug 2017 04:13:12 -0400 Subject: forcedeth: replace init_timer_deferrable with setup_deferrable_timer Replace init_timer_deferrable with setup_deferrable_timer to simplify the source code. Signed-off-by: Zhu Yanjun Signed-off-by: David S. Miller --- drivers/net/ethernet/nvidia/forcedeth.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index aa912f43e15f..994a83a1f0a5 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -5629,9 +5629,8 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) setup_timer(&np->oom_kick, nv_do_rx_refill, (unsigned long)dev); setup_timer(&np->nic_poll, nv_do_nic_poll, (unsigned long)dev); - init_timer_deferrable(&np->stats_poll); - np->stats_poll.data = (unsigned long) dev; - np->stats_poll.function = nv_do_stats_poll; /* timer handler */ + setup_deferrable_timer(&np->stats_poll, nv_do_stats_poll, + (unsigned long)dev); err = pci_enable_device(pci_dev); if (err) -- cgit v1.2.3 From baf6ee81406ab806c7db34bd1e9a0a824cb84c71 Mon Sep 17 00:00:00 2001 From: David Wu Date: Thu, 10 Aug 2017 21:56:40 +0800 Subject: net: phy: Add rockchip PHY driver support Support integrated ethernet PHY currently. Signed-off-by: David Wu Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + drivers/net/phy/rockchip.c | 233 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 drivers/net/phy/rockchip.c (limited to 'drivers/net') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index bf73969a9d2b..8c919203291a 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -366,6 +366,11 @@ config REALTEK_PHY ---help--- Supports the Realtek 821x PHY. +config ROCKCHIP_PHY + tristate "Driver for Rockchip Ethernet PHYs" + ---help--- + Currently supports the integrated Ethernet PHY. + config SMSC_PHY tristate "SMSC PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 7237255bad68..416df92fbf4f 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_MICROSEMI_PHY) += mscc.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek.o +obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_STE10XP) += ste10Xp.o obj-$(CONFIG_TERANETICS_PHY) += teranetics.o diff --git a/drivers/net/phy/rockchip.c b/drivers/net/phy/rockchip.c new file mode 100644 index 000000000000..c092af137056 --- /dev/null +++ b/drivers/net/phy/rockchip.c @@ -0,0 +1,233 @@ +/** + * drivers/net/phy/rockchip.c + * + * Driver for ROCKCHIP Ethernet PHYs + * + * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd + * + * David Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#define INTERNAL_EPHY_ID 0x1234d400 + +#define MII_INTERNAL_CTRL_STATUS 17 +#define SMI_ADDR_TSTCNTL 20 +#define SMI_ADDR_TSTREAD1 21 +#define SMI_ADDR_TSTREAD2 22 +#define SMI_ADDR_TSTWRITE 23 +#define MII_SPECIAL_CONTROL_STATUS 31 + +#define MII_AUTO_MDIX_EN BIT(7) +#define MII_MDIX_EN BIT(6) + +#define MII_SPEED_10 BIT(2) +#define MII_SPEED_100 BIT(3) + +#define TSTCNTL_RD (BIT(15) | BIT(10)) +#define TSTCNTL_WR (BIT(14) | BIT(10)) + +#define TSTMODE_ENABLE 0x400 +#define TSTMODE_DISABLE 0x0 + +#define WR_ADDR_A7CFG 0x18 + +static int rockchip_init_tstmode(struct phy_device *phydev) +{ + int ret; + + /* Enable access to Analog and DSP register banks */ + ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE); + if (ret) + return ret; + + ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE); + if (ret) + return ret; + + return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE); +} + +static int rockchip_close_tstmode(struct phy_device *phydev) +{ + /* Back to basic register bank */ + return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE); +} + +static int rockchip_integrated_phy_analog_init(struct phy_device *phydev) +{ + int ret; + + ret = rockchip_init_tstmode(phydev); + if (ret) + return ret; + + /* + * Adjust tx amplitude to make sginal better, + * the default value is 0x8. + */ + ret = phy_write(phydev, SMI_ADDR_TSTWRITE, 0xB); + if (ret) + return ret; + ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_WR | WR_ADDR_A7CFG); + if (ret) + return ret; + + return rockchip_close_tstmode(phydev); +} + +static int rockchip_integrated_phy_config_init(struct phy_device *phydev) +{ + int val, ret; + + /* + * The auto MIDX has linked problem on some board, + * workround to disable auto MDIX. + */ + val = phy_read(phydev, MII_INTERNAL_CTRL_STATUS); + if (val < 0) + return val; + val &= ~MII_AUTO_MDIX_EN; + ret = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val); + if (ret) + return ret; + + return rockchip_integrated_phy_analog_init(phydev); +} + +static void rockchip_link_change_notify(struct phy_device *phydev) +{ + int speed = SPEED_10; + + if (phydev->autoneg == AUTONEG_ENABLE) { + int reg = phy_read(phydev, MII_SPECIAL_CONTROL_STATUS); + + if (reg < 0) { + phydev_err(phydev, "phy_read err: %d.\n", reg); + return; + } + + if (reg & MII_SPEED_100) + speed = SPEED_100; + else if (reg & MII_SPEED_10) + speed = SPEED_10; + } else { + int bmcr = phy_read(phydev, MII_BMCR); + + if (bmcr < 0) { + phydev_err(phydev, "phy_read err: %d.\n", bmcr); + return; + } + + if (bmcr & BMCR_SPEED100) + speed = SPEED_100; + else + speed = SPEED_10; + } + + /* + * If mode switch happens from 10BT to 100BT, all DSP/AFE + * registers are set to default values. So any AFE/DSP + * registers have to be re-initialized in this case. + */ + if ((phydev->speed == SPEED_10) && (speed == SPEED_100)) { + int ret = rockchip_integrated_phy_analog_init(phydev); + if (ret) + phydev_err(phydev, "rockchip_integrated_phy_analog_init err: %d.\n", + ret); + } +} + +static int rockchip_set_polarity(struct phy_device *phydev, int polarity) +{ + int reg, err, val; + + /* get the current settings */ + reg = phy_read(phydev, MII_INTERNAL_CTRL_STATUS); + if (reg < 0) + return reg; + + reg &= ~MII_AUTO_MDIX_EN; + val = reg; + switch (polarity) { + case ETH_TP_MDI: + val &= ~MII_MDIX_EN; + break; + case ETH_TP_MDI_X: + val |= MII_MDIX_EN; + break; + case ETH_TP_MDI_AUTO: + case ETH_TP_MDI_INVALID: + default: + return 0; + } + + if (val != reg) { + /* Set the new polarity value in the register */ + err = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val); + if (err) + return err; + } + + return 0; +} + +static int rockchip_config_aneg(struct phy_device *phydev) +{ + int err; + + err = rockchip_set_polarity(phydev, phydev->mdix); + if (err < 0) + return err; + + return genphy_config_aneg(phydev); +} + +static int rockchip_phy_resume(struct phy_device *phydev) +{ + genphy_resume(phydev); + + return rockchip_integrated_phy_config_init(phydev); +} + +static struct phy_driver rockchip_phy_driver[] = { +{ + .phy_id = INTERNAL_EPHY_ID, + .phy_id_mask = 0xfffffff0, + .name = "Rockchip integrated EPHY", + .features = PHY_BASIC_FEATURES, + .flags = 0, + .link_change_notify = rockchip_link_change_notify, + .soft_reset = genphy_soft_reset, + .config_init = rockchip_integrated_phy_config_init, + .config_aneg = rockchip_config_aneg, + .read_status = genphy_read_status, + .suspend = genphy_suspend, + .resume = rockchip_phy_resume, +}, +}; + +module_phy_driver(rockchip_phy_driver); + +static struct mdio_device_id __maybe_unused rockchip_phy_tbl[] = { + { INTERNAL_EPHY_ID, 0xfffffff0 }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, rockchip_phy_tbl); + +MODULE_AUTHOR("David Wu "); +MODULE_DESCRIPTION("Rockchip Ethernet PHY driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From e7aa4c07462eb5dc165d7dd625c982006c27bfcc Mon Sep 17 00:00:00 2001 From: David Wu Date: Thu, 10 Aug 2017 21:56:43 +0800 Subject: net: stmmac: dwmac-rk: Remove unwanted code for rk3328_set_to_rmii() This is wrong setting for rk3328_set_to_rmii(), so remove it. Signed-off-by: David Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index f0df5193f047..a8e8fd579d07 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -365,9 +365,6 @@ static void rk3328_set_to_rmii(struct rk_priv_data *bsp_priv) regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1, RK3328_GMAC_PHY_INTF_SEL_RMII | RK3328_GMAC_RMII_MODE); - - /* set MAC to RMII mode */ - regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1, GRF_BIT(11)); } static void rk3328_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) -- cgit v1.2.3 From fecd4d7eef8b219a7e7ab3524619b7ed76d208b4 Mon Sep 17 00:00:00 2001 From: David Wu Date: Thu, 10 Aug 2017 22:00:02 +0800 Subject: net: stmmac: dwmac-rk: Add integrated PHY support To make integrated PHY work, need to configure the PHY clock, PHY cru reset and related registers. Signed-off-by: David Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 99 ++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index a8e8fd579d07..90199175cf2c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -41,6 +41,7 @@ struct rk_gmac_ops { void (*set_to_rmii)(struct rk_priv_data *bsp_priv); void (*set_rgmii_speed)(struct rk_priv_data *bsp_priv, int speed); void (*set_rmii_speed)(struct rk_priv_data *bsp_priv, int speed); + void (*integrated_phy_powerup)(struct rk_priv_data *bsp_priv); }; struct rk_priv_data { @@ -52,6 +53,7 @@ struct rk_priv_data { bool clk_enabled; bool clock_input; + bool integrated_phy; struct clk *clk_mac; struct clk *gmac_clkin; @@ -61,6 +63,9 @@ struct rk_priv_data { struct clk *clk_mac_refout; struct clk *aclk_mac; struct clk *pclk_mac; + struct clk *clk_phy; + + struct reset_control *phy_reset; int tx_delay; int rx_delay; @@ -750,9 +755,55 @@ static const struct rk_gmac_ops rk3399_ops = { .set_rmii_speed = rk3399_set_rmii_speed, }; -static int gmac_clk_init(struct rk_priv_data *bsp_priv) +#define RK_GRF_MACPHY_CON0 0xb00 +#define RK_GRF_MACPHY_CON1 0xb04 +#define RK_GRF_MACPHY_CON2 0xb08 +#define RK_GRF_MACPHY_CON3 0xb0c + +#define RK_MACPHY_ENABLE GRF_BIT(0) +#define RK_MACPHY_DISABLE GRF_CLR_BIT(0) +#define RK_MACPHY_CFG_CLK_50M GRF_BIT(14) +#define RK_GMAC2PHY_RMII_MODE (GRF_BIT(6) | GRF_CLR_BIT(7)) +#define RK_GRF_CON2_MACPHY_ID HIWORD_UPDATE(0x1234, 0xffff, 0) +#define RK_GRF_CON3_MACPHY_ID HIWORD_UPDATE(0x35, 0x3f, 0) + +static void rk_gmac_integrated_phy_powerup(struct rk_priv_data *priv) +{ + if (priv->ops->integrated_phy_powerup) + priv->ops->integrated_phy_powerup(priv); + + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_MACPHY_CFG_CLK_50M); + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_GMAC2PHY_RMII_MODE); + + regmap_write(priv->grf, RK_GRF_MACPHY_CON2, RK_GRF_CON2_MACPHY_ID); + regmap_write(priv->grf, RK_GRF_MACPHY_CON3, RK_GRF_CON3_MACPHY_ID); + + if (priv->phy_reset) { + /* PHY needs to be disabled before trying to reset it */ + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_MACPHY_DISABLE); + if (priv->phy_reset) + reset_control_assert(priv->phy_reset); + usleep_range(10, 20); + if (priv->phy_reset) + reset_control_deassert(priv->phy_reset); + usleep_range(10, 20); + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_MACPHY_ENABLE); + msleep(30); + } +} + +static void rk_gmac_integrated_phy_powerdown(struct rk_priv_data *priv) { + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_MACPHY_DISABLE); + if (priv->phy_reset) + reset_control_assert(priv->phy_reset); +} + +static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat) +{ + struct rk_priv_data *bsp_priv = plat->bsp_priv; struct device *dev = &bsp_priv->pdev->dev; + int ret; bsp_priv->clk_enabled = false; @@ -803,6 +854,16 @@ static int gmac_clk_init(struct rk_priv_data *bsp_priv) clk_set_rate(bsp_priv->clk_mac, 50000000); } + if (plat->phy_node && bsp_priv->integrated_phy) { + bsp_priv->clk_phy = of_clk_get(plat->phy_node, 0); + if (IS_ERR(bsp_priv->clk_phy)) { + ret = PTR_ERR(bsp_priv->clk_phy); + dev_err(dev, "Cannot get PHY clock: %d\n", ret); + return -EINVAL; + } + clk_set_rate(bsp_priv->clk_phy, 50000000); + } + return 0; } @@ -826,6 +887,9 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) bsp_priv->clk_mac_refout); } + if (!IS_ERR(bsp_priv->clk_phy)) + clk_prepare_enable(bsp_priv->clk_phy); + if (!IS_ERR(bsp_priv->aclk_mac)) clk_prepare_enable(bsp_priv->aclk_mac); @@ -858,6 +922,9 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) bsp_priv->clk_mac_refout); } + if (!IS_ERR(bsp_priv->clk_phy)) + clk_disable_unprepare(bsp_priv->clk_phy); + if (!IS_ERR(bsp_priv->aclk_mac)) clk_disable_unprepare(bsp_priv->aclk_mac); @@ -902,6 +969,7 @@ static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable) } static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev, + struct plat_stmmacenet_data *plat, const struct rk_gmac_ops *ops) { struct rk_priv_data *bsp_priv; @@ -964,9 +1032,22 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev, bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); - bsp_priv->pdev = pdev; - gmac_clk_init(bsp_priv); + if (plat->phy_node) { + bsp_priv->integrated_phy = of_property_read_bool(plat->phy_node, + "phy-is-integrated"); + if (bsp_priv->integrated_phy) { + bsp_priv->phy_reset = of_reset_control_get(plat->phy_node, NULL); + if (IS_ERR(bsp_priv->phy_reset)) { + dev_err(&pdev->dev, "No PHY reset control found.\n"); + bsp_priv->phy_reset = NULL; + } + } + } + dev_info(dev, "integrated PHY? (%s).\n", + bsp_priv->integrated_phy ? "yes" : "no"); + + bsp_priv->pdev = pdev; return bsp_priv; } @@ -1014,6 +1095,9 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv) pm_runtime_enable(dev); pm_runtime_get_sync(dev); + if (bsp_priv->integrated_phy) + rk_gmac_integrated_phy_powerup(bsp_priv); + return 0; } @@ -1021,6 +1105,9 @@ static void rk_gmac_powerdown(struct rk_priv_data *gmac) { struct device *dev = &gmac->pdev->dev; + if (gmac->integrated_phy) + rk_gmac_integrated_phy_powerdown(gmac); + pm_runtime_put_sync(dev); pm_runtime_disable(dev); @@ -1072,12 +1159,16 @@ static int rk_gmac_probe(struct platform_device *pdev) plat_dat->has_gmac = true; plat_dat->fix_mac_speed = rk_fix_speed; - plat_dat->bsp_priv = rk_gmac_setup(pdev, data); + plat_dat->bsp_priv = rk_gmac_setup(pdev, plat_dat, data); if (IS_ERR(plat_dat->bsp_priv)) { ret = PTR_ERR(plat_dat->bsp_priv); goto err_remove_config_dt; } + ret = rk_gmac_clk_init(plat_dat); + if (ret) + return ret; + ret = rk_gmac_powerup(plat_dat->bsp_priv); if (ret) goto err_remove_config_dt; -- cgit v1.2.3 From 6fa12c787cde8ebe07e5c9641ea4ebc490892ac9 Mon Sep 17 00:00:00 2001 From: David Wu Date: Thu, 10 Aug 2017 22:00:25 +0800 Subject: net: stmmac: dwmac-rk: Add integrated PHY support for rk3228 There is only one mac controller in rk3228, which could connect to external PHY or integrated PHY, use the grf_com_mux bit15 to route external/integrated PHY. Signed-off-by: David Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 90199175cf2c..b6db3ff38606 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -86,6 +86,8 @@ struct rk_priv_data { #define RK3228_GRF_MAC_CON0 0x0900 #define RK3228_GRF_MAC_CON1 0x0904 +#define RK3228_GRF_CON_MUX 0x50 + /* RK3228_GRF_MAC_CON0 */ #define RK3228_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) #define RK3228_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) @@ -111,6 +113,9 @@ struct rk_priv_data { #define RK3228_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1) #define RK3228_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(1) +/* RK3228_GRF_COM_MUX */ +#define RK3228_GRF_CON_MUX_GMAC_INTEGRATED_PHY GRF_BIT(15) + static void rk3228_set_to_rgmii(struct rk_priv_data *bsp_priv, int tx_delay, int rx_delay) { @@ -191,11 +196,18 @@ static void rk3228_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) dev_err(dev, "unknown speed value for RMII! speed=%d", speed); } +static void rk3228_integrated_phy_powerup(struct rk_priv_data *priv) +{ + regmap_write(priv->grf, RK3228_GRF_CON_MUX, + RK3228_GRF_CON_MUX_GMAC_INTEGRATED_PHY); +} + static const struct rk_gmac_ops rk3228_ops = { .set_to_rgmii = rk3228_set_to_rgmii, .set_to_rmii = rk3228_set_to_rmii, .set_rgmii_speed = rk3228_set_rgmii_speed, .set_rmii_speed = rk3228_set_rmii_speed, + .integrated_phy_powerup = rk3228_integrated_phy_powerup, }; #define RK3288_GRF_SOC_CON1 0x0248 -- cgit v1.2.3 From 8bdf63bdd3200539af5ecd0c41284b5aa00ce532 Mon Sep 17 00:00:00 2001 From: David Wu Date: Thu, 10 Aug 2017 22:00:52 +0800 Subject: net: stmmac: dwmac-rk: Add integrated PHY supprot for rk3328 There are two mac controllers in the rk3328, the one connects to external PHY, and the other one connects to integrated PHY. Like the mac of external PHY, the integrated PHY's mac also needs to configure the related mac registers at GRF. Signed-off-by: David Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index b6db3ff38606..2176403c72d8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -323,6 +323,8 @@ static const struct rk_gmac_ops rk3288_ops = { #define RK3328_GRF_MAC_CON0 0x0900 #define RK3328_GRF_MAC_CON1 0x0904 +#define RK3328_GRF_MAC_CON2 0x0908 +#define RK3328_GRF_MACPHY_CON1 0xb04 /* RK3328_GRF_MAC_CON0 */ #define RK3328_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) @@ -349,6 +351,9 @@ static const struct rk_gmac_ops rk3288_ops = { #define RK3328_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1) #define RK3328_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(0) +/* RK3328_GRF_MACPHY_CON1 */ +#define RK3328_MACPHY_RMII_MODE GRF_BIT(9) + static void rk3328_set_to_rgmii(struct rk_priv_data *bsp_priv, int tx_delay, int rx_delay) { @@ -373,13 +378,17 @@ static void rk3328_set_to_rgmii(struct rk_priv_data *bsp_priv, static void rk3328_set_to_rmii(struct rk_priv_data *bsp_priv) { struct device *dev = &bsp_priv->pdev->dev; + unsigned int reg; if (IS_ERR(bsp_priv->grf)) { dev_err(dev, "Missing rockchip,grf property\n"); return; } - regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1, + reg = bsp_priv->integrated_phy ? RK3328_GRF_MAC_CON2 : + RK3328_GRF_MAC_CON1; + + regmap_write(bsp_priv->grf, reg, RK3328_GMAC_PHY_INTF_SEL_RMII | RK3328_GMAC_RMII_MODE); } @@ -409,29 +418,40 @@ static void rk3328_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) static void rk3328_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) { struct device *dev = &bsp_priv->pdev->dev; + unsigned int reg; if (IS_ERR(bsp_priv->grf)) { dev_err(dev, "Missing rockchip,grf property\n"); return; } + reg = bsp_priv->integrated_phy ? RK3328_GRF_MAC_CON2 : + RK3328_GRF_MAC_CON1; + if (speed == 10) - regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1, + regmap_write(bsp_priv->grf, reg, RK3328_GMAC_RMII_CLK_2_5M | RK3328_GMAC_SPEED_10M); else if (speed == 100) - regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1, + regmap_write(bsp_priv->grf, reg, RK3328_GMAC_RMII_CLK_25M | RK3328_GMAC_SPEED_100M); else dev_err(dev, "unknown speed value for RMII! speed=%d", speed); } +static void rk3328_integrated_phy_powerup(struct rk_priv_data *priv) +{ + regmap_write(priv->grf, RK3328_GRF_MACPHY_CON1, + RK3328_MACPHY_RMII_MODE); +} + static const struct rk_gmac_ops rk3328_ops = { .set_to_rgmii = rk3328_set_to_rgmii, .set_to_rmii = rk3328_set_to_rmii, .set_rgmii_speed = rk3328_set_rgmii_speed, .set_rmii_speed = rk3328_set_rmii_speed, + .integrated_phy_powerup = rk3328_integrated_phy_powerup, }; #define RK3366_GRF_SOC_CON6 0x0418 -- cgit v1.2.3 From 9537e7cbf20787416b25833f2f9886166d8148f2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 10 Aug 2017 12:54:59 +0300 Subject: hns3pf: fix hns3_del_tunnel_port() This function has a copy and paste bug so it accidentally calls the add function instead of the delete function. Fixes: 76ad4f0ee747 ("net: hns3: Add support of HNS3 Ethernet Driver for hip08 SoC") Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index 069ae426aa24..e519795dff7c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1138,7 +1138,7 @@ static void hns3_del_tunnel_port(struct net_device *netdev, u16 port, udp_tnl->dst_port = 0; /* TBD send command to hardware to del port */ if (h->ae_algo->ops->del_tunnel_udp) - h->ae_algo->ops->add_tunnel_udp(h, port); + h->ae_algo->ops->del_tunnel_udp(h, port); } /* hns3_nic_udp_tunnel_add - Get notifiacetion about UDP tunnel ports -- cgit v1.2.3 From 720a8478b2689bd221e4a8744e1695de7966a22f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 10 Aug 2017 12:56:14 +0300 Subject: hns3pf: Fix some harmless copy and paste bugs These were copy and paste bugs, but I believe they are harmless. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index e519795dff7c..9589b7e1d24c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -330,7 +330,7 @@ static int hns3_nic_mc_sync(struct net_device *netdev, struct hns3_nic_priv *priv = netdev_priv(netdev); struct hnae3_handle *h = priv->ae_handle; - if (h->ae_algo->ops->add_uc_addr) + if (h->ae_algo->ops->add_mc_addr) return h->ae_algo->ops->add_mc_addr(h, addr); return 0; @@ -342,7 +342,7 @@ static int hns3_nic_mc_unsync(struct net_device *netdev, struct hns3_nic_priv *priv = netdev_priv(netdev); struct hnae3_handle *h = priv->ae_handle; - if (h->ae_algo->ops->rm_uc_addr) + if (h->ae_algo->ops->rm_mc_addr) return h->ae_algo->ops->rm_mc_addr(h, addr); return 0; -- cgit v1.2.3 From e37425c23afd34fb73d329d2ca7b31ae1e6f2be5 Mon Sep 17 00:00:00 2001 From: Florinel Iordache Date: Thu, 10 Aug 2017 16:47:04 +0300 Subject: fsl/fman: implement several errata workarounds Implemented workarounds for the following dTSEC Erratum: A002, A004, A0012, A0014, A004839 on several operations that involve MAC CFG register changes: adjust link, rx pause frames, modify MAC address. Signed-off-by: Florinel Iordache Acked-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/fman_dtsec.c | 118 ++++++++++++++++++----- 1 file changed, 93 insertions(+), 25 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index 98bba10fc38c..ea43b4974149 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -123,7 +123,7 @@ #define DTSEC_ECNTRL_R100M 0x00000008 #define DTSEC_ECNTRL_QSGMIIM 0x00000001 -#define DTSEC_TCTRL_GTS 0x00000020 +#define TCTRL_GTS 0x00000020 #define RCTRL_PAL_MASK 0x001f0000 #define RCTRL_PAL_SHIFT 16 @@ -863,6 +863,52 @@ int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val) return 0; } +static void graceful_start(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (mode & COMM_MODE_TX) + iowrite32be(ioread32be(®s->tctrl) & + ~TCTRL_GTS, ®s->tctrl); + if (mode & COMM_MODE_RX) + iowrite32be(ioread32be(®s->rctrl) & + ~RCTRL_GRS, ®s->rctrl); +} + +static void graceful_stop(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + /* Graceful stop - Assert the graceful Rx stop bit */ + if (mode & COMM_MODE_RX) { + tmp = ioread32be(®s->rctrl) | RCTRL_GRS; + iowrite32be(tmp, ®s->rctrl); + + if (dtsec->fm_rev_info.major == 2) { + /* Workaround for dTSEC Errata A002 */ + usleep_range(100, 200); + } else { + /* Workaround for dTSEC Errata A004839 */ + usleep_range(10, 50); + } + } + + /* Graceful stop - Assert the graceful Tx stop bit */ + if (mode & COMM_MODE_TX) { + if (dtsec->fm_rev_info.major == 2) { + /* dTSEC Errata A004: Do not use TCTRL[GTS]=1 */ + pr_debug("GTS not supported due to DTSEC_A004 Errata.\n"); + } else { + tmp = ioread32be(®s->tctrl) | TCTRL_GTS; + iowrite32be(tmp, ®s->tctrl); + + /* Workaround for dTSEC Errata A0012, A0014 */ + usleep_range(10, 50); + } + } +} + int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode) { struct dtsec_regs __iomem *regs = dtsec->regs; @@ -880,13 +926,8 @@ int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode) iowrite32be(tmp, ®s->maccfg1); - /* Graceful start - clear the graceful receive stop bit */ - if (mode & COMM_MODE_TX) - iowrite32be(ioread32be(®s->tctrl) & ~DTSEC_TCTRL_GTS, - ®s->tctrl); - if (mode & COMM_MODE_RX) - iowrite32be(ioread32be(®s->rctrl) & ~RCTRL_GRS, - ®s->rctrl); + /* Graceful start - clear the graceful Rx/Tx stop bit */ + graceful_start(dtsec, mode); return 0; } @@ -899,23 +940,8 @@ int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode) if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; - /* Gracefull stop - Assert the graceful transmit stop bit */ - if (mode & COMM_MODE_RX) { - tmp = ioread32be(®s->rctrl) | RCTRL_GRS; - iowrite32be(tmp, ®s->rctrl); - - if (dtsec->fm_rev_info.major == 2) - usleep_range(100, 200); - else - udelay(10); - } - - if (mode & COMM_MODE_TX) { - if (dtsec->fm_rev_info.major == 2) - pr_debug("GTS not supported due to DTSEC_A004 errata.\n"); - else - pr_debug("GTS not supported due to DTSEC_A0014 errata.\n"); - } + /* Graceful stop - Assert the graceful Rx/Tx stop bit */ + graceful_stop(dtsec, mode); tmp = ioread32be(®s->maccfg1); if (mode & COMM_MODE_RX) @@ -933,11 +959,19 @@ int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, u16 pause_time, u16 __maybe_unused thresh_time) { struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; u32 ptv = 0; if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + if (pause_time) { /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ if (dtsec->fm_rev_info.major == 2 && pause_time <= 320) { @@ -958,17 +992,27 @@ int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, iowrite32be(ioread32be(®s->maccfg1) & ~MACCFG1_TX_FLOW, ®s->maccfg1); + graceful_start(dtsec, mode); + return 0; } int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) { struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; u32 tmp; if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + tmp = ioread32be(®s->maccfg1); if (en) tmp |= MACCFG1_RX_FLOW; @@ -976,20 +1020,34 @@ int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) tmp &= ~MACCFG1_RX_FLOW; iowrite32be(tmp, ®s->maccfg1); + graceful_start(dtsec, mode); + return 0; } int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr) { + struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; + if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + /* Initialize MAC Station Address registers (1 & 2) * Station address have to be swapped (big endian to little endian */ dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr); set_mac_address(dtsec->regs, (u8 *)(*enet_addr)); + graceful_start(dtsec, mode); + return 0; } @@ -1162,11 +1220,19 @@ int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) { struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; u32 tmp; if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + tmp = ioread32be(®s->maccfg2); /* Full Duplex */ @@ -1186,6 +1252,8 @@ int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) tmp &= ~DTSEC_ECNTRL_R100M; iowrite32be(tmp, ®s->ecntrl); + graceful_start(dtsec, mode); + return 0; } -- cgit v1.2.3 From 6aa200058e4df8a60681ddd67d7932fdf2ce58ab Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 11 Aug 2017 01:55:20 +0300 Subject: wan: dscc4: add checks for dma mapping errors The driver does not check if mapping dma memory succeed. The patch adds the checks and failure handling. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: David S. Miller --- drivers/net/wan/dscc4.c | 53 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index 799830ffcae2..8480dbfc70f2 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -519,22 +519,30 @@ static inline int try_get_rx_skb(struct dscc4_dev_priv *dpriv, struct net_device *dev) { unsigned int dirty = dpriv->rx_dirty%RX_RING_SIZE; + struct pci_dev *pdev = dpriv->pci_priv->pdev; struct RxFD *rx_fd = dpriv->rx_fd + dirty; const int len = RX_MAX(HDLC_MAX_MRU); struct sk_buff *skb; - int ret = 0; + dma_addr_t addr; skb = dev_alloc_skb(len); + if (!skb) + goto err_out; + + skb->protocol = hdlc_type_trans(skb, dev); + addr = pci_map_single(pdev, skb->data, len, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(pdev, addr)) + goto err_free_skb; + dpriv->rx_skbuff[dirty] = skb; - if (skb) { - skb->protocol = hdlc_type_trans(skb, dev); - rx_fd->data = cpu_to_le32(pci_map_single(dpriv->pci_priv->pdev, - skb->data, len, PCI_DMA_FROMDEVICE)); - } else { - rx_fd->data = 0; - ret = -1; - } - return ret; + rx_fd->data = cpu_to_le32(addr); + return 0; + +err_free_skb: + dev_kfree_skb_any(skb); +err_out: + rx_fd->data = 0; + return -1; } /* @@ -1145,16 +1153,23 @@ static netdev_tx_t dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct dscc4_dev_priv *dpriv = dscc4_priv(dev); - struct dscc4_pci_priv *ppriv = dpriv->pci_priv; + struct pci_dev *pdev = dpriv->pci_priv->pdev; struct TxFD *tx_fd; + dma_addr_t addr; int next; + addr = pci_map_single(pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, addr)) { + dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + next = dpriv->tx_current%TX_RING_SIZE; dpriv->tx_skbuff[next] = skb; tx_fd = dpriv->tx_fd + next; tx_fd->state = FrameEnd | TO_STATE_TX(skb->len); - tx_fd->data = cpu_to_le32(pci_map_single(ppriv->pdev, skb->data, skb->len, - PCI_DMA_TODEVICE)); + tx_fd->data = cpu_to_le32(addr); tx_fd->complete = 0x00000000; tx_fd->jiffies = jiffies; mb(); @@ -1887,16 +1902,22 @@ static struct sk_buff *dscc4_init_dummy_skb(struct dscc4_dev_priv *dpriv) skb = dev_alloc_skb(DUMMY_SKB_SIZE); if (skb) { + struct pci_dev *pdev = dpriv->pci_priv->pdev; int last = dpriv->tx_dirty%TX_RING_SIZE; struct TxFD *tx_fd = dpriv->tx_fd + last; + dma_addr_t addr; skb->len = DUMMY_SKB_SIZE; skb_copy_to_linear_data(skb, version, strlen(version) % DUMMY_SKB_SIZE); + addr = pci_map_single(pdev, skb->data, DUMMY_SKB_SIZE, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, addr)) { + dev_kfree_skb_any(skb); + return NULL; + } tx_fd->state = FrameEnd | TO_STATE_TX(DUMMY_SKB_SIZE); - tx_fd->data = cpu_to_le32(pci_map_single(dpriv->pci_priv->pdev, - skb->data, DUMMY_SKB_SIZE, - PCI_DMA_TODEVICE)); + tx_fd->data = cpu_to_le32(addr); dpriv->tx_skbuff[last] = skb; } return skb; -- cgit v1.2.3 From ba6ba68f597be06f9ad381c142c65ca0af32640d Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 11 Aug 2017 01:55:21 +0300 Subject: wan: dscc4: convert to plain DMA API Make use the dma_*() interfaces rather than the pci_*() interfaces. Signed-off-by: Alexey Khoroshilov Signed-off-by: David S. Miller --- drivers/net/wan/dscc4.c | 96 ++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 45 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index 8480dbfc70f2..a043fb1367bd 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -483,20 +483,20 @@ static void dscc4_tx_print(struct net_device *dev, static void dscc4_release_ring(struct dscc4_dev_priv *dpriv) { - struct pci_dev *pdev = dpriv->pci_priv->pdev; + struct device *d = &dpriv->pci_priv->pdev->dev; struct TxFD *tx_fd = dpriv->tx_fd; struct RxFD *rx_fd = dpriv->rx_fd; struct sk_buff **skbuff; int i; - pci_free_consistent(pdev, TX_TOTAL_SIZE, tx_fd, dpriv->tx_fd_dma); - pci_free_consistent(pdev, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma); + dma_free_coherent(d, TX_TOTAL_SIZE, tx_fd, dpriv->tx_fd_dma); + dma_free_coherent(d, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma); skbuff = dpriv->tx_skbuff; for (i = 0; i < TX_RING_SIZE; i++) { if (*skbuff) { - pci_unmap_single(pdev, le32_to_cpu(tx_fd->data), - (*skbuff)->len, PCI_DMA_TODEVICE); + dma_unmap_single(d, le32_to_cpu(tx_fd->data), + (*skbuff)->len, DMA_TO_DEVICE); dev_kfree_skb(*skbuff); } skbuff++; @@ -506,8 +506,9 @@ static void dscc4_release_ring(struct dscc4_dev_priv *dpriv) skbuff = dpriv->rx_skbuff; for (i = 0; i < RX_RING_SIZE; i++) { if (*skbuff) { - pci_unmap_single(pdev, le32_to_cpu(rx_fd->data), - RX_MAX(HDLC_MAX_MRU), PCI_DMA_FROMDEVICE); + dma_unmap_single(d, le32_to_cpu(rx_fd->data), + RX_MAX(HDLC_MAX_MRU), + DMA_FROM_DEVICE); dev_kfree_skb(*skbuff); } skbuff++; @@ -519,7 +520,7 @@ static inline int try_get_rx_skb(struct dscc4_dev_priv *dpriv, struct net_device *dev) { unsigned int dirty = dpriv->rx_dirty%RX_RING_SIZE; - struct pci_dev *pdev = dpriv->pci_priv->pdev; + struct device *d = &dpriv->pci_priv->pdev->dev; struct RxFD *rx_fd = dpriv->rx_fd + dirty; const int len = RX_MAX(HDLC_MAX_MRU); struct sk_buff *skb; @@ -530,8 +531,8 @@ static inline int try_get_rx_skb(struct dscc4_dev_priv *dpriv, goto err_out; skb->protocol = hdlc_type_trans(skb, dev); - addr = pci_map_single(pdev, skb->data, len, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(pdev, addr)) + addr = dma_map_single(d, skb->data, len, DMA_FROM_DEVICE); + if (dma_mapping_error(d, addr)) goto err_free_skb; dpriv->rx_skbuff[dirty] = skb; @@ -654,7 +655,7 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, struct net_device *dev) { struct RxFD *rx_fd = dpriv->rx_fd + dpriv->rx_current%RX_RING_SIZE; - struct pci_dev *pdev = dpriv->pci_priv->pdev; + struct device *d = &dpriv->pci_priv->pdev->dev; struct sk_buff *skb; int pkt_len; @@ -664,8 +665,8 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, goto refill; } pkt_len = TO_SIZE(le32_to_cpu(rx_fd->state2)); - pci_unmap_single(pdev, le32_to_cpu(rx_fd->data), - RX_MAX(HDLC_MAX_MRU), PCI_DMA_FROMDEVICE); + dma_unmap_single(d, le32_to_cpu(rx_fd->data), + RX_MAX(HDLC_MAX_MRU), DMA_FROM_DEVICE); if ((skb->data[--pkt_len] & FrameOk) == FrameOk) { dev->stats.rx_packets++; dev->stats.rx_bytes += pkt_len; @@ -782,8 +783,8 @@ static int dscc4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) rc = -ENOMEM; - priv->iqcfg = (__le32 *) pci_alloc_consistent(pdev, - IRQ_RING_SIZE*sizeof(__le32), &priv->iqcfg_dma); + priv->iqcfg = (__le32 *)dma_alloc_coherent(&pdev->dev, + IRQ_RING_SIZE*sizeof(__le32), &priv->iqcfg_dma, GFP_KERNEL); if (!priv->iqcfg) goto err_free_irq_5; writel(priv->iqcfg_dma, ioaddr + IQCFG); @@ -794,16 +795,18 @@ static int dscc4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) */ for (i = 0; i < dev_per_card; i++) { dpriv = priv->root + i; - dpriv->iqtx = (__le32 *) pci_alloc_consistent(pdev, - IRQ_RING_SIZE*sizeof(u32), &dpriv->iqtx_dma); + dpriv->iqtx = (__le32 *)dma_alloc_coherent(&pdev->dev, + IRQ_RING_SIZE*sizeof(u32), &dpriv->iqtx_dma, + GFP_KERNEL); if (!dpriv->iqtx) goto err_free_iqtx_6; writel(dpriv->iqtx_dma, ioaddr + IQTX0 + i*4); } for (i = 0; i < dev_per_card; i++) { dpriv = priv->root + i; - dpriv->iqrx = (__le32 *) pci_alloc_consistent(pdev, - IRQ_RING_SIZE*sizeof(u32), &dpriv->iqrx_dma); + dpriv->iqrx = (__le32 *)dma_alloc_coherent(&pdev->dev, + IRQ_RING_SIZE*sizeof(u32), &dpriv->iqrx_dma, + GFP_KERNEL); if (!dpriv->iqrx) goto err_free_iqrx_7; writel(dpriv->iqrx_dma, ioaddr + IQRX0 + i*4); @@ -827,18 +830,18 @@ out: err_free_iqrx_7: while (--i >= 0) { dpriv = priv->root + i; - pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), - dpriv->iqrx, dpriv->iqrx_dma); + dma_free_coherent(&pdev->dev, IRQ_RING_SIZE*sizeof(u32), + dpriv->iqrx, dpriv->iqrx_dma); } i = dev_per_card; err_free_iqtx_6: while (--i >= 0) { dpriv = priv->root + i; - pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), - dpriv->iqtx, dpriv->iqtx_dma); + dma_free_coherent(&pdev->dev, IRQ_RING_SIZE*sizeof(u32), + dpriv->iqtx, dpriv->iqtx_dma); } - pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), priv->iqcfg, - priv->iqcfg_dma); + dma_free_coherent(&pdev->dev, IRQ_RING_SIZE*sizeof(u32), priv->iqcfg, + priv->iqcfg_dma); err_free_irq_5: free_irq(pdev->irq, priv->root); err_release_4: @@ -1153,13 +1156,13 @@ static netdev_tx_t dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct dscc4_dev_priv *dpriv = dscc4_priv(dev); - struct pci_dev *pdev = dpriv->pci_priv->pdev; + struct device *d = &dpriv->pci_priv->pdev->dev; struct TxFD *tx_fd; dma_addr_t addr; int next; - addr = pci_map_single(pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(pdev, addr)) { + addr = dma_map_single(d, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(d, addr)) { dev_kfree_skb_any(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; @@ -1587,8 +1590,9 @@ try: tx_fd = dpriv->tx_fd + cur; skb = dpriv->tx_skbuff[cur]; if (skb) { - pci_unmap_single(ppriv->pdev, le32_to_cpu(tx_fd->data), - skb->len, PCI_DMA_TODEVICE); + dma_unmap_single(&ppriv->pdev->dev, + le32_to_cpu(tx_fd->data), + skb->len, DMA_TO_DEVICE); if (tx_fd->state & FrameEnd) { dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; @@ -1902,7 +1906,7 @@ static struct sk_buff *dscc4_init_dummy_skb(struct dscc4_dev_priv *dpriv) skb = dev_alloc_skb(DUMMY_SKB_SIZE); if (skb) { - struct pci_dev *pdev = dpriv->pci_priv->pdev; + struct device *d = &dpriv->pci_priv->pdev->dev; int last = dpriv->tx_dirty%TX_RING_SIZE; struct TxFD *tx_fd = dpriv->tx_fd + last; dma_addr_t addr; @@ -1910,9 +1914,9 @@ static struct sk_buff *dscc4_init_dummy_skb(struct dscc4_dev_priv *dpriv) skb->len = DUMMY_SKB_SIZE; skb_copy_to_linear_data(skb, version, strlen(version) % DUMMY_SKB_SIZE); - addr = pci_map_single(pdev, skb->data, DUMMY_SKB_SIZE, - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(pdev, addr)) { + addr = dma_map_single(d, skb->data, DUMMY_SKB_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(d, addr)) { dev_kfree_skb_any(skb); return NULL; } @@ -1926,18 +1930,20 @@ static struct sk_buff *dscc4_init_dummy_skb(struct dscc4_dev_priv *dpriv) static int dscc4_init_ring(struct net_device *dev) { struct dscc4_dev_priv *dpriv = dscc4_priv(dev); - struct pci_dev *pdev = dpriv->pci_priv->pdev; + struct device *d = &dpriv->pci_priv->pdev->dev; struct TxFD *tx_fd; struct RxFD *rx_fd; void *ring; int i; - ring = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &dpriv->rx_fd_dma); + ring = dma_alloc_coherent(d, RX_TOTAL_SIZE, &dpriv->rx_fd_dma, + GFP_KERNEL); if (!ring) goto err_out; dpriv->rx_fd = rx_fd = (struct RxFD *) ring; - ring = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &dpriv->tx_fd_dma); + ring = dma_alloc_coherent(d, TX_TOTAL_SIZE, &dpriv->tx_fd_dma, + GFP_KERNEL); if (!ring) goto err_free_dma_rx; dpriv->tx_fd = tx_fd = (struct TxFD *) ring; @@ -1975,9 +1981,9 @@ static int dscc4_init_ring(struct net_device *dev) return 0; err_free_dma_tx: - pci_free_consistent(pdev, TX_TOTAL_SIZE, ring, dpriv->tx_fd_dma); + dma_free_coherent(d, TX_TOTAL_SIZE, ring, dpriv->tx_fd_dma); err_free_dma_rx: - pci_free_consistent(pdev, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma); + dma_free_coherent(d, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma); err_out: return -ENOMEM; } @@ -1997,16 +2003,16 @@ static void dscc4_remove_one(struct pci_dev *pdev) dscc4_pci_reset(pdev, ioaddr); free_irq(pdev->irq, root); - pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), ppriv->iqcfg, - ppriv->iqcfg_dma); + dma_free_coherent(&pdev->dev, IRQ_RING_SIZE*sizeof(u32), ppriv->iqcfg, + ppriv->iqcfg_dma); for (i = 0; i < dev_per_card; i++) { struct dscc4_dev_priv *dpriv = root + i; dscc4_release_ring(dpriv); - pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), - dpriv->iqrx, dpriv->iqrx_dma); - pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), - dpriv->iqtx, dpriv->iqtx_dma); + dma_free_coherent(&pdev->dev, IRQ_RING_SIZE*sizeof(u32), + dpriv->iqrx, dpriv->iqrx_dma); + dma_free_coherent(&pdev->dev, IRQ_RING_SIZE*sizeof(u32), + dpriv->iqtx, dpriv->iqtx_dma); } dscc4_free1(pdev); -- cgit v1.2.3 From 12acb133ece5a0b25b0984d31d870619966f63fe Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 11 Aug 2017 10:25:44 +0200 Subject: nfp: send control message when MAC representors are created The firmware expects a MAC_REPR control message when a MAC representor is created. The driver should expect a PORTMOD message to follow which will provide the link states of the physical port associated with the MAC representor. Signed-off-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/flower/cmsg.c | 33 ++++++++++++++++++++++++ drivers/net/ethernet/netronome/nfp/flower/cmsg.h | 21 +++++++++++++++ drivers/net/ethernet/netronome/nfp/flower/main.c | 29 ++++++++++++++++++--- 3 files changed, 80 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index dd7fa9cf225f..a69d68ba3d0c 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -75,6 +75,39 @@ nfp_flower_cmsg_alloc(struct nfp_app *app, unsigned int size, return skb; } +struct sk_buff * +nfp_flower_cmsg_mac_repr_start(struct nfp_app *app, unsigned int num_ports) +{ + struct nfp_flower_cmsg_mac_repr *msg; + struct sk_buff *skb; + unsigned int size; + + size = sizeof(*msg) + num_ports * sizeof(msg->ports[0]); + skb = nfp_flower_cmsg_alloc(app, size, NFP_FLOWER_CMSG_TYPE_MAC_REPR); + if (!skb) + return NULL; + + msg = nfp_flower_cmsg_get_data(skb); + memset(msg->reserved, 0, sizeof(msg->reserved)); + msg->num_ports = num_ports; + + return skb; +} + +void +nfp_flower_cmsg_mac_repr_add(struct sk_buff *skb, unsigned int idx, + unsigned int nbi, unsigned int nbi_port, + unsigned int phys_port) +{ + struct nfp_flower_cmsg_mac_repr *msg; + + msg = nfp_flower_cmsg_get_data(skb); + msg->ports[idx].idx = idx; + msg->ports[idx].info = nbi & NFP_FLOWER_CMSG_MAC_REPR_NBI; + msg->ports[idx].nbi_port = nbi_port; + msg->ports[idx].phys_port = phys_port; +} + int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok) { struct nfp_flower_cmsg_portmod *msg; diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index cf738de170ab..aa92a8711a02 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -247,12 +247,27 @@ struct nfp_flower_cmsg_hdr { enum nfp_flower_cmsg_type_port { NFP_FLOWER_CMSG_TYPE_FLOW_ADD = 0, NFP_FLOWER_CMSG_TYPE_FLOW_DEL = 2, + NFP_FLOWER_CMSG_TYPE_MAC_REPR = 7, NFP_FLOWER_CMSG_TYPE_PORT_MOD = 8, NFP_FLOWER_CMSG_TYPE_FLOW_STATS = 15, NFP_FLOWER_CMSG_TYPE_PORT_ECHO = 16, NFP_FLOWER_CMSG_TYPE_MAX = 32, }; +/* NFP_FLOWER_CMSG_TYPE_MAC_REPR */ +struct nfp_flower_cmsg_mac_repr { + u8 reserved[3]; + u8 num_ports; + struct { + u8 idx; + u8 info; + u8 nbi_port; + u8 phys_port; + } ports[0]; +}; + +#define NFP_FLOWER_CMSG_MAC_REPR_NBI GENMASK(1, 0) + /* NFP_FLOWER_CMSG_TYPE_PORT_MOD */ struct nfp_flower_cmsg_portmod { __be32 portnum; @@ -308,6 +323,12 @@ static inline void *nfp_flower_cmsg_get_data(struct sk_buff *skb) return (unsigned char *)skb->data + NFP_FLOWER_CMSG_HLEN; } +struct sk_buff * +nfp_flower_cmsg_mac_repr_start(struct nfp_app *app, unsigned int num_ports); +void +nfp_flower_cmsg_mac_repr_add(struct sk_buff *skb, unsigned int idx, + unsigned int nbi, unsigned int nbi_port, + unsigned int phys_port); int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok); void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb); struct sk_buff * diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index 6a65c8b33807..b905454b30ca 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -214,15 +214,22 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv) { struct nfp_eth_table *eth_tbl = app->pf->eth_tbl; struct nfp_reprs *reprs, *old_reprs; + struct sk_buff *ctrl_skb; unsigned int i; int err; - reprs = nfp_reprs_alloc(eth_tbl->max_index + 1); - if (!reprs) + ctrl_skb = nfp_flower_cmsg_mac_repr_start(app, eth_tbl->count); + if (!ctrl_skb) return -ENOMEM; + reprs = nfp_reprs_alloc(eth_tbl->max_index + 1); + if (!reprs) { + err = -ENOMEM; + goto err_free_ctrl_skb; + } + for (i = 0; i < eth_tbl->count; i++) { - int phys_port = eth_tbl->ports[i].index; + unsigned int phys_port = eth_tbl->ports[i].index; struct nfp_port *port; u32 cmsg_port_id; @@ -255,6 +262,11 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv) goto err_reprs_clean; } + nfp_flower_cmsg_mac_repr_add(ctrl_skb, i, + eth_tbl->ports[i].nbi, + eth_tbl->ports[i].base, + phys_port); + nfp_info(app->cpp, "Phys Port %d Representor(%s) created\n", phys_port, reprs->reprs[phys_port]->name); } @@ -265,9 +277,20 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv) goto err_reprs_clean; } + /* The MAC_REPR control message should be sent after the MAC + * representors are registered using nfp_app_reprs_set(). This is + * because the firmware may respond with control messages for the + * MAC representors, f.e. to provide the driver with information + * about their state, and without registration the driver will drop + * any such messages. + */ + nfp_ctrl_tx(app->ctrl, ctrl_skb); + return 0; err_reprs_clean: nfp_reprs_clean_and_free(reprs); +err_free_ctrl_skb: + kfree_skb(ctrl_skb); return err; } -- cgit v1.2.3 From 159fe88efda2f5b84681727a4291511338132c1f Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Fri, 11 Aug 2017 19:10:42 +0530 Subject: mlxsw: make mlxsw_config_profile const Make these structures const as they only stored in the profile field of a mlxsw_driver structure, which is of type const. Done using Coccinelle. Signed-off-by: Bhumika Goyal Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/switchib.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index a99600333a49..90a95cdc1626 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3757,7 +3757,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) mlxsw_sp_fids_fini(mlxsw_sp); } -static struct mlxsw_config_profile mlxsw_sp_config_profile = { +static const struct mlxsw_config_profile mlxsw_sp_config_profile = { .used_max_vepa_channels = 1, .max_vepa_channels = 0, .used_max_mid = 1, diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c index 74341fe0eb25..ab7a29846bfa 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchib.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c @@ -497,7 +497,7 @@ static void mlxsw_sib_fini(struct mlxsw_core *mlxsw_core) mlxsw_sib_ports_remove(mlxsw_sib); } -static struct mlxsw_config_profile mlxsw_sib_config_profile = { +static const struct mlxsw_config_profile mlxsw_sib_config_profile = { .used_max_system_port = 1, .max_system_port = 48000, .used_max_ib_mc = 1, diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 3b0f72455681..f3c29bbf07e2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1674,7 +1674,7 @@ static void mlxsw_sx_fini(struct mlxsw_core *mlxsw_core) mlxsw_sx_ports_remove(mlxsw_sx); } -static struct mlxsw_config_profile mlxsw_sx_config_profile = { +static const struct mlxsw_config_profile mlxsw_sx_config_profile = { .used_max_vepa_channels = 1, .max_vepa_channels = 0, .used_max_mid = 1, -- cgit v1.2.3 From aa69ff9e9c32db8aa84835baffea1b70c39e5112 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Fri, 11 Aug 2017 11:22:09 -0700 Subject: liquidio: moved ptp_enable to octeon_device structure ptp_enable was a global static variable. Moved this global variable to octeon_device structure and removed extra device id check. Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 19 ++++++++++--------- drivers/net/ethernet/cavium/liquidio/octeon_device.h | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index cbd6287e578e..8bf6dfcf5881 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -78,8 +78,6 @@ int octeon_console_debug_enabled(u32 console) return (console_bitmask >> (console)) & 0x1; } -static int ptp_enable = 1; - /* Polling interval for determining when NIC application is alive */ #define LIQUIDIO_STARTER_POLL_INTERVAL_MS 100 @@ -1363,6 +1361,13 @@ liquidio_probe(struct pci_dev *pdev, if (pdev->device == OCTEON_CN23XX_PF_VID) oct_dev->msix_on = LIO_FLAG_MSIX_ENABLED; + /* Enable PTP for 6XXX Device */ + if (((pdev->device == OCTEON_CN66XX) || + (pdev->device == OCTEON_CN68XX))) + oct_dev->ptp_enable = true; + else + oct_dev->ptp_enable = false; + dev_info(&pdev->dev, "Initializing device %x:%x.\n", (u32)pdev->vendor, (u32)pdev->device); @@ -2388,9 +2393,7 @@ liquidio_push_packet(u32 octeon_id __attribute__((unused)), r_dh_off = (rh->r_dh.len - 1) * BYTES_PER_DHLEN_UNIT; - if (((oct->chip_id == OCTEON_CN66XX) || - (oct->chip_id == OCTEON_CN68XX)) && - ptp_enable) { + if (oct->ptp_enable) { if (rh->r_dh.has_hwtstamp) { /* timestamp is included from the hardware at * the beginning of the packet. @@ -2735,8 +2738,7 @@ static int liquidio_open(struct net_device *netdev) oct->droq[0]->ops.poll_mode = 1; } - if ((oct->chip_id == OCTEON_CN66XX || oct->chip_id == OCTEON_CN68XX) && - ptp_enable) + if (oct->ptp_enable) oct_ptp_open(netdev); ifstate_set(lio, LIO_IFSTATE_RUNNING); @@ -3091,8 +3093,7 @@ static int liquidio_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) switch (cmd) { case SIOCSHWTSTAMP: - if ((lio->oct_dev->chip_id == OCTEON_CN66XX || - lio->oct_dev->chip_id == OCTEON_CN68XX) && ptp_enable) + if (lio->oct_dev->ptp_enable) return hwtstamp_ioctl(netdev, ifr); default: return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 31efdef02a24..b014e6ad0e9a 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -554,6 +554,7 @@ struct octeon_device { } loc; atomic_t *adapter_refcount; /* reference count of adapter */ + bool ptp_enable; }; #define OCT_DRV_ONLINE 1 -- cgit v1.2.3 From 66ccbc9c87c2ae96585f9aa5f733609e6f4acf25 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 11 Aug 2017 19:41:16 +0800 Subject: tap: use build_skb() for small packet We use tun_alloc_skb() which calls sock_alloc_send_pskb() to allocate skb in the past. This socket based method is not suitable for high speed userspace like virtualization which usually: - ignore sk_sndbuf (INT_MAX) and expect to receive the packet as fast as possible - don't want to be block at sendmsg() To eliminate the above overheads, this patch tries to use build_skb() for small packet. We will do this only when the following conditions are all met: - TAP instead of TUN - sk_sndbuf is INT_MAX - caller don't want to be blocked - zerocopy is not used - packet size is smaller enough to use build_skb() Pktgen from guest to host shows ~11% improvement for rx pps of tap: Before: ~1.70Mpps After : ~1.88Mpps What's more important, this makes it possible to implement XDP for tap before creating skbs. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/tun.c | 112 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 21 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index d21510d47aa2..9736df40d2bf 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -105,6 +105,8 @@ do { \ } while (0) #endif +#define TUN_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD) + /* TUN device flags */ /* IFF_ATTACH_QUEUE is never stored in device flags, @@ -170,6 +172,7 @@ struct tun_file { struct list_head next; struct tun_struct *detached; struct skb_array tx_array; + struct page_frag alloc_frag; }; struct tun_flow_entry { @@ -571,6 +574,8 @@ static void __tun_detach(struct tun_file *tfile, bool clean) } if (tun) skb_array_cleanup(&tfile->tx_array); + if (tfile->alloc_frag.page) + put_page(tfile->alloc_frag.page); sock_put(&tfile->sk); } } @@ -1190,6 +1195,61 @@ static void tun_rx_batched(struct tun_struct *tun, struct tun_file *tfile, } } +static bool tun_can_build_skb(struct tun_struct *tun, struct tun_file *tfile, + int len, int noblock, bool zerocopy) +{ + if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP) + return false; + + if (tfile->socket.sk->sk_sndbuf != INT_MAX) + return false; + + if (!noblock) + return false; + + if (zerocopy) + return false; + + if (SKB_DATA_ALIGN(len + TUN_RX_PAD) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) > PAGE_SIZE) + return false; + + return true; +} + +static struct sk_buff *tun_build_skb(struct tun_file *tfile, + struct iov_iter *from, + int len) +{ + struct page_frag *alloc_frag = &tfile->alloc_frag; + struct sk_buff *skb; + int buflen = SKB_DATA_ALIGN(len + TUN_RX_PAD) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + char *buf; + size_t copied; + + if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL))) + return ERR_PTR(-ENOMEM); + + buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset; + copied = copy_page_from_iter(alloc_frag->page, + alloc_frag->offset + TUN_RX_PAD, + len, from); + if (copied != len) + return ERR_PTR(-EFAULT); + + skb = build_skb(buf, buflen); + if (!skb) + return ERR_PTR(-ENOMEM); + + skb_reserve(skb, TUN_RX_PAD); + skb_put(skb, len); + get_page(alloc_frag->page); + alloc_frag->offset += buflen; + + return skb; +} + /* Get packet from user space buffer */ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, void *msg_control, struct iov_iter *from, @@ -1263,30 +1323,38 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, zerocopy = true; } - if (!zerocopy) { - copylen = len; - if (tun16_to_cpu(tun, gso.hdr_len) > good_linear) - linear = good_linear; - else - linear = tun16_to_cpu(tun, gso.hdr_len); - } - - skb = tun_alloc_skb(tfile, align, copylen, linear, noblock); - if (IS_ERR(skb)) { - if (PTR_ERR(skb) != -EAGAIN) + if (tun_can_build_skb(tun, tfile, len, noblock, zerocopy)) { + skb = tun_build_skb(tfile, from, len); + if (IS_ERR(skb)) { this_cpu_inc(tun->pcpu_stats->rx_dropped); - return PTR_ERR(skb); - } + return PTR_ERR(skb); + } + } else { + if (!zerocopy) { + copylen = len; + if (tun16_to_cpu(tun, gso.hdr_len) > good_linear) + linear = good_linear; + else + linear = tun16_to_cpu(tun, gso.hdr_len); + } - if (zerocopy) - err = zerocopy_sg_from_iter(skb, from); - else - err = skb_copy_datagram_from_iter(skb, 0, from, len); + skb = tun_alloc_skb(tfile, align, copylen, linear, noblock); + if (IS_ERR(skb)) { + if (PTR_ERR(skb) != -EAGAIN) + this_cpu_inc(tun->pcpu_stats->rx_dropped); + return PTR_ERR(skb); + } - if (err) { - this_cpu_inc(tun->pcpu_stats->rx_dropped); - kfree_skb(skb); - return -EFAULT; + if (zerocopy) + err = zerocopy_sg_from_iter(skb, from); + else + err = skb_copy_datagram_from_iter(skb, 0, from, len); + + if (err) { + this_cpu_inc(tun->pcpu_stats->rx_dropped); + kfree_skb(skb); + return -EFAULT; + } } if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) { @@ -2377,6 +2445,8 @@ static int tun_chr_open(struct inode *inode, struct file * file) tfile->sk.sk_write_space = tun_sock_write_space; tfile->sk.sk_sndbuf = INT_MAX; + tfile->alloc_frag.page = NULL; + file->private_data = tfile; INIT_LIST_HEAD(&tfile->next); -- cgit v1.2.3 From 761876c857cb2ef8489fbee01907151da902af91 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 11 Aug 2017 19:41:18 +0800 Subject: tap: XDP support This patch tries to implement XDP for tun. The implementation was split into two parts: - fast path: small and no gso packet. We try to do XDP at page level before build_skb(). For XDP_TX, since creating/destroying queues were completely under control of userspace, it was implemented through generic XDP helper after skb has been built. This could be optimized in the future. - slow path: big or gso packet. We try to do it after skb was created through generic XDP helpers. Test were done through pktgen with small packets. xdp1 test shows ~41.1% improvement: Before: ~1.7Mpps After: ~2.3Mpps xdp_redirect to ixgbe shows ~60% improvement: Before: ~0.8Mpps After: ~1.38Mpps Suggested-by: Michael S. Tsirkin Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/tun.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 142 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 9736df40d2bf..5892284eb8d0 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -73,6 +73,8 @@ #include #include #include +#include +#include #include @@ -105,7 +107,8 @@ do { \ } while (0) #endif -#define TUN_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD) +#define TUN_HEADROOM 256 +#define TUN_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD + TUN_HEADROOM) /* TUN device flags */ @@ -224,6 +227,7 @@ struct tun_struct { u32 flow_count; u32 rx_batched; struct tun_pcpu_stats __percpu *pcpu_stats; + struct bpf_prog __rcu *xdp_prog; }; #ifdef CONFIG_TUN_VNET_CROSS_LE @@ -590,6 +594,7 @@ static void tun_detach(struct tun_file *tfile, bool clean) static void tun_detach_all(struct net_device *dev) { struct tun_struct *tun = netdev_priv(dev); + struct bpf_prog *xdp_prog = rtnl_dereference(tun->xdp_prog); struct tun_file *tfile, *tmp; int i, n = tun->numqueues; @@ -622,6 +627,9 @@ static void tun_detach_all(struct net_device *dev) } BUG_ON(tun->numdisabled != 0); + if (xdp_prog) + bpf_prog_put(xdp_prog); + if (tun->flags & IFF_PERSIST) module_put(THIS_MODULE); } @@ -1008,6 +1016,46 @@ tun_net_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) stats->tx_dropped = tx_dropped; } +static int tun_xdp_set(struct net_device *dev, struct bpf_prog *prog, + struct netlink_ext_ack *extack) +{ + struct tun_struct *tun = netdev_priv(dev); + struct bpf_prog *old_prog; + + old_prog = rtnl_dereference(tun->xdp_prog); + rcu_assign_pointer(tun->xdp_prog, prog); + if (old_prog) + bpf_prog_put(old_prog); + + return 0; +} + +static u32 tun_xdp_query(struct net_device *dev) +{ + struct tun_struct *tun = netdev_priv(dev); + const struct bpf_prog *xdp_prog; + + xdp_prog = rtnl_dereference(tun->xdp_prog); + if (xdp_prog) + return xdp_prog->aux->id; + + return 0; +} + +static int tun_xdp(struct net_device *dev, struct netdev_xdp *xdp) +{ + switch (xdp->command) { + case XDP_SETUP_PROG: + return tun_xdp_set(dev, xdp->prog, xdp->extack); + case XDP_QUERY_PROG: + xdp->prog_id = tun_xdp_query(dev); + xdp->prog_attached = !!xdp->prog_id; + return 0; + default: + return -EINVAL; + } +} + static const struct net_device_ops tun_netdev_ops = { .ndo_uninit = tun_net_uninit, .ndo_open = tun_net_open, @@ -1038,6 +1086,7 @@ static const struct net_device_ops tap_netdev_ops = { .ndo_features_check = passthru_features_check, .ndo_set_rx_headroom = tun_set_headroom, .ndo_get_stats64 = tun_net_get_stats64, + .ndo_xdp = tun_xdp, }; static void tun_flow_init(struct tun_struct *tun) @@ -1217,16 +1266,22 @@ static bool tun_can_build_skb(struct tun_struct *tun, struct tun_file *tfile, return true; } -static struct sk_buff *tun_build_skb(struct tun_file *tfile, +static struct sk_buff *tun_build_skb(struct tun_struct *tun, + struct tun_file *tfile, struct iov_iter *from, - int len) + struct virtio_net_hdr *hdr, + int len, int *generic_xdp) { struct page_frag *alloc_frag = &tfile->alloc_frag; struct sk_buff *skb; + struct bpf_prog *xdp_prog; int buflen = SKB_DATA_ALIGN(len + TUN_RX_PAD) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + unsigned int delta = 0; char *buf; size_t copied; + bool xdp_xmit = false; + int err; if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL))) return ERR_PTR(-ENOMEM); @@ -1238,16 +1293,77 @@ static struct sk_buff *tun_build_skb(struct tun_file *tfile, if (copied != len) return ERR_PTR(-EFAULT); + if (hdr->gso_type) + *generic_xdp = 1; + else + *generic_xdp = 0; + + rcu_read_lock(); + xdp_prog = rcu_dereference(tun->xdp_prog); + if (xdp_prog && !*generic_xdp) { + struct xdp_buff xdp; + void *orig_data; + u32 act; + + xdp.data_hard_start = buf; + xdp.data = buf + TUN_RX_PAD; + xdp.data_end = xdp.data + len; + orig_data = xdp.data; + act = bpf_prog_run_xdp(xdp_prog, &xdp); + + switch (act) { + case XDP_REDIRECT: + get_page(alloc_frag->page); + alloc_frag->offset += buflen; + err = xdp_do_redirect(tun->dev, &xdp, xdp_prog); + if (err) + goto err_redirect; + return NULL; + case XDP_TX: + xdp_xmit = true; + /* fall through */ + case XDP_PASS: + delta = orig_data - xdp.data; + break; + default: + bpf_warn_invalid_xdp_action(act); + /* fall through */ + case XDP_ABORTED: + trace_xdp_exception(tun->dev, xdp_prog, act); + /* fall through */ + case XDP_DROP: + goto err_xdp; + } + } + skb = build_skb(buf, buflen); - if (!skb) + if (!skb) { + rcu_read_unlock(); return ERR_PTR(-ENOMEM); + } - skb_reserve(skb, TUN_RX_PAD); - skb_put(skb, len); + skb_reserve(skb, TUN_RX_PAD - delta); + skb_put(skb, len + delta); get_page(alloc_frag->page); alloc_frag->offset += buflen; + if (xdp_xmit) { + skb->dev = tun->dev; + generic_xdp_tx(skb, xdp_prog); + rcu_read_lock(); + return NULL; + } + + rcu_read_unlock(); + return skb; + +err_redirect: + put_page(alloc_frag->page); +err_xdp: + rcu_read_unlock(); + this_cpu_inc(tun->pcpu_stats->rx_dropped); + return NULL; } /* Get packet from user space buffer */ @@ -1266,6 +1382,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, bool zerocopy = false; int err; u32 rxhash; + int generic_xdp = 1; if (!(tun->dev->flags & IFF_UP)) return -EIO; @@ -1324,11 +1441,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (tun_can_build_skb(tun, tfile, len, noblock, zerocopy)) { - skb = tun_build_skb(tfile, from, len); + skb = tun_build_skb(tun, tfile, from, &gso, len, &generic_xdp); if (IS_ERR(skb)) { this_cpu_inc(tun->pcpu_stats->rx_dropped); return PTR_ERR(skb); } + if (!skb) + return total_len; } else { if (!zerocopy) { copylen = len; @@ -1402,6 +1521,22 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb_reset_network_header(skb); skb_probe_transport_header(skb, 0); + if (generic_xdp) { + struct bpf_prog *xdp_prog; + int ret; + + rcu_read_lock(); + xdp_prog = rcu_dereference(tun->xdp_prog); + if (xdp_prog) { + ret = do_xdp_generic(xdp_prog, skb); + if (ret != XDP_PASS) { + rcu_read_unlock(); + return total_len; + } + } + rcu_read_unlock(); + } + rxhash = __skb_get_hash_symmetric(skb); #ifndef CONFIG_4KSTACKS tun_rx_batched(tun, tfile, skb, more); -- cgit v1.2.3 From 653ef6a3e4af21fda8e73061d4f1b069a9982301 Mon Sep 17 00:00:00 2001 From: Girish Moodalbail Date: Fri, 11 Aug 2017 15:20:59 -0700 Subject: vxlan: change vxlan_[config_]validate() to use netlink_ext_ack for error reporting The kernel log is not where users expect error messages for netlink requests; as we have extended acks now, we can replace pr_debug() with NL_SET_ERR_MSG_ATTR(). Signed-off-by: Matthias Schiffer Signed-off-by: Girish Moodalbail Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 99 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 26 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 35e84a9e1cfb..ae3a1da703c2 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2729,12 +2729,14 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[], { if (tb[IFLA_ADDRESS]) { if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) { - pr_debug("invalid link address (not ethernet)\n"); + NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_ADDRESS], + "Provided link layer address is not Ethernet"); return -EINVAL; } if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) { - pr_debug("invalid all zero ethernet address\n"); + NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_ADDRESS], + "Provided Ethernet address is not unicast"); return -EADDRNOTAVAIL; } } @@ -2742,18 +2744,27 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[], if (tb[IFLA_MTU]) { u32 mtu = nla_get_u32(tb[IFLA_MTU]); - if (mtu < ETH_MIN_MTU || mtu > ETH_MAX_MTU) + if (mtu < ETH_MIN_MTU || mtu > ETH_MAX_MTU) { + NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_MTU], + "MTU must be between 68 and 65535"); return -EINVAL; + } } - if (!data) + if (!data) { + NL_SET_ERR_MSG(extack, + "Required attributes not provided to perform the operation"); return -EINVAL; + } if (data[IFLA_VXLAN_ID]) { u32 id = nla_get_u32(data[IFLA_VXLAN_ID]); - if (id >= VXLAN_N_VID) + if (id >= VXLAN_N_VID) { + NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_VXLAN_ID], + "VXLAN ID must be lower than 16777216"); return -ERANGE; + } } if (data[IFLA_VXLAN_PORT_RANGE]) { @@ -2761,8 +2772,8 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[], = nla_data(data[IFLA_VXLAN_PORT_RANGE]); if (ntohs(p->high) < ntohs(p->low)) { - pr_debug("port range %u .. %u not valid\n", - ntohs(p->low), ntohs(p->high)); + NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_VXLAN_PORT_RANGE], + "Invalid source port range"); return -EINVAL; } } @@ -2919,7 +2930,8 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan) static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, struct net_device **lower, - struct vxlan_dev *old) + struct vxlan_dev *old, + struct netlink_ext_ack *extack) { struct vxlan_net *vn = net_generic(src_net, vxlan_net_id); struct vxlan_dev *tmp; @@ -2933,6 +2945,8 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, */ if ((conf->flags & ~VXLAN_F_ALLOWED_GPE) || !(conf->flags & VXLAN_F_COLLECT_METADATA)) { + NL_SET_ERR_MSG(extack, + "VXLAN GPE does not support this combination of attributes"); return -EINVAL; } } @@ -2947,15 +2961,23 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, conf->saddr.sa.sa_family = conf->remote_ip.sa.sa_family; } - if (conf->saddr.sa.sa_family != conf->remote_ip.sa.sa_family) + if (conf->saddr.sa.sa_family != conf->remote_ip.sa.sa_family) { + NL_SET_ERR_MSG(extack, + "Local and remote address must be from the same family"); return -EINVAL; + } - if (vxlan_addr_multicast(&conf->saddr)) + if (vxlan_addr_multicast(&conf->saddr)) { + NL_SET_ERR_MSG(extack, "Local address cannot be multicast"); return -EINVAL; + } if (conf->saddr.sa.sa_family == AF_INET6) { - if (!IS_ENABLED(CONFIG_IPV6)) + if (!IS_ENABLED(CONFIG_IPV6)) { + NL_SET_ERR_MSG(extack, + "IPv6 support not enabled in the kernel"); return -EPFNOSUPPORT; + } use_ipv6 = true; conf->flags |= VXLAN_F_IPV6; @@ -2967,46 +2989,68 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, if (local_type & IPV6_ADDR_LINKLOCAL) { if (!(remote_type & IPV6_ADDR_LINKLOCAL) && - (remote_type != IPV6_ADDR_ANY)) + (remote_type != IPV6_ADDR_ANY)) { + NL_SET_ERR_MSG(extack, + "Invalid combination of local and remote address scopes"); return -EINVAL; + } conf->flags |= VXLAN_F_IPV6_LINKLOCAL; } else { if (remote_type == - (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL)) + (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL)) { + NL_SET_ERR_MSG(extack, + "Invalid combination of local and remote address scopes"); return -EINVAL; + } conf->flags &= ~VXLAN_F_IPV6_LINKLOCAL; } } } - if (conf->label && !use_ipv6) + if (conf->label && !use_ipv6) { + NL_SET_ERR_MSG(extack, + "Label attribute only applies to IPv6 VXLAN devices"); return -EINVAL; + } if (conf->remote_ifindex) { struct net_device *lowerdev; lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex); - if (!lowerdev) + if (!lowerdev) { + NL_SET_ERR_MSG(extack, + "Invalid local interface, device not found"); return -ENODEV; + } #if IS_ENABLED(CONFIG_IPV6) if (use_ipv6) { struct inet6_dev *idev = __in6_dev_get(lowerdev); - if (idev && idev->cnf.disable_ipv6) + if (idev && idev->cnf.disable_ipv6) { + NL_SET_ERR_MSG(extack, + "IPv6 support disabled by administrator"); return -EPERM; + } } #endif *lower = lowerdev; } else { - if (vxlan_addr_multicast(&conf->remote_ip)) + if (vxlan_addr_multicast(&conf->remote_ip)) { + NL_SET_ERR_MSG(extack, + "Local interface required for multicast remote destination"); + return -EINVAL; + } #if IS_ENABLED(CONFIG_IPV6) - if (conf->flags & VXLAN_F_IPV6_LINKLOCAL) + if (conf->flags & VXLAN_F_IPV6_LINKLOCAL) { + NL_SET_ERR_MSG(extack, + "Local interface required for link-local local/remote addresses"); return -EINVAL; + } #endif *lower = NULL; @@ -3038,6 +3082,8 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, tmp->cfg.remote_ifindex != conf->remote_ifindex) continue; + NL_SET_ERR_MSG(extack, + "A VXLAN device with the specified VNI already exists"); return -EEXIST; } @@ -3097,14 +3143,14 @@ static void vxlan_config_apply(struct net_device *dev, } static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, - struct vxlan_config *conf, - bool changelink) + struct vxlan_config *conf, bool changelink, + struct netlink_ext_ack *extack) { struct vxlan_dev *vxlan = netdev_priv(dev); struct net_device *lowerdev; int ret; - ret = vxlan_config_validate(src_net, conf, &lowerdev, vxlan); + ret = vxlan_config_validate(src_net, conf, &lowerdev, vxlan, extack); if (ret) return ret; @@ -3114,13 +3160,14 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, } static int __vxlan_dev_create(struct net *net, struct net_device *dev, - struct vxlan_config *conf) + struct vxlan_config *conf, + struct netlink_ext_ack *extack) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_dev *vxlan = netdev_priv(dev); int err; - err = vxlan_dev_configure(net, dev, conf, false); + err = vxlan_dev_configure(net, dev, conf, false, extack); if (err) return err; @@ -3366,7 +3413,7 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev, if (err) return err; - return __vxlan_dev_create(src_net, dev, &conf); + return __vxlan_dev_create(src_net, dev, &conf, extack); } static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], @@ -3386,7 +3433,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], memcpy(&old_dst, dst, sizeof(struct vxlan_rdst)); - err = vxlan_dev_configure(vxlan->net, dev, &conf, true); + err = vxlan_dev_configure(vxlan->net, dev, &conf, true, extack); if (err) return err; @@ -3592,7 +3639,7 @@ struct net_device *vxlan_dev_create(struct net *net, const char *name, if (IS_ERR(dev)) return dev; - err = __vxlan_dev_create(net, dev, conf); + err = __vxlan_dev_create(net, dev, conf, NULL); if (err < 0) { free_netdev(dev); return ERR_PTR(err); -- cgit v1.2.3 From 4f04256c983a4f115417d2009b44dcb7d70a6375 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 11 Aug 2017 17:11:14 -0700 Subject: net: vrf: Drop local rtable and rt6_info The VRF cached rtable and rt6_info for local traffic are no longer needed and actually prevent local traffic through enslaved devices. Remove them. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/vrf.c | 122 ++++-------------------------------------------------- 1 file changed, 8 insertions(+), 114 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index abd2010c48ae..7e19051f3230 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -47,9 +47,7 @@ static unsigned int vrf_net_id; struct net_vrf { struct rtable __rcu *rth; - struct rtable __rcu *rth_local; struct rt6_info __rcu *rt6; - struct rt6_info __rcu *rt6_local; u32 tb_id; }; @@ -194,42 +192,10 @@ static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb, /* if dst.dev is loopback or the VRF device again this is locally * originated traffic destined to a local address. Short circuit - * to Rx path using our local dst + * to Rx path */ - if (dst->dev == net->loopback_dev || dst->dev == dev) { - struct net_vrf *vrf = netdev_priv(dev); - struct rt6_info *rt6_local; - - /* release looked up dst and use cached local dst */ - dst_release(dst); - - rcu_read_lock(); - - rt6_local = rcu_dereference(vrf->rt6_local); - if (unlikely(!rt6_local)) { - rcu_read_unlock(); - goto err; - } - - /* Ordering issue: cached local dst is created on newlink - * before the IPv6 initialization. Using the local dst - * requires rt6i_idev to be set so make sure it is. - */ - if (unlikely(!rt6_local->rt6i_idev)) { - rt6_local->rt6i_idev = in6_dev_get(dev); - if (!rt6_local->rt6i_idev) { - rcu_read_unlock(); - goto err; - } - } - - dst = &rt6_local->dst; - dst_hold(dst); - - rcu_read_unlock(); - - return vrf_local_xmit(skb, dev, &rt6_local->dst); - } + if (dst->dev == dev) + return vrf_local_xmit(skb, dev, dst); skb_dst_set(skb, dst); @@ -296,30 +262,10 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, /* if dst.dev is loopback or the VRF device again this is locally * originated traffic destined to a local address. Short circuit - * to Rx path using our local dst + * to Rx path */ - if (rt->dst.dev == net->loopback_dev || rt->dst.dev == vrf_dev) { - struct net_vrf *vrf = netdev_priv(vrf_dev); - struct rtable *rth_local; - struct dst_entry *dst = NULL; - - ip_rt_put(rt); - - rcu_read_lock(); - - rth_local = rcu_dereference(vrf->rth_local); - if (likely(rth_local)) { - dst = &rth_local->dst; - dst_hold(dst); - } - - rcu_read_unlock(); - - if (unlikely(!dst)) - goto err; - - return vrf_local_xmit(skb, vrf_dev, dst); - } + if (rt->dst.dev == vrf_dev) + return vrf_local_xmit(skb, vrf_dev, &rt->dst); skb_dst_set(skb, &rt->dst); @@ -528,12 +474,10 @@ static struct sk_buff *vrf_ip6_out(struct net_device *vrf_dev, static void vrf_rt6_release(struct net_device *dev, struct net_vrf *vrf) { struct rt6_info *rt6 = rtnl_dereference(vrf->rt6); - struct rt6_info *rt6_local = rtnl_dereference(vrf->rt6_local); struct net *net = dev_net(dev); struct dst_entry *dst; RCU_INIT_POINTER(vrf->rt6, NULL); - RCU_INIT_POINTER(vrf->rt6_local, NULL); synchronize_rcu(); /* move dev in dst's to loopback so this VRF device can be deleted @@ -546,19 +490,6 @@ static void vrf_rt6_release(struct net_device *dev, struct net_vrf *vrf) dev_hold(dst->dev); dst_release(dst); } - - if (rt6_local) { - if (rt6_local->rt6i_idev) { - in6_dev_put(rt6_local->rt6i_idev); - rt6_local->rt6i_idev = NULL; - } - - dst = &rt6_local->dst; - dev_put(dst->dev); - dst->dev = net->loopback_dev; - dev_hold(dst->dev); - dst_release(dst); - } } static int vrf_rt6_create(struct net_device *dev) @@ -567,7 +498,7 @@ static int vrf_rt6_create(struct net_device *dev) struct net_vrf *vrf = netdev_priv(dev); struct net *net = dev_net(dev); struct fib6_table *rt6i_table; - struct rt6_info *rt6, *rt6_local; + struct rt6_info *rt6; int rc = -ENOMEM; /* IPv6 can be CONFIG enabled and then disabled runtime */ @@ -586,22 +517,7 @@ static int vrf_rt6_create(struct net_device *dev) rt6->rt6i_table = rt6i_table; rt6->dst.output = vrf_output6; - /* create a dst for local routing - packets sent locally - * to local address via the VRF device as a loopback - */ - rt6_local = ip6_dst_alloc(net, dev, flags); - if (!rt6_local) { - dst_release(&rt6->dst); - goto out; - } - - rt6_local->rt6i_idev = in6_dev_get(dev); - rt6_local->rt6i_flags = RTF_UP | RTF_NONEXTHOP | RTF_LOCAL; - rt6_local->rt6i_table = rt6i_table; - rt6_local->dst.input = ip6_input; - rcu_assign_pointer(vrf->rt6, rt6); - rcu_assign_pointer(vrf->rt6_local, rt6_local); rc = 0; out: @@ -788,12 +704,10 @@ static struct sk_buff *vrf_l3_out(struct net_device *vrf_dev, static void vrf_rtable_release(struct net_device *dev, struct net_vrf *vrf) { struct rtable *rth = rtnl_dereference(vrf->rth); - struct rtable *rth_local = rtnl_dereference(vrf->rth_local); struct net *net = dev_net(dev); struct dst_entry *dst; RCU_INIT_POINTER(vrf->rth, NULL); - RCU_INIT_POINTER(vrf->rth_local, NULL); synchronize_rcu(); /* move dev in dst's to loopback so this VRF device can be deleted @@ -806,20 +720,12 @@ static void vrf_rtable_release(struct net_device *dev, struct net_vrf *vrf) dev_hold(dst->dev); dst_release(dst); } - - if (rth_local) { - dst = &rth_local->dst; - dev_put(dst->dev); - dst->dev = net->loopback_dev; - dev_hold(dst->dev); - dst_release(dst); - } } static int vrf_rtable_create(struct net_device *dev) { struct net_vrf *vrf = netdev_priv(dev); - struct rtable *rth, *rth_local; + struct rtable *rth; if (!fib_new_table(dev_net(dev), vrf->tb_id)) return -ENOMEM; @@ -829,22 +735,10 @@ static int vrf_rtable_create(struct net_device *dev) if (!rth) return -ENOMEM; - /* create a dst for local ingress routing - packets sent locally - * to local address via the VRF device as a loopback - */ - rth_local = rt_dst_alloc(dev, RTCF_LOCAL, RTN_LOCAL, 1, 1, 0); - if (!rth_local) { - dst_release(&rth->dst); - return -ENOMEM; - } - rth->dst.output = vrf_output; rth->rt_table_id = vrf->tb_id; - rth_local->rt_table_id = vrf->tb_id; - rcu_assign_pointer(vrf->rth, rth); - rcu_assign_pointer(vrf->rth_local, rth_local); return 0; } -- cgit v1.2.3 From 7acd43296889a71ecf9c20498465f672e667a9ad Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 12 Aug 2017 22:45:53 +0100 Subject: virtio-net: make array guest_offloads static The array guest_offloads is local to the source and does not need to be in global scope, so make it static. Also tweak formatting. Cleans up sparse warnings: symbol 'guest_offloads' was not declared. Should it be static? Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index e90de2186ffc..a3f3c66b4530 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -57,10 +57,12 @@ DECLARE_EWMA(pkt_len, 0, 64) #define VIRTNET_DRIVER_VERSION "1.0.0" -const unsigned long guest_offloads[] = { VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, - VIRTIO_NET_F_GUEST_ECN, - VIRTIO_NET_F_GUEST_UFO }; +static const unsigned long guest_offloads[] = { + VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_UFO +}; struct virtnet_stats { struct u64_stats_sync tx_syncp; -- cgit v1.2.3 From d17eb73bb713dc8dd389aa87606b90edb0d258b2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 12 Aug 2017 22:52:31 +0100 Subject: tap: make struct tap_fops static The structure tap_fops is local to the source and does not need to be in global scope, so make it static. Cleans up sparse warning: symbol 'tap_fops' was not declared. Should it be static? Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/tap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/tap.c b/drivers/net/tap.c index ca267fd28ab8..0d039411e64c 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1127,7 +1127,7 @@ static long tap_compat_ioctl(struct file *file, unsigned int cmd, } #endif -const struct file_operations tap_fops = { +static const struct file_operations tap_fops = { .owner = THIS_MODULE, .open = tap_open, .release = tap_release, -- cgit v1.2.3 From 7496bece4dbbdc5b849cdcad8c6d99f9a0185c93 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sun, 13 Aug 2017 16:41:45 +0530 Subject: can: constify platform_device_id platform_device_id are not supposed to change at runtime. All functions working with platform_device_id provided by work with const platform_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/can/c_can/c_can_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index e36d10520e24..46a746ee80bb 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -220,7 +220,7 @@ static const struct c_can_driver_data am3352_dcan_drvdata = { .raminit_bits = am3352_raminit_bits, }; -static struct platform_device_id c_can_id_table[] = { +static const struct platform_device_id c_can_id_table[] = { { .name = KBUILD_MODNAME, .driver_data = (kernel_ulong_t)&c_can_drvdata, -- cgit v1.2.3 From bef0fed4d012af9651716aec85094a83df7a87bc Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sun, 13 Aug 2017 16:42:08 +0530 Subject: net: dpaa_eth: constify platform_device_id platform_device_id are not supposed to change at runtime. All functions working with platform_device_id provided by work with const platform_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 733d54caabb6..c7fa285378b5 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -2830,7 +2830,7 @@ static int dpaa_remove(struct platform_device *pdev) return err; } -static struct platform_device_id dpaa_devtype[] = { +static const struct platform_device_id dpaa_devtype[] = { { .name = "dpaa-ethernet", .driver_data = 0, -- cgit v1.2.3 From ef00df854b979eb6f1e4c203f34327887f143025 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sun, 13 Aug 2017 16:42:42 +0530 Subject: net: sh_eth: constify platform_device_id platform_device_id are not supposed to change at runtime. All functions working with platform_device_id provided by work with const platform_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index d2dc0a8ef305..d2e88a30f57b 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -3402,7 +3402,7 @@ static const struct dev_pm_ops sh_eth_dev_pm_ops = { #define SH_ETH_PM_OPS NULL #endif -static struct platform_device_id sh_eth_id_table[] = { +static const struct platform_device_id sh_eth_id_table[] = { { "sh7619-ether", (kernel_ulong_t)&sh7619_data }, { "sh771x-ether", (kernel_ulong_t)&sh771x_data }, { "sh7724-ether", (kernel_ulong_t)&sh7724_data }, -- cgit v1.2.3 From f5b589488ea5ed3bb6168b1a4e7f7b95841d8513 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sun, 13 Aug 2017 16:43:18 +0530 Subject: net: ti: cpsw:: constify platform_device_id platform_device_id are not supposed to change at runtime. All functions working with platform_device_id provided by work with const platform_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c8776dbf1a55..db8a4bcfc6c7 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2827,7 +2827,7 @@ static int cpsw_probe_dual_emac(struct cpsw_priv *priv) #define CPSW_QUIRK_IRQ BIT(0) -static struct platform_device_id cpsw_devtype[] = { +static const struct platform_device_id cpsw_devtype[] = { { /* keep it for existing comaptibles */ .name = "cpsw", -- cgit v1.2.3 From da1542b01b38ba29e0d28b1ef980071e7d38d6d4 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Fri, 11 Aug 2017 18:43:14 -0700 Subject: liquidio: update debug console logging mechanism - remove logging dependency upon global func octeon_console_debug_enabled() - abstract debug console logging using console structure (via function ptr) to allow for more flexible logging Signed-off-by: Rick Farrington Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 44 +++++++++++++++++- .../net/ethernet/cavium/liquidio/octeon_console.c | 54 ++++++++++++++-------- .../net/ethernet/cavium/liquidio/octeon_device.h | 17 +++++-- 3 files changed, 90 insertions(+), 25 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 8bf6dfcf5881..8ec0b6d978d2 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -73,7 +73,7 @@ MODULE_PARM_DESC(console_bitmask, * @param console console to check * @returns 1 = enabled. 0 otherwise */ -int octeon_console_debug_enabled(u32 console) +static int octeon_console_debug_enabled(u32 console) { return (console_bitmask >> (console)) & 0x1; } @@ -185,6 +185,9 @@ struct octeon_device_priv { static int liquidio_enable_sriov(struct pci_dev *dev, int num_vfs); #endif +static int octeon_dbg_console_print(struct octeon_device *oct, u32 console_num, + char *prefix, char *suffix); + static int octeon_device_init(struct octeon_device *); static int liquidio_stop(struct net_device *netdev); static void liquidio_remove(struct pci_dev *pdev); @@ -4556,6 +4559,7 @@ static int octeon_device_init(struct octeon_device *octeon_dev) int j, ret; int fw_loaded = 0; char bootcmd[] = "\n"; + char *dbg_enb = NULL; struct octeon_device_priv *oct_priv = (struct octeon_device_priv *)octeon_dev->priv; atomic_set(&octeon_dev->status, OCT_DEV_BEGIN_STATE); @@ -4762,10 +4766,19 @@ static int octeon_device_init(struct octeon_device *octeon_dev) dev_err(&octeon_dev->pci_dev->dev, "Could not access board consoles\n"); return 1; } - ret = octeon_add_console(octeon_dev, 0); + /* If console debug enabled, specify empty string to use default + * enablement ELSE specify NULL string for 'disabled'. + */ + dbg_enb = octeon_console_debug_enabled(0) ? "" : NULL; + ret = octeon_add_console(octeon_dev, 0, dbg_enb); if (ret) { dev_err(&octeon_dev->pci_dev->dev, "Could not access board console\n"); return 1; + } else if (octeon_console_debug_enabled(0)) { + /* If console was added AND we're logging console output + * then set our console print function. + */ + octeon_dev->console[0].print = octeon_dbg_console_print; } atomic_set(&octeon_dev->status, OCT_DEV_CONSOLE_INIT_DONE); @@ -4800,6 +4813,33 @@ static int octeon_device_init(struct octeon_device *octeon_dev) return 0; } +/** + * \brief Debug console print function + * @param octeon_dev octeon device + * @param console_num console number + * @param prefix first portion of line to display + * @param suffix second portion of line to display + * + * The OCTEON debug console outputs entire lines (excluding '\n'). + * Normally, the line will be passed in the 'prefix' parameter. + * However, due to buffering, it is possible for a line to be split into two + * parts, in which case they will be passed as the 'prefix' parameter and + * 'suffix' parameter. + */ +static int octeon_dbg_console_print(struct octeon_device *oct, u32 console_num, + char *prefix, char *suffix) +{ + if (prefix && suffix) + dev_info(&oct->pci_dev->dev, "%u: %s%s\n", console_num, prefix, + suffix); + else if (prefix) + dev_info(&oct->pci_dev->dev, "%u: %s\n", console_num, prefix); + else if (suffix) + dev_info(&oct->pci_dev->dev, "%u: %s\n", console_num, suffix); + + return 0; +} + /** * \brief Exits the module */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index dd0efc9b4286..19e5212f66d1 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -437,20 +437,31 @@ static void output_console_line(struct octeon_device *oct, { char *line; s32 i; + size_t len; line = console_buffer; for (i = 0; i < bytes_read; i++) { /* Output a line at a time, prefixed */ if (console_buffer[i] == '\n') { console_buffer[i] = '\0'; - if (console->leftover[0]) { - dev_info(&oct->pci_dev->dev, "%lu: %s%s\n", - console_num, console->leftover, - line); + /* We need to output 'line', prefaced by 'leftover'. + * However, it is possible we're being called to + * output 'leftover' by itself (in the case of nothing + * having been read from the console). + * + * To avoid duplication, check for this condition. + */ + if (console->leftover[0] && + (line != console->leftover)) { + if (console->print) + (*console->print)(oct, (u32)console_num, + console->leftover, + line); console->leftover[0] = '\0'; } else { - dev_info(&oct->pci_dev->dev, "%lu: %s\n", - console_num, line); + if (console->print) + (*console->print)(oct, (u32)console_num, + line, NULL); } line = &console_buffer[i + 1]; } @@ -459,13 +470,16 @@ static void output_console_line(struct octeon_device *oct, /* Save off any leftovers */ if (line != &console_buffer[bytes_read]) { console_buffer[bytes_read] = '\0'; - strcpy(console->leftover, line); + len = strlen(console->leftover); + strncpy(&console->leftover[len], line, + sizeof(console->leftover) - len); } } static void check_console(struct work_struct *work) { s32 bytes_read, tries, total_read; + size_t len; struct octeon_console *console; struct cavium_wk *wk = (struct cavium_wk *)work; struct octeon_device *oct = (struct octeon_device *)wk->ctxptr; @@ -487,7 +501,7 @@ static void check_console(struct work_struct *work) total_read += bytes_read; if (console->waiting) octeon_console_handle_result(oct, console_num); - if (octeon_console_debug_enabled(console_num)) { + if (console->print) { output_console_line(oct, console, console_num, console_buffer, bytes_read); } @@ -502,10 +516,13 @@ static void check_console(struct work_struct *work) /* If nothing is read after polling the console, * output any leftovers if any */ - if (octeon_console_debug_enabled(console_num) && - (total_read == 0) && (console->leftover[0])) { - dev_info(&oct->pci_dev->dev, "%u: %s\n", - console_num, console->leftover); + if (console->print && (total_read == 0) && + (console->leftover[0])) { + /* append '\n' as terminator for 'output_console_line' */ + len = strlen(console->leftover); + console->leftover[len] = '\n'; + output_console_line(oct, console, console_num, + console->leftover, (s32)(len + 1)); console->leftover[0] = '\0'; } @@ -557,7 +574,8 @@ int octeon_init_consoles(struct octeon_device *oct) return ret; } -int octeon_add_console(struct octeon_device *oct, u32 console_num) +int octeon_add_console(struct octeon_device *oct, u32 console_num, + char *dbg_enb) { int ret = 0; u32 delay; @@ -599,11 +617,11 @@ int octeon_add_console(struct octeon_device *oct, u32 console_num) delay = OCTEON_CONSOLE_POLL_INTERVAL_MS; schedule_delayed_work(work, msecs_to_jiffies(delay)); - if (octeon_console_debug_enabled(console_num)) { - ret = octeon_console_send_cmd(oct, - "setenv pci_console_active 1", - 2000); - } + /* an empty string means use default debug console enablement */ + if (dbg_enb && !dbg_enb[0]) + dbg_enb = "setenv pci_console_active 1"; + if (dbg_enb) + ret = octeon_console_send_cmd(oct, dbg_enb, 2000); console->active = 1; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index b014e6ad0e9a..2c554729bfe4 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -194,6 +194,8 @@ struct octeon_reg_list { }; #define OCTEON_CONSOLE_MAX_READ_BYTES 512 +typedef int (*octeon_console_print_fn)(struct octeon_device *oct, + u32 num, char *pre, char *suf); struct octeon_console { u32 active; u32 waiting; @@ -201,6 +203,7 @@ struct octeon_console { u32 buffer_size; u64 input_base_addr; u64 output_base_addr; + octeon_console_print_fn print; char leftover[OCTEON_CONSOLE_MAX_READ_BYTES]; }; @@ -740,16 +743,20 @@ int octeon_wait_for_bootloader(struct octeon_device *oct, */ int octeon_init_consoles(struct octeon_device *oct); -int octeon_console_debug_enabled(u32 console); - /** * Adds access to a console to the device. * - * @param oct which octeon to add to - * @param console_num which console + * @param oct: which octeon to add to + * @param console_num: which console + * @param dbg_enb: ptr to debug enablement string, one of: + * * NULL for no debug output (i.e. disabled) + * * empty string enables debug output (via default method) + * * specific string to enable debug console output + * * @return Zero on success, negative on failure. */ -int octeon_add_console(struct octeon_device *oct, u32 console_num); +int octeon_add_console(struct octeon_device *oct, u32 console_num, + char *dbg_enb); /** write or read from a console */ int octeon_console_write(struct octeon_device *oct, u32 console_num, -- cgit v1.2.3 From b5e7dc47425860172eec6e1116ace7759d13b999 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Sat, 12 Aug 2017 20:38:55 -0500 Subject: liquidio: fix duplicated code for different branches Refactor code in order to avoid identical code for different branches. This issue was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Acked-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index b78e296c4cba..4f65c08461f6 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -578,23 +578,18 @@ static int lio_set_phys_id(struct net_device *netdev, break; case ETHTOOL_ID_ON: - if (oct->chip_id == OCTEON_CN66XX) { + if (oct->chip_id == OCTEON_CN66XX) octnet_gpio_access(netdev, VITESSE_PHY_GPIO_CFG, VITESSE_PHY_GPIO_HIGH); - - } else if (oct->chip_id == OCTEON_CN68XX) { - return -EINVAL; - } else { + else return -EINVAL; - } + break; case ETHTOOL_ID_OFF: if (oct->chip_id == OCTEON_CN66XX) octnet_gpio_access(netdev, VITESSE_PHY_GPIO_CFG, VITESSE_PHY_GPIO_LOW); - else if (oct->chip_id == OCTEON_CN68XX) - return -EINVAL; else return -EINVAL; -- cgit v1.2.3 From 4db93fb8aca3e9e56987c0e873166bdf4b6576ce Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Sat, 12 Aug 2017 20:58:40 -0500 Subject: qlge: fix duplicated code for different branches Refactor code in order to avoid identical code for different branches. This issue was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlge/qlge_dbg.c | 47 +++++++++-------------------- 1 file changed, 14 insertions(+), 33 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c index 28ea0af89aef..458d55ba423f 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c @@ -144,42 +144,23 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, xaui_direct_valid = xaui_indirect_valid = 1; /* The XAUI needs to be read out per port */ - if (qdev->func & 1) { - /* We are NIC 2 */ - status = ql_read_other_func_serdes_reg(qdev, - XG_SERDES_XAUI_HSS_PCS_START, &temp); - if (status) - temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; - if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == - XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_indirect_valid = 0; + status = ql_read_other_func_serdes_reg(qdev, + XG_SERDES_XAUI_HSS_PCS_START, &temp); + if (status) + temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; - status = ql_read_serdes_reg(qdev, - XG_SERDES_XAUI_HSS_PCS_START, &temp); - if (status) - temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; + if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == + XG_SERDES_ADDR_XAUI_PWR_DOWN) + xaui_indirect_valid = 0; - if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == - XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_direct_valid = 0; - } else { - /* We are NIC 1 */ - status = ql_read_other_func_serdes_reg(qdev, - XG_SERDES_XAUI_HSS_PCS_START, &temp); - if (status) - temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; - if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == - XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_indirect_valid = 0; + status = ql_read_serdes_reg(qdev, XG_SERDES_XAUI_HSS_PCS_START, &temp); - status = ql_read_serdes_reg(qdev, - XG_SERDES_XAUI_HSS_PCS_START, &temp); - if (status) - temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; - if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == - XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_direct_valid = 0; - } + if (status) + temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; + + if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == + XG_SERDES_ADDR_XAUI_PWR_DOWN) + xaui_direct_valid = 0; /* * XFI register is shared so only need to read one -- cgit v1.2.3 From cc70267008d5a22bc044fb1670937d52cb024cd3 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 14 Aug 2017 10:54:03 +0200 Subject: mlxsw: spectrum_router: Return void from deletion functions There is no point in returning a value from function whose return value is never checked. Even if the return value was checked, there wouldn't be anything to do about it, as these functions are either called from error or deletion paths. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 93b6da88e79c..220e7e742aa4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -505,15 +505,15 @@ static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); } -static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_lpm_tree *lpm_tree) +static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_lpm_tree *lpm_tree) { char ralta_pl[MLXSW_REG_RALTA_LEN]; mlxsw_reg_ralta_pack(ralta_pl, false, (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto, lpm_tree->id); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); } static int @@ -569,10 +569,10 @@ err_left_struct_set: return ERR_PTR(err); } -static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_lpm_tree *lpm_tree) +static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_lpm_tree *lpm_tree) { - return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree); + mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree); } static struct mlxsw_sp_lpm_tree * @@ -601,12 +601,11 @@ inc_ref_count: return lpm_tree; } -static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_lpm_tree *lpm_tree) +static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_lpm_tree *lpm_tree) { if (--lpm_tree->ref_count == 0) - return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree); - return 0; + mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree); } #define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */ -- cgit v1.2.3 From 0adb214ba2c04489eb13bdbd145c5bf77d78b078 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 14 Aug 2017 10:54:04 +0200 Subject: mlxsw: spectrum_router: Pass argument explicitly Instead of relying on the LPM tree to be assigned to the virtual router before binding the two, lets pass it explicitly. This will later allow us to return upon binding error instead of having to perform a rollback of the assignment. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 220e7e742aa4..3c204d2144fa 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -659,13 +659,13 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp) } static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_fib *fib) + const struct mlxsw_sp_fib *fib, u8 tree_id) { char raltb_pl[MLXSW_REG_RALTB_LEN]; mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id, (enum mlxsw_reg_ralxx_protocol) fib->proto, - fib->lpm_tree->id); + tree_id); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); } @@ -777,7 +777,7 @@ mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib *fib, /* Prevent packet loss by overwriting existing binding */ fib->lpm_tree = new_tree; - err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib); + err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id); if (err) goto err_tree_bind; mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); @@ -2631,7 +2631,7 @@ static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, if (IS_ERR(lpm_tree)) return PTR_ERR(lpm_tree); fib->lpm_tree = lpm_tree; - err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib); + err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, lpm_tree->id); if (err) goto err_tree_bind; } -- cgit v1.2.3 From fc922bb0dd9406dd9897fd47df958789891c380e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 14 Aug 2017 10:54:05 +0200 Subject: mlxsw: spectrum_router: Use one LPM tree for all virtual routers The number of LPM trees available for lookup is much smaller than the number of virtual routers, which are used to implement VRFs. In addition, an LPM tree can only be used by one protocol - either IPv4 or IPv6. Therefore, in order to increase the number of supported virtual routers to the maximum we need to be able to share LPM trees across virtual routers instead of trying to find an optimized tree for each. Do that by allocating one LPM tree for each protocol, but make sure it will only include prefixes that are actually used, so as to not perform unnecessary lookups. Since changing the structure of a bound tree isn't recommended, whenever a new tree it required, it's first created and then bound to each virtual router, replacing the old one. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 259 +++++++++++++-------- 1 file changed, 165 insertions(+), 94 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 3c204d2144fa..3d9be36965f6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -320,19 +320,6 @@ struct mlxsw_sp_prefix_usage { #define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \ for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT) -static bool -mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1, - struct mlxsw_sp_prefix_usage *prefix_usage2) -{ - unsigned char prefix; - - mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) { - if (!test_bit(prefix, prefix_usage2->b)) - return false; - } - return true; -} - static bool mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1, struct mlxsw_sp_prefix_usage *prefix_usage2) @@ -589,16 +576,14 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp, lpm_tree->proto == proto && mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage, prefix_usage)) - goto inc_ref_count; + return lpm_tree; } - lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, - proto); - if (IS_ERR(lpm_tree)) - return lpm_tree; + return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto); +} -inc_ref_count: +static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree) +{ lpm_tree->ref_count++; - return lpm_tree; } static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, @@ -750,46 +735,6 @@ static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr) vr->fib4 = NULL; } -static int -mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib *fib, - struct mlxsw_sp_prefix_usage *req_prefix_usage) -{ - struct mlxsw_sp_lpm_tree *lpm_tree = fib->lpm_tree; - struct mlxsw_sp_lpm_tree *new_tree; - int err; - - if (mlxsw_sp_prefix_usage_eq(req_prefix_usage, &lpm_tree->prefix_usage)) - return 0; - - new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage, - fib->proto); - if (IS_ERR(new_tree)) { - /* We failed to get a tree according to the required - * prefix usage. However, the current tree might be still good - * for us if our requirement is subset of the prefixes used - * in the tree. - */ - if (mlxsw_sp_prefix_usage_subset(req_prefix_usage, - &lpm_tree->prefix_usage)) - return 0; - return PTR_ERR(new_tree); - } - - /* Prevent packet loss by overwriting existing binding */ - fib->lpm_tree = new_tree; - err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id); - if (err) - goto err_tree_bind; - mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); - - return 0; - -err_tree_bind: - fib->lpm_tree = lpm_tree; - mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree); - return err; -} - static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id) { struct mlxsw_sp_vr *vr; @@ -808,6 +753,100 @@ static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr) mlxsw_sp_vr_destroy(vr); } +static bool +mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr, + enum mlxsw_sp_l3proto proto, u8 tree_id) +{ + struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto); + + if (!mlxsw_sp_vr_is_used(vr)) + return false; + if (fib->lpm_tree && fib->lpm_tree->id == tree_id) + return true; + return false; +} + +static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib *fib, + struct mlxsw_sp_lpm_tree *new_tree) +{ + struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree; + int err; + + err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id); + if (err) + return err; + fib->lpm_tree = new_tree; + mlxsw_sp_lpm_tree_hold(new_tree); + mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree); + return 0; +} + +static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib *fib, + struct mlxsw_sp_lpm_tree *new_tree) +{ + struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree; + enum mlxsw_sp_l3proto proto = fib->proto; + u8 old_id, new_id = new_tree->id; + struct mlxsw_sp_vr *vr; + int i, err; + + if (!old_tree) + goto no_replace; + old_id = old_tree->id; + + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { + vr = &mlxsw_sp->router->vrs[i]; + if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id)) + continue; + err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp, + mlxsw_sp_vr_fib(vr, proto), + new_tree); + if (err) + goto err_tree_replace; + } + + return 0; + +err_tree_replace: + for (i--; i >= 0; i--) { + if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id)) + continue; + mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp, + mlxsw_sp_vr_fib(vr, proto), + old_tree); + } + return err; + +no_replace: + err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id); + if (err) + return err; + fib->lpm_tree = new_tree; + mlxsw_sp_lpm_tree_hold(new_tree); + return 0; +} + +static void +mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_l3proto proto, + struct mlxsw_sp_prefix_usage *req_prefix_usage) +{ + int i; + + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { + struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i]; + struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto); + unsigned char prefix; + + if (!mlxsw_sp_vr_is_used(vr)) + continue; + mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage) + mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix); + } +} + static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_vr *vr; @@ -2586,6 +2625,67 @@ mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, struct mlxsw_sp_fib_entry, list) == fib_entry; } +static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib *fib, + struct mlxsw_sp_fib_node *fib_node) +{ + struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } }; + struct mlxsw_sp_lpm_tree *lpm_tree; + int err; + + /* Since the tree is shared between all virtual routers we must + * make sure it contains all the required prefix lengths. This + * can be computed by either adding the new prefix length to the + * existing prefix usage of a bound tree, or by aggregating the + * prefix lengths across all virtual routers and adding the new + * one as well. + */ + if (fib->lpm_tree) + mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, + &fib->lpm_tree->prefix_usage); + else + mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage); + mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len); + + lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, + fib->proto); + if (IS_ERR(lpm_tree)) + return PTR_ERR(lpm_tree); + + if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id) + return 0; + + err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree); + if (err) + return err; + + return 0; +} + +static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib *fib) +{ + struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } }; + struct mlxsw_sp_lpm_tree *lpm_tree; + + /* Aggregate prefix lengths across all virtual routers to make + * sure we only have used prefix lengths in the LPM tree. + */ + mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage); + lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, + fib->proto); + if (IS_ERR(lpm_tree)) + goto err_tree_get; + mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree); + +err_tree_get: + if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) + return; + mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib); + mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree); + fib->lpm_tree = NULL; +} + static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node) { unsigned char prefix_len = fib_node->key.prefix_len; @@ -2608,8 +2708,6 @@ static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node, struct mlxsw_sp_fib *fib) { - struct mlxsw_sp_prefix_usage req_prefix_usage; - struct mlxsw_sp_lpm_tree *lpm_tree; int err; err = mlxsw_sp_fib_node_insert(fib, fib_node); @@ -2617,33 +2715,15 @@ static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, return err; fib_node->fib = fib; - mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &fib->prefix_usage); - mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len); - - if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) { - err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, - &req_prefix_usage); - if (err) - goto err_tree_check; - } else { - lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, - fib->proto); - if (IS_ERR(lpm_tree)) - return PTR_ERR(lpm_tree); - fib->lpm_tree = lpm_tree; - err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, lpm_tree->id); - if (err) - goto err_tree_bind; - } + err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node); + if (err) + goto err_fib_lpm_tree_link; mlxsw_sp_fib_node_prefix_inc(fib_node); return 0; -err_tree_bind: - fib->lpm_tree = NULL; - mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); -err_tree_check: +err_fib_lpm_tree_link: fib_node->fib = NULL; mlxsw_sp_fib_node_remove(fib, fib_node); return err; @@ -2652,19 +2732,10 @@ err_tree_check: static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { - struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree; struct mlxsw_sp_fib *fib = fib_node->fib; mlxsw_sp_fib_node_prefix_dec(fib_node); - - if (mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) { - mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib); - fib->lpm_tree = NULL; - mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); - } else { - mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, &fib->prefix_usage); - } - + mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib); fib_node->fib = NULL; mlxsw_sp_fib_node_remove(fib, fib_node); } -- cgit v1.2.3 From 54161ed4eedeef9442a0a7d3b1a15ad44f8f9831 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 14 Aug 2017 15:43:00 +0200 Subject: net: phy: Use tab for indentation in Kconfig Using tabs instead of space for indentation. Signed-off-by: Michal Simek Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 8c919203291a..5afe6fdcc968 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -5,7 +5,7 @@ menuconfig MDIO_DEVICE tristate "MDIO bus device drivers" help - MDIO devices and driver infrastructure code. + MDIO devices and driver infrastructure code. config MDIO_BUS tristate @@ -117,11 +117,11 @@ config MDIO_I2C This is library mode. config MDIO_MOXART - tristate "MOXA ART MDIO interface support" - depends on ARCH_MOXART - help - This driver supports the MDIO interface found in the network - interface units of the MOXA ART SoC + tristate "MOXA ART MDIO interface support" + depends on ARCH_MOXART + help + This driver supports the MDIO interface found in the network + interface units of the MOXA ART SoC config MDIO_OCTEON tristate "Octeon and some ThunderX SOCs MDIO buses" @@ -192,7 +192,7 @@ config LED_TRIGGER_PHY state change will trigger the events, for consumption by an LED class driver. There are triggers for each link speed currently supported by the phy, and are of the form: - :: + :: Where speed is in the form: Mbps or Gbps @@ -211,9 +211,9 @@ config AMD_PHY Currently supports the am79c874 config AQUANTIA_PHY - tristate "Aquantia PHYs" - ---help--- - Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 + tristate "Aquantia PHYs" + ---help--- + Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 config AT803X_PHY tristate "AT803X PHYs" @@ -382,21 +382,21 @@ config STE10XP This is the driver for the STe100p and STe101p PHYs. config TERANETICS_PHY - tristate "Teranetics PHYs" - ---help--- - Currently supports the Teranetics TN2020 + tristate "Teranetics PHYs" + ---help--- + Currently supports the Teranetics TN2020 config VITESSE_PHY - tristate "Vitesse PHYs" - ---help--- - Currently supports the vsc8244 + tristate "Vitesse PHYs" + ---help--- + Currently supports the vsc8244 config XILINX_GMII2RGMII - tristate "Xilinx GMII2RGMII converter driver" - ---help--- - This driver support xilinx GMII to RGMII IP core it provides - the Reduced Gigabit Media Independent Interface(RGMII) between - Ethernet physical media devices and the Gigabit Ethernet controller. + tristate "Xilinx GMII2RGMII converter driver" + ---help--- + This driver support xilinx GMII to RGMII IP core it provides + the Reduced Gigabit Media Independent Interface(RGMII) between + Ethernet physical media devices and the Gigabit Ethernet controller. endif # PHYLIB -- cgit v1.2.3 From 8122e08b1d876b9af605db5ad351961c5ea276ed Mon Sep 17 00:00:00 2001 From: Ohad Oz Date: Mon, 14 Aug 2017 15:38:21 +0000 Subject: Allow Mellanox switch devices to be configured if only I2C bus is set Mellanox switches (mlxsw) supports I2C systems without PCI, in order to give the ability to the users to use such functionality, there is need to update Kconfig. Signed-off-by: Ohad Oz Acked-by: Leon Romanovsky Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/Kconfig b/drivers/net/ethernet/mellanox/Kconfig index 84a200764111..09497419eab2 100644 --- a/drivers/net/ethernet/mellanox/Kconfig +++ b/drivers/net/ethernet/mellanox/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_MELLANOX bool "Mellanox devices" default y - depends on PCI + depends on PCI || I2C ---help--- If you have a network (Ethernet) card belonging to this class, say Y. -- cgit v1.2.3 From a656d34a6e5acc4779c4cb8bad19f4ab040def45 Mon Sep 17 00:00:00 2001 From: Ohad Oz Date: Mon, 14 Aug 2017 15:38:22 +0000 Subject: Change Kconfig description This patch apply Mellanox network vendor which includes: - Mellanox card devices: ConnectX-4, ConnectX-5 and Connect-IB cards. - Mellanox switch device: SwitchX-2 Switch-IB, Spectrum. Therefore rephrasing help. Signed-off-by: Ohad Oz Acked-by: Leon Romanovsky Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/Kconfig b/drivers/net/ethernet/mellanox/Kconfig index 09497419eab2..872548cd9431 100644 --- a/drivers/net/ethernet/mellanox/Kconfig +++ b/drivers/net/ethernet/mellanox/Kconfig @@ -7,7 +7,8 @@ config NET_VENDOR_MELLANOX default y depends on PCI || I2C ---help--- - If you have a network (Ethernet) card belonging to this class, say Y. + If you have a network (Ethernet or RDMA) device belonging to this + class, say Y. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all -- cgit v1.2.3 From e65a8ccb0d89d09b5c096b1ee706eae42c4a6671 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Mon, 14 Aug 2017 12:01:31 -0700 Subject: liquidio: moved wait_for_pending_requests to octeon_network.h Moving common function wait_for_pending_requests to octeon_network.h Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_vf_device.h | 2 -- drivers/net/ethernet/cavium/liquidio/lio_main.c | 26 -------------------- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 28 +--------------------- .../net/ethernet/cavium/liquidio/octeon_device.h | 2 ++ .../net/ethernet/cavium/liquidio/octeon_network.h | 26 ++++++++++++++++++++ 5 files changed, 29 insertions(+), 55 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h index 3f98c7334957..2d06097d3f61 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h @@ -36,8 +36,6 @@ struct octeon_cn23xx_vf { #define CN23XX_MAILBOX_MSGPARAM_SIZE 6 -#define MAX_VF_IP_OP_PENDING_PKT_COUNT 100 - void cn23xx_vf_ask_pf_to_do_flr(struct octeon_device *oct); int cn23xx_octeon_pfvf_handshake(struct octeon_device *oct); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 8ec0b6d978d2..73b3547bf142 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -275,32 +275,6 @@ static void force_io_queues_off(struct octeon_device *oct) } } -/** - * \brief wait for all pending requests to complete - * @param oct Pointer to Octeon device - * - * Called during shutdown sequence - */ -static int wait_for_pending_requests(struct octeon_device *oct) -{ - int i, pcount = 0; - - for (i = 0; i < 100; i++) { - pcount = - atomic_read(&oct->response_list - [OCTEON_ORDERED_SC_LIST].pending_req_count); - if (pcount) - schedule_timeout_uninterruptible(HZ / 10); - else - break; - } - - if (pcount) - return 1; - - return 0; -} - /** * \brief Cause device to go quiet so it can be safely removed/reset/etc * @param oct Pointer to Octeon device diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index c6f52f235647..17623ed1f0fa 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -123,7 +123,7 @@ static int lio_wait_for_oq_pkts(struct octeon_device *oct) { struct octeon_device_priv *oct_priv = (struct octeon_device_priv *)oct->priv; - int retry = MAX_VF_IP_OP_PENDING_PKT_COUNT; + int retry = MAX_IO_PENDING_PKT_COUNT; int pkt_cnt = 0, pending_pkts; int i; @@ -147,32 +147,6 @@ static int lio_wait_for_oq_pkts(struct octeon_device *oct) return pkt_cnt; } -/** - * \brief wait for all pending requests to complete - * @param oct Pointer to Octeon device - * - * Called during shutdown sequence - */ -static int wait_for_pending_requests(struct octeon_device *oct) -{ - int i, pcount = 0; - - for (i = 0; i < MAX_VF_IP_OP_PENDING_PKT_COUNT; i++) { - pcount = atomic_read( - &oct->response_list[OCTEON_ORDERED_SC_LIST] - .pending_req_count); - if (pcount) - schedule_timeout_uninterruptible(HZ / 10); - else - break; - } - - if (pcount) - return 1; - - return 0; -} - /** * \brief Cause device to go quiet so it can be safely removed/reset/etc * @param oct Pointer to Octeon device diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 2c554729bfe4..894af199ddef 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -571,6 +571,8 @@ struct octeon_device { #define CHIP_CONF(oct, TYPE) \ (((struct octeon_ ## TYPE *)((oct)->chip))->conf) +#define MAX_IO_PENDING_PKT_COUNT 100 + /*------------------ Function Prototypes ----------------------*/ /** Initialize device list memory */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index ec8504b2942d..043f6e653731 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -448,4 +448,30 @@ static inline void ifstate_reset(struct lio *lio, int state_flag) atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) & ~(state_flag))); } +/** + * \brief wait for all pending requests to complete + * @param oct Pointer to Octeon device + * + * Called during shutdown sequence + */ +static inline int wait_for_pending_requests(struct octeon_device *oct) +{ + int i, pcount = 0; + + for (i = 0; i < MAX_IO_PENDING_PKT_COUNT; i++) { + pcount = atomic_read( + &oct->response_list[OCTEON_ORDERED_SC_LIST] + .pending_req_count); + if (pcount) + schedule_timeout_uninterruptible(HZ / 10); + else + break; + } + + if (pcount) + return 1; + + return 0; +} + #endif -- cgit v1.2.3 From 25d43f182d42b40ba750e2e0516b693e62f8f723 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Mon, 14 Aug 2017 12:01:37 -0700 Subject: liquidio: moved update_txq_status to lio_core.c Moving common update_txq_status to lio_core.c Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 33 ++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 35 +--------------------- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 26 +--------------- .../net/ethernet/cavium/liquidio/octeon_network.h | 1 + 4 files changed, 36 insertions(+), 59 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index adde7745d069..b55ab75a7d9c 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -364,3 +364,36 @@ void cleanup_rx_oom_poll_fn(struct net_device *netdev) destroy_workqueue(lio->rxq_status_wq.wq); } } + +/* Runs in interrupt context. */ +void lio_update_txq_status(struct octeon_device *oct, int iq_num) +{ + struct octeon_instr_queue *iq = oct->instr_queue[iq_num]; + struct net_device *netdev; + struct lio *lio; + + netdev = oct->props[iq->ifidx].netdev; + + /* This is needed because the first IQ does not have + * a netdev associated with it. + */ + if (!netdev) + return; + + lio = GET_LIO(netdev); + if (netif_is_multiqueue(netdev)) { + if (__netif_subqueue_stopped(netdev, iq->q_index) && + lio->linfo.link.s.link_up && + (!octnet_iq_is_full(oct, iq_num))) { + netif_wake_subqueue(netdev, iq->q_index); + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, + tx_restart, 1); + } + } else if (netif_queue_stopped(netdev) && + lio->linfo.link.s.link_up && + (!octnet_iq_is_full(oct, lio->txq))) { + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, lio->txq, + tx_restart, 1); + netif_wake_queue(netdev); + } +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 73b3547bf142..01c6985cff6d 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -906,39 +906,6 @@ static inline void update_link_status(struct net_device *netdev, } } -/* Runs in interrupt context. */ -static void update_txq_status(struct octeon_device *oct, int iq_num) -{ - struct net_device *netdev; - struct lio *lio; - struct octeon_instr_queue *iq = oct->instr_queue[iq_num]; - - netdev = oct->props[iq->ifidx].netdev; - - /* This is needed because the first IQ does not have - * a netdev associated with it. - */ - if (!netdev) - return; - - lio = GET_LIO(netdev); - if (netif_is_multiqueue(netdev)) { - if (__netif_subqueue_stopped(netdev, iq->q_index) && - lio->linfo.link.s.link_up && - (!octnet_iq_is_full(oct, iq_num))) { - INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, - tx_restart, 1); - netif_wake_subqueue(netdev, iq->q_index); - } - } else if (netif_queue_stopped(netdev) && - lio->linfo.link.s.link_up && - (!octnet_iq_is_full(oct, lio->txq))) { - INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, - lio->txq, tx_restart, 1); - netif_wake_queue(netdev); - } -} - static int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) { @@ -2518,7 +2485,7 @@ static int liquidio_napi_poll(struct napi_struct *napi, int budget) /* Update iq read-index rather than waiting for next interrupt. * Return back if tx_done is false. */ - update_txq_status(oct, iq_no); + lio_update_txq_status(oct, iq_no); } else { dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", __func__, iq_no); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 17623ed1f0fa..dd0265a52521 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -647,30 +647,6 @@ static void update_link_status(struct net_device *netdev, } } -static void update_txq_status(struct octeon_device *oct, int iq_num) -{ - struct octeon_instr_queue *iq = oct->instr_queue[iq_num]; - struct net_device *netdev; - struct lio *lio; - - netdev = oct->props[iq->ifidx].netdev; - lio = GET_LIO(netdev); - if (netif_is_multiqueue(netdev)) { - if (__netif_subqueue_stopped(netdev, iq->q_index) && - lio->linfo.link.s.link_up && - (!octnet_iq_is_full(oct, iq_num))) { - netif_wake_subqueue(netdev, iq->q_index); - INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, - tx_restart, 1); - } - } else if (netif_queue_stopped(netdev) && lio->linfo.link.s.link_up && - (!octnet_iq_is_full(oct, lio->txq))) { - INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, - lio->txq, tx_restart, 1); - netif_wake_queue(netdev); - } -} - static int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) { @@ -1608,7 +1584,7 @@ static int liquidio_napi_poll(struct napi_struct *napi, int budget) /* Update iq read-index rather than waiting for next interrupt. * Return back if tx_done is false. */ - update_txq_status(oct, iq_no); + lio_update_txq_status(oct, iq_no); } else { dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", __func__, iq_no); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index 043f6e653731..0e44ee1d4cb2 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -474,4 +474,5 @@ static inline int wait_for_pending_requests(struct octeon_device *oct) return 0; } +void lio_update_txq_status(struct octeon_device *oct, int iq_num); #endif -- cgit v1.2.3 From 69f9c60e832575fe73cea0387c2d6a079f0fc96c Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Mon, 14 Aug 2017 12:01:41 -0700 Subject: liquidio: moved octeon_setup_droq to lio_core.c Moving common octeon_setup_droq to lio_core.c Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 35 ++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 37 ---------------------- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 35 -------------------- .../net/ethernet/cavium/liquidio/octeon_network.h | 2 ++ 4 files changed, 37 insertions(+), 72 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index b55ab75a7d9c..90583ce8642d 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -397,3 +397,38 @@ void lio_update_txq_status(struct octeon_device *oct, int iq_num) netif_wake_queue(netdev); } } + +/** + * \brief Setup output queue + * @param oct octeon device + * @param q_no which queue + * @param num_descs how many descriptors + * @param desc_size size of each descriptor + * @param app_ctx application context + */ +int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, + int desc_size, void *app_ctx) +{ + int ret_val; + + dev_dbg(&oct->pci_dev->dev, "Creating Droq: %d\n", q_no); + /* droq creation and local register settings. */ + ret_val = octeon_create_droq(oct, q_no, num_descs, desc_size, app_ctx); + if (ret_val < 0) + return ret_val; + + if (ret_val == 1) { + dev_dbg(&oct->pci_dev->dev, "Using default droq %d\n", q_no); + return 0; + } + + /* Enable the droq queues */ + octeon_set_droq_pkt_op(oct, q_no, 1); + + /* Send Credit for Octeon Output queues. Credits are always + * sent after the output queue is enabled. + */ + writel(oct->droq[q_no]->max_count, oct->droq[q_no]->pkts_credit_reg); + + return ret_val; +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 01c6985cff6d..02e71184fa4c 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2198,43 +2198,6 @@ static int load_firmware(struct octeon_device *oct) return ret; } -/** - * \brief Setup output queue - * @param oct octeon device - * @param q_no which queue - * @param num_descs how many descriptors - * @param desc_size size of each descriptor - * @param app_ctx application context - */ -static int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, - int desc_size, void *app_ctx) -{ - int ret_val = 0; - - dev_dbg(&oct->pci_dev->dev, "Creating Droq: %d\n", q_no); - /* droq creation and local register settings. */ - ret_val = octeon_create_droq(oct, q_no, num_descs, desc_size, app_ctx); - if (ret_val < 0) - return ret_val; - - if (ret_val == 1) { - dev_dbg(&oct->pci_dev->dev, "Using default droq %d\n", q_no); - return 0; - } - /* tasklet creation for the droq */ - - /* Enable the droq queues */ - octeon_set_droq_pkt_op(oct, q_no, 1); - - /* Send Credit for Octeon Output queues. Credits are always - * sent after the output queue is enabled. - */ - writel(oct->droq[q_no]->max_count, - oct->droq[q_no]->pkts_credit_reg); - - return ret_val; -} - /** * \brief Callback for getting interface configuration * @param status status of request diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index dd0265a52521..a6efd75c30bb 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1344,41 +1344,6 @@ static void free_netsgbuf_with_resp(void *buf) check_txq_state(lio, skb); } -/** - * \brief Setup output queue - * @param oct octeon device - * @param q_no which queue - * @param num_descs how many descriptors - * @param desc_size size of each descriptor - * @param app_ctx application context - */ -static int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, - int desc_size, void *app_ctx) -{ - int ret_val; - - dev_dbg(&oct->pci_dev->dev, "Creating Droq: %d\n", q_no); - /* droq creation and local register settings. */ - ret_val = octeon_create_droq(oct, q_no, num_descs, desc_size, app_ctx); - if (ret_val < 0) - return ret_val; - - if (ret_val == 1) { - dev_dbg(&oct->pci_dev->dev, "Using default droq %d\n", q_no); - return 0; - } - - /* Enable the droq queues */ - octeon_set_droq_pkt_op(oct, q_no, 1); - - /* Send Credit for Octeon Output queues. Credits are always - * sent after the output queue is enabled. - */ - writel(oct->droq[q_no]->max_count, oct->droq[q_no]->pkts_credit_reg); - - return ret_val; -} - /** * \brief Callback for getting interface configuration * @param status status of request diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index 0e44ee1d4cb2..b2bb34dbc900 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -475,4 +475,6 @@ static inline int wait_for_pending_requests(struct octeon_device *oct) } void lio_update_txq_status(struct octeon_device *oct, int iq_num); +int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, + int desc_size, void *app_ctx); #endif -- cgit v1.2.3 From 21f0888b5a8a1d42630f2c7bb602a7a348714dd3 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Mon, 14 Aug 2017 12:01:44 -0700 Subject: liquidio: moved liquidio_push_packet to lio_core.c Moving common liquidio_push_packet to lio_core.c Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 149 +++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 147 -------------------- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 128 ------------------ .../net/ethernet/cavium/liquidio/octeon_network.h | 7 + 4 files changed, 156 insertions(+), 275 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 90583ce8642d..b0b246e654cd 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -432,3 +432,152 @@ int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, return ret_val; } + +/** Routine to push packets arriving on Octeon interface upto network layer. + * @param oct_id - octeon device id. + * @param skbuff - skbuff struct to be passed to network layer. + * @param len - size of total data received. + * @param rh - Control header associated with the packet + * @param param - additional control data with the packet + * @param arg - farg registered in droq_ops + */ +void +liquidio_push_packet(u32 octeon_id __attribute__((unused)), + void *skbuff, + u32 len, + union octeon_rh *rh, + void *param, + void *arg) +{ + struct net_device *netdev = (struct net_device *)arg; + struct octeon_droq *droq = + container_of(param, struct octeon_droq, napi); + struct sk_buff *skb = (struct sk_buff *)skbuff; + struct skb_shared_hwtstamps *shhwtstamps; + struct napi_struct *napi = param; + u16 vtag = 0; + u32 r_dh_off; + u64 ns; + + if (netdev) { + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + int packet_was_received; + + /* Do not proceed if the interface is not in RUNNING state. */ + if (!ifstate_check(lio, LIO_IFSTATE_RUNNING)) { + recv_buffer_free(skb); + droq->stats.rx_dropped++; + return; + } + + skb->dev = netdev; + + skb_record_rx_queue(skb, droq->q_no); + if (likely(len > MIN_SKB_SIZE)) { + struct octeon_skb_page_info *pg_info; + unsigned char *va; + + pg_info = ((struct octeon_skb_page_info *)(skb->cb)); + if (pg_info->page) { + /* For Paged allocation use the frags */ + va = page_address(pg_info->page) + + pg_info->page_offset; + memcpy(skb->data, va, MIN_SKB_SIZE); + skb_put(skb, MIN_SKB_SIZE); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + pg_info->page, + pg_info->page_offset + + MIN_SKB_SIZE, + len - MIN_SKB_SIZE, + LIO_RXBUFFER_SZ); + } + } else { + struct octeon_skb_page_info *pg_info = + ((struct octeon_skb_page_info *)(skb->cb)); + skb_copy_to_linear_data(skb, page_address(pg_info->page) + + pg_info->page_offset, len); + skb_put(skb, len); + put_page(pg_info->page); + } + + r_dh_off = (rh->r_dh.len - 1) * BYTES_PER_DHLEN_UNIT; + + if (oct->ptp_enable) { + if (rh->r_dh.has_hwtstamp) { + /* timestamp is included from the hardware at + * the beginning of the packet. + */ + if (ifstate_check + (lio, + LIO_IFSTATE_RX_TIMESTAMP_ENABLED)) { + /* Nanoseconds are in the first 64-bits + * of the packet. + */ + memcpy(&ns, (skb->data + r_dh_off), + sizeof(ns)); + r_dh_off -= BYTES_PER_DHLEN_UNIT; + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = + ns_to_ktime(ns + + lio->ptp_adjust); + } + } + } + + if (rh->r_dh.has_hash) { + __be32 *hash_be = (__be32 *)(skb->data + r_dh_off); + u32 hash = be32_to_cpu(*hash_be); + + skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); + r_dh_off -= BYTES_PER_DHLEN_UNIT; + } + + skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT); + skb->protocol = eth_type_trans(skb, skb->dev); + + if ((netdev->features & NETIF_F_RXCSUM) && + (((rh->r_dh.encap_on) && + (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) || + (!(rh->r_dh.encap_on) && + (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED)))) + /* checksum has already been verified */ + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + + /* Setting Encapsulation field on basis of status received + * from the firmware + */ + if (rh->r_dh.encap_on) { + skb->encapsulation = 1; + skb->csum_level = 1; + droq->stats.rx_vxlan++; + } + + /* inbound VLAN tag */ + if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + rh->r_dh.vlan) { + u16 priority = rh->r_dh.priority; + u16 vid = rh->r_dh.vlan; + + vtag = (priority << VLAN_PRIO_SHIFT) | vid; + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag); + } + + packet_was_received = (napi_gro_receive(napi, skb) != GRO_DROP); + + if (packet_was_received) { + droq->stats.rx_bytes_received += len; + droq->stats.rx_pkts_received++; + } else { + droq->stats.rx_dropped++; + netif_info(lio, rx_err, lio->netdev, + "droq:%d error rx_dropped:%llu\n", + droq->q_no, droq->stats.rx_dropped); + } + + } else { + recv_buffer_free(skb); + } +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 02e71184fa4c..48a178ea30a6 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2231,153 +2231,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** Routine to push packets arriving on Octeon interface upto network layer. - * @param oct_id - octeon device id. - * @param skbuff - skbuff struct to be passed to network layer. - * @param len - size of total data received. - * @param rh - Control header associated with the packet - * @param param - additional control data with the packet - * @param arg - farg registered in droq_ops - */ -static void -liquidio_push_packet(u32 octeon_id __attribute__((unused)), - void *skbuff, - u32 len, - union octeon_rh *rh, - void *param, - void *arg) -{ - struct napi_struct *napi = param; - struct sk_buff *skb = (struct sk_buff *)skbuff; - struct skb_shared_hwtstamps *shhwtstamps; - u64 ns; - u16 vtag = 0; - u32 r_dh_off; - struct net_device *netdev = (struct net_device *)arg; - struct octeon_droq *droq = container_of(param, struct octeon_droq, - napi); - if (netdev) { - int packet_was_received; - struct lio *lio = GET_LIO(netdev); - struct octeon_device *oct = lio->oct_dev; - - /* Do not proceed if the interface is not in RUNNING state. */ - if (!ifstate_check(lio, LIO_IFSTATE_RUNNING)) { - recv_buffer_free(skb); - droq->stats.rx_dropped++; - return; - } - - skb->dev = netdev; - - skb_record_rx_queue(skb, droq->q_no); - if (likely(len > MIN_SKB_SIZE)) { - struct octeon_skb_page_info *pg_info; - unsigned char *va; - - pg_info = ((struct octeon_skb_page_info *)(skb->cb)); - if (pg_info->page) { - /* For Paged allocation use the frags */ - va = page_address(pg_info->page) + - pg_info->page_offset; - memcpy(skb->data, va, MIN_SKB_SIZE); - skb_put(skb, MIN_SKB_SIZE); - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - pg_info->page, - pg_info->page_offset + - MIN_SKB_SIZE, - len - MIN_SKB_SIZE, - LIO_RXBUFFER_SZ); - } - } else { - struct octeon_skb_page_info *pg_info = - ((struct octeon_skb_page_info *)(skb->cb)); - skb_copy_to_linear_data(skb, page_address(pg_info->page) - + pg_info->page_offset, len); - skb_put(skb, len); - put_page(pg_info->page); - } - - r_dh_off = (rh->r_dh.len - 1) * BYTES_PER_DHLEN_UNIT; - - if (oct->ptp_enable) { - if (rh->r_dh.has_hwtstamp) { - /* timestamp is included from the hardware at - * the beginning of the packet. - */ - if (ifstate_check - (lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED)) { - /* Nanoseconds are in the first 64-bits - * of the packet. - */ - memcpy(&ns, (skb->data + r_dh_off), - sizeof(ns)); - r_dh_off -= BYTES_PER_DHLEN_UNIT; - shhwtstamps = skb_hwtstamps(skb); - shhwtstamps->hwtstamp = - ns_to_ktime(ns + - lio->ptp_adjust); - } - } - } - - if (rh->r_dh.has_hash) { - __be32 *hash_be = (__be32 *)(skb->data + r_dh_off); - u32 hash = be32_to_cpu(*hash_be); - - skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); - r_dh_off -= BYTES_PER_DHLEN_UNIT; - } - - skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT); - - skb->protocol = eth_type_trans(skb, skb->dev); - if ((netdev->features & NETIF_F_RXCSUM) && - (((rh->r_dh.encap_on) && - (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) || - (!(rh->r_dh.encap_on) && - (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED)))) - /* checksum has already been verified */ - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - - /* Setting Encapsulation field on basis of status received - * from the firmware - */ - if (rh->r_dh.encap_on) { - skb->encapsulation = 1; - skb->csum_level = 1; - droq->stats.rx_vxlan++; - } - - /* inbound VLAN tag */ - if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && - (rh->r_dh.vlan != 0)) { - u16 vid = rh->r_dh.vlan; - u16 priority = rh->r_dh.priority; - - vtag = priority << 13 | vid; - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag); - } - - packet_was_received = napi_gro_receive(napi, skb) != GRO_DROP; - - if (packet_was_received) { - droq->stats.rx_bytes_received += len; - droq->stats.rx_pkts_received++; - } else { - droq->stats.rx_dropped++; - netif_info(lio, rx_err, lio->netdev, - "droq:%d error rx_dropped:%llu\n", - droq->q_no, droq->stats.rx_dropped); - } - - } else { - recv_buffer_free(skb); - } -} - /** * \brief wrapper for calling napi_schedule * @param param parameters to pass to napi_schedule diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index a6efd75c30bb..013a8613d4de 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1376,134 +1376,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** Routine to push packets arriving on Octeon interface upto network layer. - * @param oct_id - octeon device id. - * @param skbuff - skbuff struct to be passed to network layer. - * @param len - size of total data received. - * @param rh - Control header associated with the packet - * @param param - additional control data with the packet - * @param arg - farg registered in droq_ops - */ -static void -liquidio_push_packet(u32 octeon_id __attribute__((unused)), - void *skbuff, - u32 len, - union octeon_rh *rh, - void *param, - void *arg) -{ - struct napi_struct *napi = param; - struct octeon_droq *droq = - container_of(param, struct octeon_droq, napi); - struct net_device *netdev = (struct net_device *)arg; - struct sk_buff *skb = (struct sk_buff *)skbuff; - u16 vtag = 0; - u32 r_dh_off; - - if (netdev) { - struct lio *lio = GET_LIO(netdev); - int packet_was_received; - - /* Do not proceed if the interface is not in RUNNING state. */ - if (!ifstate_check(lio, LIO_IFSTATE_RUNNING)) { - recv_buffer_free(skb); - droq->stats.rx_dropped++; - return; - } - - skb->dev = netdev; - - skb_record_rx_queue(skb, droq->q_no); - if (likely(len > MIN_SKB_SIZE)) { - struct octeon_skb_page_info *pg_info; - unsigned char *va; - - pg_info = ((struct octeon_skb_page_info *)(skb->cb)); - if (pg_info->page) { - /* For Paged allocation use the frags */ - va = page_address(pg_info->page) + - pg_info->page_offset; - memcpy(skb->data, va, MIN_SKB_SIZE); - skb_put(skb, MIN_SKB_SIZE); - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - pg_info->page, - pg_info->page_offset + - MIN_SKB_SIZE, - len - MIN_SKB_SIZE, - LIO_RXBUFFER_SZ); - } - } else { - struct octeon_skb_page_info *pg_info = - ((struct octeon_skb_page_info *)(skb->cb)); - skb_copy_to_linear_data(skb, - page_address(pg_info->page) + - pg_info->page_offset, len); - skb_put(skb, len); - put_page(pg_info->page); - } - - r_dh_off = (rh->r_dh.len - 1) * BYTES_PER_DHLEN_UNIT; - - if (rh->r_dh.has_hwtstamp) - r_dh_off -= BYTES_PER_DHLEN_UNIT; - - if (rh->r_dh.has_hash) { - __be32 *hash_be = (__be32 *)(skb->data + r_dh_off); - u32 hash = be32_to_cpu(*hash_be); - - skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); - r_dh_off -= BYTES_PER_DHLEN_UNIT; - } - - skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT); - skb->protocol = eth_type_trans(skb, skb->dev); - - if ((netdev->features & NETIF_F_RXCSUM) && - (((rh->r_dh.encap_on) && - (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) || - (!(rh->r_dh.encap_on) && - (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED)))) - /* checksum has already been verified */ - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - - /* Setting Encapsulation field on basis of status received - * from the firmware - */ - if (rh->r_dh.encap_on) { - skb->encapsulation = 1; - skb->csum_level = 1; - droq->stats.rx_vxlan++; - } - - /* inbound VLAN tag */ - if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && - rh->r_dh.vlan) { - u16 priority = rh->r_dh.priority; - u16 vid = rh->r_dh.vlan; - - vtag = (priority << VLAN_PRIO_SHIFT) | vid; - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag); - } - - packet_was_received = (napi_gro_receive(napi, skb) != GRO_DROP); - - if (packet_was_received) { - droq->stats.rx_bytes_received += len; - droq->stats.rx_pkts_received++; - } else { - droq->stats.rx_dropped++; - netif_info(lio, rx_err, lio->netdev, - "droq:%d error rx_dropped:%llu\n", - droq->q_no, droq->stats.rx_dropped); - } - - } else { - recv_buffer_free(skb); - } -} - /** * \brief callback when receive interrupt occurs and we are in NAPI mode * @param arg pointer to octeon output queue diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index b2bb34dbc900..5d78fd6ad3cd 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -477,4 +477,11 @@ static inline int wait_for_pending_requests(struct octeon_device *oct) void lio_update_txq_status(struct octeon_device *oct, int iq_num); int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, int desc_size, void *app_ctx); +void +liquidio_push_packet(u32 octeon_id __attribute__((unused)), + void *skbuff, + u32 len, + union octeon_rh *rh, + void *param, + void *arg); #endif -- cgit v1.2.3 From d1d97ee6e3a8e337883f203e90b507196e64e5fb Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Mon, 14 Aug 2017 12:01:48 -0700 Subject: liquidio: moved liquidio_napi_drv_callback to lio_core.c Moving common liquidio_napi_drv_callback to lio_core.c Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 39 ++++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 38 --------------------- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 13 +------- .../net/ethernet/cavium/liquidio/octeon_network.h | 1 + 4 files changed, 41 insertions(+), 50 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index b0b246e654cd..8cba9278c274 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -581,3 +581,42 @@ liquidio_push_packet(u32 octeon_id __attribute__((unused)), recv_buffer_free(skb); } } + +/** + * \brief wrapper for calling napi_schedule + * @param param parameters to pass to napi_schedule + * + * Used when scheduling on different CPUs + */ +static void napi_schedule_wrapper(void *param) +{ + struct napi_struct *napi = param; + + napi_schedule(napi); +} + +/** + * \brief callback when receive interrupt occurs and we are in NAPI mode + * @param arg pointer to octeon output queue + */ +void liquidio_napi_drv_callback(void *arg) +{ + struct octeon_device *oct; + struct octeon_droq *droq = arg; + int this_cpu = smp_processor_id(); + + oct = droq->oct_dev; + + if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct) || + droq->cpu_id == this_cpu) { + napi_schedule_irqoff(&droq->napi); + } else { + struct call_single_data *csd = &droq->csd; + + csd->func = napi_schedule_wrapper; + csd->info = &droq->napi; + csd->flags = 0; + + smp_call_function_single_async(droq->cpu_id, csd); + } +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 48a178ea30a6..e72618f6b2eb 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2231,44 +2231,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** - * \brief wrapper for calling napi_schedule - * @param param parameters to pass to napi_schedule - * - * Used when scheduling on different CPUs - */ -static void napi_schedule_wrapper(void *param) -{ - struct napi_struct *napi = param; - - napi_schedule(napi); -} - -/** - * \brief callback when receive interrupt occurs and we are in NAPI mode - * @param arg pointer to octeon output queue - */ -static void liquidio_napi_drv_callback(void *arg) -{ - struct octeon_device *oct; - struct octeon_droq *droq = arg; - int this_cpu = smp_processor_id(); - - oct = droq->oct_dev; - - if (OCTEON_CN23XX_PF(oct) || droq->cpu_id == this_cpu) { - napi_schedule_irqoff(&droq->napi); - } else { - struct call_single_data *csd = &droq->csd; - - csd->func = napi_schedule_wrapper; - csd->info = &droq->napi; - csd->flags = 0; - - smp_call_function_single_async(droq->cpu_id, csd); - } -} - /** * \brief Entry point for NAPI polling * @param napi NAPI structure diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 013a8613d4de..2663bd60e5d2 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1376,17 +1376,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** - * \brief callback when receive interrupt occurs and we are in NAPI mode - * @param arg pointer to octeon output queue - */ -static void liquidio_vf_napi_drv_callback(void *arg) -{ - struct octeon_droq *droq = arg; - - napi_schedule_irqoff(&droq->napi); -} - /** * \brief Entry point for NAPI polling * @param napi NAPI structure @@ -1473,7 +1462,7 @@ static int setup_io_queues(struct octeon_device *octeon_dev, int ifidx) droq_ops.farg = netdev; droq_ops.poll_mode = 1; - droq_ops.napi_fn = liquidio_vf_napi_drv_callback; + droq_ops.napi_fn = liquidio_napi_drv_callback; cpu_id = 0; cpu_id_modulus = num_present_cpus(); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index 5d78fd6ad3cd..076fdfcdd103 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -484,4 +484,5 @@ liquidio_push_packet(u32 octeon_id __attribute__((unused)), union octeon_rh *rh, void *param, void *arg); +void liquidio_napi_drv_callback(void *arg); #endif -- cgit v1.2.3 From d314ac222829c4e5cf7c0f505f207cb8848e0b8f Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Mon, 14 Aug 2017 12:01:50 -0700 Subject: liquidio: moved liquidio_napi_poll to lio_core.c Moving common liquidio_napi_poll to lio_core.c Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 61 +++++++++++++++++++++- drivers/net/ethernet/cavium/liquidio/lio_main.c | 52 ------------------ drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 54 ------------------- .../net/ethernet/cavium/liquidio/octeon_network.h | 2 +- 4 files changed, 61 insertions(+), 108 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 8cba9278c274..2030c2531309 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -366,7 +366,7 @@ void cleanup_rx_oom_poll_fn(struct net_device *netdev) } /* Runs in interrupt context. */ -void lio_update_txq_status(struct octeon_device *oct, int iq_num) +static void lio_update_txq_status(struct octeon_device *oct, int iq_num) { struct octeon_instr_queue *iq = oct->instr_queue[iq_num]; struct net_device *netdev; @@ -620,3 +620,62 @@ void liquidio_napi_drv_callback(void *arg) smp_call_function_single_async(droq->cpu_id, csd); } } + +/** + * \brief Entry point for NAPI polling + * @param napi NAPI structure + * @param budget maximum number of items to process + */ +int liquidio_napi_poll(struct napi_struct *napi, int budget) +{ + struct octeon_instr_queue *iq; + struct octeon_device *oct; + struct octeon_droq *droq; + int tx_done = 0, iq_no; + int work_done; + + droq = container_of(napi, struct octeon_droq, napi); + oct = droq->oct_dev; + iq_no = droq->q_no; + + /* Handle Droq descriptors */ + work_done = octeon_process_droq_poll_cmd(oct, droq->q_no, + POLL_EVENT_PROCESS_PKTS, + budget); + + /* Flush the instruction queue */ + iq = oct->instr_queue[iq_no]; + if (iq) { + /* TODO: move this check to inside octeon_flush_iq, + * once check_db_timeout is removed + */ + if (atomic_read(&iq->instr_pending)) + /* Process iq buffers with in the budget limits */ + tx_done = octeon_flush_iq(oct, iq, budget); + else + tx_done = 1; + /* Update iq read-index rather than waiting for next interrupt. + * Return back if tx_done is false. + */ + /* sub-queue status update */ + lio_update_txq_status(oct, iq_no); + } else { + dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", + __func__, iq_no); + } + +#define MAX_REG_CNT 2000000U + /* force enable interrupt if reg cnts are high to avoid wraparound */ + if (((work_done < budget) && (tx_done)) || + (iq->pkt_in_done >= MAX_REG_CNT) || + (droq->pkt_count >= MAX_REG_CNT)) { + tx_done = 1; + napi_complete_done(napi, work_done); + + octeon_process_droq_poll_cmd(droq->oct_dev, droq->q_no, + POLL_EVENT_ENABLE_INTR, 0); + return 0; + } + + return (!tx_done) ? (budget) : (work_done); +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index e72618f6b2eb..632c395a4f44 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2231,58 +2231,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** - * \brief Entry point for NAPI polling - * @param napi NAPI structure - * @param budget maximum number of items to process - */ -static int liquidio_napi_poll(struct napi_struct *napi, int budget) -{ - struct octeon_droq *droq; - int work_done; - int tx_done = 0, iq_no; - struct octeon_instr_queue *iq; - struct octeon_device *oct; - - droq = container_of(napi, struct octeon_droq, napi); - oct = droq->oct_dev; - iq_no = droq->q_no; - /* Handle Droq descriptors */ - work_done = octeon_process_droq_poll_cmd(oct, droq->q_no, - POLL_EVENT_PROCESS_PKTS, - budget); - - /* Flush the instruction queue */ - iq = oct->instr_queue[iq_no]; - if (iq) { - if (atomic_read(&iq->instr_pending)) - /* Process iq buffers with in the budget limits */ - tx_done = octeon_flush_iq(oct, iq, budget); - else - tx_done = 1; - /* Update iq read-index rather than waiting for next interrupt. - * Return back if tx_done is false. - */ - lio_update_txq_status(oct, iq_no); - } else { - dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", - __func__, iq_no); - } - - /* force enable interrupt if reg cnts are high to avoid wraparound */ - if ((work_done < budget && tx_done) || - (iq && iq->pkt_in_done >= MAX_REG_CNT) || - (droq->pkt_count >= MAX_REG_CNT)) { - tx_done = 1; - napi_complete_done(napi, work_done); - octeon_process_droq_poll_cmd(droq->oct_dev, droq->q_no, - POLL_EVENT_ENABLE_INTR, 0); - return 0; - } - - return (!tx_done) ? (budget) : (work_done); -} - /** * \brief Setup input and output queues * @param octeon_dev octeon device diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 2663bd60e5d2..0bd0c30077c7 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1376,60 +1376,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** - * \brief Entry point for NAPI polling - * @param napi NAPI structure - * @param budget maximum number of items to process - */ -static int liquidio_napi_poll(struct napi_struct *napi, int budget) -{ - struct octeon_instr_queue *iq; - struct octeon_device *oct; - struct octeon_droq *droq; - int tx_done = 0, iq_no; - int work_done; - - droq = container_of(napi, struct octeon_droq, napi); - oct = droq->oct_dev; - iq_no = droq->q_no; - - /* Handle Droq descriptors */ - work_done = octeon_process_droq_poll_cmd(oct, droq->q_no, - POLL_EVENT_PROCESS_PKTS, - budget); - - /* Flush the instruction queue */ - iq = oct->instr_queue[iq_no]; - if (iq) { - if (atomic_read(&iq->instr_pending)) - /* Process iq buffers with in the budget limits */ - tx_done = octeon_flush_iq(oct, iq, budget); - else - tx_done = 1; - - /* Update iq read-index rather than waiting for next interrupt. - * Return back if tx_done is false. - */ - lio_update_txq_status(oct, iq_no); - } else { - dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", - __func__, iq_no); - } - - /* force enable interrupt if reg cnts are high to avoid wraparound */ - if ((work_done < budget && tx_done) || - (iq && iq->pkt_in_done >= MAX_REG_CNT) || - (droq->pkt_count >= MAX_REG_CNT)) { - tx_done = 1; - napi_complete_done(napi, work_done); - octeon_process_droq_poll_cmd(droq->oct_dev, droq->q_no, - POLL_EVENT_ENABLE_INTR, 0); - return 0; - } - - return (!tx_done) ? (budget) : (work_done); -} - /** * \brief Setup input and output queues * @param octeon_dev octeon device diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index 076fdfcdd103..b6597ef56968 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -474,7 +474,6 @@ static inline int wait_for_pending_requests(struct octeon_device *oct) return 0; } -void lio_update_txq_status(struct octeon_device *oct, int iq_num); int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, int desc_size, void *app_ctx); void @@ -485,4 +484,5 @@ liquidio_push_packet(u32 octeon_id __attribute__((unused)), void *param, void *arg); void liquidio_napi_drv_callback(void *arg); +int liquidio_napi_poll(struct napi_struct *napi, int budget); #endif -- cgit v1.2.3 From 8974de1b7dadfea29a4d2182af775cd73df02319 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Mon, 14 Aug 2017 12:01:53 -0700 Subject: liquidio: moved liquidio_setup_io_queues to lio_core.c Moving common liquidio_setup_io_queues to lio_core.c Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 119 ++++++++++++++++++++- drivers/net/ethernet/cavium/liquidio/lio_main.c | 109 +------------------ drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 93 +--------------- .../net/ethernet/cavium/liquidio/octeon_network.h | 13 +-- 4 files changed, 118 insertions(+), 216 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 2030c2531309..d20d0eb45048 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -406,8 +406,8 @@ static void lio_update_txq_status(struct octeon_device *oct, int iq_num) * @param desc_size size of each descriptor * @param app_ctx application context */ -int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, - int desc_size, void *app_ctx) +static int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, + int desc_size, void *app_ctx) { int ret_val; @@ -441,7 +441,7 @@ int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, * @param param - additional control data with the packet * @param arg - farg registered in droq_ops */ -void +static void liquidio_push_packet(u32 octeon_id __attribute__((unused)), void *skbuff, u32 len, @@ -599,7 +599,7 @@ static void napi_schedule_wrapper(void *param) * \brief callback when receive interrupt occurs and we are in NAPI mode * @param arg pointer to octeon output queue */ -void liquidio_napi_drv_callback(void *arg) +static void liquidio_napi_drv_callback(void *arg) { struct octeon_device *oct; struct octeon_droq *droq = arg; @@ -626,7 +626,7 @@ void liquidio_napi_drv_callback(void *arg) * @param napi NAPI structure * @param budget maximum number of items to process */ -int liquidio_napi_poll(struct napi_struct *napi, int budget) +static int liquidio_napi_poll(struct napi_struct *napi, int budget) { struct octeon_instr_queue *iq; struct octeon_device *oct; @@ -679,3 +679,112 @@ int liquidio_napi_poll(struct napi_struct *napi, int budget) return (!tx_done) ? (budget) : (work_done); } + +/** + * \brief Setup input and output queues + * @param octeon_dev octeon device + * @param ifidx Interface index + * + * Note: Queues are with respect to the octeon device. Thus + * an input queue is for egress packets, and output queues + * are for ingress packets. + */ +int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx) +{ + struct octeon_droq_ops droq_ops; + struct net_device *netdev; + struct octeon_droq *droq; + struct napi_struct *napi; + int cpu_id_modulus; + int num_tx_descs; + struct lio *lio; + int retval = 0; + int q, q_no; + int cpu_id; + + netdev = octeon_dev->props[ifidx].netdev; + + lio = GET_LIO(netdev); + + memset(&droq_ops, 0, sizeof(struct octeon_droq_ops)); + + droq_ops.fptr = liquidio_push_packet; + droq_ops.farg = netdev; + + droq_ops.poll_mode = 1; + droq_ops.napi_fn = liquidio_napi_drv_callback; + cpu_id = 0; + cpu_id_modulus = num_present_cpus(); + + /* set up DROQs. */ + for (q = 0; q < lio->linfo.num_rxpciq; q++) { + q_no = lio->linfo.rxpciq[q].s.q_no; + dev_dbg(&octeon_dev->pci_dev->dev, + "%s index:%d linfo.rxpciq.s.q_no:%d\n", + __func__, q, q_no); + retval = octeon_setup_droq( + octeon_dev, q_no, + CFG_GET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(octeon_dev), + lio->ifidx), + CFG_GET_NUM_RX_BUF_SIZE_NIC_IF(octeon_get_conf(octeon_dev), + lio->ifidx), + NULL); + if (retval) { + dev_err(&octeon_dev->pci_dev->dev, + "%s : Runtime DROQ(RxQ) creation failed.\n", + __func__); + return 1; + } + + droq = octeon_dev->droq[q_no]; + napi = &droq->napi; + dev_dbg(&octeon_dev->pci_dev->dev, "netif_napi_add netdev:%llx oct:%llx\n", + (u64)netdev, (u64)octeon_dev); + netif_napi_add(netdev, napi, liquidio_napi_poll, 64); + + /* designate a CPU for this droq */ + droq->cpu_id = cpu_id; + cpu_id++; + if (cpu_id >= cpu_id_modulus) + cpu_id = 0; + + octeon_register_droq_ops(octeon_dev, q_no, &droq_ops); + } + + if (OCTEON_CN23XX_PF(octeon_dev) || OCTEON_CN23XX_VF(octeon_dev)) { + /* 23XX PF/VF can send/recv control messages (via the first + * PF/VF-owned droq) from the firmware even if the ethX + * interface is down, so that's why poll_mode must be off + * for the first droq. + */ + octeon_dev->droq[0]->ops.poll_mode = 0; + } + + /* set up IQs. */ + for (q = 0; q < lio->linfo.num_txpciq; q++) { + num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF( + octeon_get_conf(octeon_dev), lio->ifidx); + retval = octeon_setup_iq(octeon_dev, ifidx, q, + lio->linfo.txpciq[q], num_tx_descs, + netdev_get_tx_queue(netdev, q)); + if (retval) { + dev_err(&octeon_dev->pci_dev->dev, + " %s : Runtime IQ(TxQ) creation failed.\n", + __func__); + return 1; + } + + /* XPS */ + if (!OCTEON_CN23XX_VF(octeon_dev) && octeon_dev->msix_on && + octeon_dev->ioq_vector) { + struct octeon_ioq_vector *ioq_vector; + + ioq_vector = &octeon_dev->ioq_vector[q]; + netif_set_xps_queue(netdev, + &ioq_vector->affinity_mask, + ioq_vector->iq_index); + } + } + + return 0; +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 632c395a4f44..832db5abff48 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2231,113 +2231,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** - * \brief Setup input and output queues - * @param octeon_dev octeon device - * @param ifidx Interface Index - * - * Note: Queues are with respect to the octeon device. Thus - * an input queue is for egress packets, and output queues - * are for ingress packets. - */ -static inline int setup_io_queues(struct octeon_device *octeon_dev, - int ifidx) -{ - struct octeon_droq_ops droq_ops; - struct net_device *netdev; - int cpu_id; - int cpu_id_modulus; - struct octeon_droq *droq; - struct napi_struct *napi; - int q, q_no, retval = 0; - struct lio *lio; - int num_tx_descs; - - netdev = octeon_dev->props[ifidx].netdev; - - lio = GET_LIO(netdev); - - memset(&droq_ops, 0, sizeof(struct octeon_droq_ops)); - - droq_ops.fptr = liquidio_push_packet; - droq_ops.farg = (void *)netdev; - - droq_ops.poll_mode = 1; - droq_ops.napi_fn = liquidio_napi_drv_callback; - cpu_id = 0; - cpu_id_modulus = num_present_cpus(); - - /* set up DROQs. */ - for (q = 0; q < lio->linfo.num_rxpciq; q++) { - q_no = lio->linfo.rxpciq[q].s.q_no; - dev_dbg(&octeon_dev->pci_dev->dev, - "setup_io_queues index:%d linfo.rxpciq.s.q_no:%d\n", - q, q_no); - retval = octeon_setup_droq(octeon_dev, q_no, - CFG_GET_NUM_RX_DESCS_NIC_IF - (octeon_get_conf(octeon_dev), - lio->ifidx), - CFG_GET_NUM_RX_BUF_SIZE_NIC_IF - (octeon_get_conf(octeon_dev), - lio->ifidx), NULL); - if (retval) { - dev_err(&octeon_dev->pci_dev->dev, - "%s : Runtime DROQ(RxQ) creation failed.\n", - __func__); - return 1; - } - - droq = octeon_dev->droq[q_no]; - napi = &droq->napi; - dev_dbg(&octeon_dev->pci_dev->dev, "netif_napi_add netdev:%llx oct:%llx pf_num:%d\n", - (u64)netdev, (u64)octeon_dev, octeon_dev->pf_num); - netif_napi_add(netdev, napi, liquidio_napi_poll, 64); - - /* designate a CPU for this droq */ - droq->cpu_id = cpu_id; - cpu_id++; - if (cpu_id >= cpu_id_modulus) - cpu_id = 0; - - octeon_register_droq_ops(octeon_dev, q_no, &droq_ops); - } - - if (OCTEON_CN23XX_PF(octeon_dev)) { - /* 23XX PF can receive control messages (via the first PF-owned - * droq) from the firmware even if the ethX interface is down, - * so that's why poll_mode must be off for the first droq. - */ - octeon_dev->droq[0]->ops.poll_mode = 0; - } - - /* set up IQs. */ - for (q = 0; q < lio->linfo.num_txpciq; q++) { - num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF(octeon_get_conf - (octeon_dev), - lio->ifidx); - retval = octeon_setup_iq(octeon_dev, ifidx, q, - lio->linfo.txpciq[q], num_tx_descs, - netdev_get_tx_queue(netdev, q)); - if (retval) { - dev_err(&octeon_dev->pci_dev->dev, - " %s : Runtime IQ(TxQ) creation failed.\n", - __func__); - return 1; - } - - if (octeon_dev->ioq_vector) { - struct octeon_ioq_vector *ioq_vector; - - ioq_vector = &octeon_dev->ioq_vector[q]; - netif_set_xps_queue(netdev, - &ioq_vector->affinity_mask, - ioq_vector->iq_index); - } - } - - return 0; -} - /** * \brief Poll routine for checking transmit queue status * @param work work_struct data structure @@ -3898,7 +3791,7 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) */ lio->txq = lio->linfo.txpciq[0].s.q_no; lio->rxq = lio->linfo.rxpciq[0].s.q_no; - if (setup_io_queues(octeon_dev, i)) { + if (liquidio_setup_io_queues(octeon_dev, i)) { dev_err(&octeon_dev->pci_dev->dev, "I/O queues creation failed\n"); goto setup_nic_dev_fail; } diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 0bd0c30077c7..aa502a8d3fdb 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1376,97 +1376,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** - * \brief Setup input and output queues - * @param octeon_dev octeon device - * @param ifidx Interface index - * - * Note: Queues are with respect to the octeon device. Thus - * an input queue is for egress packets, and output queues - * are for ingress packets. - */ -static int setup_io_queues(struct octeon_device *octeon_dev, int ifidx) -{ - struct octeon_droq_ops droq_ops; - struct net_device *netdev; - int cpu_id_modulus; - struct octeon_droq *droq; - struct napi_struct *napi; - int cpu_id; - int num_tx_descs; - struct lio *lio; - int retval = 0; - int q, q_no; - - netdev = octeon_dev->props[ifidx].netdev; - - lio = GET_LIO(netdev); - - memset(&droq_ops, 0, sizeof(struct octeon_droq_ops)); - - droq_ops.fptr = liquidio_push_packet; - droq_ops.farg = netdev; - - droq_ops.poll_mode = 1; - droq_ops.napi_fn = liquidio_napi_drv_callback; - cpu_id = 0; - cpu_id_modulus = num_present_cpus(); - - /* set up DROQs. */ - for (q = 0; q < lio->linfo.num_rxpciq; q++) { - q_no = lio->linfo.rxpciq[q].s.q_no; - - retval = octeon_setup_droq( - octeon_dev, q_no, - CFG_GET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(octeon_dev), - lio->ifidx), - CFG_GET_NUM_RX_BUF_SIZE_NIC_IF(octeon_get_conf(octeon_dev), - lio->ifidx), - NULL); - if (retval) { - dev_err(&octeon_dev->pci_dev->dev, - "%s : Runtime DROQ(RxQ) creation failed.\n", - __func__); - return 1; - } - - droq = octeon_dev->droq[q_no]; - napi = &droq->napi; - netif_napi_add(netdev, napi, liquidio_napi_poll, 64); - - /* designate a CPU for this droq */ - droq->cpu_id = cpu_id; - cpu_id++; - if (cpu_id >= cpu_id_modulus) - cpu_id = 0; - - octeon_register_droq_ops(octeon_dev, q_no, &droq_ops); - } - - /* 23XX VF can send/recv control messages (via the first VF-owned - * droq) from the firmware even if the ethX interface is down, - * so that's why poll_mode must be off for the first droq. - */ - octeon_dev->droq[0]->ops.poll_mode = 0; - - /* set up IQs. */ - for (q = 0; q < lio->linfo.num_txpciq; q++) { - num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF( - octeon_get_conf(octeon_dev), lio->ifidx); - retval = octeon_setup_iq(octeon_dev, ifidx, q, - lio->linfo.txpciq[q], num_tx_descs, - netdev_get_tx_queue(netdev, q)); - if (retval) { - dev_err(&octeon_dev->pci_dev->dev, - " %s : Runtime IQ(TxQ) creation failed.\n", - __func__); - return 1; - } - } - - return 0; -} - /** * \brief Net device open for LiquidIO * @param netdev network device @@ -2695,7 +2604,7 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) /* Copy MAC Address to OS network device structure */ ether_addr_copy(netdev->dev_addr, mac); - if (setup_io_queues(octeon_dev, i)) { + if (liquidio_setup_io_queues(octeon_dev, i)) { dev_err(&octeon_dev->pci_dev->dev, "I/O queues creation failed\n"); goto setup_nic_dev_fail; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index b6597ef56968..b49b155ef523 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -166,6 +166,8 @@ void cleanup_rx_oom_poll_fn(struct net_device *netdev); */ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr); +int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx); + /** * \brief Register ethtool operations * @param netdev pointer to network device @@ -474,15 +476,4 @@ static inline int wait_for_pending_requests(struct octeon_device *oct) return 0; } -int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, - int desc_size, void *app_ctx); -void -liquidio_push_packet(u32 octeon_id __attribute__((unused)), - void *skbuff, - u32 len, - union octeon_rh *rh, - void *param, - void *arg); -void liquidio_napi_drv_callback(void *arg); -int liquidio_napi_poll(struct napi_struct *napi, int budget); #endif -- cgit v1.2.3 From d18ca7df9f2a863cf67645609bb4a97b0b088116 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Mon, 14 Aug 2017 12:01:56 -0700 Subject: liquidio: added support for ethtool --set-ring feature added support for ethtool --set-ring feature Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 131 +++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 6 +- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 6 +- .../net/ethernet/cavium/liquidio/octeon_config.h | 13 +- .../net/ethernet/cavium/liquidio/octeon_device.c | 14 +-- .../net/ethernet/cavium/liquidio/octeon_network.h | 1 + 6 files changed, 160 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 4f65c08461f6..a59c8ccebd10 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -637,6 +637,9 @@ lio_ethtool_get_ringparam(struct net_device *netdev, u32 tx_max_pending = 0, rx_max_pending = 0, tx_pending = 0, rx_pending = 0; + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return; + if (OCTEON_CN6XXX(oct)) { struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx); @@ -661,6 +664,126 @@ lio_ethtool_get_ringparam(struct net_device *netdev, ering->rx_jumbo_max_pending = 0; } +static int lio_reset_queues(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct napi_struct *napi, *n; + int i; + + dev_dbg(&oct->pci_dev->dev, "%s:%d ifidx %d\n", + __func__, __LINE__, lio->ifidx); + + if (wait_for_pending_requests(oct)) + dev_err(&oct->pci_dev->dev, "There were pending requests\n"); + + if (lio_wait_for_instr_fetch(oct)) + dev_err(&oct->pci_dev->dev, "IQ had pending instructions\n"); + + if (octeon_set_io_queues_off(oct)) { + dev_err(&oct->pci_dev->dev, "setting io queues off failed\n"); + return -1; + } + + /* Disable the input and output queues now. No more packets will + * arrive from Octeon. + */ + oct->fn_list.disable_io_queues(oct); + /* Delete NAPI */ + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + netif_napi_del(napi); + + for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { + if (!(oct->io_qmask.oq & BIT_ULL(i))) + continue; + octeon_delete_droq(oct, i); + } + + for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { + if (!(oct->io_qmask.iq & BIT_ULL(i))) + continue; + octeon_delete_instr_queue(oct, i); + } + + if (oct->fn_list.setup_device_regs(oct)) { + dev_err(&oct->pci_dev->dev, "Failed to configure device registers\n"); + return -1; + } + + if (liquidio_setup_io_queues(oct, 0)) { + dev_err(&oct->pci_dev->dev, "IO queues initialization failed\n"); + return -1; + } + + /* Enable the input and output queues for this Octeon device */ + if (oct->fn_list.enable_io_queues(oct)) { + dev_err(&oct->pci_dev->dev, "Failed to enable input/output queues"); + return -1; + } + + return 0; +} + +static int lio_ethtool_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ering) +{ + u32 rx_count, tx_count, rx_count_old, tx_count_old; + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + int stopped = 0; + + if (!OCTEON_CN23XX_PF(oct) && !OCTEON_CN23XX_VF(oct)) + return -EINVAL; + + if (ering->rx_mini_pending || ering->rx_jumbo_pending) + return -EINVAL; + + rx_count = clamp_t(u32, ering->rx_pending, CN23XX_MIN_OQ_DESCRIPTORS, + CN23XX_MAX_OQ_DESCRIPTORS); + tx_count = clamp_t(u32, ering->tx_pending, CN23XX_MIN_IQ_DESCRIPTORS, + CN23XX_MAX_IQ_DESCRIPTORS); + + rx_count_old = oct->droq[0]->max_count; + tx_count_old = oct->instr_queue[0]->max_count; + + if (rx_count == rx_count_old && tx_count == tx_count_old) + return 0; + + ifstate_set(lio, LIO_IFSTATE_RESETTING); + + if (netif_running(netdev)) { + netdev->netdev_ops->ndo_stop(netdev); + stopped = 1; + } + + /* Change RX/TX DESCS count */ + if (tx_count != tx_count_old) + CFG_SET_NUM_TX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx, + tx_count); + if (rx_count != rx_count_old) + CFG_SET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx, + rx_count); + + if (lio_reset_queues(netdev)) + goto err_lio_reset_queues; + + if (stopped) + netdev->netdev_ops->ndo_open(netdev); + + ifstate_reset(lio, LIO_IFSTATE_RESETTING); + + return 0; + +err_lio_reset_queues: + if (tx_count != tx_count_old) + CFG_SET_NUM_TX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx, + tx_count_old); + if (rx_count != rx_count_old) + CFG_SET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx, + rx_count_old); + return -EINVAL; +} + static u32 lio_get_msglevel(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); @@ -779,6 +902,9 @@ lio_get_ethtool_stats(struct net_device *netdev, struct net_device_stats *netstats = &netdev->stats; int i = 0, j; + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return; + netdev->netdev_ops->ndo_get_stats(netdev); octnet_get_link_stats(netdev); @@ -1043,6 +1169,9 @@ static void lio_vf_get_ethtool_stats(struct net_device *netdev, struct octeon_device *oct_dev = lio->oct_dev; int i = 0, j, vj; + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return; + netdev->netdev_ops->ndo_get_stats(netdev); /* sum of oct->droq[oq_no]->stats->rx_pkts_received */ data[i++] = CVM_CAST64(netstats->rx_packets); @@ -2574,6 +2703,7 @@ static const struct ethtool_ops lio_ethtool_ops = { .get_link = ethtool_op_get_link, .get_drvinfo = lio_get_drvinfo, .get_ringparam = lio_ethtool_get_ringparam, + .set_ringparam = lio_ethtool_set_ringparam, .get_channels = lio_ethtool_get_channels, .set_phys_id = lio_set_phys_id, .get_eeprom_len = lio_get_eeprom_len, @@ -2599,6 +2729,7 @@ static const struct ethtool_ops lio_vf_ethtool_ops = { .get_link = ethtool_op_get_link, .get_drvinfo = lio_get_vf_drvinfo, .get_ringparam = lio_ethtool_get_ringparam, + .set_ringparam = lio_ethtool_set_ringparam, .get_channels = lio_ethtool_get_channels, .get_strings = lio_vf_get_strings, .get_ethtool_stats = lio_vf_get_ethtool_stats, diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 832db5abff48..38b7ea591d04 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -817,7 +817,8 @@ static void print_link_info(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); - if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) { + if (!ifstate_check(lio, LIO_IFSTATE_RESETTING) && + ifstate_check(lio, LIO_IFSTATE_REGISTERED)) { struct oct_link_info *linfo = &lio->linfo; if (linfo->link.s.link_up) { @@ -2520,6 +2521,9 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) oct = lio->oct_dev; + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return stats; + for (i = 0; i < lio->linfo.num_txpciq; i++) { iq_no = lio->linfo.txpciq[i].s.q_no; iq_stats = &oct->instr_queue[iq_no]->stats; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index aa502a8d3fdb..2fc2da3a8018 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -548,7 +548,8 @@ static void print_link_info(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); - if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) { + if (!ifstate_check(lio, LIO_IFSTATE_RESETTING) && + ifstate_check(lio, LIO_IFSTATE_REGISTERED)) { struct oct_link_info *linfo = &lio->linfo; if (linfo->link.s.link_up) { @@ -1633,6 +1634,9 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) oct = lio->oct_dev; + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return stats; + for (i = 0; i < lio->linfo.num_txpciq; i++) { iq_no = lio->linfo.txpciq[i].s.q_no; iq_stats = &oct->instr_queue[iq_no]->stats; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_config.h b/drivers/net/ethernet/cavium/liquidio/octeon_config.h index f229d792c2b3..63bd9c94e547 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_config.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_config.h @@ -71,13 +71,17 @@ #define CN23XX_MAX_RINGS_PER_VF 8 #define CN23XX_MAX_INPUT_QUEUES CN23XX_MAX_RINGS_PER_PF -#define CN23XX_MAX_IQ_DESCRIPTORS 512 +#define CN23XX_MAX_IQ_DESCRIPTORS 2048 +#define CN23XX_DEFAULT_IQ_DESCRIPTORS 512 +#define CN23XX_MIN_IQ_DESCRIPTORS 128 #define CN23XX_DB_MIN 1 #define CN23XX_DB_MAX 8 #define CN23XX_DB_TIMEOUT 1 #define CN23XX_MAX_OUTPUT_QUEUES CN23XX_MAX_RINGS_PER_PF -#define CN23XX_MAX_OQ_DESCRIPTORS 512 +#define CN23XX_MAX_OQ_DESCRIPTORS 2048 +#define CN23XX_DEFAULT_OQ_DESCRIPTORS 512 +#define CN23XX_MIN_OQ_DESCRIPTORS 128 #define CN23XX_OQ_BUF_SIZE 1664 #define CN23XX_OQ_PKTSPER_INTR 128 /*#define CAVIUM_ONLY_CN23XX_RX_PERF*/ @@ -163,6 +167,11 @@ ((cfg)->misc.oct_link_query_interval) #define CFG_GET_IS_SLI_BP_ON(cfg) ((cfg)->misc.enable_sli_oq_bp) +#define CFG_SET_NUM_RX_DESCS_NIC_IF(cfg, idx, value) \ + ((cfg)->nic_if_cfg[idx].num_rx_descs = value) +#define CFG_SET_NUM_TX_DESCS_NIC_IF(cfg, idx, value) \ + ((cfg)->nic_if_cfg[idx].num_tx_descs = value) + /* Max IOQs per OCTEON Link */ #define MAX_IOQS_PER_NICIF 64 diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 495cc8880646..29d53b1763a7 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -418,7 +418,7 @@ static struct octeon_config default_cn23xx_conf = { /** IQ attributes */ .iq = { .max_iqs = CN23XX_CFG_IO_QUEUES, - .pending_list_size = (CN23XX_MAX_IQ_DESCRIPTORS * + .pending_list_size = (CN23XX_DEFAULT_IQ_DESCRIPTORS * CN23XX_CFG_IO_QUEUES), .instr_type = OCTEON_64BYTE_INSTR, .db_min = CN23XX_DB_MIN, @@ -436,8 +436,8 @@ static struct octeon_config default_cn23xx_conf = { }, .num_nic_ports = DEFAULT_NUM_NIC_PORTS_23XX, - .num_def_rx_descs = CN23XX_MAX_OQ_DESCRIPTORS, - .num_def_tx_descs = CN23XX_MAX_IQ_DESCRIPTORS, + .num_def_rx_descs = CN23XX_DEFAULT_OQ_DESCRIPTORS, + .num_def_tx_descs = CN23XX_DEFAULT_IQ_DESCRIPTORS, .def_rx_buf_size = CN23XX_OQ_BUF_SIZE, /* For ethernet interface 0: Port cfg Attributes */ @@ -455,10 +455,10 @@ static struct octeon_config default_cn23xx_conf = { .num_rxqs = DEF_RXQS_PER_INTF, /* Num of desc for rx rings */ - .num_rx_descs = CN23XX_MAX_OQ_DESCRIPTORS, + .num_rx_descs = CN23XX_DEFAULT_OQ_DESCRIPTORS, /* Num of desc for tx rings */ - .num_tx_descs = CN23XX_MAX_IQ_DESCRIPTORS, + .num_tx_descs = CN23XX_DEFAULT_IQ_DESCRIPTORS, /* SKB size, We need not change buf size even for Jumbo frames. * Octeon can send jumbo frames in 4 consecutive descriptors, @@ -484,10 +484,10 @@ static struct octeon_config default_cn23xx_conf = { .num_rxqs = DEF_RXQS_PER_INTF, /* Num of desc for rx rings */ - .num_rx_descs = CN23XX_MAX_OQ_DESCRIPTORS, + .num_rx_descs = CN23XX_DEFAULT_OQ_DESCRIPTORS, /* Num of desc for tx rings */ - .num_tx_descs = CN23XX_MAX_IQ_DESCRIPTORS, + .num_tx_descs = CN23XX_DEFAULT_IQ_DESCRIPTORS, /* SKB size, We need not change buf size even for Jumbo frames. * Octeon can send jumbo frames in 4 consecutive descriptors, diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index b49b155ef523..d4b39305ad68 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -33,6 +33,7 @@ #define LIO_IFSTATE_REGISTERED 0x02 #define LIO_IFSTATE_RUNNING 0x04 #define LIO_IFSTATE_RX_TIMESTAMP_ENABLED 0x08 +#define LIO_IFSTATE_RESETTING 0x10 struct oct_nic_stats_resp { u64 rh; -- cgit v1.2.3 From ba31d3666924adf1b4ad8e19400324a06b99c52c Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Mon, 14 Aug 2017 21:09:19 +0200 Subject: mlxsw: spectrum_router: Prepare nexthop group's hash table for IPv6 This patch does preparation before introducing IPv6 nexthop group consolidation. Currently the nexthop group hash table is used only by IPv4 and uses fixed key size. In order to support the IPv6's variable length key the current table is changed. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 69 ++++++++++++++++------ 1 file changed, 52 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 3d9be36965f6..510042905f22 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1522,15 +1522,11 @@ struct mlxsw_sp_nexthop { struct mlxsw_sp_neigh_entry *neigh_entry; }; -struct mlxsw_sp_nexthop_group_key { - struct fib_info *fi; -}; - struct mlxsw_sp_nexthop_group { + void *priv; struct rhash_head ht_node; struct list_head fib_list; /* list of fib entries that use this group */ struct neigh_table *neigh_tbl; - struct mlxsw_sp_nexthop_group_key key; u8 adj_index_valid:1, gateway:1; /* routes using the group use a gateway */ u32 adj_index; @@ -1540,10 +1536,46 @@ struct mlxsw_sp_nexthop_group { #define nh_rif nexthops[0].rif }; +static struct fib_info * +mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp) +{ + return nh_grp->priv; +} + +struct mlxsw_sp_nexthop_group_cmp_arg { + struct fib_info *fi; +}; + +static int +mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr) +{ + const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key; + const struct mlxsw_sp_nexthop_group *nh_grp = ptr; + + return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp); +} + +static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed) +{ + const struct mlxsw_sp_nexthop_group *nh_grp = data; + struct fib_info *fi = mlxsw_sp_nexthop4_group_fi(nh_grp); + + return jhash(&fi, sizeof(fi), seed); +} + +static u32 +mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed) +{ + const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data; + + return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed); +} + static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = { - .key_offset = offsetof(struct mlxsw_sp_nexthop_group, key), .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node), - .key_len = sizeof(struct mlxsw_sp_nexthop_group_key), + .hashfn = mlxsw_sp_nexthop_group_hash, + .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj, + .obj_cmpfn = mlxsw_sp_nexthop_group_cmp, }; static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp, @@ -1563,10 +1595,14 @@ static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp, } static struct mlxsw_sp_nexthop_group * -mlxsw_sp_nexthop_group_lookup(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop_group_key key) +mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp, + struct fib_info *fi) { - return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, &key, + struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg; + + cmp_arg.fi = fi; + return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, + &cmp_arg, mlxsw_sp_nexthop_group_ht_params); } @@ -2063,12 +2099,12 @@ mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) nh_grp = kzalloc(alloc_size, GFP_KERNEL); if (!nh_grp) return ERR_PTR(-ENOMEM); + nh_grp->priv = fi; INIT_LIST_HEAD(&nh_grp->fib_list); nh_grp->neigh_tbl = &arp_tbl; nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK; nh_grp->count = fi->fib_nhs; - nh_grp->key.fi = fi; fib_info_hold(fi); for (i = 0; i < nh_grp->count; i++) { nh = &nh_grp->nexthops[i]; @@ -2089,7 +2125,7 @@ err_nexthop4_init: nh = &nh_grp->nexthops[i]; mlxsw_sp_nexthop4_fini(mlxsw_sp, nh); } - fib_info_put(nh_grp->key.fi); + fib_info_put(fi); kfree(nh_grp); return ERR_PTR(err); } @@ -2108,7 +2144,7 @@ mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp, } mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); WARN_ON_ONCE(nh_grp->adj_index_valid); - fib_info_put(nh_grp->key.fi); + fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp)); kfree(nh_grp); } @@ -2116,11 +2152,9 @@ static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry, struct fib_info *fi) { - struct mlxsw_sp_nexthop_group_key key; struct mlxsw_sp_nexthop_group *nh_grp; - key.fi = fi; - nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key); + nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi); if (!nh_grp) { nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi); if (IS_ERR(nh_grp)) @@ -2551,7 +2585,8 @@ mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp, if (fib4_entry->tb_id == fen_info->tb_id && fib4_entry->tos == fen_info->tos && fib4_entry->type == fen_info->type && - fib4_entry->common.nh_group->key.fi == fen_info->fi) { + mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) == + fen_info->fi) { return fib4_entry; } } -- cgit v1.2.3 From e6f3b379c0c599a870bc0e7d8a0cbb0b316502f5 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Mon, 14 Aug 2017 21:09:20 +0200 Subject: mlxsw: spectrum_router: Add support for nexthop group consolidation for IPv6 Due to limited ASIC resources the maximum number of routes is limited by the nexthop resource. In order to improve the routing scale nexthop consolidation should be performed. This patch adds support for IPv6 neighbor consolidation. The hash value is calculated based on the nexthop set, by performing bitwise xor on the ifindexs of the nexthops, in a similar way to IPv4's kernel implementation. In case of collision a full match is performed between the sets which include address and ifindex comparison. Non gateway nexthop groups are not inserted to the hash table due to lack of nexthop device (ifindex). Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 150 +++++++++++++++++++-- 1 file changed, 141 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 510042905f22..16676fffbf70 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1509,6 +1509,7 @@ struct mlxsw_sp_nexthop { struct rhash_head ht_node; struct mlxsw_sp_nexthop_key key; unsigned char gw_addr[sizeof(struct in6_addr)]; + int ifindex; struct mlxsw_sp_rif *rif; u8 should_offload:1, /* set indicates this neigh is connected and * should be put to KVD linear area of this group. @@ -1543,24 +1544,115 @@ mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp) } struct mlxsw_sp_nexthop_group_cmp_arg { - struct fib_info *fi; + enum mlxsw_sp_l3proto proto; + union { + struct fib_info *fi; + struct mlxsw_sp_fib6_entry *fib6_entry; + }; }; +static bool +mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp, + const struct in6_addr *gw, int ifindex) +{ + int i; + + for (i = 0; i < nh_grp->count; i++) { + const struct mlxsw_sp_nexthop *nh; + + nh = &nh_grp->nexthops[i]; + if (nh->ifindex == ifindex && + ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr)) + return true; + } + + return false; +} + +static bool +mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp, + const struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + if (nh_grp->count != fib6_entry->nrt6) + return false; + + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + struct in6_addr *gw; + int ifindex; + + ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex; + gw = &mlxsw_sp_rt6->rt->rt6i_gateway; + if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex)) + return false; + } + + return true; +} + static int mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr) { const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key; const struct mlxsw_sp_nexthop_group *nh_grp = ptr; - return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp); + switch (cmp_arg->proto) { + case MLXSW_SP_L3_PROTO_IPV4: + return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp); + case MLXSW_SP_L3_PROTO_IPV6: + return !mlxsw_sp_nexthop6_group_cmp(nh_grp, + cmp_arg->fib6_entry); + default: + WARN_ON(1); + return 1; + } +} + +static int +mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp) +{ + return nh_grp->neigh_tbl->family; } static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed) { const struct mlxsw_sp_nexthop_group *nh_grp = data; - struct fib_info *fi = mlxsw_sp_nexthop4_group_fi(nh_grp); + const struct mlxsw_sp_nexthop *nh; + struct fib_info *fi; + unsigned int val; + int i; + + switch (mlxsw_sp_nexthop_group_type(nh_grp)) { + case AF_INET: + fi = mlxsw_sp_nexthop4_group_fi(nh_grp); + return jhash(&fi, sizeof(fi), seed); + case AF_INET6: + val = nh_grp->count; + for (i = 0; i < nh_grp->count; i++) { + nh = &nh_grp->nexthops[i]; + val ^= nh->ifindex; + } + return jhash(&val, sizeof(val), seed); + default: + WARN_ON(1); + return 0; + } +} + +static u32 +mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed) +{ + unsigned int val = fib6_entry->nrt6; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + struct net_device *dev; + + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + dev = mlxsw_sp_rt6->rt->dst.dev; + val ^= dev->ifindex; + } - return jhash(&fi, sizeof(fi), seed); + return jhash(&val, sizeof(val), seed); } static u32 @@ -1568,7 +1660,15 @@ mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed) { const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data; - return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed); + switch (cmp_arg->proto) { + case MLXSW_SP_L3_PROTO_IPV4: + return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed); + case MLXSW_SP_L3_PROTO_IPV6: + return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed); + default: + WARN_ON(1); + return 0; + } } static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = { @@ -1581,6 +1681,10 @@ static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = { static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp) { + if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 && + !nh_grp->gateway) + return 0; + return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht, &nh_grp->ht_node, mlxsw_sp_nexthop_group_ht_params); @@ -1589,6 +1693,10 @@ static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp) { + if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 && + !nh_grp->gateway) + return; + rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht, &nh_grp->ht_node, mlxsw_sp_nexthop_group_ht_params); @@ -1600,12 +1708,26 @@ mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg; + cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4; cmp_arg.fi = fi; return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, &cmp_arg, mlxsw_sp_nexthop_group_ht_params); } +static struct mlxsw_sp_nexthop_group * +mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg; + + cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6; + cmp_arg.fib6_entry = fib6_entry; + return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, + &cmp_arg, + mlxsw_sp_nexthop_group_ht_params); +} + static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = { .key_offset = offsetof(struct mlxsw_sp_nexthop, key), .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node), @@ -3197,6 +3319,7 @@ static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp, if (!dev) return 0; + nh->ifindex = dev->ifindex; rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); if (!rif) @@ -3254,9 +3377,15 @@ mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp, goto err_nexthop6_init; mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list); } + + err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp); + if (err) + goto err_nexthop_group_insert; + mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); return nh_grp; +err_nexthop_group_insert: err_nexthop6_init: for (i--; i >= 0; i--) { nh = &nh_grp->nexthops[i]; @@ -3273,6 +3402,7 @@ mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh; int i = nh_grp->count; + mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp); for (i--; i >= 0; i--) { nh = &nh_grp->nexthops[i]; mlxsw_sp_nexthop6_fini(mlxsw_sp, nh); @@ -3287,10 +3417,12 @@ static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_nexthop_group *nh_grp; - /* For now, don't consolidate nexthop groups */ - nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry); - if (IS_ERR(nh_grp)) - return PTR_ERR(nh_grp); + nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry); + if (!nh_grp) { + nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry); + if (IS_ERR(nh_grp)) + return PTR_ERR(nh_grp); + } list_add_tail(&fib6_entry->common.nexthop_group_node, &nh_grp->fib_list); -- cgit v1.2.3 From d396179c166932e51b3a65c4f766671ac4e48763 Mon Sep 17 00:00:00 2001 From: Derek Chickles Date: Mon, 14 Aug 2017 12:17:56 -0700 Subject: liquidio: fix issues with fw_type module parameter The fw_type module parameter isn't showing up in the /sys/module/liquidio/parameters directory. Fix it by setting the read permission bits for user, group, other in module_param_string(). Revise the description of fw_type. Initialize the fw_type static char array with the default value to conform to the module parameter description. Signed-off-by: Derek Chickles Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 38b7ea591d04..247f5de6da57 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -59,9 +59,9 @@ static int debug = -1; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "NETIF_MSG debug bits"); -static char fw_type[LIO_MAX_FW_TYPE_LEN]; -module_param_string(fw_type, fw_type, sizeof(fw_type), 0000); -MODULE_PARM_DESC(fw_type, "Type of firmware to be loaded. Default \"nic\""); +static char fw_type[LIO_MAX_FW_TYPE_LEN] = LIO_FW_NAME_TYPE_NIC; +module_param_string(fw_type, fw_type, sizeof(fw_type), 0444); +MODULE_PARM_DESC(fw_type, "Type of firmware to be loaded. Default \"nic\". Use \"none\" to load firmware from flash."); static u32 console_bitmask; module_param(console_bitmask, int, 0644); -- cgit v1.2.3 From e084a8b89c173d7b37e886b277b6d3e05669259a Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Tue, 15 Aug 2017 02:33:05 -0400 Subject: mlx4: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not necessary to manually clear the device driver data to NULL. Cc: Joe Jin Cc: Junxiao Bi Signed-off-by: Zhu Yanjun Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 09b9bc17bce9..df9b0efb5ab1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -3782,7 +3782,6 @@ err_release_regions: err_disable_pdev: mlx4_pci_disable_device(&priv->dev); - pci_set_drvdata(pdev, NULL); return err; } @@ -3997,7 +3996,6 @@ static void mlx4_remove_one(struct pci_dev *pdev) devlink_unregister(devlink); kfree(dev->persist); devlink_free(devlink); - pci_set_drvdata(pdev, NULL); } static int restore_current_port_types(struct mlx4_dev *dev, -- cgit v1.2.3 From 26d159482a0283568557eb606d7f869db7fa03d4 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Tue, 15 Aug 2017 02:33:06 -0400 Subject: mlx5: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not necessary to manually clear the device driver data to NULL. Cc: Joe Jin Cc: Junxiao Bi Signed-off-by: Zhu Yanjun Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 6dbd637b4e66..7e6e24398926 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1400,7 +1400,6 @@ clean_srcu: cleanup_srcu_struct(&priv->pfault_srcu); clean_dev: #endif - pci_set_drvdata(pdev, NULL); devlink_free(devlink); return err; @@ -1427,7 +1426,6 @@ static void remove_one(struct pci_dev *pdev) #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING cleanup_srcu_struct(&priv->pfault_srcu); #endif - pci_set_drvdata(pdev, NULL); devlink_free(devlink); } -- cgit v1.2.3 From fe4007999599c02598c17b643e8de43e487d48e8 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Aug 2017 09:09:49 +0200 Subject: ipv6: fib: Provide offload indication using nexthop flags IPv6 routes currently lack nexthop flags as in IPv4. This has several implications. In the forwarding path, it requires us to check the carrier state of the nexthop device and potentially ignore a linkdown route, instead of checking for RTNH_F_LINKDOWN. It also requires capable drivers to use the user facing IPv6-specific route flags to provide offload indication, instead of using the nexthop flags as in IPv4. Add nexthop flags to IPv6 routes in the 40 bytes hole and use it to provide offload indication instead of the RTF_OFFLOAD flag, which is removed while it's still not part of any official kernel release. In the near future we would like to use the field for the RTNH_F_{LINKDOWN,DEAD} flags, but this change is more involved and might not be ready in time for the current cycle. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Acked-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 8 ++++---- include/net/ip6_fib.h | 2 ++ include/uapi/linux/ipv6_route.h | 1 - net/ipv6/route.c | 7 +------ 4 files changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 16676fffbf70..4895d5b8942b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2397,7 +2397,7 @@ mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) { list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6, - list)->rt->rt6i_flags |= RTF_OFFLOAD; + list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD; return; } @@ -2407,9 +2407,9 @@ mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6); if (nh && nh->offloaded) - mlxsw_sp_rt6->rt->rt6i_flags |= RTF_OFFLOAD; + mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD; else - mlxsw_sp_rt6->rt->rt6i_flags &= ~RTF_OFFLOAD; + mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD; } } @@ -2424,7 +2424,7 @@ mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { struct rt6_info *rt = mlxsw_sp_rt6->rt; - rt->rt6i_flags &= ~RTF_OFFLOAD; + rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD; } } diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 1d790ea40ea7..71c1646298ae 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -120,6 +120,8 @@ struct rt6_info { atomic_t rt6i_ref; + unsigned int rt6i_nh_flags; + /* These are in a separate cache line. */ struct rt6key rt6i_dst ____cacheline_aligned_in_smp; u32 rt6i_flags; diff --git a/include/uapi/linux/ipv6_route.h b/include/uapi/linux/ipv6_route.h index 33e2a5732bd1..d496c02e14bc 100644 --- a/include/uapi/linux/ipv6_route.h +++ b/include/uapi/linux/ipv6_route.h @@ -35,7 +35,6 @@ #define RTF_PREF(pref) ((pref) << 27) #define RTF_PREF_MASK 0x18000000 -#define RTF_OFFLOAD 0x20000000 /* offloaded route */ #define RTF_PCPU 0x40000000 /* read-only: can not be set by user */ #define RTF_LOCAL 0x80000000 diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 035762fed07d..6793135d49db 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1820,11 +1820,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, goto out; } - if (cfg->fc_flags & RTF_OFFLOAD) { - NL_SET_ERR_MSG(extack, "Userspace can not set RTF_OFFLOAD"); - goto out; - } - if (cfg->fc_dst_len > 128) { NL_SET_ERR_MSG(extack, "Invalid prefix length"); goto out; @@ -3335,7 +3330,7 @@ static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt, goto nla_put_failure; } - if (rt->rt6i_flags & RTF_OFFLOAD) + if (rt->rt6i_nh_flags & RTNH_F_OFFLOAD) *flags |= RTNH_F_OFFLOAD; /* not needed for multipath encoding b/c it has a rtnexthop struct */ -- cgit v1.2.3 From df9a21f11f14e393ca48689e1d4b596c4d9200e5 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Aug 2017 09:10:33 +0200 Subject: mlxsw: spectrum_router: Use correct config option I made an embarrassing mistake and used 'IPV6' instead of 'CONFIG_IPV6' around the function that updates the kernel about IPv6 neighbours activity. This can be a problem if the kernel has more neighbours than a certain threshold and it starts deleting those that are supposedly inactive. Fixes: b5f3e0d43012 ("mlxsw: spectrum_router: Fix build when IPv6 isn't enabled") Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 4895d5b8942b..a0a9728af989 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1042,7 +1042,7 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp, neigh_release(n); } -#if IS_ENABLED(IPV6) +#if IS_ENABLED(CONFIG_IPV6) static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp, char *rauhtd_pl, int rec_index) -- cgit v1.2.3 From 1ff392689f2eee806fad57977488181130c05830 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Tue, 15 Aug 2017 12:46:05 -0700 Subject: liquidio: moved liquidio_msix_intr_handler to lio_core.c Moving common liquidio_msix_intr_handler to lio_core.c Signed-off-by: Intiyaz Basha Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 41 ++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 43 --------------------- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 45 ---------------------- drivers/net/ethernet/cavium/liquidio/octeon_main.h | 6 +++ .../net/ethernet/cavium/liquidio/octeon_network.h | 3 ++ 5 files changed, 50 insertions(+), 88 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index d20d0eb45048..03746d8f2778 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -788,3 +788,44 @@ int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx) return 0; } + +static +int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) +{ + struct octeon_device *oct = droq->oct_dev; + struct octeon_device_priv *oct_priv = + (struct octeon_device_priv *)oct->priv; + + if (droq->ops.poll_mode) { + droq->ops.napi_fn(droq); + } else { + if (ret & MSIX_PO_INT) { + if (OCTEON_CN23XX_VF(oct)) + dev_err(&oct->pci_dev->dev, + "should not come here should not get rx when poll mode = 0 for vf\n"); + tasklet_schedule(&oct_priv->droq_tasklet); + return 1; + } + /* this will be flushed periodically by check iq db */ + if (ret & MSIX_PI_INT) + return 0; + } + + return 0; +} + +irqreturn_t +liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev) +{ + struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev; + struct octeon_device *oct = ioq_vector->oct_dev; + struct octeon_droq *droq = oct->droq[ioq_vector->droq_index]; + u64 ret; + + ret = oct->fn_list.msix_interrupt_handler(ioq_vector); + + if (ret & MSIX_PO_INT || ret & MSIX_PI_INT) + liquidio_schedule_msix_droq_pkt_handler(droq, ret); + + return IRQ_HANDLED; +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 247f5de6da57..96ba5ec756ad 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -175,12 +175,6 @@ struct handshake { int started_ok; }; -struct octeon_device_priv { - /** Tasklet structures for this device. */ - struct tasklet_struct droq_tasklet; - unsigned long napi_mask; -}; - #ifdef CONFIG_PCI_IOV static int liquidio_enable_sriov(struct pci_dev *dev, int num_vfs); #endif @@ -907,27 +901,6 @@ static inline void update_link_status(struct net_device *netdev, } } -static -int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) -{ - struct octeon_device *oct = droq->oct_dev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; - - if (droq->ops.poll_mode) { - droq->ops.napi_fn(droq); - } else { - if (ret & MSIX_PO_INT) { - tasklet_schedule(&oct_priv->droq_tasklet); - return 1; - } - /* this will be flushed periodically by check iq db */ - if (ret & MSIX_PI_INT) - return 0; - } - return 0; -} - /** * \brief Droq packet processor sceduler * @param oct octeon device @@ -957,22 +930,6 @@ static void liquidio_schedule_droq_pkt_handlers(struct octeon_device *oct) } } -static irqreturn_t -liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev) -{ - u64 ret; - struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev; - struct octeon_device *oct = ioq_vector->oct_dev; - struct octeon_droq *droq = oct->droq[ioq_vector->droq_index]; - - ret = oct->fn_list.msix_interrupt_handler(ioq_vector); - - if ((ret & MSIX_PO_INT) || (ret & MSIX_PI_INT)) - liquidio_schedule_msix_droq_pkt_handler(droq, ret); - - return IRQ_HANDLED; -} - /** * \brief Interrupt handler for octeon * @param irq unused diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 2fc2da3a8018..688b438e3e19 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -107,12 +107,6 @@ struct octnic_gather { dma_addr_t sg_dma_ptr; }; -struct octeon_device_priv { - /* Tasklet structures for this device. */ - struct tasklet_struct droq_tasklet; - unsigned long napi_mask; -}; - static int liquidio_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void liquidio_vf_remove(struct pci_dev *pdev); @@ -648,45 +642,6 @@ static void update_link_status(struct net_device *netdev, } } -static -int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) -{ - struct octeon_device *oct = droq->oct_dev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; - - if (droq->ops.poll_mode) { - droq->ops.napi_fn(droq); - } else { - if (ret & MSIX_PO_INT) { - dev_err(&oct->pci_dev->dev, - "should not come here should not get rx when poll mode = 0 for vf\n"); - tasklet_schedule(&oct_priv->droq_tasklet); - return 1; - } - /* this will be flushed periodically by check iq db */ - if (ret & MSIX_PI_INT) - return 0; - } - return 0; -} - -static irqreturn_t -liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev) -{ - struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev; - struct octeon_device *oct = ioq_vector->oct_dev; - struct octeon_droq *droq = oct->droq[ioq_vector->droq_index]; - u64 ret; - - ret = oct->fn_list.msix_interrupt_handler(ioq_vector); - - if ((ret & MSIX_PO_INT) || (ret & MSIX_PI_INT)) - liquidio_schedule_msix_droq_pkt_handler(droq, ret); - - return IRQ_HANDLED; -} - /** * \brief Setup interrupt for octeon device * @param oct octeon device diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_main.h b/drivers/net/ethernet/cavium/liquidio/octeon_main.h index 7ccffbb0019e..32ef3a7d88d8 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_main.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_main.h @@ -35,6 +35,12 @@ #define DRV_NAME "LiquidIO" +struct octeon_device_priv { + /** Tasklet structures for this device. */ + struct tasklet_struct droq_tasklet; + unsigned long napi_mask; +}; + /** This structure is used by NIC driver to store information required * to free the sk_buff when the packet has been fetched by Octeon. * Bytes offset below assume worst-case of a 64-bit system. diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index d4b39305ad68..7454d711dd86 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -169,6 +169,9 @@ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr); int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx); +irqreturn_t liquidio_msix_intr_handler(int irq __attribute__((unused)), + void *dev); + /** * \brief Register ethtool operations * @param netdev pointer to network device -- cgit v1.2.3 From 5ef4ddb3397d95eef2a71b063913dbfef72c6d7b Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Tue, 15 Aug 2017 12:46:11 -0700 Subject: liquidio: moved liquidio_legacy_intr_handler to lio_core.c Moving liquidio_legacy_intr_handler to lio_core.c Signed-off-by: Intiyaz Basha Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 55 +++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 56 ---------------------- .../net/ethernet/cavium/liquidio/octeon_network.h | 2 + 3 files changed, 57 insertions(+), 56 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 03746d8f2778..5c5f957f9d40 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -829,3 +829,58 @@ liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev) return IRQ_HANDLED; } + +/** + * \brief Droq packet processor sceduler + * @param oct octeon device + */ +static void liquidio_schedule_droq_pkt_handlers(struct octeon_device *oct) +{ + struct octeon_device_priv *oct_priv = + (struct octeon_device_priv *)oct->priv; + struct octeon_droq *droq; + u64 oq_no; + + if (oct->int_status & OCT_DEV_INTR_PKT_DATA) { + for (oq_no = 0; oq_no < MAX_OCTEON_OUTPUT_QUEUES(oct); + oq_no++) { + if (!(oct->droq_intr & BIT_ULL(oq_no))) + continue; + + droq = oct->droq[oq_no]; + + if (droq->ops.poll_mode) { + droq->ops.napi_fn(droq); + oct_priv->napi_mask |= (1 << oq_no); + } else { + tasklet_schedule(&oct_priv->droq_tasklet); + } + } + } +} + +/** + * \brief Interrupt handler for octeon + * @param irq unused + * @param dev octeon device + */ +irqreturn_t liquidio_legacy_intr_handler(int irq __attribute__((unused)), + void *dev) +{ + struct octeon_device *oct = (struct octeon_device *)dev; + irqreturn_t ret; + + /* Disable our interrupts for the duration of ISR */ + oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); + + ret = oct->fn_list.process_interrupt_regs(oct); + + if (ret == IRQ_HANDLED) + liquidio_schedule_droq_pkt_handlers(oct); + + /* Re-enable our interrupts */ + if (!(atomic_read(&oct->status) == OCT_DEV_IN_RESET)) + oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR); + + return ret; +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 96ba5ec756ad..478144ddebe2 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -901,62 +901,6 @@ static inline void update_link_status(struct net_device *netdev, } } -/** - * \brief Droq packet processor sceduler - * @param oct octeon device - */ -static void liquidio_schedule_droq_pkt_handlers(struct octeon_device *oct) -{ - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; - u64 oq_no; - struct octeon_droq *droq; - - if (oct->int_status & OCT_DEV_INTR_PKT_DATA) { - for (oq_no = 0; oq_no < MAX_OCTEON_OUTPUT_QUEUES(oct); - oq_no++) { - if (!(oct->droq_intr & BIT_ULL(oq_no))) - continue; - - droq = oct->droq[oq_no]; - - if (droq->ops.poll_mode) { - droq->ops.napi_fn(droq); - oct_priv->napi_mask |= (1 << oq_no); - } else { - tasklet_schedule(&oct_priv->droq_tasklet); - } - } - } -} - -/** - * \brief Interrupt handler for octeon - * @param irq unused - * @param dev octeon device - */ -static -irqreturn_t liquidio_legacy_intr_handler(int irq __attribute__((unused)), - void *dev) -{ - struct octeon_device *oct = (struct octeon_device *)dev; - irqreturn_t ret; - - /* Disable our interrupts for the duration of ISR */ - oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); - - ret = oct->fn_list.process_interrupt_regs(oct); - - if (ret == IRQ_HANDLED) - liquidio_schedule_droq_pkt_handlers(oct); - - /* Re-enable our interrupts */ - if (!(atomic_read(&oct->status) == OCT_DEV_IN_RESET)) - oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR); - - return ret; -} - /** * \brief Setup interrupt for octeon device * @param oct octeon device diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index 7454d711dd86..de5eecb56a15 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -172,6 +172,8 @@ int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx); irqreturn_t liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev); +irqreturn_t liquidio_legacy_intr_handler(int irq __attribute__((unused)), + void *dev); /** * \brief Register ethtool operations * @param netdev pointer to network device -- cgit v1.2.3 From 14aec73aabcedb6be30f6a541f81295f5df44fbf Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Tue, 15 Aug 2017 12:46:15 -0700 Subject: liquidio: moved octeon_setup_interrupt to lio_core.c Moving common octeon_setup_interrupt to lio_core.c Signed-off-by: Intiyaz Basha Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 200 +++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 163 ----------------- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 98 ---------- .../net/ethernet/cavium/liquidio/octeon_network.h | 4 +- 4 files changed, 202 insertions(+), 263 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 5c5f957f9d40..217200ceecc7 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -864,6 +864,7 @@ static void liquidio_schedule_droq_pkt_handlers(struct octeon_device *oct) * @param irq unused * @param dev octeon device */ +static irqreturn_t liquidio_legacy_intr_handler(int irq __attribute__((unused)), void *dev) { @@ -884,3 +885,202 @@ irqreturn_t liquidio_legacy_intr_handler(int irq __attribute__((unused)), return ret; } + +/** + * \brief Setup interrupt for octeon device + * @param oct octeon device + * + * Enable interrupt in Octeon device as given in the PCI interrupt mask. + */ +int octeon_setup_interrupt(struct octeon_device *oct) +{ + struct msix_entry *msix_entries; + char *queue_irq_names = NULL; + int i, num_interrupts = 0; + int num_alloc_ioq_vectors; + char *aux_irq_name = NULL; + int num_ioq_vectors; + int irqret, err; + + if (oct->msix_on) { + if (OCTEON_CN23XX_PF(oct)) { + oct->num_msix_irqs = oct->sriov_info.num_pf_rings; + num_interrupts = MAX_IOQ_INTERRUPTS_PER_PF + 1; + + /* one non ioq interrupt for handling + * sli_mac_pf_int_sum + */ + oct->num_msix_irqs += 1; + } else if (OCTEON_CN23XX_VF(oct)) { + oct->num_msix_irqs = oct->sriov_info.rings_per_vf; + num_interrupts = MAX_IOQ_INTERRUPTS_PER_VF; + } + + /* allocate storage for the names assigned to each irq */ + oct->irq_name_storage = + kcalloc(num_interrupts, INTRNAMSIZ, GFP_KERNEL); + if (!oct->irq_name_storage) { + dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n"); + return -ENOMEM; + } + + queue_irq_names = oct->irq_name_storage; + + if (OCTEON_CN23XX_PF(oct)) + aux_irq_name = &queue_irq_names + [IRQ_NAME_OFF(MAX_IOQ_INTERRUPTS_PER_PF)]; + + oct->msix_entries = kcalloc(oct->num_msix_irqs, + sizeof(struct msix_entry), + GFP_KERNEL); + if (!oct->msix_entries) { + dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n"); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return -ENOMEM; + } + + msix_entries = (struct msix_entry *)oct->msix_entries; + + /*Assumption is that pf msix vectors start from pf srn to pf to + * trs and not from 0. if not change this code + */ + if (OCTEON_CN23XX_PF(oct)) { + for (i = 0; i < oct->num_msix_irqs - 1; i++) + msix_entries[i].entry = + oct->sriov_info.pf_srn + i; + + msix_entries[oct->num_msix_irqs - 1].entry = + oct->sriov_info.trs; + } else if (OCTEON_CN23XX_VF(oct)) { + for (i = 0; i < oct->num_msix_irqs; i++) + msix_entries[i].entry = i; + } + num_alloc_ioq_vectors = pci_enable_msix_range( + oct->pci_dev, msix_entries, + oct->num_msix_irqs, + oct->num_msix_irqs); + if (num_alloc_ioq_vectors < 0) { + dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); + kfree(oct->msix_entries); + oct->msix_entries = NULL; + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return num_alloc_ioq_vectors; + } + + dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); + + num_ioq_vectors = oct->num_msix_irqs; + /** For PF, there is one non-ioq interrupt handler */ + if (OCTEON_CN23XX_PF(oct)) { + num_ioq_vectors -= 1; + + snprintf(aux_irq_name, INTRNAMSIZ, + "LiquidIO%u-pf%u-aux", oct->octeon_id, + oct->pf_num); + irqret = request_irq( + msix_entries[num_ioq_vectors].vector, + liquidio_legacy_intr_handler, 0, + aux_irq_name, oct); + if (irqret) { + dev_err(&oct->pci_dev->dev, + "Request_irq failed for MSIX interrupt Error: %d\n", + irqret); + pci_disable_msix(oct->pci_dev); + kfree(oct->msix_entries); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + oct->msix_entries = NULL; + return irqret; + } + } + for (i = 0 ; i < num_ioq_vectors ; i++) { + if (OCTEON_CN23XX_PF(oct)) + snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], + INTRNAMSIZ, "LiquidIO%u-pf%u-rxtx-%u", + oct->octeon_id, oct->pf_num, i); + + if (OCTEON_CN23XX_VF(oct)) + snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], + INTRNAMSIZ, "LiquidIO%u-vf%u-rxtx-%u", + oct->octeon_id, oct->vf_num, i); + + irqret = request_irq(msix_entries[i].vector, + liquidio_msix_intr_handler, 0, + &queue_irq_names[IRQ_NAME_OFF(i)], + &oct->ioq_vector[i]); + + if (irqret) { + dev_err(&oct->pci_dev->dev, + "Request_irq failed for MSIX interrupt Error: %d\n", + irqret); + /** Freeing the non-ioq irq vector here . */ + free_irq(msix_entries[num_ioq_vectors].vector, + oct); + + while (i) { + i--; + /** clearing affinity mask. */ + irq_set_affinity_hint( + msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + } + pci_disable_msix(oct->pci_dev); + kfree(oct->msix_entries); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + oct->msix_entries = NULL; + return irqret; + } + oct->ioq_vector[i].vector = msix_entries[i].vector; + /* assign the cpu mask for this msix interrupt vector */ + irq_set_affinity_hint(msix_entries[i].vector, + &oct->ioq_vector[i].affinity_mask + ); + } + dev_dbg(&oct->pci_dev->dev, "OCTEON[%d]: MSI-X enabled\n", + oct->octeon_id); + } else { + err = pci_enable_msi(oct->pci_dev); + if (err) + dev_warn(&oct->pci_dev->dev, "Reverting to legacy interrupts. Error: %d\n", + err); + else + oct->flags |= LIO_FLAG_MSI_ENABLED; + + /* allocate storage for the names assigned to the irq */ + oct->irq_name_storage = kcalloc(1, INTRNAMSIZ, GFP_KERNEL); + if (!oct->irq_name_storage) + return -ENOMEM; + + queue_irq_names = oct->irq_name_storage; + + if (OCTEON_CN23XX_PF(oct)) + snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ, + "LiquidIO%u-pf%u-rxtx-%u", + oct->octeon_id, oct->pf_num, 0); + + if (OCTEON_CN23XX_VF(oct)) + snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ, + "LiquidIO%u-vf%u-rxtx-%u", + oct->octeon_id, oct->vf_num, 0); + + irqret = request_irq(oct->pci_dev->irq, + liquidio_legacy_intr_handler, + IRQF_SHARED, + &queue_irq_names[IRQ_NAME_OFF(0)], oct); + if (irqret) { + if (oct->flags & LIO_FLAG_MSI_ENABLED) + pci_disable_msi(oct->pci_dev); + dev_err(&oct->pci_dev->dev, "Request IRQ failed with code: %d\n", + irqret); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return irqret; + } + } + return 0; +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 478144ddebe2..82ed201e7a30 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -901,169 +901,6 @@ static inline void update_link_status(struct net_device *netdev, } } -/** - * \brief Setup interrupt for octeon device - * @param oct octeon device - * - * Enable interrupt in Octeon device as given in the PCI interrupt mask. - */ -static int octeon_setup_interrupt(struct octeon_device *oct) -{ - int irqret, err; - struct msix_entry *msix_entries; - int i; - int num_ioq_vectors; - int num_alloc_ioq_vectors; - char *queue_irq_names = NULL; - char *aux_irq_name = NULL; - - if (OCTEON_CN23XX_PF(oct) && oct->msix_on) { - oct->num_msix_irqs = oct->sriov_info.num_pf_rings; - /* one non ioq interrupt for handling sli_mac_pf_int_sum */ - oct->num_msix_irqs += 1; - - /* allocate storage for the names assigned to each irq */ - oct->irq_name_storage = - kcalloc((MAX_IOQ_INTERRUPTS_PER_PF + 1), INTRNAMSIZ, - GFP_KERNEL); - if (!oct->irq_name_storage) { - dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n"); - return -ENOMEM; - } - - queue_irq_names = oct->irq_name_storage; - aux_irq_name = &queue_irq_names - [IRQ_NAME_OFF(MAX_IOQ_INTERRUPTS_PER_PF)]; - - oct->msix_entries = kcalloc( - oct->num_msix_irqs, sizeof(struct msix_entry), GFP_KERNEL); - if (!oct->msix_entries) { - dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n"); - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return -ENOMEM; - } - - msix_entries = (struct msix_entry *)oct->msix_entries; - /*Assumption is that pf msix vectors start from pf srn to pf to - * trs and not from 0. if not change this code - */ - for (i = 0; i < oct->num_msix_irqs - 1; i++) - msix_entries[i].entry = oct->sriov_info.pf_srn + i; - msix_entries[oct->num_msix_irqs - 1].entry = - oct->sriov_info.trs; - num_alloc_ioq_vectors = pci_enable_msix_range( - oct->pci_dev, msix_entries, - oct->num_msix_irqs, - oct->num_msix_irqs); - if (num_alloc_ioq_vectors < 0) { - dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return num_alloc_ioq_vectors; - } - dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); - - num_ioq_vectors = oct->num_msix_irqs; - - /** For PF, there is one non-ioq interrupt handler */ - num_ioq_vectors -= 1; - - snprintf(aux_irq_name, INTRNAMSIZ, - "LiquidIO%u-pf%u-aux", oct->octeon_id, oct->pf_num); - irqret = request_irq(msix_entries[num_ioq_vectors].vector, - liquidio_legacy_intr_handler, 0, - aux_irq_name, oct); - if (irqret) { - dev_err(&oct->pci_dev->dev, - "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", - irqret); - pci_disable_msix(oct->pci_dev); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return irqret; - } - - for (i = 0; i < num_ioq_vectors; i++) { - snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], INTRNAMSIZ, - "LiquidIO%u-pf%u-rxtx-%u", - oct->octeon_id, oct->pf_num, i); - - irqret = request_irq(msix_entries[i].vector, - liquidio_msix_intr_handler, 0, - &queue_irq_names[IRQ_NAME_OFF(i)], - &oct->ioq_vector[i]); - if (irqret) { - dev_err(&oct->pci_dev->dev, - "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", - irqret); - /** Freeing the non-ioq irq vector here . */ - free_irq(msix_entries[num_ioq_vectors].vector, - oct); - - while (i) { - i--; - /** clearing affinity mask. */ - irq_set_affinity_hint( - msix_entries[i].vector, NULL); - free_irq(msix_entries[i].vector, - &oct->ioq_vector[i]); - } - pci_disable_msix(oct->pci_dev); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return irqret; - } - oct->ioq_vector[i].vector = msix_entries[i].vector; - /* assign the cpu mask for this msix interrupt vector */ - irq_set_affinity_hint( - msix_entries[i].vector, - (&oct->ioq_vector[i].affinity_mask)); - } - dev_dbg(&oct->pci_dev->dev, "OCTEON[%d]: MSI-X enabled\n", - oct->octeon_id); - } else { - err = pci_enable_msi(oct->pci_dev); - if (err) - dev_warn(&oct->pci_dev->dev, "Reverting to legacy interrupts. Error: %d\n", - err); - else - oct->flags |= LIO_FLAG_MSI_ENABLED; - - /* allocate storage for the names assigned to the irq */ - oct->irq_name_storage = kcalloc(1, INTRNAMSIZ, GFP_KERNEL); - if (!oct->irq_name_storage) - return -ENOMEM; - - queue_irq_names = oct->irq_name_storage; - - snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ, - "LiquidIO%u-pf%u-rxtx-%u", - oct->octeon_id, oct->pf_num, 0); - - irqret = request_irq(oct->pci_dev->irq, - liquidio_legacy_intr_handler, - IRQF_SHARED, - &queue_irq_names[IRQ_NAME_OFF(0)], oct); - if (irqret) { - if (oct->flags & LIO_FLAG_MSI_ENABLED) - pci_disable_msi(oct->pci_dev); - dev_err(&oct->pci_dev->dev, "Request IRQ failed with code: %d\n", - irqret); - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return irqret; - } - } - return 0; -} - static struct octeon_device *get_other_octeon_device(struct octeon_device *oct) { struct octeon_device *other_oct; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 688b438e3e19..a2f0d628958d 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -642,104 +642,6 @@ static void update_link_status(struct net_device *netdev, } } -/** - * \brief Setup interrupt for octeon device - * @param oct octeon device - * - * Enable interrupt in Octeon device as given in the PCI interrupt mask. - */ -static int octeon_setup_interrupt(struct octeon_device *oct) -{ - struct msix_entry *msix_entries; - char *queue_irq_names = NULL; - int num_alloc_ioq_vectors; - int num_ioq_vectors; - int irqret; - int i; - - if (oct->msix_on) { - oct->num_msix_irqs = oct->sriov_info.rings_per_vf; - - /* allocate storage for the names assigned to each irq */ - oct->irq_name_storage = - kcalloc(MAX_IOQ_INTERRUPTS_PER_VF, INTRNAMSIZ, - GFP_KERNEL); - if (!oct->irq_name_storage) { - dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n"); - return -ENOMEM; - } - - queue_irq_names = oct->irq_name_storage; - - oct->msix_entries = kcalloc( - oct->num_msix_irqs, sizeof(struct msix_entry), GFP_KERNEL); - if (!oct->msix_entries) { - dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n"); - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return -ENOMEM; - } - - msix_entries = (struct msix_entry *)oct->msix_entries; - - for (i = 0; i < oct->num_msix_irqs; i++) - msix_entries[i].entry = i; - num_alloc_ioq_vectors = pci_enable_msix_range( - oct->pci_dev, msix_entries, - oct->num_msix_irqs, - oct->num_msix_irqs); - if (num_alloc_ioq_vectors < 0) { - dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return num_alloc_ioq_vectors; - } - dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); - - num_ioq_vectors = oct->num_msix_irqs; - - for (i = 0; i < num_ioq_vectors; i++) { - snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], INTRNAMSIZ, - "LiquidIO%u-vf%u-rxtx-%u", - oct->octeon_id, oct->vf_num, i); - - irqret = request_irq(msix_entries[i].vector, - liquidio_msix_intr_handler, 0, - &queue_irq_names[IRQ_NAME_OFF(i)], - &oct->ioq_vector[i]); - if (irqret) { - dev_err(&oct->pci_dev->dev, - "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", - irqret); - - while (i) { - i--; - irq_set_affinity_hint( - msix_entries[i].vector, NULL); - free_irq(msix_entries[i].vector, - &oct->ioq_vector[i]); - } - pci_disable_msix(oct->pci_dev); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return irqret; - } - oct->ioq_vector[i].vector = msix_entries[i].vector; - /* assign the cpu mask for this msix interrupt vector */ - irq_set_affinity_hint( - msix_entries[i].vector, - (&oct->ioq_vector[i].affinity_mask)); - } - dev_dbg(&oct->pci_dev->dev, - "OCTEON[%d]: MSI-X enabled\n", oct->octeon_id); - } - return 0; -} - /** * \brief PCI probe handler * @param pdev PCI device structure diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index de5eecb56a15..ad29550c91f7 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -172,8 +172,8 @@ int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx); irqreturn_t liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev); -irqreturn_t liquidio_legacy_intr_handler(int irq __attribute__((unused)), - void *dev); +int octeon_setup_interrupt(struct octeon_device *oct); + /** * \brief Register ethtool operations * @param netdev pointer to network device -- cgit v1.2.3 From a82457f1b4bd37ab20be956f14bb18e679fde124 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Tue, 15 Aug 2017 12:46:18 -0700 Subject: liquidio: added support for ethtool --set-channels feature adding support for ethtool --set-channels feature Signed-off-by: Intiyaz Basha Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 17 +- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 190 +++++++++++++++++++-- drivers/net/ethernet/cavium/liquidio/lio_main.c | 33 ++-- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 28 +-- .../net/ethernet/cavium/liquidio/liquidio_common.h | 3 + .../net/ethernet/cavium/liquidio/octeon_network.h | 5 +- 6 files changed, 226 insertions(+), 50 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 217200ceecc7..8b8e78f04f94 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -275,6 +275,11 @@ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr) netif_info(lio, probe, lio->netdev, "Set RX/TX flow control parameters\n"); break; + case OCTNET_CMD_QUEUE_COUNT_CTL: + netif_info(lio, probe, lio->netdev, "Queue count updated to %d\n", + nctrl->ncmd.s.param1); + break; + default: dev_err(&oct->pci_dev->dev, "%s Unknown cmd %d\n", __func__, nctrl->ncmd.s.cmd); @@ -689,7 +694,8 @@ static int liquidio_napi_poll(struct napi_struct *napi, int budget) * an input queue is for egress packets, and output queues * are for ingress packets. */ -int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx) +int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx, + u32 num_iqs, u32 num_oqs) { struct octeon_droq_ops droq_ops; struct net_device *netdev; @@ -717,7 +723,7 @@ int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx) cpu_id_modulus = num_present_cpus(); /* set up DROQs. */ - for (q = 0; q < lio->linfo.num_rxpciq; q++) { + for (q = 0; q < num_oqs; q++) { q_no = lio->linfo.rxpciq[q].s.q_no; dev_dbg(&octeon_dev->pci_dev->dev, "%s index:%d linfo.rxpciq.s.q_no:%d\n", @@ -761,7 +767,7 @@ int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx) } /* set up IQs. */ - for (q = 0; q < lio->linfo.num_txpciq; q++) { + for (q = 0; q < num_iqs; q++) { num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF( octeon_get_conf(octeon_dev), lio->ifidx); retval = octeon_setup_iq(octeon_dev, ifidx, q, @@ -892,7 +898,7 @@ irqreturn_t liquidio_legacy_intr_handler(int irq __attribute__((unused)), * * Enable interrupt in Octeon device as given in the PCI interrupt mask. */ -int octeon_setup_interrupt(struct octeon_device *oct) +int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs) { struct msix_entry *msix_entries; char *queue_irq_names = NULL; @@ -902,9 +908,9 @@ int octeon_setup_interrupt(struct octeon_device *oct) int num_ioq_vectors; int irqret, err; + oct->num_msix_irqs = num_ioqs; if (oct->msix_on) { if (OCTEON_CN23XX_PF(oct)) { - oct->num_msix_irqs = oct->sriov_info.num_pf_rings; num_interrupts = MAX_IOQ_INTERRUPTS_PER_PF + 1; /* one non ioq interrupt for handling @@ -912,7 +918,6 @@ int octeon_setup_interrupt(struct octeon_device *oct) */ oct->num_msix_irqs += 1; } else if (OCTEON_CN23XX_VF(oct)) { - oct->num_msix_irqs = oct->sriov_info.rings_per_vf; num_interrupts = MAX_IOQ_INTERRUPTS_PER_VF; } diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index a59c8ccebd10..08aa06c90d46 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -31,6 +31,7 @@ #include "cn23xx_pf_device.h" #include "cn23xx_vf_device.h" +static int lio_reset_queues(struct net_device *netdev, uint32_t num_qs); static int octnet_get_link_stats(struct net_device *netdev); struct oct_intrmod_context { @@ -300,6 +301,35 @@ lio_get_vf_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) strncpy(drvinfo->bus_info, pci_name(oct->pci_dev), 32); } +static int +lio_send_queue_count_update(struct net_device *netdev, uint32_t num_queues) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + int ret = 0; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = OCTNET_CMD_QUEUE_COUNT_CTL; + nctrl.ncmd.s.param1 = num_queues; + nctrl.ncmd.s.param2 = num_queues; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.wait_time = 100; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, "Failed to send Queue reset command (ret: 0x%x)\n", + ret); + return -1; + } + + return 0; +} + static void lio_ethtool_get_channels(struct net_device *dev, struct ethtool_channels *channel) @@ -307,6 +337,7 @@ lio_ethtool_get_channels(struct net_device *dev, struct lio *lio = GET_LIO(dev); struct octeon_device *oct = lio->oct_dev; u32 max_rx = 0, max_tx = 0, tx_count = 0, rx_count = 0; + u32 combined_count = 0, max_combined = 0; if (OCTEON_CN6XXX(oct)) { struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx); @@ -316,22 +347,137 @@ lio_ethtool_get_channels(struct net_device *dev, rx_count = CFG_GET_NUM_RXQS_NIC_IF(conf6x, lio->ifidx); tx_count = CFG_GET_NUM_TXQS_NIC_IF(conf6x, lio->ifidx); } else if (OCTEON_CN23XX_PF(oct)) { - - max_rx = oct->sriov_info.num_pf_rings; - max_tx = oct->sriov_info.num_pf_rings; - rx_count = lio->linfo.num_rxpciq; - tx_count = lio->linfo.num_txpciq; + max_combined = lio->linfo.num_txpciq; + combined_count = oct->num_iqs; } else if (OCTEON_CN23XX_VF(oct)) { - max_tx = oct->sriov_info.rings_per_vf; - max_rx = oct->sriov_info.rings_per_vf; - rx_count = lio->linfo.num_rxpciq; - tx_count = lio->linfo.num_txpciq; + u64 reg_val = 0ULL; + u64 ctrl = CN23XX_VF_SLI_IQ_PKT_CONTROL64(0); + + reg_val = octeon_read_csr64(oct, ctrl); + reg_val = reg_val >> CN23XX_PKT_INPUT_CTL_RPVF_POS; + max_combined = reg_val & CN23XX_PKT_INPUT_CTL_RPVF_MASK; + combined_count = oct->num_iqs; } channel->max_rx = max_rx; channel->max_tx = max_tx; + channel->max_combined = max_combined; channel->rx_count = rx_count; channel->tx_count = tx_count; + channel->combined_count = combined_count; +} + +static int +lio_irq_reallocate_irqs(struct octeon_device *oct, uint32_t num_ioqs) +{ + struct msix_entry *msix_entries; + int num_msix_irqs = 0; + int i; + + if (!oct->msix_on) + return 0; + + /* Disable the input and output queues now. No more packets will + * arrive from Octeon. + */ + oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); + + if (oct->msix_on) { + if (OCTEON_CN23XX_PF(oct)) + num_msix_irqs = oct->num_msix_irqs - 1; + else if (OCTEON_CN23XX_VF(oct)) + num_msix_irqs = oct->num_msix_irqs; + + msix_entries = (struct msix_entry *)oct->msix_entries; + for (i = 0; i < num_msix_irqs; i++) { + if (oct->ioq_vector[i].vector) { + /* clear the affinity_cpumask */ + irq_set_affinity_hint(msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + oct->ioq_vector[i].vector = 0; + } + } + + /* non-iov vector's argument is oct struct */ + if (OCTEON_CN23XX_PF(oct)) + free_irq(msix_entries[i].vector, oct); + + pci_disable_msix(oct->pci_dev); + kfree(oct->msix_entries); + oct->msix_entries = NULL; + } + + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + if (octeon_setup_interrupt(oct, num_ioqs)) { + dev_info(&oct->pci_dev->dev, "Setup interuupt failed\n"); + return 1; + } + + /* Enable Octeon device interrupts */ + oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR); + + return 0; +} + +static int +lio_ethtool_set_channels(struct net_device *dev, + struct ethtool_channels *channel) +{ + u32 combined_count, max_combined; + struct lio *lio = GET_LIO(dev); + struct octeon_device *oct = lio->oct_dev; + int stopped = 0; + + if (strcmp(oct->fw_info.liquidio_firmware_version, "1.6.1") < 0) { + dev_err(&oct->pci_dev->dev, "Minimum firmware version required is 1.6.1\n"); + return -EINVAL; + } + + if (!channel->combined_count || channel->other_count || + channel->rx_count || channel->tx_count) + return -EINVAL; + + combined_count = channel->combined_count; + + if (OCTEON_CN23XX_PF(oct)) { + max_combined = channel->max_combined; + } else if (OCTEON_CN23XX_VF(oct)) { + u64 reg_val = 0ULL; + u64 ctrl = CN23XX_VF_SLI_IQ_PKT_CONTROL64(0); + + reg_val = octeon_read_csr64(oct, ctrl); + reg_val = reg_val >> CN23XX_PKT_INPUT_CTL_RPVF_POS; + max_combined = reg_val & CN23XX_PKT_INPUT_CTL_RPVF_MASK; + } else { + return -EINVAL; + } + + if (combined_count > max_combined || combined_count < 1) + return -EINVAL; + + if (combined_count == oct->num_iqs) + return 0; + + ifstate_set(lio, LIO_IFSTATE_RESETTING); + + if (netif_running(dev)) { + dev->netdev_ops->ndo_stop(dev); + stopped = 1; + } + + if (lio_reset_queues(dev, combined_count)) + return -EINVAL; + + lio_irq_reallocate_irqs(oct, combined_count); + if (stopped) + dev->netdev_ops->ndo_open(dev); + + ifstate_reset(lio, LIO_IFSTATE_RESETTING); + + return 0; } static int lio_get_eeprom_len(struct net_device *netdev) @@ -664,15 +810,12 @@ lio_ethtool_get_ringparam(struct net_device *netdev, ering->rx_jumbo_max_pending = 0; } -static int lio_reset_queues(struct net_device *netdev) +static int lio_reset_queues(struct net_device *netdev, uint32_t num_qs) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; struct napi_struct *napi, *n; - int i; - - dev_dbg(&oct->pci_dev->dev, "%s:%d ifidx %d\n", - __func__, __LINE__, lio->ifidx); + int i, update = 0; if (wait_for_pending_requests(oct)) dev_err(&oct->pci_dev->dev, "There were pending requests\n"); @@ -693,6 +836,12 @@ static int lio_reset_queues(struct net_device *netdev) list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) netif_napi_del(napi); + if (num_qs != oct->num_iqs) { + netif_set_real_num_rx_queues(netdev, num_qs); + netif_set_real_num_tx_queues(netdev, num_qs); + update = 1; + } + for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { if (!(oct->io_qmask.oq & BIT_ULL(i))) continue; @@ -710,7 +859,7 @@ static int lio_reset_queues(struct net_device *netdev) return -1; } - if (liquidio_setup_io_queues(oct, 0)) { + if (liquidio_setup_io_queues(oct, 0, num_qs, num_qs)) { dev_err(&oct->pci_dev->dev, "IO queues initialization failed\n"); return -1; } @@ -721,6 +870,9 @@ static int lio_reset_queues(struct net_device *netdev) return -1; } + if (update && lio_send_queue_count_update(netdev, num_qs)) + return -1; + return 0; } @@ -764,7 +916,7 @@ static int lio_ethtool_set_ringparam(struct net_device *netdev, CFG_SET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx, rx_count); - if (lio_reset_queues(netdev)) + if (lio_reset_queues(netdev, lio->linfo.num_txpciq)) goto err_lio_reset_queues; if (stopped) @@ -1194,7 +1346,7 @@ static void lio_vf_get_ethtool_stats(struct net_device *netdev, /* lio->link_changes */ data[i++] = CVM_CAST64(lio->link_changes); - for (vj = 0; vj < lio->linfo.num_txpciq; vj++) { + for (vj = 0; vj < oct_dev->num_iqs; vj++) { j = lio->linfo.txpciq[vj].s.q_no; /* packets to network port */ @@ -1236,7 +1388,7 @@ static void lio_vf_get_ethtool_stats(struct net_device *netdev, } /* RX */ - for (vj = 0; vj < lio->linfo.num_rxpciq; vj++) { + for (vj = 0; vj < oct_dev->num_oqs; vj++) { j = lio->linfo.rxpciq[vj].s.q_no; /* packets send to TCP/IP network stack */ @@ -2705,6 +2857,7 @@ static const struct ethtool_ops lio_ethtool_ops = { .get_ringparam = lio_ethtool_get_ringparam, .set_ringparam = lio_ethtool_set_ringparam, .get_channels = lio_ethtool_get_channels, + .set_channels = lio_ethtool_set_channels, .set_phys_id = lio_set_phys_id, .get_eeprom_len = lio_get_eeprom_len, .get_eeprom = lio_get_eeprom, @@ -2731,6 +2884,7 @@ static const struct ethtool_ops lio_vf_ethtool_ops = { .get_ringparam = lio_ethtool_get_ringparam, .set_ringparam = lio_ethtool_set_ringparam, .get_channels = lio_ethtool_get_channels, + .set_channels = lio_ethtool_set_channels, .get_strings = lio_vf_get_strings, .get_ethtool_stats = lio_vf_get_ethtool_stats, .get_regs_len = lio_get_regs_len, diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 82ed201e7a30..0eea6a2d0200 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -560,7 +560,7 @@ static inline void txqs_wake(struct net_device *netdev) for (i = 0; i < netdev->num_tx_queues; i++) { int qno = lio->linfo.txpciq[i % - (lio->linfo.num_txpciq)].s.q_no; + lio->oct_dev->num_iqs].s.q_no; if (__netif_subqueue_stopped(netdev, i)) { INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno, @@ -640,7 +640,7 @@ static inline int check_txq_status(struct lio *lio) /* check each sub-queue state */ for (q = 0; q < numqs; q++) { iq = lio->linfo.txpciq[q % - (lio->linfo.num_txpciq)].s.q_no; + lio->oct_dev->num_iqs].s.q_no; if (octnet_iq_is_full(lio->oct_dev, iq)) continue; if (__netif_subqueue_stopped(lio->netdev, q)) { @@ -1181,11 +1181,15 @@ static void octeon_destroy_resources(struct octeon_device *oct) if (oct->msix_on) { msix_entries = (struct msix_entry *)oct->msix_entries; for (i = 0; i < oct->num_msix_irqs - 1; i++) { - /* clear the affinity_cpumask */ - irq_set_affinity_hint(msix_entries[i].vector, - NULL); - free_irq(msix_entries[i].vector, - &oct->ioq_vector[i]); + if (oct->ioq_vector[i].vector) { + /* clear the affinity_cpumask */ + irq_set_affinity_hint( + msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + oct->ioq_vector[i].vector = 0; + } } /* non-iov vector's argument is oct struct */ free_irq(msix_entries[i].vector, oct); @@ -1465,7 +1469,7 @@ static int liquidio_stop_nic_module(struct octeon_device *oct) for (i = 0; i < oct->ifcount; i++) { lio = GET_LIO(oct->props[i].netdev); - for (j = 0; j < lio->linfo.num_rxpciq; j++) + for (j = 0; j < oct->num_oqs; j++) octeon_unregister_droq_ops(oct, lio->linfo.rxpciq[j].s.q_no); } @@ -1605,7 +1609,7 @@ static inline int check_txq_state(struct lio *lio, struct sk_buff *skb) if (netif_is_multiqueue(lio->netdev)) { q = skb->queue_mapping; - iq = lio->linfo.txpciq[(q % (lio->linfo.num_txpciq))].s.q_no; + iq = lio->linfo.txpciq[(q % lio->oct_dev->num_iqs)].s.q_no; } else { iq = lio->txq; q = iq; @@ -2262,7 +2266,7 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) return stats; - for (i = 0; i < lio->linfo.num_txpciq; i++) { + for (i = 0; i < oct->num_iqs; i++) { iq_no = lio->linfo.txpciq[i].s.q_no; iq_stats = &oct->instr_queue[iq_no]->stats; pkts += iq_stats->tx_done; @@ -2278,7 +2282,7 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) drop = 0; bytes = 0; - for (i = 0; i < lio->linfo.num_rxpciq; i++) { + for (i = 0; i < oct->num_oqs; i++) { oq_no = lio->linfo.rxpciq[i].s.q_no; oq_stats = &oct->droq[oq_no]->stats; pkts += oq_stats->rx_pkts_received; @@ -3533,7 +3537,9 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) */ lio->txq = lio->linfo.txpciq[0].s.q_no; lio->rxq = lio->linfo.rxpciq[0].s.q_no; - if (liquidio_setup_io_queues(octeon_dev, i)) { + if (liquidio_setup_io_queues(octeon_dev, i, + lio->linfo.num_txpciq, + lio->linfo.num_rxpciq)) { dev_err(&octeon_dev->pci_dev->dev, "I/O queues creation failed\n"); goto setup_nic_dev_fail; } @@ -4012,7 +4018,8 @@ static int octeon_device_init(struct octeon_device *octeon_dev) /* Setup the interrupt handler and record the INT SUM register address */ - if (octeon_setup_interrupt(octeon_dev)) + if (octeon_setup_interrupt(octeon_dev, + octeon_dev->sriov_info.num_pf_rings)) return 1; /* Enable Octeon device interrupts */ diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index a2f0d628958d..35a977abc7c4 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -342,7 +342,7 @@ static void txqs_wake(struct net_device *netdev) int i; for (i = 0; i < netdev->num_tx_queues; i++) { - int qno = lio->linfo.txpciq[i % (lio->linfo.num_txpciq)] + int qno = lio->linfo.txpciq[i % lio->oct_dev->num_iqs] .s.q_no; if (__netif_subqueue_stopped(netdev, i)) { INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno, @@ -750,10 +750,14 @@ static void octeon_destroy_resources(struct octeon_device *oct) if (oct->msix_on) { msix_entries = (struct msix_entry *)oct->msix_entries; for (i = 0; i < oct->num_msix_irqs; i++) { - irq_set_affinity_hint(msix_entries[i].vector, - NULL); - free_irq(msix_entries[i].vector, - &oct->ioq_vector[i]); + if (oct->ioq_vector[i].vector) { + irq_set_affinity_hint( + msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + oct->ioq_vector[i].vector = 0; + } } pci_disable_msix(oct->pci_dev); kfree(oct->msix_entries); @@ -986,7 +990,7 @@ static int liquidio_stop_nic_module(struct octeon_device *oct) for (i = 0; i < oct->ifcount; i++) { lio = GET_LIO(oct->props[i].netdev); - for (j = 0; j < lio->linfo.num_rxpciq; j++) + for (j = 0; j < oct->num_oqs; j++) octeon_unregister_droq_ops(oct, lio->linfo.rxpciq[j].s.q_no); } @@ -1074,7 +1078,7 @@ static int check_txq_state(struct lio *lio, struct sk_buff *skb) if (netif_is_multiqueue(lio->netdev)) { q = skb->queue_mapping; - iq = lio->linfo.txpciq[(q % (lio->linfo.num_txpciq))].s.q_no; + iq = lio->linfo.txpciq[q % lio->oct_dev->num_iqs].s.q_no; } else { iq = lio->txq; q = iq; @@ -1494,7 +1498,7 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) return stats; - for (i = 0; i < lio->linfo.num_txpciq; i++) { + for (i = 0; i < oct->num_iqs; i++) { iq_no = lio->linfo.txpciq[i].s.q_no; iq_stats = &oct->instr_queue[iq_no]->stats; pkts += iq_stats->tx_done; @@ -1510,7 +1514,7 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) drop = 0; bytes = 0; - for (i = 0; i < lio->linfo.num_rxpciq; i++) { + for (i = 0; i < oct->num_oqs; i++) { oq_no = lio->linfo.rxpciq[i].s.q_no; oq_stats = &oct->droq[oq_no]->stats; pkts += oq_stats->rx_pkts_received; @@ -2465,7 +2469,9 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) /* Copy MAC Address to OS network device structure */ ether_addr_copy(netdev->dev_addr, mac); - if (liquidio_setup_io_queues(octeon_dev, i)) { + if (liquidio_setup_io_queues(octeon_dev, i, + lio->linfo.num_txpciq, + lio->linfo.num_rxpciq)) { dev_err(&octeon_dev->pci_dev->dev, "I/O queues creation failed\n"); goto setup_nic_dev_fail; } @@ -2688,7 +2694,7 @@ static int octeon_device_init(struct octeon_device *oct) LIQUIDIO_VERSION, oct->sriov_info.rings_per_vf); /* Setup the interrupt handler and record the INT SUM register address*/ - if (octeon_setup_interrupt(oct)) + if (octeon_setup_interrupt(oct, oct->sriov_info.rings_per_vf)) return 1; atomic_set(&oct->status, OCT_DEV_INTR_SET_DONE); diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 3b9e3646b971..18d29550e2f8 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -226,6 +226,9 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry, #define OCTNET_CMD_SET_UC_LIST 0x1b #define OCTNET_CMD_SET_VF_LINKSTATE 0x1c + +#define OCTNET_CMD_QUEUE_COUNT_CTL 0x1f + #define OCTNET_CMD_VXLAN_PORT_ADD 0x0 #define OCTNET_CMD_VXLAN_PORT_DEL 0x1 #define OCTNET_CMD_RXCSUM_ENABLE 0x0 diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index ad29550c91f7..9e36319cead6 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -167,12 +167,13 @@ void cleanup_rx_oom_poll_fn(struct net_device *netdev); */ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr); -int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx); +int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx, + u32 num_iqs, u32 num_oqs); irqreturn_t liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev); -int octeon_setup_interrupt(struct octeon_device *oct); +int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs); /** * \brief Register ethtool operations -- cgit v1.2.3 From 120390468b38f04373e67dbc9f361e2bb2996691 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 15 Aug 2017 10:29:16 -0700 Subject: tun/tap: use paren's with sizeof Although sizeof is an operator in C. The kernel coding style convention is to always use it like a function and add parenthesis. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/tap.c | 2 +- drivers/net/tun.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 0d039411e64c..21b71ae947fd 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1215,7 +1215,7 @@ int tap_queue_resize(struct tap_dev *tap) int n = tap->numqueues; int ret, i = 0; - arrays = kmalloc(sizeof *arrays * n, GFP_KERNEL); + arrays = kmalloc_array(n, sizeof(*arrays), GFP_KERNEL); if (!arrays) return -ENOMEM; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 5892284eb8d0..f5017121cd57 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2737,7 +2737,7 @@ static int tun_queue_resize(struct tun_struct *tun) int n = tun->numqueues + tun->numdisabled; int ret, i; - arrays = kmalloc(sizeof *arrays * n, GFP_KERNEL); + arrays = kmalloc_array(n, sizeof(*arrays), GFP_KERNEL); if (!arrays) return -ENOMEM; -- cgit v1.2.3 From a4a765031d007191c36a25edb9df2fa23c8e6496 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 15 Aug 2017 10:29:17 -0700 Subject: virtio: put paren around sizeof Kernel coding style is to put paren around operand of sizeof. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index a3f3c66b4530..4302f313d9a7 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -319,7 +319,7 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi, hdr_len = vi->hdr_len; if (vi->mergeable_rx_bufs) - hdr_padded_len = sizeof *hdr; + hdr_padded_len = sizeof(*hdr); else hdr_padded_len = sizeof(struct padded_vnet_hdr); -- cgit v1.2.3 From 9d2ee98dafd439a768f4cef3757bcce328b86386 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 15 Aug 2017 10:29:18 -0700 Subject: skge: add paren around sizeof arg Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/skge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 5d7d94de4e00..8a835e82256a 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -3516,7 +3516,7 @@ static const char *skge_board_name(const struct skge_hw *hw) if (skge_chips[i].id == hw->chip_id) return skge_chips[i].name; - snprintf(buf, sizeof buf, "chipid 0x%x", hw->chip_id); + snprintf(buf, sizeof(buf), "chipid 0x%x", hw->chip_id); return buf; } -- cgit v1.2.3 From 31975e27a4b5ca3ff1ca42d7d12bc936d3166d4c Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 15 Aug 2017 10:29:19 -0700 Subject: mlx4: sizeof style usage The kernel coding style is to treat sizeof as a function (ie. with parenthesis) not as an operator. Also use kcalloc and kmalloc_array Signed-off-by: Stephen Hemminger Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/alloc.c | 2 +- drivers/net/ethernet/mellanox/mlx4/cmd.c | 4 ++-- drivers/net/ethernet/mellanox/mlx4/en_resources.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 2 +- drivers/net/ethernet/mellanox/mlx4/eq.c | 20 +++++++++--------- drivers/net/ethernet/mellanox/mlx4/fw.c | 2 +- drivers/net/ethernet/mellanox/mlx4/icm.c | 2 +- drivers/net/ethernet/mellanox/mlx4/icm.h | 4 ++-- drivers/net/ethernet/mellanox/mlx4/intf.c | 2 +- drivers/net/ethernet/mellanox/mlx4/main.c | 12 +++++------ drivers/net/ethernet/mellanox/mlx4/mcg.c | 12 +++++------ drivers/net/ethernet/mellanox/mlx4/mr.c | 10 ++++----- drivers/net/ethernet/mellanox/mlx4/qp.c | 12 +++++------ .../net/ethernet/mellanox/mlx4/resource_tracker.c | 24 +++++++++++----------- 15 files changed, 56 insertions(+), 56 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/alloc.c b/drivers/net/ethernet/mellanox/mlx4/alloc.c index b651c1210555..6dabd983e7e0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx4/alloc.c @@ -186,7 +186,7 @@ int mlx4_bitmap_init(struct mlx4_bitmap *bitmap, u32 num, u32 mask, bitmap->effective_len = bitmap->avail; spin_lock_init(&bitmap->lock); bitmap->table = kzalloc(BITS_TO_LONGS(bitmap->max) * - sizeof (long), GFP_KERNEL); + sizeof(long), GFP_KERNEL); if (!bitmap->table) return -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 674773b28b2e..97aed30ead21 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -2637,7 +2637,7 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev) int err = 0; priv->cmd.context = kmalloc(priv->cmd.max_cmds * - sizeof (struct mlx4_cmd_context), + sizeof(struct mlx4_cmd_context), GFP_KERNEL); if (!priv->cmd.context) return -ENOMEM; @@ -2695,7 +2695,7 @@ struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev) { struct mlx4_cmd_mailbox *mailbox; - mailbox = kmalloc(sizeof *mailbox, GFP_KERNEL); + mailbox = kmalloc(sizeof(*mailbox), GFP_KERNEL); if (!mailbox) return ERR_PTR(-ENOMEM); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_resources.c b/drivers/net/ethernet/mellanox/mlx4/en_resources.c index 86d2d42d658d..5a47f9669621 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_resources.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_resources.c @@ -44,7 +44,7 @@ void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride, struct mlx4_en_dev *mdev = priv->mdev; struct net_device *dev = priv->dev; - memset(context, 0, sizeof *context); + memset(context, 0, sizeof(*context)); context->flags = cpu_to_be32(7 << 16 | rss << MLX4_RSS_QPC_FLAG_OFFSET); context->pd = cpu_to_be32(mdev->priv_pdn); context->mtu_msgmax = 0xff; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index bf1638044a7a..dcb8f8f84a97 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -1056,7 +1056,7 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn, } qp->event = mlx4_en_sqp_event; - memset(context, 0, sizeof *context); + memset(context, 0, sizeof(*context)); mlx4_en_fill_qp_context(priv, ring->actual_size, ring->stride, 0, 0, qpn, ring->cqn, -1, context); context->db_rec_addr = cpu_to_be64(ring->wqres.db.dma); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 73faa3d77921..bcf422efd3b8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -643,7 +643,7 @@ static void build_inline_wqe(struct mlx4_en_tx_desc *tx_desc, void *fragptr) { struct mlx4_wqe_inline_seg *inl = &tx_desc->inl; - int spc = MLX4_INLINE_ALIGN - CTRL_SIZE - sizeof *inl; + int spc = MLX4_INLINE_ALIGN - CTRL_SIZE - sizeof(*inl); unsigned int hlen = skb_headlen(skb); if (skb->len <= spc) { diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 07406cf2eacd..b98698bf75dd 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -259,7 +259,7 @@ int mlx4_gen_pkey_eqe(struct mlx4_dev *dev, int slave, u8 port) if (!s_slave->active) return 0; - memset(&eqe, 0, sizeof eqe); + memset(&eqe, 0, sizeof(eqe)); eqe.type = MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT; eqe.subtype = MLX4_DEV_PMC_SUBTYPE_PKEY_TABLE; @@ -276,7 +276,7 @@ int mlx4_gen_guid_change_eqe(struct mlx4_dev *dev, int slave, u8 port) /*don't send if we don't have the that slave */ if (dev->persist->num_vfs < slave) return 0; - memset(&eqe, 0, sizeof eqe); + memset(&eqe, 0, sizeof(eqe)); eqe.type = MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT; eqe.subtype = MLX4_DEV_PMC_SUBTYPE_GUID_INFO; @@ -295,7 +295,7 @@ int mlx4_gen_port_state_change_eqe(struct mlx4_dev *dev, int slave, u8 port, /*don't send if we don't have the that slave */ if (dev->persist->num_vfs < slave) return 0; - memset(&eqe, 0, sizeof eqe); + memset(&eqe, 0, sizeof(eqe)); eqe.type = MLX4_EVENT_TYPE_PORT_CHANGE; eqe.subtype = port_subtype_change; @@ -432,7 +432,7 @@ int mlx4_gen_slaves_port_mgt_ev(struct mlx4_dev *dev, u8 port, int attr) { struct mlx4_eqe eqe; - memset(&eqe, 0, sizeof eqe); + memset(&eqe, 0, sizeof(eqe)); eqe.type = MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT; eqe.subtype = MLX4_DEV_PMC_SUBTYPE_PORT_INFO; @@ -726,7 +726,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) } memcpy(&priv->mfunc.master.comm_arm_bit_vector, eqe->event.comm_channel_arm.bit_vec, - sizeof eqe->event.comm_channel_arm.bit_vec); + sizeof(eqe)->event.comm_channel_arm.bit_vec); queue_work(priv->mfunc.master.comm_wq, &priv->mfunc.master.comm_work); break; @@ -984,15 +984,15 @@ static int mlx4_create_eq(struct mlx4_dev *dev, int nent, */ npages = PAGE_ALIGN(eq->nent * dev->caps.eqe_size) / PAGE_SIZE; - eq->page_list = kmalloc(npages * sizeof *eq->page_list, - GFP_KERNEL); + eq->page_list = kmalloc_array(npages, sizeof(*eq->page_list), + GFP_KERNEL); if (!eq->page_list) goto err_out; for (i = 0; i < npages; ++i) eq->page_list[i].buf = NULL; - dma_list = kmalloc(npages * sizeof *dma_list, GFP_KERNEL); + dma_list = kmalloc_array(npages, sizeof(*dma_list), GFP_KERNEL); if (!dma_list) goto err_out_free; @@ -1161,7 +1161,7 @@ int mlx4_alloc_eq_table(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); priv->eq_table.eq = kcalloc(dev->caps.num_eqs - dev->caps.reserved_eqs, - sizeof *priv->eq_table.eq, GFP_KERNEL); + sizeof(*priv->eq_table.eq), GFP_KERNEL); if (!priv->eq_table.eq) return -ENOMEM; @@ -1180,7 +1180,7 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) int i; priv->eq_table.uar_map = kcalloc(mlx4_num_eq_uar(dev), - sizeof *priv->eq_table.uar_map, + sizeof(*priv->eq_table.uar_map), GFP_KERNEL); if (!priv->eq_table.uar_map) { err = -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 041c0ed65929..042707623922 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -57,7 +57,7 @@ MODULE_PARM_DESC(enable_qos, "Enable Enhanced QoS support (default: off)"); do { \ void *__p = (char *) (source) + (offset); \ u64 val; \ - switch (sizeof (dest)) { \ + switch (sizeof(dest)) { \ case 1: (dest) = *(u8 *) __p; break; \ case 2: (dest) = be16_to_cpup(__p); break; \ case 4: (dest) = be32_to_cpup(__p); break; \ diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c index 5a7816e7c7b4..a822f7a56bc5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.c +++ b/drivers/net/ethernet/mellanox/mlx4/icm.c @@ -400,7 +400,7 @@ int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table, obj_per_chunk = MLX4_TABLE_CHUNK_SIZE / obj_size; num_icm = (nobj + obj_per_chunk - 1) / obj_per_chunk; - table->icm = kcalloc(num_icm, sizeof *table->icm, GFP_KERNEL); + table->icm = kcalloc(num_icm, sizeof(*table->icm), GFP_KERNEL); if (!table->icm) return -ENOMEM; table->virt = virt; diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.h b/drivers/net/ethernet/mellanox/mlx4/icm.h index dee67fa39107..c9169a490557 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.h +++ b/drivers/net/ethernet/mellanox/mlx4/icm.h @@ -39,8 +39,8 @@ #include #define MLX4_ICM_CHUNK_LEN \ - ((256 - sizeof (struct list_head) - 2 * sizeof (int)) / \ - (sizeof (struct scatterlist))) + ((256 - sizeof(struct list_head) - 2 * sizeof(int)) / \ + (sizeof(struct scatterlist))) enum { MLX4_ICM_PAGE_SHIFT = 12, diff --git a/drivers/net/ethernet/mellanox/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c index e00f627331cb..2edcce98ab2d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/intf.c +++ b/drivers/net/ethernet/mellanox/mlx4/intf.c @@ -53,7 +53,7 @@ static void mlx4_add_device(struct mlx4_interface *intf, struct mlx4_priv *priv) { struct mlx4_device_context *dev_ctx; - dev_ctx = kmalloc(sizeof *dev_ctx, GFP_KERNEL); + dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL); if (!dev_ctx) return; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index df9b0efb5ab1..3797491f4b6b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -925,10 +925,10 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) mlx4_replace_zero_macs(dev); dev->caps.qp0_qkey = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp1_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp1_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); + dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); + dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); + dev->caps.qp1_tunnel = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); + dev->caps.qp1_proxy = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); if (!dev->caps.qp0_tunnel || !dev->caps.qp0_proxy || !dev->caps.qp1_tunnel || !dev->caps.qp1_proxy || @@ -2399,7 +2399,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev) dev->caps.rx_checksum_flags_port[2] = params.rx_csum_flags_port_2; } priv->eq_table.inta_pin = adapter.inta_pin; - memcpy(dev->board_id, adapter.board_id, sizeof dev->board_id); + memcpy(dev->board_id, adapter.board_id, sizeof(dev->board_id)); return 0; @@ -2869,7 +2869,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) dev->caps.num_eqs - dev->caps.reserved_eqs, MAX_MSIX); - entries = kcalloc(nreq, sizeof *entries, GFP_KERNEL); + entries = kcalloc(nreq, sizeof(*entries), GFP_KERNEL); if (!entries) goto no_msi; diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 0710b3677464..4c5306dbcf11 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -162,7 +162,7 @@ static int new_steering_entry(struct mlx4_dev *dev, u8 port, return -EINVAL; s_steer = &mlx4_priv(dev)->steer[port - 1]; - new_entry = kzalloc(sizeof *new_entry, GFP_KERNEL); + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); if (!new_entry) return -ENOMEM; @@ -175,7 +175,7 @@ static int new_steering_entry(struct mlx4_dev *dev, u8 port, */ pqp = get_promisc_qp(dev, port, steer, qpn); if (pqp) { - dqp = kmalloc(sizeof *dqp, GFP_KERNEL); + dqp = kmalloc(sizeof(*dqp), GFP_KERNEL); if (!dqp) { err = -ENOMEM; goto out_alloc; @@ -274,7 +274,7 @@ static int existing_steering_entry(struct mlx4_dev *dev, u8 port, } /* add the qp as a duplicate on this index */ - dqp = kmalloc(sizeof *dqp, GFP_KERNEL); + dqp = kmalloc(sizeof(*dqp), GFP_KERNEL); if (!dqp) return -ENOMEM; dqp->qpn = qpn; @@ -443,7 +443,7 @@ static int add_promisc_qp(struct mlx4_dev *dev, u8 port, goto out_mutex; } - pqp = kmalloc(sizeof *pqp, GFP_KERNEL); + pqp = kmalloc(sizeof(*pqp), GFP_KERNEL); if (!pqp) { err = -ENOMEM; goto out_mutex; @@ -514,7 +514,7 @@ static int add_promisc_qp(struct mlx4_dev *dev, u8 port, /* add the new qpn to list of promisc qps */ list_add_tail(&pqp->list, &s_steer->promisc_qps[steer]); /* now need to add all the promisc qps to default entry */ - memset(mgm, 0, sizeof *mgm); + memset(mgm, 0, sizeof(*mgm)); members_count = 0; list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list) { if (members_count == dev->caps.num_qp_per_mgm) { @@ -1144,7 +1144,7 @@ int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], index += dev->caps.num_mgms; new_entry = 1; - memset(mgm, 0, sizeof *mgm); + memset(mgm, 0, sizeof(*mgm)); memcpy(mgm->gid, gid, 16); } diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 24282cd017d3..c7c0764991c9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -106,9 +106,9 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order) buddy->max_order = max_order; spin_lock_init(&buddy->lock); - buddy->bits = kcalloc(buddy->max_order + 1, sizeof (long *), + buddy->bits = kcalloc(buddy->max_order + 1, sizeof(long *), GFP_KERNEL); - buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free, + buddy->num_free = kcalloc(buddy->max_order + 1, sizeof(*buddy->num_free), GFP_KERNEL); if (!buddy->bits || !buddy->num_free) goto err_out; @@ -703,13 +703,13 @@ static int mlx4_write_mtt_chunk(struct mlx4_dev *dev, struct mlx4_mtt *mtt, return -ENOMEM; dma_sync_single_for_cpu(&dev->persist->pdev->dev, dma_handle, - npages * sizeof (u64), DMA_TO_DEVICE); + npages * sizeof(u64), DMA_TO_DEVICE); for (i = 0; i < npages; ++i) mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); dma_sync_single_for_device(&dev->persist->pdev->dev, dma_handle, - npages * sizeof (u64), DMA_TO_DEVICE); + npages * sizeof(u64), DMA_TO_DEVICE); return 0; } @@ -1052,7 +1052,7 @@ int mlx4_fmr_alloc(struct mlx4_dev *dev, u32 pd, u32 access, int max_pages, return -EINVAL; /* All MTTs must fit in the same page */ - if (max_pages * sizeof *fmr->mtts > PAGE_SIZE) + if (max_pages * sizeof(*fmr->mtts) > PAGE_SIZE) return -EINVAL; fmr->page_shift = page_shift; diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 26747212526b..2b067763a6bc 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -174,7 +174,7 @@ static int __mlx4_qp_modify(struct mlx4_dev *dev, struct mlx4_mtt *mtt, cpu_to_be16(mlx4_qp_roce_entropy(dev, qp->qpn)); *(__be32 *) mailbox->buf = cpu_to_be32(optpar); - memcpy(mailbox->buf + 8, context, sizeof *context); + memcpy(mailbox->buf + 8, context, sizeof(*context)); ((struct mlx4_qp_context *) (mailbox->buf + 8))->local_qpn = cpu_to_be32(qp->qpn); @@ -844,10 +844,10 @@ int mlx4_init_qp_table(struct mlx4_dev *dev) /* In mfunc, calculate proxy and tunnel qp offsets for the PF here, * since the PF does not call mlx4_slave_caps */ - dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp1_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp1_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); + dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); + dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); + dev->caps.qp1_tunnel = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); + dev->caps.qp1_proxy = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); if (!dev->caps.qp0_tunnel || !dev->caps.qp0_proxy || !dev->caps.qp1_tunnel || !dev->caps.qp1_proxy) { @@ -907,7 +907,7 @@ int mlx4_qp_query(struct mlx4_dev *dev, struct mlx4_qp *qp, MLX4_CMD_QUERY_QP, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); if (!err) - memcpy(context, mailbox->buf + 8, sizeof *context); + memcpy(context, mailbox->buf + 8, sizeof(*context)); mlx4_free_cmd_mailbox(dev, mailbox); return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 215e21c3dc8a..fabb53379727 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -1040,7 +1040,7 @@ static struct res_common *alloc_qp_tr(int id) { struct res_qp *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1058,7 +1058,7 @@ static struct res_common *alloc_mtt_tr(int id, int order) { struct res_mtt *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1074,7 +1074,7 @@ static struct res_common *alloc_mpt_tr(int id, int key) { struct res_mpt *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1089,7 +1089,7 @@ static struct res_common *alloc_eq_tr(int id) { struct res_eq *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1103,7 +1103,7 @@ static struct res_common *alloc_cq_tr(int id) { struct res_cq *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1118,7 +1118,7 @@ static struct res_common *alloc_srq_tr(int id) { struct res_srq *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1133,7 +1133,7 @@ static struct res_common *alloc_counter_tr(int id, int port) { struct res_counter *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1148,7 +1148,7 @@ static struct res_common *alloc_xrcdn_tr(int id) { struct res_xrcdn *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1162,7 +1162,7 @@ static struct res_common *alloc_fs_rule_tr(u64 id, int qpn) { struct res_fs_rule *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1274,7 +1274,7 @@ static int add_res_range(struct mlx4_dev *dev, int slave, u64 base, int count, struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker; struct rb_root *root = &tracker->res_tree[type]; - res_arr = kzalloc(count * sizeof *res_arr, GFP_KERNEL); + res_arr = kcalloc(count, sizeof(*res_arr), GFP_KERNEL); if (!res_arr) return -ENOMEM; @@ -2027,7 +2027,7 @@ static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port, if (mlx4_grant_resource(dev, slave, RES_MAC, 1, port)) return -EINVAL; - res = kzalloc(sizeof *res, GFP_KERNEL); + res = kzalloc(sizeof(*res), GFP_KERNEL); if (!res) { mlx4_release_resource(dev, slave, RES_MAC, 1, port); return -ENOMEM; @@ -4020,7 +4020,7 @@ static int add_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp, struct res_gid *res; int err; - res = kzalloc(sizeof *res, GFP_KERNEL); + res = kzalloc(sizeof(*res), GFP_KERNEL); if (!res) return -ENOMEM; -- cgit v1.2.3 From 251564f601a26d01b3b0e5a40889b4efb6823403 Mon Sep 17 00:00:00 2001 From: Veerasenareddy Burru Date: Tue, 15 Aug 2017 16:26:22 -0700 Subject: liquidio: update VF's netdev->max_mtu if there's a change in PF's MTU A VF's MTU is capped at the parent PF's MTU. So if there's a change in the PF's MTU, then update the VF's netdev->max_mtu. Also remove duplicate log messages for MTU change. Signed-off-by: Veerasenareddy Burru Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 3 --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 9 ++++++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 8b8e78f04f94..d4f0646084b7 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -165,9 +165,6 @@ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr) /* If command is successful, change the MTU. */ netif_info(lio, probe, lio->netdev, "MTU Changed from %d to %d\n", netdev->mtu, nctrl->ncmd.s.param1); - dev_info(&oct->pci_dev->dev, "%s MTU Changed from %d to %d\n", - netdev->name, netdev->mtu, - nctrl->ncmd.s.param1); netdev->mtu = nctrl->ncmd.s.param1; queue_delayed_work(lio->link_status_wq.wq, &lio->link_status_wq.wk.work, 0); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 35a977abc7c4..0402b18d4689 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -630,6 +630,12 @@ static void update_link_status(struct net_device *netdev, txqs_stop(netdev); } + if (lio->linfo.link.s.mtu != netdev->max_mtu) { + dev_info(&oct->pci_dev->dev, "Max MTU Changed from %d to %d\n", + netdev->max_mtu, lio->linfo.link.s.mtu); + netdev->max_mtu = lio->linfo.link.s.mtu; + } + if (lio->linfo.link.s.mtu < netdev->mtu) { dev_warn(&oct->pci_dev->dev, "PF has changed the MTU for gmx port. Reducing the mtu from %d to %d\n", @@ -1539,14 +1545,11 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) static int liquidio_change_mtu(struct net_device *netdev, int new_mtu) { struct lio *lio = GET_LIO(netdev); - struct octeon_device *oct = lio->oct_dev; lio->mtu = new_mtu; netif_info(lio, probe, lio->netdev, "MTU Changed from %d to %d\n", netdev->mtu, new_mtu); - dev_info(&oct->pci_dev->dev, "%s MTU Changed from %d to %d\n", - netdev->name, netdev->mtu, new_mtu); netdev->mtu = new_mtu; -- cgit v1.2.3 From d369bcaf7dcbe1a5d09ed2e519f35e4841303003 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 16 Aug 2017 10:25:59 +0530 Subject: net: 3c509: constify pnp_device_id pnp_device_id are not supposed to change at runtime. All functions working with pnp_device_id provided by work with const pnp_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/3c509.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index f66c9710cb81..077d01d9f141 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -392,7 +392,7 @@ static struct isa_driver el3_isa_driver = { static int isa_registered; #ifdef CONFIG_PNP -static struct pnp_device_id el3_pnp_ids[] = { +static const struct pnp_device_id el3_pnp_ids[] = { { .id = "TCM5090" }, /* 3Com Etherlink III (TP) */ { .id = "TCM5091" }, /* 3Com Etherlink III */ { .id = "TCM5094" }, /* 3Com Etherlink III (combo) */ -- cgit v1.2.3 From b985f870a5f08da0a2d6b45a3ea33f5558cf7e4e Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 16 Aug 2017 09:37:43 +0200 Subject: nfp: process control messages in workqueue in flower app Processing of control messages is not time-critical and future processing of some messages will require taking the RTNL which is not possible in a BH handler. It seems simplest to move all control message processing to a workqueue. Signed-off-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/flower/cmsg.c | 24 +++++++++++++++++++++++- drivers/net/ethernet/netronome/nfp/flower/cmsg.h | 1 + drivers/net/ethernet/netronome/nfp/flower/main.c | 14 ++++++++++++-- drivers/net/ethernet/netronome/nfp/flower/main.h | 5 +++++ 4 files changed, 41 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index aa46b23cdfb1..6c8c22491fe7 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -34,10 +34,12 @@ #include #include #include +#include #include #include "main.h" #include "../nfpcore/nfp_cpp.h" +#include "../nfp_net.h" #include "../nfp_net_repr.h" #include "./cmsg.h" @@ -155,7 +157,8 @@ nfp_flower_cmsg_portmod_rx(struct nfp_app *app, struct sk_buff *skb) rcu_read_unlock(); } -void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) +static void +nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb) { struct nfp_flower_cmsg_hdr *cmsg_hdr; enum nfp_flower_cmsg_type_port type; @@ -184,3 +187,22 @@ void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) out: dev_kfree_skb_any(skb); } + +void nfp_flower_cmsg_process_rx(struct work_struct *work) +{ + struct nfp_flower_priv *priv; + struct sk_buff *skb; + + priv = container_of(work, struct nfp_flower_priv, cmsg_work); + + while ((skb = skb_dequeue(&priv->cmsg_skbs))) + nfp_flower_cmsg_process_one_rx(priv->nn->app, skb); +} + +void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) +{ + struct nfp_flower_priv *priv = app->priv; + + skb_queue_tail(&priv->cmsg_skbs, skb); + schedule_work(&priv->cmsg_work); +} diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index aa92a8711a02..a2ec60344236 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -330,6 +330,7 @@ nfp_flower_cmsg_mac_repr_add(struct sk_buff *skb, unsigned int idx, unsigned int nbi, unsigned int nbi_port, unsigned int phys_port); int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok); +void nfp_flower_cmsg_process_rx(struct work_struct *work); void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb); struct sk_buff * nfp_flower_cmsg_alloc(struct nfp_app *app, unsigned int size, diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index b905454b30ca..3088e959f2a3 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -332,6 +332,7 @@ err_invalid_port: static int nfp_flower_init(struct nfp_app *app) { const struct nfp_pf *pf = app->pf; + struct nfp_flower_priv *app_priv; u64 version; int err; @@ -362,10 +363,14 @@ static int nfp_flower_init(struct nfp_app *app) return -EINVAL; } - app->priv = vzalloc(sizeof(struct nfp_flower_priv)); - if (!app->priv) + app_priv = vzalloc(sizeof(struct nfp_flower_priv)); + if (!app_priv) return -ENOMEM; + app->priv = app_priv; + skb_queue_head_init(&app_priv->cmsg_skbs); + INIT_WORK(&app_priv->cmsg_work, nfp_flower_cmsg_process_rx); + err = nfp_flower_metadata_init(app); if (err) goto err_free_app_priv; @@ -379,6 +384,11 @@ err_free_app_priv: static void nfp_flower_clean(struct nfp_app *app) { + struct nfp_flower_priv *app_priv = app->priv; + + skb_queue_purge(&app_priv->cmsg_skbs); + flush_work(&app_priv->cmsg_work); + nfp_flower_metadata_cleanup(app); vfree(app->priv); app->priv = NULL; diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 71e4f4f4e9ba..b7043ca9b9fc 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -39,6 +39,7 @@ #include #include #include +#include struct net_device; struct nfp_app; @@ -78,6 +79,8 @@ struct nfp_fl_stats_id { * @mask_ids: List of free mask ids * @mask_table: Hash table used to store masks * @flow_table: Hash table used to store flower rules + * @cmsg_work: Workqueue for control messages processing + * @cmsg_skbs: List of skbs for control message processing */ struct nfp_flower_priv { struct nfp_net *nn; @@ -87,6 +90,8 @@ struct nfp_flower_priv { struct nfp_fl_mask_id mask_ids; DECLARE_HASHTABLE(mask_table, NFP_FLOWER_MASK_HASH_BITS); DECLARE_HASHTABLE(flow_table, NFP_FLOWER_HASH_BITS); + struct work_struct cmsg_work; + struct sk_buff_head cmsg_skbs; }; struct nfp_fl_key_ls { -- cgit v1.2.3 From 2dff1962242111cc4517119f690587b87573ffc5 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 16 Aug 2017 09:37:44 +0200 Subject: nfp: process MTU updates from firmware flower app Now that control message processing occurs in a workqueue rather than a BH handler MTU updates received from the firmware may be safely processed. Signed-off-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/flower/cmsg.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index 6c8c22491fe7..806924b82adc 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -150,10 +150,17 @@ nfp_flower_cmsg_portmod_rx(struct nfp_app *app, struct sk_buff *skb) return; } - if (link) + if (link) { + u16 mtu = be16_to_cpu(msg->mtu); + netif_carrier_on(netdev); - else + + /* An MTU of 0 from the firmware should be ignored */ + if (mtu) + dev_set_mtu(netdev, mtu); + } else { netif_carrier_off(netdev); + } rcu_read_unlock(); } -- cgit v1.2.3 From ba5c4dac033b3b2f9e6a99793d1284c2dcf98b3f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 16 Aug 2017 10:05:11 +0100 Subject: net/mlx4: fix spelling mistake: "availible" -> "available" Trivial fix to spelling mistakes in the mlx4 driver Signed-off-by: Colin Ian King Reviewed-by: Yuval Shaia Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 16 ++++++++-------- drivers/net/ethernet/mellanox/mlx4/fw_qos.c | 6 +++--- drivers/net/ethernet/mellanox/mlx4/fw_qos.h | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 97aed30ead21..4ec1ef61a472 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1958,19 +1958,19 @@ static void mlx4_allocate_port_vpps(struct mlx4_dev *dev, int port) int i; int err; int num_vfs; - u16 availible_vpp; + u16 available_vpp; u8 vpp_param[MLX4_NUM_UP]; struct mlx4_qos_manager *port_qos; struct mlx4_priv *priv = mlx4_priv(dev); - err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param); + err = mlx4_ALLOCATE_VPP_get(dev, port, &available_vpp, vpp_param); if (err) { - mlx4_info(dev, "Failed query availible VPPs\n"); + mlx4_info(dev, "Failed query available VPPs\n"); return; } port_qos = &priv->mfunc.master.qos_ctl[port]; - num_vfs = (availible_vpp / + num_vfs = (available_vpp / bitmap_weight(port_qos->priority_bm, MLX4_NUM_UP)); for (i = 0; i < MLX4_NUM_UP; i++) { @@ -1985,14 +1985,14 @@ static void mlx4_allocate_port_vpps(struct mlx4_dev *dev, int port) } /* Query actual allocated VPP, just to make sure */ - err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param); + err = mlx4_ALLOCATE_VPP_get(dev, port, &available_vpp, vpp_param); if (err) { - mlx4_info(dev, "Failed query availible VPPs\n"); + mlx4_info(dev, "Failed query available VPPs\n"); return; } port_qos->num_of_qos_vfs = num_vfs; - mlx4_dbg(dev, "Port %d Availible VPPs %d\n", port, availible_vpp); + mlx4_dbg(dev, "Port %d Available VPPs %d\n", port, available_vpp); for (i = 0; i < MLX4_NUM_UP; i++) mlx4_dbg(dev, "Port %d UP %d Allocated %d VPPs\n", port, i, @@ -2891,7 +2891,7 @@ static int mlx4_set_vport_qos(struct mlx4_priv *priv, int slave, int port, memset(vpp_qos, 0, sizeof(struct mlx4_vport_qos_param) * MLX4_NUM_UP); if (slave > port_qos->num_of_qos_vfs) { - mlx4_info(dev, "No availible VPP resources for this VF\n"); + mlx4_info(dev, "No available VPP resources for this VF\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c index 8f2fde0487c4..3a09d7122d3b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c @@ -65,7 +65,7 @@ struct mlx4_set_port_scheduler_context { /* Granular Qos (per VF) section */ struct mlx4_alloc_vpp_param { - __be32 availible_vpp; + __be32 available_vpp; __be32 vpp_p_up[MLX4_NUM_UP]; }; @@ -157,7 +157,7 @@ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER); int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, - u16 *availible_vpp, u8 *vpp_p_up) + u16 *available_vpp, u8 *vpp_p_up) { int i; int err; @@ -179,7 +179,7 @@ int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, goto out; /* Total number of supported VPPs */ - *availible_vpp = (u16)be32_to_cpu(out_param->availible_vpp); + *available_vpp = (u16)be32_to_cpu(out_param->available_vpp); for (i = 0; i < MLX4_NUM_UP; i++) vpp_p_up[i] = (u8)be32_to_cpu(out_param->vpp_p_up[i]); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h index ac1f331878e6..582997577a04 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h @@ -84,23 +84,23 @@ int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc); int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, u8 *pg, u16 *ratelimit); /** - * mlx4_ALLOCATE_VPP_get - Query port VPP availible resources and allocation. - * Before distribution of VPPs to priorities, only availible_vpp is returned. + * mlx4_ALLOCATE_VPP_get - Query port VPP available resources and allocation. + * Before distribution of VPPs to priorities, only available_vpp is returned. * After initialization it returns the distribution of VPPs among priorities. * * @dev: mlx4_dev. * @port: Physical port number. - * @availible_vpp: Pointer to variable where number of availible VPPs is stored + * @available_vpp: Pointer to variable where number of available VPPs is stored * @vpp_p_up: Distribution of VPPs to priorities is stored in this array * * Returns 0 on success or a negative mlx4_core errno code. **/ int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, - u16 *availible_vpp, u8 *vpp_p_up); + u16 *available_vpp, u8 *vpp_p_up); /** * mlx4_ALLOCATE_VPP_set - Distribution of VPPs among differnt priorities. * The total number of VPPs assigned to all for a port must not exceed - * the value reported by availible_vpp in mlx4_ALLOCATE_VPP_get. + * the value reported by available_vpp in mlx4_ALLOCATE_VPP_get. * VPP allocation is allowed only after the port type has been set, * and while no QPs are open for this port. * -- cgit v1.2.3 From 0bbd7dad34f81e5e724cb08252160a1796c388b2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 16 Aug 2017 22:14:33 +0800 Subject: tun: make tun_build_skb() thread safe tun_build_skb() is not thread safe since it uses per queue page frag, this will break things when multiple threads are sending through same queue. Switch to use per-thread generator (no lock involved). Fixes: 66ccbc9c87c2 ("tap: use build_skb() for small packet") Tested-by: Jason Wang Signed-off-by: Eric Dumazet Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/tun.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index f5017121cd57..19cbbbb1b63b 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -175,7 +175,6 @@ struct tun_file { struct list_head next; struct tun_struct *detached; struct skb_array tx_array; - struct page_frag alloc_frag; }; struct tun_flow_entry { @@ -578,8 +577,6 @@ static void __tun_detach(struct tun_file *tfile, bool clean) } if (tun) skb_array_cleanup(&tfile->tx_array); - if (tfile->alloc_frag.page) - put_page(tfile->alloc_frag.page); sock_put(&tfile->sk); } } @@ -1272,7 +1269,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, struct virtio_net_hdr *hdr, int len, int *generic_xdp) { - struct page_frag *alloc_frag = &tfile->alloc_frag; + struct page_frag *alloc_frag = ¤t->task_frag; struct sk_buff *skb; struct bpf_prog *xdp_prog; int buflen = SKB_DATA_ALIGN(len + TUN_RX_PAD) + @@ -2580,8 +2577,6 @@ static int tun_chr_open(struct inode *inode, struct file * file) tfile->sk.sk_write_space = tun_sock_write_space; tfile->sk.sk_sndbuf = INT_MAX; - tfile->alloc_frag.page = NULL; - file->private_data = tfile; INIT_LIST_HEAD(&tfile->next); -- cgit v1.2.3 From 5a668d8cddbe8bf14379ce110c49ca088a1e9fae Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 16 Aug 2017 08:56:25 -0700 Subject: vmbus: remove unused vmubs_sendpacket_pagebuffer_ctl The function vmbus_sendpacket_pagebuffer_ctl was never used directly. Just have vmbus_send_pagebuffer Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/hv/channel.c | 30 ++++++------------------------ drivers/net/hyperv/netvsc.c | 10 ++++------ include/linux/hyperv.h | 8 -------- 3 files changed, 10 insertions(+), 38 deletions(-) (limited to 'drivers/net') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 756a1e841142..9223fe8823e0 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -702,16 +702,16 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, EXPORT_SYMBOL(vmbus_sendpacket); /* - * vmbus_sendpacket_pagebuffer_ctl - Send a range of single-page buffer + * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer * packets using a GPADL Direct packet type. This interface allows you * to control notifying the host. This will be useful for sending * batched data. Also the sender can control the send flags * explicitly. */ -int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, - struct hv_page_buffer pagebuffers[], - u32 pagecount, void *buffer, u32 bufferlen, - u64 requestid, u32 flags) +int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, + struct hv_page_buffer pagebuffers[], + u32 pagecount, void *buffer, u32 bufferlen, + u64 requestid) { int i; struct vmbus_channel_packet_page_buffer desc; @@ -736,7 +736,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, /* Setup the descriptor */ desc.type = VM_PKT_DATA_USING_GPA_DIRECT; - desc.flags = flags; + desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; desc.dataoffset8 = descsize >> 3; /* in 8-bytes granularity */ desc.length8 = (u16)(packetlen_aligned >> 3); desc.transactionid = requestid; @@ -757,24 +757,6 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, return hv_ringbuffer_write(channel, bufferlist, 3); } -EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer_ctl); - -/* - * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer - * packets using a GPADL Direct packet type. - */ -int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, - struct hv_page_buffer pagebuffers[], - u32 pagecount, void *buffer, u32 bufferlen, - u64 requestid) -{ - u32 flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; - - return vmbus_sendpacket_pagebuffer_ctl(channel, pagebuffers, pagecount, - buffer, bufferlen, - requestid, flags); - -} EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); /* diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0530e7d729e1..6031102cbba3 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -775,12 +775,10 @@ static inline int netvsc_send_pkt( if (packet->cp_partial) pb += packet->rmsg_pgcnt; - ret = vmbus_sendpacket_pagebuffer_ctl(out_channel, - pb, packet->page_buf_cnt, - &nvmsg, - sizeof(struct nvsp_message), - req_id, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + ret = vmbus_sendpacket_pagebuffer(out_channel, + pb, packet->page_buf_cnt, + &nvmsg, sizeof(nvmsg), + req_id); } else { ret = vmbus_sendpacket_ctl(out_channel, &nvmsg, sizeof(struct nvsp_message), diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 39a080ce17da..9692592d43a3 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1044,14 +1044,6 @@ extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, u32 bufferlen, u64 requestid); -extern int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, - struct hv_page_buffer pagebuffers[], - u32 pagecount, - void *buffer, - u32 bufferlen, - u64 requestid, - u32 flags); - extern int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel, struct vmbus_packet_mpb_array *mpb, u32 desc_size, -- cgit v1.2.3 From 5dd0fb9b9ffc0ef9b312d05604f4ad0fffc50505 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 16 Aug 2017 08:56:26 -0700 Subject: vmbus: remove unused vmbus_sendpacket_ctl The only usage of vmbus_sendpacket_ctl was by vmbus_sendpacket. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/hv/channel.c | 43 +++++++++++++++++-------------------------- drivers/net/hyperv/netvsc.c | 9 ++++----- include/linux/hyperv.h | 7 ------- 3 files changed, 21 insertions(+), 38 deletions(-) (limited to 'drivers/net') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 9223fe8823e0..d9e9676e2b40 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -647,9 +647,23 @@ void vmbus_close(struct vmbus_channel *channel) } EXPORT_SYMBOL_GPL(vmbus_close); -int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, - u32 bufferlen, u64 requestid, - enum vmbus_packet_type type, u32 flags) +/** + * vmbus_sendpacket() - Send the specified buffer on the given channel + * @channel: Pointer to vmbus_channel structure. + * @buffer: Pointer to the buffer you want to receive the data into. + * @bufferlen: Maximum size of what the the buffer will hold + * @requestid: Identifier of the request + * @type: Type of packet that is being send e.g. negotiate, time + * packet etc. + * + * Sends data in @buffer directly to hyper-v via the vmbus + * This will send the data unparsed to hyper-v. + * + * Mainly used by Hyper-V drivers. + */ +int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, + u32 bufferlen, u64 requestid, + enum vmbus_packet_type type, u32 flags) { struct vmpacket_descriptor desc; u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; @@ -676,29 +690,6 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, return hv_ringbuffer_write(channel, bufferlist, num_vecs); } -EXPORT_SYMBOL(vmbus_sendpacket_ctl); - -/** - * vmbus_sendpacket() - Send the specified buffer on the given channel - * @channel: Pointer to vmbus_channel structure. - * @buffer: Pointer to the buffer you want to receive the data into. - * @bufferlen: Maximum size of what the the buffer will hold - * @requestid: Identifier of the request - * @type: Type of packet that is being send e.g. negotiate, time - * packet etc. - * - * Sends data in @buffer directly to hyper-v via the vmbus - * This will send the data unparsed to hyper-v. - * - * Mainly used by Hyper-V drivers. - */ -int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, - u32 bufferlen, u64 requestid, - enum vmbus_packet_type type, u32 flags) -{ - return vmbus_sendpacket_ctl(channel, buffer, bufferlen, requestid, - type, flags); -} EXPORT_SYMBOL(vmbus_sendpacket); /* diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 6031102cbba3..0062b802676f 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -780,11 +780,10 @@ static inline int netvsc_send_pkt( &nvmsg, sizeof(nvmsg), req_id); } else { - ret = vmbus_sendpacket_ctl(out_channel, &nvmsg, - sizeof(struct nvsp_message), - req_id, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + ret = vmbus_sendpacket(out_channel, + &nvmsg, sizeof(nvmsg), + req_id, VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); } if (ret == 0) { diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 9692592d43a3..a5f961c4149e 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1030,13 +1030,6 @@ extern int vmbus_sendpacket(struct vmbus_channel *channel, enum vmbus_packet_type type, u32 flags); -extern int vmbus_sendpacket_ctl(struct vmbus_channel *channel, - void *buffer, - u32 bufferLen, - u64 requestid, - enum vmbus_packet_type type, - u32 flags); - extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, struct hv_page_buffer pagebuffers[], u32 pagecount, -- cgit v1.2.3 From 2af2c2c77e5a89a02f508571c3994d9e78f9013d Mon Sep 17 00:00:00 2001 From: Ganapathi Bhat Date: Thu, 10 Aug 2017 18:11:25 +0530 Subject: mwifiex: do not use random MAC for pre-association scanning Driver should use random MAC address only if the scan is requested by user(provided NL80211_SCAN_FLAG_RANDOM_ADDR is set in scan request). It should not be used for a scan performed before association. Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/scan.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index d8e8b857ddfb..79b4aff3509e 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -2790,7 +2790,6 @@ static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv, if (!scan_cfg) return -ENOMEM; - ether_addr_copy(scan_cfg->random_mac, priv->random_mac); scan_cfg->ssid_list = req_ssid; scan_cfg->num_ssids = 1; -- cgit v1.2.3 From 89001c1c8dc32698112df0119ad5c632405892ca Mon Sep 17 00:00:00 2001 From: Ganapathi Bhat Date: Thu, 10 Aug 2017 18:11:26 +0530 Subject: mwifiex: check for NL80211_SCAN_FLAG_RANDOM_ADDR during hidden SSID scan At the end of user scan request, driver will perform an active scan for hidden SSIDs in passive channels. While doing this, driver unconditionally adding random_mac in scan command, which is no expected. It should add random_mac only if scan_request has NL80211_SCAN_FLAG_RANDOM_ADDR flag set. Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index 79b4aff3509e..c9d41ed77fc7 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -1947,7 +1947,8 @@ mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv) } adapter->active_scan_triggered = true; - ether_addr_copy(user_scan_cfg->random_mac, priv->random_mac); + if (priv->scan_request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) + ether_addr_copy(user_scan_cfg->random_mac, priv->random_mac); user_scan_cfg->num_ssids = priv->scan_request->n_ssids; user_scan_cfg->ssid_list = priv->scan_request->ssids; -- cgit v1.2.3 From 0db63e37992cdf159ac1f27e0cbe0f0bae62ca3a Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 15 Aug 2017 16:06:31 +0300 Subject: qtnfmac: remove unused qtnf_rx_frame declaration Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/bus.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/bus.h b/drivers/net/wireless/quantenna/qtnfmac/bus.h index dda05003d522..56e5fed92a2a 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/bus.h +++ b/drivers/net/wireless/quantenna/qtnfmac/bus.h @@ -130,7 +130,6 @@ static __always_inline void qtnf_bus_unlock(struct qtnf_bus *bus) /* interface functions from common layer */ -void qtnf_rx_frame(struct device *dev, struct sk_buff *rxp); int qtnf_core_attach(struct qtnf_bus *bus); void qtnf_core_detach(struct qtnf_bus *bus); void qtnf_txflowblock(struct device *dev, bool state); -- cgit v1.2.3 From 7376947dfb8094b7c8307d61f49ce48d4d6aec09 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 15 Aug 2017 16:06:32 +0300 Subject: qtnfmac: switch to napi_gro_receive Use napi_gro_receive() rather than netif_receive_skb() to improve performance when GRO is enabled. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index ae8acc1bf291..08b35dc30bc8 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -762,7 +762,7 @@ static int qtnf_rx_poll(struct napi_struct *napi, int budget) ndev->stats.rx_bytes += skb->len; skb->protocol = eth_type_trans(skb, ndev); - netif_receive_skb(skb); + napi_gro_receive(napi, skb); } else { pr_debug("drop untagged skb\n"); bus->mux_dev.stats.rx_dropped++; -- cgit v1.2.3 From c58730cab8ea02b9f09404d548de7eab7cc33302 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 15 Aug 2017 16:06:33 +0300 Subject: qtnfmac: use __netdev_alloc_skb_ip_align Replace __dev_alloc_skb and explicit NET_IP_ALIGN alignment by built-in __netdev_alloc_skb_ip_align function. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index 08b35dc30bc8..079aa1693ff5 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -429,8 +429,7 @@ static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 rx_bd_index) struct sk_buff *skb; dma_addr_t paddr; - skb = __dev_alloc_skb(SKB_BUF_SIZE + NET_IP_ALIGN, - GFP_ATOMIC); + skb = __netdev_alloc_skb_ip_align(NULL, SKB_BUF_SIZE, GFP_ATOMIC); if (!skb) { priv->rx_skb[rx_bd_index] = NULL; return -ENOMEM; @@ -438,8 +437,6 @@ static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 rx_bd_index) priv->rx_skb[rx_bd_index] = skb; - skb_reserve(skb, NET_IP_ALIGN); - rxbd = &priv->rx_bd_vbase[rx_bd_index]; paddr = pci_map_single(priv->pdev, skb->data, -- cgit v1.2.3 From 867ba964fa6990686116b706919f90848912e7ed Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 15 Aug 2017 16:06:34 +0300 Subject: qtnfmac: skb2rbd_attach cleanup Update PCIE_HDP_TX_HOST_Q_WR_PTR register in skb2rbd_attach as a part of procedure of passing new Rx buffer to hardware. Sync up all the the qtnf_rx_bd descriptor updates before passing Rx buffer to hardware. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- .../net/wireless/quantenna/qtnfmac/pearl/pcie.c | 31 +++++++++------------- 1 file changed, 13 insertions(+), 18 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index 079aa1693ff5..a0b65d487ddb 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -411,11 +411,6 @@ static int alloc_bd_table(struct qtnf_pcie_bus_priv *priv) writel(priv->rx_bd_num | (sizeof(struct qtnf_rx_bd)) << 16, PCIE_HDP_TX_HOST_Q_SZ_CTRL(priv->pcie_reg_base)); - priv->hw_txproc_wr_ptr = priv->rx_bd_num - rx_bd_reserved_param; - - writel(priv->hw_txproc_wr_ptr, - PCIE_HDP_TX_HOST_Q_WR_PTR(priv->pcie_reg_base)); - pr_debug("RX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr); priv->rx_bd_index = 0; @@ -423,7 +418,7 @@ static int alloc_bd_table(struct qtnf_pcie_bus_priv *priv) return 0; } -static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 rx_bd_index) +static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 index) { struct qtnf_rx_bd *rxbd; struct sk_buff *skb; @@ -431,13 +426,12 @@ static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 rx_bd_index) skb = __netdev_alloc_skb_ip_align(NULL, SKB_BUF_SIZE, GFP_ATOMIC); if (!skb) { - priv->rx_skb[rx_bd_index] = NULL; + priv->rx_skb[index] = NULL; return -ENOMEM; } - priv->rx_skb[rx_bd_index] = skb; - - rxbd = &priv->rx_bd_vbase[rx_bd_index]; + priv->rx_skb[index] = skb; + rxbd = &priv->rx_bd_vbase[index]; paddr = pci_map_single(priv->pdev, skb->data, SKB_BUF_SIZE, PCI_DMA_FROMDEVICE); @@ -446,17 +440,20 @@ static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 rx_bd_index) return -ENOMEM; } - writel(QTN_HOST_LO32(paddr), - PCIE_HDP_HHBM_BUF_PTR(priv->pcie_reg_base)); - writel(QTN_HOST_HI32(paddr), - PCIE_HDP_HHBM_BUF_PTR_H(priv->pcie_reg_base)); - /* keep rx skb paddrs in rx buffer descriptors for cleanup purposes */ rxbd->addr = cpu_to_le32(QTN_HOST_LO32(paddr)); rxbd->addr_h = cpu_to_le32(QTN_HOST_HI32(paddr)); - rxbd->info = 0x0; + /* sync up all descriptor updates */ + wmb(); + + writel(QTN_HOST_HI32(paddr), + PCIE_HDP_HHBM_BUF_PTR_H(priv->pcie_reg_base)); + writel(QTN_HOST_LO32(paddr), + PCIE_HDP_HHBM_BUF_PTR(priv->pcie_reg_base)); + + writel(index, PCIE_HDP_TX_HOST_Q_WR_PTR(priv->pcie_reg_base)); return 0; } @@ -787,8 +784,6 @@ static int qtnf_rx_poll(struct napi_struct *napi, int budget) break; } - writel(priv->hw_txproc_wr_ptr, - PCIE_HDP_TX_HOST_Q_WR_PTR(priv->pcie_reg_base)); } if (processed < budget) { -- cgit v1.2.3 From dfb13db68f3eaad21c9bbb9ad5e7df502a8deb83 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 15 Aug 2017 16:06:35 +0300 Subject: qtnfmac: decrease default Tx queue size Avoid extra buffering in driver by default. Use max hardware Tx queue size. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index a0b65d487ddb..f18e8a724c68 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -36,7 +36,7 @@ static bool use_msi = true; module_param(use_msi, bool, 0644); MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt"); -static unsigned int tx_bd_size_param = 256; +static unsigned int tx_bd_size_param = 32; module_param(tx_bd_size_param, uint, 0644); MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size"); -- cgit v1.2.3 From 3cbc3a0f19ac73e7711dcc1b6d1528035a5d9cdc Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 15 Aug 2017 16:06:36 +0300 Subject: qtnfmac: switch to kernel circ_buf implementation Current code for both Rx and Tx queue management is a custom and incomplete circular buffer implementation. It makes a lot of sense to switch to kernel built-in circ_buf implementation. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- .../net/wireless/quantenna/qtnfmac/pearl/pcie.c | 206 +++++++++++++-------- .../quantenna/qtnfmac/pearl/pcie_bus_priv.h | 10 +- 2 files changed, 136 insertions(+), 80 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index f18e8a724c68..f8207ab25576 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "qtn_hw_ids.h" #include "pcie_bus_priv.h" @@ -44,10 +45,6 @@ static unsigned int rx_bd_size_param = 256; module_param(rx_bd_size_param, uint, 0644); MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size"); -static unsigned int rx_bd_reserved_param = 16; -module_param(rx_bd_reserved_param, uint, 0644); -MODULE_PARM_DESC(rx_bd_reserved_param, "Reserved RX descriptors"); - static u8 flashboot = 1; module_param(flashboot, byte, 0644); MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS"); @@ -392,9 +389,8 @@ static int alloc_bd_table(struct qtnf_pcie_bus_priv *priv) pr_debug("TX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr); - priv->tx_bd_reclaim_start = 0; - priv->tx_bd_index = 0; - priv->tx_queue_len = 0; + priv->tx_bd_r_index = 0; + priv->tx_bd_w_index = 0; /* rx bd */ @@ -413,8 +409,6 @@ static int alloc_bd_table(struct qtnf_pcie_bus_priv *priv) pr_debug("RX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr); - priv->rx_bd_index = 0; - return 0; } @@ -445,6 +439,8 @@ static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 index) rxbd->addr_h = cpu_to_le32(QTN_HOST_HI32(paddr)); rxbd->info = 0x0; + priv->rx_bd_w_index = index; + /* sync up all descriptor updates */ wmb(); @@ -510,6 +506,8 @@ static int qtnf_pcie_init_xfer(struct qtnf_pcie_bus_priv *priv) priv->tx_bd_num = tx_bd_size_param; priv->rx_bd_num = rx_bd_size_param; + priv->rx_bd_w_index = 0; + priv->rx_bd_r_index = 0; ret = alloc_skb_array(priv); if (ret) { @@ -532,67 +530,69 @@ static int qtnf_pcie_init_xfer(struct qtnf_pcie_bus_priv *priv) return ret; } -static int qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv) +static void qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv) { struct qtnf_tx_bd *txbd; struct sk_buff *skb; dma_addr_t paddr; - int last_sent; - int count; + u32 tx_done_index; + int count = 0; int i; - last_sent = readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base)) - % priv->tx_bd_num; - i = priv->tx_bd_reclaim_start; - count = 0; - while (i != last_sent) { - skb = priv->tx_skb[i]; - if (!skb) - break; + tx_done_index = readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base)) + & (priv->tx_bd_num - 1); - txbd = &priv->tx_bd_vbase[i]; - paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h), - le32_to_cpu(txbd->addr)); - pci_unmap_single(priv->pdev, paddr, skb->len, PCI_DMA_TODEVICE); + i = priv->tx_bd_r_index; - if (skb->dev) { - skb->dev->stats.tx_packets++; - skb->dev->stats.tx_bytes += skb->len; + while (CIRC_CNT(tx_done_index, i, priv->tx_bd_num)) { + skb = priv->tx_skb[i]; + if (likely(skb)) { + txbd = &priv->tx_bd_vbase[i]; + paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h), + le32_to_cpu(txbd->addr)); + pci_unmap_single(priv->pdev, paddr, skb->len, + PCI_DMA_TODEVICE); + + if (skb->dev) { + skb->dev->stats.tx_packets++; + skb->dev->stats.tx_bytes += skb->len; + + if (netif_queue_stopped(skb->dev)) + netif_wake_queue(skb->dev); + } - if (netif_queue_stopped(skb->dev)) - netif_wake_queue(skb->dev); + dev_kfree_skb_any(skb); } - dev_kfree_skb_any(skb); priv->tx_skb[i] = NULL; - priv->tx_queue_len--; count++; if (++i >= priv->tx_bd_num) i = 0; } - priv->tx_bd_reclaim_start = i; priv->tx_reclaim_done += count; priv->tx_reclaim_req++; + priv->tx_bd_r_index = i; - return count; } -static bool qtnf_tx_queue_ready(struct qtnf_pcie_bus_priv *priv) +static int qtnf_tx_queue_ready(struct qtnf_pcie_bus_priv *priv) { - if (priv->tx_queue_len >= priv->tx_bd_num - 1) { + if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index, + priv->tx_bd_num)) { pr_err_ratelimited("reclaim full Tx queue\n"); qtnf_pcie_data_tx_reclaim(priv); - if (priv->tx_queue_len >= priv->tx_bd_num - 1) { + if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index, + priv->tx_bd_num)) { priv->tx_full_count++; - return false; + return 0; } } - return true; + return 1; } static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) @@ -617,7 +617,7 @@ static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) return NETDEV_TX_BUSY; } - i = priv->tx_bd_index; + i = priv->tx_bd_w_index; priv->tx_skb[i] = skb; len = skb->len; @@ -649,8 +649,7 @@ static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) if (++i >= priv->tx_bd_num) i = 0; - priv->tx_bd_index = i; - priv->tx_queue_len++; + priv->tx_bd_w_index = i; tx_done: if (ret && skb) { @@ -709,16 +708,19 @@ irq_done: return IRQ_HANDLED; } -static inline void hw_txproc_wr_ptr_inc(struct qtnf_pcie_bus_priv *priv) +static int qtnf_rx_data_ready(struct qtnf_pcie_bus_priv *priv) { - u32 index; + u16 index = priv->rx_bd_r_index; + struct qtnf_rx_bd *rxbd; + u32 descw; - index = priv->hw_txproc_wr_ptr; + rxbd = &priv->rx_bd_vbase[index]; + descw = le32_to_cpu(rxbd->info); - if (++index >= priv->rx_bd_num) - index = 0; + if (descw & QTN_TXDONE_MASK) + return 1; - priv->hw_txproc_wr_ptr = index; + return 0; } static int qtnf_rx_poll(struct napi_struct *napi, int budget) @@ -730,26 +732,52 @@ static int qtnf_rx_poll(struct napi_struct *napi, int budget) int processed = 0; struct qtnf_rx_bd *rxbd; dma_addr_t skb_paddr; + int consume; u32 descw; - u16 index; + u32 psize; + u16 r_idx; + u16 w_idx; int ret; - index = priv->rx_bd_index; - rxbd = &priv->rx_bd_vbase[index]; + while (processed < budget) { - descw = le32_to_cpu(rxbd->info); - while ((descw & QTN_TXDONE_MASK) && (processed < budget)) { - skb = priv->rx_skb[index]; + if (!qtnf_rx_data_ready(priv)) + goto rx_out; - if (likely(skb)) { - skb_put(skb, QTN_GET_LEN(descw)); + r_idx = priv->rx_bd_r_index; + rxbd = &priv->rx_bd_vbase[r_idx]; + descw = le32_to_cpu(rxbd->info); + + skb = priv->rx_skb[r_idx]; + psize = QTN_GET_LEN(descw); + consume = 1; + if (!(descw & QTN_TXDONE_MASK)) { + pr_warn("skip invalid rxbd[%d]\n", r_idx); + consume = 0; + } + + if (!skb) { + pr_warn("skip missing rx_skb[%d]\n", r_idx); + consume = 0; + } + + if (skb && (skb_tailroom(skb) < psize)) { + pr_err("skip packet with invalid length: %u > %u\n", + psize, skb_tailroom(skb)); + consume = 0; + } + + if (skb) { skb_paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h), le32_to_cpu(rxbd->addr)); pci_unmap_single(priv->pdev, skb_paddr, SKB_BUF_SIZE, PCI_DMA_FROMDEVICE); + } + if (consume) { + skb_put(skb, psize); ndev = qtnf_classify_skb(bus, skb); if (likely(ndev)) { ndev->stats.rx_packets++; @@ -762,30 +790,38 @@ static int qtnf_rx_poll(struct napi_struct *napi, int budget) bus->mux_dev.stats.rx_dropped++; dev_kfree_skb_any(skb); } - - processed++; } else { - pr_err("missing rx_skb[%d]\n", index); + if (skb) { + bus->mux_dev.stats.rx_dropped++; + dev_kfree_skb_any(skb); + } } - /* attached rx buffer is passed upstream: map a new one */ - ret = skb2rbd_attach(priv, index); - if (likely(!ret)) { - if (++index >= priv->rx_bd_num) - index = 0; + priv->rx_skb[r_idx] = NULL; + if (++r_idx >= priv->rx_bd_num) + r_idx = 0; - priv->rx_bd_index = index; - hw_txproc_wr_ptr_inc(priv); + priv->rx_bd_r_index = r_idx; - rxbd = &priv->rx_bd_vbase[index]; - descw = le32_to_cpu(rxbd->info); - } else { - pr_err("failed to allocate new rx_skb[%d]\n", index); - break; + /* repalce processed buffer by a new one */ + w_idx = priv->rx_bd_w_index; + while (CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index, + priv->rx_bd_num) > 0) { + if (++w_idx >= priv->rx_bd_num) + w_idx = 0; + + ret = skb2rbd_attach(priv, w_idx); + if (ret) { + pr_err("failed to allocate new rx_skb[%d]\n", + w_idx); + break; + } } + processed++; } +rx_out: if (processed < budget) { napi_complete(napi); qtnf_en_rxdone_irq(priv); @@ -1056,10 +1092,18 @@ static int qtnf_dbg_irq_stats(struct seq_file *s, void *data) { struct qtnf_bus *bus = dev_get_drvdata(s->private); struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); + u32 reg = readl(PCIE_HDP_INT_EN(priv->pcie_reg_base)); + u32 status; seq_printf(s, "pcie_irq_count(%u)\n", priv->pcie_irq_count); seq_printf(s, "pcie_irq_tx_count(%u)\n", priv->pcie_irq_tx_count); + status = reg & PCIE_HDP_INT_TX_BITS; + seq_printf(s, "pcie_irq_tx_status(%s)\n", + (status == PCIE_HDP_INT_TX_BITS) ? "EN" : "DIS"); seq_printf(s, "pcie_irq_rx_count(%u)\n", priv->pcie_irq_rx_count); + status = reg & PCIE_HDP_INT_RX_BITS; + seq_printf(s, "pcie_irq_rx_status(%s)\n", + (status == PCIE_HDP_INT_RX_BITS) ? "EN" : "DIS"); return 0; } @@ -1073,10 +1117,24 @@ static int qtnf_dbg_hdp_stats(struct seq_file *s, void *data) seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count); seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done); seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req); - seq_printf(s, "tx_bd_reclaim_start(%u)\n", priv->tx_bd_reclaim_start); - seq_printf(s, "tx_bd_index(%u)\n", priv->tx_bd_index); - seq_printf(s, "rx_bd_index(%u)\n", priv->rx_bd_index); - seq_printf(s, "tx_queue_len(%u)\n", priv->tx_queue_len); + + seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index); + seq_printf(s, "tx_bd_p_index(%u)\n", + readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base)) + & (priv->tx_bd_num - 1)); + seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index); + seq_printf(s, "tx queue len(%u)\n", + CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index, + priv->tx_bd_num)); + + seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index); + seq_printf(s, "rx_bd_p_index(%u)\n", + readl(PCIE_HDP_TX0DMA_CNT(priv->pcie_reg_base)) + & (priv->rx_bd_num - 1)); + seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index); + seq_printf(s, "rx alloc queue len(%u)\n", + CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index, + priv->rx_bd_num)); return 0; } diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h index 2a897db2bd79..1b37914299e9 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h @@ -66,13 +66,11 @@ struct qtnf_pcie_bus_priv { void *bd_table_vaddr; u32 bd_table_len; - u32 hw_txproc_wr_ptr; + u32 rx_bd_w_index; + u32 rx_bd_r_index; - u16 tx_bd_reclaim_start; - u16 tx_bd_index; - u32 tx_queue_len; - - u16 rx_bd_index; + u32 tx_bd_w_index; + u32 tx_bd_r_index; u32 pcie_irq_mask; -- cgit v1.2.3 From cc75f9e5bc66dd63ff5770c24f0e34a69177ddeb Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 15 Aug 2017 16:06:37 +0300 Subject: qtnfmac: introduce counter for Rx underflow events Introduce counter for Rx underflow events. Export this counter via debugfs. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 16 ++++++++++++++-- .../net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h | 1 + drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h | 1 + .../wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h | 1 + 4 files changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index f8207ab25576..72730aff2a41 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -686,14 +686,21 @@ static irqreturn_t qtnf_interrupt(int irq, void *data) if (!(status & priv->pcie_irq_mask)) goto irq_done; - if (status & PCIE_HDP_INT_RX_BITS) { + if (status & PCIE_HDP_INT_RX_BITS) priv->pcie_irq_rx_count++; + + if (status & PCIE_HDP_INT_TX_BITS) + priv->pcie_irq_tx_count++; + + if (status & PCIE_HDP_INT_HHBM_UF) + priv->pcie_irq_uf_count++; + + if (status & PCIE_HDP_INT_RX_BITS) { qtnf_dis_rxdone_irq(priv); napi_schedule(&bus->mux_napi); } if (status & PCIE_HDP_INT_TX_BITS) { - priv->pcie_irq_tx_count++; qtnf_dis_txdone_irq(priv); tasklet_hi_schedule(&priv->reclaim_tq); } @@ -1104,6 +1111,10 @@ static int qtnf_dbg_irq_stats(struct seq_file *s, void *data) status = reg & PCIE_HDP_INT_RX_BITS; seq_printf(s, "pcie_irq_rx_status(%s)\n", (status == PCIE_HDP_INT_RX_BITS) ? "EN" : "DIS"); + seq_printf(s, "pcie_irq_uf_count(%u)\n", priv->pcie_irq_uf_count); + status = reg & PCIE_HDP_INT_HHBM_UF; + seq_printf(s, "pcie_irq_hhbm_uf_status(%s)\n", + (status == PCIE_HDP_INT_HHBM_UF) ? "EN" : "DIS"); return 0; } @@ -1189,6 +1200,7 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) pcie_priv->pcie_irq_count = 0; pcie_priv->pcie_irq_rx_count = 0; pcie_priv->pcie_irq_tx_count = 0; + pcie_priv->pcie_irq_uf_count = 0; pcie_priv->tx_reclaim_done = 0; pcie_priv->tx_reclaim_req = 0; diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h index 1b37914299e9..698e42132ed4 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h @@ -78,6 +78,7 @@ struct qtnf_pcie_bus_priv { u32 pcie_irq_count; u32 pcie_irq_rx_count; u32 pcie_irq_tx_count; + u32 pcie_irq_uf_count; u32 tx_full_count; u32 tx_done_count; u32 tx_reclaim_done; diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h index e00d508fbcf0..667f5ec457e3 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h @@ -50,6 +50,7 @@ #define PCIE_HDP_INT_RX_BITS (0 \ | PCIE_HDP_INT_EP_TXDMA \ | PCIE_HDP_INT_EP_TXEMPTY \ + | PCIE_HDP_INT_HHBM_UF \ ) #define PCIE_HDP_INT_TX_BITS (0 \ diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h index 78715b8a8ef9..69696f118769 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h @@ -333,6 +333,7 @@ #define PCIE_HDP_INT_RX_LEN_ERR (BIT(2)) #define PCIE_HDP_INT_RX_HDR_LEN_ERR (BIT(3)) #define PCIE_HDP_INT_EP_TXDMA (BIT(12)) +#define PCIE_HDP_INT_HHBM_UF (BIT(13)) #define PCIE_HDP_INT_EP_TXEMPTY (BIT(15)) #define PCIE_HDP_INT_IPC (BIT(29)) -- cgit v1.2.3 From 0593da274d4d7fec6edc8a5ad6993e88ab36c8be Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 15 Aug 2017 16:06:38 +0300 Subject: qtnfmac: modify tx reclaim locking Perform additional reclaim from qtnf_pcie_data_tx. Lock tx_lock serves only reclaim synchronization purposes. Rename it accordingly and improve granularity moving this lock to qtnf_pcie_data_tx_reclaim. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 17 ++++++----------- .../wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h | 4 ++-- 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index 72730aff2a41..cd2f2b667643 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -534,11 +534,13 @@ static void qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv) { struct qtnf_tx_bd *txbd; struct sk_buff *skb; + unsigned long flags; dma_addr_t paddr; u32 tx_done_index; int count = 0; int i; + spin_lock_irqsave(&priv->tx_reclaim_lock, flags); tx_done_index = readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base)) & (priv->tx_bd_num - 1); @@ -576,6 +578,7 @@ static void qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv) priv->tx_reclaim_req++; priv->tx_bd_r_index = i; + spin_unlock_irqrestore(&priv->tx_reclaim_lock, flags); } static int qtnf_tx_queue_ready(struct qtnf_pcie_bus_priv *priv) @@ -600,20 +603,14 @@ static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); dma_addr_t txbd_paddr, skb_paddr; struct qtnf_tx_bd *txbd; - unsigned long flags; int len, i; u32 info; int ret = 0; - spin_lock_irqsave(&priv->tx_lock, flags); - - priv->tx_done_count++; - if (!qtnf_tx_queue_ready(priv)) { if (skb->dev) netif_stop_queue(skb->dev); - spin_unlock_irqrestore(&priv->tx_lock, flags); return NETDEV_TX_BUSY; } @@ -659,7 +656,8 @@ tx_done: dev_kfree_skb_any(skb); } - spin_unlock_irqrestore(&priv->tx_lock, flags); + qtnf_pcie_data_tx_reclaim(priv); + priv->tx_done_count++; return NETDEV_TX_OK; } @@ -1067,11 +1065,8 @@ static int qtnf_bringup_fw(struct qtnf_bus *bus) static void qtnf_reclaim_tasklet_fn(unsigned long data) { struct qtnf_pcie_bus_priv *priv = (void *)data; - unsigned long flags; - spin_lock_irqsave(&priv->tx_lock, flags); qtnf_pcie_data_tx_reclaim(priv); - spin_unlock_irqrestore(&priv->tx_lock, flags); qtnf_en_txdone_irq(priv); } @@ -1192,7 +1187,7 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) init_completion(&bus->request_firmware_complete); mutex_init(&bus->bus_lock); spin_lock_init(&pcie_priv->irq_lock); - spin_lock_init(&pcie_priv->tx_lock); + spin_lock_init(&pcie_priv->tx_reclaim_lock); /* init stats */ pcie_priv->tx_full_count = 0; diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h index 698e42132ed4..e76a23716ee0 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h @@ -32,8 +32,8 @@ struct qtnf_pcie_bus_priv { /* lock for irq configuration changes */ spinlock_t irq_lock; - /* lock for tx operations */ - spinlock_t tx_lock; + /* lock for tx reclaim operations */ + spinlock_t tx_reclaim_lock; u8 msi_enabled; int mps; -- cgit v1.2.3 From 6da1e00a539d978339d6a6471b4346c6d82797ff Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:08 +0530 Subject: rsi: advertise ap mode support AP mode support is advertised to cfg80211. Necessary wiphy parameters are initialized. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 28 +++++++++++++++++++++++++++- drivers/net/wireless/rsi/rsi_main.h | 10 ++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 210ad79038ed..2da54932070d 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -122,6 +122,23 @@ const u16 rsi_mcsrates[8] = { RSI_RATE_MCS4, RSI_RATE_MCS5, RSI_RATE_MCS6, RSI_RATE_MCS7 }; +static const u32 rsi_max_ap_stas[16] = { + 32, /* 1 - Wi-Fi alone */ + 0, /* 2 */ + 0, /* 3 */ + 0, /* 4 - BT EDR alone */ + 4, /* 5 - STA + BT EDR */ + 32, /* 6 - AP + BT EDR */ + 0, /* 7 */ + 0, /* 8 - BT LE alone */ + 4, /* 9 - STA + BE LE */ + 0, /* 10 */ + 0, /* 11 */ + 0, /* 12 */ + 1, /* 13 - STA + BT Dual */ + 4, /* 14 - AP + BT Dual */ +}; + /** * rsi_is_cipher_wep() - This function determines if the cipher is WEP or not. * @common: Pointer to the driver private structure. @@ -1348,7 +1365,8 @@ int rsi_mac80211_attach(struct rsi_common *common) SET_IEEE80211_PERM_ADDR(hw, common->mac_addr); ether_addr_copy(hw->wiphy->addr_mask, addr_mask); - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP); wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->retry_short = RETRY_SHORT; wiphy->retry_long = RETRY_LONG; @@ -1363,6 +1381,14 @@ int rsi_mac80211_attach(struct rsi_common *common) wiphy->bands[NL80211_BAND_5GHZ] = &adapter->sbands[NL80211_BAND_5GHZ]; + /* AP Parameters */ + wiphy->max_ap_assoc_sta = rsi_max_ap_stas[common->oper_mode - 1]; + common->max_stations = wiphy->max_ap_assoc_sta; + rsi_dbg(ERR_ZONE, "Max Stations Allowed = %d\n", common->max_stations); + hw->sta_data_size = sizeof(struct rsi_sta); + wiphy->flags = WIPHY_FLAG_REPORTS_OBSS; + wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; wiphy->reg_notifier = rsi_reg_notify; wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index d05b5e0847bc..0077888190c3 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -21,6 +21,13 @@ #include #include +struct rsi_sta { + struct ieee80211_sta *sta; + s16 sta_id; + u16 seq_start[IEEE80211_NUM_TIDS]; + bool start_tx_aggr[IEEE80211_NUM_TIDS]; +}; + struct rsi_hw; #include "rsi_ps.h" @@ -253,6 +260,9 @@ struct rsi_common { u16 beacon_interval; u8 dtim_cnt; + + /* AP mode parameters */ + int max_stations; }; enum host_intf { -- cgit v1.2.3 From 03c34c0d73ae872e6e962e02e3c4e98ae364379b Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:09 +0530 Subject: rsi: add interface changes for ap mode AP mode is handled in add_interface callback of mac80211. Also for AP mode, sending rx filter frame to disallow beacons to host is added. Station structures are initialized to NULL. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 43 +++++++++++++++++++++++------ drivers/net/wireless/rsi/rsi_91x_mgmt.c | 5 ++-- drivers/net/wireless/rsi/rsi_main.h | 3 ++ drivers/net/wireless/rsi/rsi_mgmt.h | 6 ++-- 4 files changed, 44 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 2da54932070d..edcba567bfe8 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -342,25 +342,51 @@ static int rsi_mac80211_add_interface(struct ieee80211_hw *hw, { struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + enum opmode intf_mode; int ret = -EOPNOTSUPP; vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; mutex_lock(&common->mutex); + + if (adapter->sc_nvifs > 1) { + mutex_unlock(&common->mutex); + return -EOPNOTSUPP; + } + switch (vif->type) { case NL80211_IFTYPE_STATION: - if (!adapter->sc_nvifs) { - ++adapter->sc_nvifs; - adapter->vifs[0] = vif; - ret = rsi_set_vap_capabilities(common, - STA_OPMODE, - VAP_ADD); - } + rsi_dbg(INFO_ZONE, "Station Mode"); + intf_mode = STA_OPMODE; + break; + case NL80211_IFTYPE_AP: + rsi_dbg(INFO_ZONE, "AP Mode"); + intf_mode = AP_OPMODE; break; default: rsi_dbg(ERR_ZONE, "%s: Interface type %d not supported\n", __func__, vif->type); + goto out; } + + adapter->vifs[adapter->sc_nvifs++] = vif; + ret = rsi_set_vap_capabilities(common, intf_mode, common->mac_addr, + 0, VAP_ADD); + if (ret) { + rsi_dbg(ERR_ZONE, "Failed to set VAP capabilities\n"); + goto out; + } + + if (vif->type == NL80211_IFTYPE_AP) { + int i; + + rsi_send_rx_filter_frame(common, DISALLOW_BEACONS); + common->min_rate = RSI_RATE_AUTO; + for (i = 0; i < common->max_stations; i++) + common->stations[i].sta = NULL; + } + +out: mutex_unlock(&common->mutex); return ret; @@ -383,7 +409,8 @@ static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw, mutex_lock(&common->mutex); if (vif->type == NL80211_IFTYPE_STATION) { adapter->sc_nvifs--; - rsi_set_vap_capabilities(common, STA_OPMODE, VAP_DELETE); + rsi_set_vap_capabilities(common, STA_OPMODE, vif->addr, + 0, VAP_DELETE); } if (!memcmp(adapter->vifs[0], vif, sizeof(struct ieee80211_vif))) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index f93499d0b8fa..233a418555bb 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -624,6 +624,8 @@ static int rsi_program_bb_rf(struct rsi_common *common) */ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode, + u8 *mac_addr, + u8 vap_id, u8 vap_status) { struct sk_buff *skb = NULL; @@ -632,7 +634,6 @@ int rsi_set_vap_capabilities(struct rsi_common *common, struct ieee80211_hw *hw = adapter->hw; struct ieee80211_conf *conf = &hw->conf; u16 frame_len = sizeof(struct rsi_vap_caps); - u16 vap_id = 0; rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__); @@ -656,7 +657,7 @@ int rsi_set_vap_capabilities(struct rsi_common *common, vap_caps->radioid_macid = ((common->mac_id & 0xf) << 4) | (common->radio_id & 0xf); - memcpy(vap_caps->mac_addr, common->mac_addr, IEEE80211_ADDR_LEN); + memcpy(vap_caps->mac_addr, mac_addr, IEEE80211_ADDR_LEN); vap_caps->keep_alive_period = cpu_to_le16(90); vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD); diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 0077888190c3..9f5f33f616b6 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -90,6 +90,7 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...); #define IEEE80211_MGMT_FRAME 0x00 #define IEEE80211_CTL_FRAME 0x04 +#define RSI_MAX_ASSOC_STAS 32 #define IEEE80211_QOS_TID 0x0f #define IEEE80211_NONQOS_TID 16 @@ -262,6 +263,8 @@ struct rsi_common { u8 dtim_cnt; /* AP mode parameters */ + struct rsi_sta stations[RSI_MAX_ASSOC_STAS + 1]; + int num_stations; int max_stations; }; diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 201a46572c69..9093ba685fb0 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -188,8 +188,8 @@ IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) enum opmode { - STA_OPMODE = 1, - AP_OPMODE = 2 + AP_OPMODE = 0, + STA_OPMODE, }; enum vap_status { @@ -591,7 +591,7 @@ static inline void rsi_set_len_qno(__le16 *addr, u16 len, u8 qno) int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg); int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode, - u8 vap_status); + u8 *mac_addr, u8 vap_id, u8 vap_status); int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid, u16 ssn, u8 buf_size, u8 event); int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len, -- cgit v1.2.3 From 75ca0049aad68136c8f673013792063dde530810 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:10 +0530 Subject: rsi: remove interface changes for AP mode remove_interface callback of mac80211 is handled for AP mode. Same is notified to firmware through vap_capabilities frame with VAP status VAP_DELETE. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index edcba567bfe8..99446bbc0516 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -405,13 +405,31 @@ static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw, { struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + enum opmode opmode; + + rsi_dbg(INFO_ZONE, "Remove Interface Called\n"); mutex_lock(&common->mutex); - if (vif->type == NL80211_IFTYPE_STATION) { - adapter->sc_nvifs--; - rsi_set_vap_capabilities(common, STA_OPMODE, vif->addr, - 0, VAP_DELETE); + + if (adapter->sc_nvifs <= 0) { + mutex_unlock(&common->mutex); + return; + } + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + opmode = STA_OPMODE; + break; + case NL80211_IFTYPE_AP: + opmode = AP_OPMODE; + break; + default: + mutex_unlock(&common->mutex); + return; } + rsi_set_vap_capabilities(common, opmode, vif->addr, + 0, VAP_DELETE); + adapter->sc_nvifs--; if (!memcmp(adapter->vifs[0], vif, sizeof(struct ieee80211_vif))) adapter->vifs[0] = NULL; -- cgit v1.2.3 From d26a9559403c7c3ec3b430f5825bc22c3d40abdb Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:11 +0530 Subject: rsi: add beacon changes for AP mode Mac80211 config parameter BEACON_ENABLE is handled. When VAP capabilities frame with AP mode is configured to firmware, beacon events start coming to host at each PreTBTT. At this time, beacon is taken from mac80211, descriptor is prepared and send to firmware. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_core.c | 15 +++++-- drivers/net/wireless/rsi/rsi_91x_hal.c | 65 ++++++++++++++++++++++++++++- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 12 ++++++ drivers/net/wireless/rsi/rsi_91x_mgmt.c | 48 ++++++++++++++++++--- drivers/net/wireless/rsi/rsi_hal.h | 2 + drivers/net/wireless/rsi/rsi_main.h | 13 +++--- drivers/net/wireless/rsi/rsi_mgmt.h | 11 +++++ 7 files changed, 152 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index 88a1a56a20ab..6cfda8626cfe 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -16,6 +16,7 @@ #include "rsi_mgmt.h" #include "rsi_common.h" +#include "rsi_hal.h" /** * rsi_determine_min_weight_queue() - This function determines the queue with @@ -136,6 +137,10 @@ static u8 rsi_core_determine_hal_queue(struct rsi_common *common) u8 q_num = INVALID_QUEUE; u8 ii = 0; + if (skb_queue_len(&common->tx_queue[MGMT_BEACON_Q])) { + q_num = MGMT_BEACON_Q; + return q_num; + } if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) { if (!common->mgmt_q_block) q_num = MGMT_SOFT_Q; @@ -291,10 +296,14 @@ void rsi_core_qos_processor(struct rsi_common *common) break; } - if (q_num == MGMT_SOFT_Q) + if (q_num == MGMT_SOFT_Q) { status = rsi_send_mgmt_pkt(common, skb); - else + } else if (q_num == MGMT_BEACON_Q) { + status = rsi_send_pkt_to_bus(common, skb); + dev_kfree_skb(skb); + } else { status = rsi_send_data_pkt(common, skb); + } if (status) { mutex_unlock(&common->tx_lock); @@ -358,7 +367,7 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb) tx_params->sta_id = 0; } - if ((q_num != MGMT_SOFT_Q) && + if ((q_num < MGMT_SOFT_Q) && ((skb_queue_len(&common->tx_queue[q_num]) + 1) >= DATA_QUEUE_WATER_MARK)) { rsi_dbg(ERR_ZONE, "%s: sw queue full\n", __func__); diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index 4addcc0826db..1ed73320e19f 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -25,7 +25,15 @@ static struct ta_metadata metadata_flash_content[] = { {"rsi/rs9113_wlan_qspi.rps", 0x00010000}, }; -/*This function prepares descriptor for given management packet*/ +int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb) +{ + struct rsi_hw *adapter = common->priv; + int status; + + status = adapter->host_intf_ops->write_pkt(common->priv, + skb->data, skb->len); + return status; +} static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) { @@ -306,6 +314,61 @@ err: return status; } +int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb) +{ + struct rsi_hw *adapter = (struct rsi_hw *)common->priv; + struct rsi_data_desc *bcn_frm; + struct ieee80211_hw *hw = common->priv->hw; + struct ieee80211_conf *conf = &hw->conf; + struct sk_buff *mac_bcn; + u8 vap_id = 0; + u16 tim_offset; + + mac_bcn = ieee80211_beacon_get_tim(adapter->hw, + adapter->vifs[adapter->sc_nvifs - 1], + &tim_offset, NULL); + if (!mac_bcn) { + rsi_dbg(ERR_ZONE, "Failed to get beacon from mac80211\n"); + return -EINVAL; + } + + common->beacon_cnt++; + bcn_frm = (struct rsi_data_desc *)skb->data; + rsi_set_len_qno(&bcn_frm->len_qno, mac_bcn->len, RSI_WIFI_DATA_Q); + bcn_frm->header_len = MIN_802_11_HDR_LEN; + bcn_frm->frame_info = cpu_to_le16(RSI_DATA_DESC_MAC_BBP_INFO | + RSI_DATA_DESC_NO_ACK_IND | + RSI_DATA_DESC_BEACON_FRAME | + RSI_DATA_DESC_INSERT_TSF | + RSI_DATA_DESC_INSERT_SEQ_NO | + RATE_INFO_ENABLE); + bcn_frm->rate_info = cpu_to_le16(vap_id << 14); + bcn_frm->qid_tid = BEACON_HW_Q; + + if (conf_is_ht40_plus(conf)) { + bcn_frm->bbp_info = cpu_to_le16(LOWER_20_ENABLE); + bcn_frm->bbp_info |= cpu_to_le16(LOWER_20_ENABLE >> 12); + } else if (conf_is_ht40_minus(conf)) { + bcn_frm->bbp_info = cpu_to_le16(UPPER_20_ENABLE); + bcn_frm->bbp_info |= cpu_to_le16(UPPER_20_ENABLE >> 12); + } + + if (common->band == NL80211_BAND_2GHZ) + bcn_frm->bbp_info |= cpu_to_le16(RSI_RATE_1); + else + bcn_frm->bbp_info |= cpu_to_le16(RSI_RATE_6); + + if (mac_bcn->data[tim_offset + 2] == 0) + bcn_frm->frame_info |= cpu_to_le16(RSI_DATA_DESC_DTIM_BEACON); + + memcpy(&skb->data[FRAME_DESC_SZ], mac_bcn->data, mac_bcn->len); + skb_put(skb, mac_bcn->len + FRAME_DESC_SZ); + + dev_kfree_skb(mac_bcn); + + return 0; +} + static void bl_cmd_timeout(unsigned long priv) { struct rsi_hw *adapter = (struct rsi_hw *)priv; diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 99446bbc0516..6038a2fc9eda 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -652,6 +652,18 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw, common->cqm_info.rssi_thold, common->cqm_info.rssi_hyst); } + + if ((changed & BSS_CHANGED_BEACON_ENABLED) && + (vif->type == NL80211_IFTYPE_AP)) { + if (bss->enable_beacon) { + rsi_dbg(INFO_ZONE, "===> BEACON ENABLED <===\n"); + common->beacon_enabled = 1; + } else { + rsi_dbg(INFO_ZONE, "===> BEACON DISABLED <===\n"); + common->beacon_enabled = 0; + } + } + mutex_unlock(&common->mutex); } diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 233a418555bb..e47fc0d96475 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -18,6 +18,7 @@ #include "rsi_mgmt.h" #include "rsi_common.h" #include "rsi_ps.h" +#include "rsi_hal.h" static struct bootup_params boot_params_20 = { .magic_number = cpu_to_le16(0x5aa5), @@ -1518,6 +1519,31 @@ int rsi_set_antenna(struct rsi_common *common, u8 antenna) return rsi_send_internal_mgmt_frame(common, skb); } +static int rsi_send_beacon(struct rsi_common *common) +{ + struct sk_buff *skb = NULL; + u8 dword_align_bytes = 0; + + skb = dev_alloc_skb(MAX_MGMT_PKT_SIZE); + if (!skb) + return -ENOMEM; + + memset(skb->data, 0, MAX_MGMT_PKT_SIZE); + + dword_align_bytes = ((unsigned long)skb->data & 0x3f); + if (dword_align_bytes) + skb_pull(skb, (64 - dword_align_bytes)); + if (rsi_prepare_beacon(common, skb)) { + rsi_dbg(ERR_ZONE, "Failed to prepare beacon\n"); + return -EINVAL; + } + skb_queue_tail(&common->tx_queue[MGMT_BEACON_Q], skb); + rsi_set_event(&common->tx_thread.event); + rsi_dbg(DATA_TX_ZONE, "%s: Added to beacon queue\n", __func__); + + return 0; +} + /** * rsi_handle_ta_confirm_type() - This function handles the confirm frames. * @common: Pointer to the driver private structure. @@ -1722,21 +1748,33 @@ int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg) rsi_dbg(FSM_ZONE, "%s: Msg Len: %d, Msg Type: %4x\n", __func__, msg_len, msg_type); - if (msg_type == TA_CONFIRM_TYPE) { + switch (msg_type) { + case TA_CONFIRM_TYPE: return rsi_handle_ta_confirm_type(common, msg); - } else if (msg_type == CARD_READY_IND) { + case CARD_READY_IND: rsi_dbg(FSM_ZONE, "%s: Card ready indication received\n", __func__); return rsi_handle_card_ready(common, msg); - } else if (msg_type == TX_STATUS_IND) { + case TX_STATUS_IND: if (msg[15] == PROBEREQ_CONFIRM) { common->mgmt_q_block = false; rsi_dbg(FSM_ZONE, "%s: Probe confirm received\n", __func__); } - } else if (msg_type == RX_DOT11_MGMT) { + break; + case BEACON_EVENT_IND: + rsi_dbg(INFO_ZONE, "Beacon event\n"); + if (common->fsm_state != FSM_MAC_INIT_DONE) + return -1; + if (common->iface_down) + return -1; + if (!common->beacon_enabled) + return -1; + rsi_send_beacon(common); + break; + case RX_DOT11_MGMT: return rsi_mgmt_pkt_to_core(common, msg, msg_len); - } else { + default: rsi_dbg(INFO_ZONE, "Received packet type: 0x%x\n", msg_type); } return 0; diff --git a/drivers/net/wireless/rsi/rsi_hal.h b/drivers/net/wireless/rsi/rsi_hal.h index 00c6a0c5a891..297f4ce2c39e 100644 --- a/drivers/net/wireless/rsi/rsi_hal.h +++ b/drivers/net/wireless/rsi/rsi_hal.h @@ -144,5 +144,7 @@ struct rsi_data_desc { } __packed; int rsi_hal_device_init(struct rsi_hw *adapter); +int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb); +int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 9f5f33f616b6..169e2f9e3c5e 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -72,7 +72,7 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...); #define MULTICAST_WATER_MARK 200 #define MAC_80211_HDR_FRAME_CONTROL 0 #define WME_NUM_AC 4 -#define NUM_SOFT_QUEUES 5 +#define NUM_SOFT_QUEUES 6 #define MAX_HW_QUEUES 12 #define INVALID_QUEUE 0xff #define MAX_CONTINUOUS_VO_PKTS 8 @@ -131,7 +131,8 @@ enum edca_queue { BE_Q, VI_Q, VO_Q, - MGMT_SOFT_Q + MGMT_SOFT_Q, + MGMT_BEACON_Q }; struct security_info { @@ -148,8 +149,8 @@ struct wmm_qinfo { }; struct transmit_q_stats { - u32 total_tx_pkt_send[NUM_EDCA_QUEUES + 1]; - u32 total_tx_pkt_freed[NUM_EDCA_QUEUES + 1]; + u32 total_tx_pkt_send[NUM_EDCA_QUEUES + 2]; + u32 total_tx_pkt_freed[NUM_EDCA_QUEUES + 2]; }; struct vif_priv { @@ -199,7 +200,7 @@ struct rsi_common { struct version_info fw_ver; struct rsi_thread tx_thread; - struct sk_buff_head tx_queue[NUM_EDCA_QUEUES + 1]; + struct sk_buff_head tx_queue[NUM_EDCA_QUEUES + 2]; /* Mutex declaration */ struct mutex mutex; /* Mutex used for tx thread */ @@ -263,6 +264,8 @@ struct rsi_common { u8 dtim_cnt; /* AP mode parameters */ + u8 beacon_enabled; + u16 beacon_cnt; struct rsi_sta stations[RSI_MAX_ASSOC_STAS + 1]; int num_stations; int max_stations; diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 9093ba685fb0..a00aa10b1be5 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -49,6 +49,7 @@ #define TA_CONFIRM_TYPE 0x01 #define RX_DOT11_MGMT 0x02 #define TX_STATUS_IND 0x04 +#define BEACON_EVENT_IND 0x08 #define PROBEREQ_CONFIRM 2 #define CARD_READY_IND 0x00 @@ -187,6 +188,16 @@ IEEE80211_WMM_IE_STA_QOSINFO_AC_BE | \ IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) +#define RSI_DATA_DESC_MAC_BBP_INFO BIT(0) +#define RSI_DATA_DESC_NO_ACK_IND BIT(9) +#define RSI_DATA_DESC_QOS_EN BIT(12) +#define RSI_DATA_DESC_NORMAL_FRAME 0x00 +#define RSI_DATA_DESC_DTIM_BEACON_GATED_FRAME BIT(10) +#define RSI_DATA_DESC_BEACON_FRAME BIT(11) +#define RSI_DATA_DESC_DTIM_BEACON (BIT(10) | BIT(11)) +#define RSI_DATA_DESC_INSERT_TSF BIT(15) +#define RSI_DATA_DESC_INSERT_SEQ_NO BIT(2) + enum opmode { AP_OPMODE = 0, STA_OPMODE, -- cgit v1.2.3 From 3528608f3a7919ab34a4fe60aaea5996260ef75e Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:12 +0530 Subject: rsi: handle station connection in AP mode Station structures are maintained in driver with required fields. When mac80211 callback sta_add is called, driver iterates through list of connected stations to check available index and assigns station id which is important for further communication to that station. Then peer notify frame is send to firmware to inform the firmware about new station connection. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 78 ++++++++++++++++++++++++++--- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 49 +++++++++++------- drivers/net/wireless/rsi/rsi_mgmt.h | 9 +++- 3 files changed, 110 insertions(+), 26 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 6038a2fc9eda..b1c08491c754 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -626,10 +626,12 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw, rsi_send_rx_filter_frame(common, rx_filter_word); } rsi_inform_bss_status(common, + STA_OPMODE, bss_conf->assoc, bss_conf->bssid, bss_conf->qos, - bss_conf->aid); + bss_conf->aid, + NULL, 0); adapter->ps_info.dtim_interval_duration = bss->dtim_period; adapter->ps_info.listen_interval = conf->listen_interval; @@ -1157,18 +1159,80 @@ static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, { struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + bool sta_exist = false; + struct rsi_sta *rsta; + + rsi_dbg(INFO_ZONE, "Station Add: %pM\n", sta->addr); mutex_lock(&common->mutex); - rsi_set_min_rate(hw, sta, common); + if (vif->type == NL80211_IFTYPE_AP) { + u8 cnt; + int sta_idx = -1; + int free_index = -1; + + /* Check if max stations reached */ + if (common->num_stations >= common->max_stations) { + rsi_dbg(ERR_ZONE, "Reject: Max Stations exists\n"); + mutex_unlock(&common->mutex); + return -EOPNOTSUPP; + } + for (cnt = 0; cnt < common->max_stations; cnt++) { + rsta = &common->stations[cnt]; - if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) || - (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)) { - common->vif_info[0].sgi = true; + if (!rsta->sta) { + if (free_index < 0) + free_index = cnt; + continue; + } + if (!memcmp(rsta->sta->addr, sta->addr, ETH_ALEN)) { + rsi_dbg(INFO_ZONE, "Station exists\n"); + sta_idx = cnt; + sta_exist = true; + break; + } + } + if (!sta_exist) { + if (free_index >= 0) + sta_idx = free_index; + } + if (sta_idx < 0) { + rsi_dbg(ERR_ZONE, + "%s: Some problem reaching here...\n", + __func__); + return -EINVAL; + } + rsta = &common->stations[sta_idx]; + rsta->sta = sta; + rsta->sta_id = sta_idx; + for (cnt = 0; cnt < IEEE80211_NUM_TIDS; cnt++) + rsta->start_tx_aggr[cnt] = false; + for (cnt = 0; cnt < IEEE80211_NUM_TIDS; cnt++) + rsta->seq_start[cnt] = 0; + if (!sta_exist) { + rsi_dbg(INFO_ZONE, "New Station\n"); + + /* Send peer notify to device */ + rsi_dbg(INFO_ZONE, "Indicate bss status to device\n"); + rsi_inform_bss_status(common, AP_OPMODE, 1, sta->addr, + sta->wme, sta->aid, sta, sta_idx); + + common->num_stations++; + } } - if (sta->ht_cap.ht_supported) - ieee80211_start_tx_ba_session(sta, 0, 0); + if (vif->type == NL80211_IFTYPE_STATION) { + rsi_set_min_rate(hw, sta, common); + if (sta->ht_cap.ht_supported) { + common->vif_info[0].is_ht = true; + common->bitrate_mask[NL80211_BAND_2GHZ] = + sta->supp_rates[NL80211_BAND_2GHZ]; + if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) || + (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)) + common->vif_info[0].sgi = true; + ieee80211_start_tx_ba_session(sta, 0, 0); + } + } mutex_unlock(&common->mutex); diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index e47fc0d96475..7c0f27a21699 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -455,12 +455,14 @@ static int rsi_mgmt_pkt_to_core(struct rsi_common *common, * Return: status: 0 on success, corresponding negative error code on failure. */ static int rsi_hal_send_sta_notify_frame(struct rsi_common *common, - u8 opmode, + enum opmode opmode, u8 notify_event, const unsigned char *bssid, u8 qos_enable, - u16 aid) + u16 aid, + u16 sta_id) { + struct ieee80211_vif *vif = common->priv->vifs[0]; struct sk_buff *skb = NULL; struct rsi_peer_notify *peer_notify; u16 vap_id = 0; @@ -480,7 +482,10 @@ static int rsi_hal_send_sta_notify_frame(struct rsi_common *common, memset(skb->data, 0, frame_len); peer_notify = (struct rsi_peer_notify *)skb->data; - peer_notify->command = cpu_to_le16(opmode << 1); + if (opmode == STA_OPMODE) + peer_notify->command = cpu_to_le16(PEER_TYPE_AP << 1); + else if (opmode == AP_OPMODE) + peer_notify->command = cpu_to_le16(PEER_TYPE_STA << 1); switch (notify_event) { case STA_CONNECTED: @@ -502,13 +507,15 @@ static int rsi_hal_send_sta_notify_frame(struct rsi_common *common, (frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q); peer_notify->desc.desc_dword0.frame_type = PEER_NOTIFY; + peer_notify->desc.desc_dword3.qid_tid = sta_id; peer_notify->desc.desc_dword3.sta_id = vap_id; skb_put(skb, frame_len); status = rsi_send_internal_mgmt_frame(common, skb); - if (!status && qos_enable) { + if ((vif->type == NL80211_IFTYPE_STATION) && + (!status && qos_enable)) { rsi_set_contention_vals(common); status = rsi_load_radio_caps(common); } @@ -1279,32 +1286,40 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) * Return: None. */ void rsi_inform_bss_status(struct rsi_common *common, + enum opmode opmode, u8 status, - const unsigned char *bssid, + const u8 *addr, u8 qos_enable, - u16 aid) + u16 aid, + struct ieee80211_sta *sta, + u16 sta_id) { if (status) { - common->hw_data_qs_blocked = true; + if (opmode == STA_OPMODE) + common->hw_data_qs_blocked = true; rsi_hal_send_sta_notify_frame(common, - RSI_IFTYPE_STATION, + opmode, STA_CONNECTED, - bssid, + addr, qos_enable, - aid); + aid, sta_id); if (common->min_rate == 0xffff) rsi_send_auto_rate_request(common); - if (!rsi_send_block_unblock_frame(common, false)) - common->hw_data_qs_blocked = false; + if (opmode == STA_OPMODE) { + if (!rsi_send_block_unblock_frame(common, false)) + common->hw_data_qs_blocked = false; + } } else { - common->hw_data_qs_blocked = true; + if (opmode == STA_OPMODE) + common->hw_data_qs_blocked = true; rsi_hal_send_sta_notify_frame(common, - RSI_IFTYPE_STATION, + opmode, STA_DISCONNECTED, - bssid, + addr, qos_enable, - aid); - rsi_send_block_unblock_frame(common, true); + aid, sta_id); + if (opmode == STA_OPMODE) + rsi_send_block_unblock_frame(common, true); } } diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index a00aa10b1be5..a2e377f3519b 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -209,6 +209,10 @@ enum vap_status { VAP_UPDATE = 3 }; +enum peer_type { + PEER_TYPE_AP, + PEER_TYPE_STA, +}; extern struct ieee80211_rate rsi_rates[12]; extern const u16 rsi_mcsrates[8]; @@ -611,8 +615,9 @@ int rsi_set_channel(struct rsi_common *common, struct ieee80211_channel *channel); int rsi_send_vap_dynamic_update(struct rsi_common *common); int rsi_send_block_unblock_frame(struct rsi_common *common, bool event); -void rsi_inform_bss_status(struct rsi_common *common, u8 status, - const u8 *bssid, u8 qos_enable, u16 aid); +void rsi_inform_bss_status(struct rsi_common *common, enum opmode opmode, + u8 status, const u8 *addr, u8 qos_enable, u16 aid, + struct ieee80211_sta *sta, u16 sta_id); void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb); int rsi_mac80211_attach(struct rsi_common *common); void rsi_indicate_tx_status(struct rsi_hw *common, struct sk_buff *skb, -- cgit v1.2.3 From 571b050b42ae8475741332393d0da916e9f96717 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:13 +0530 Subject: rsi: handle station disconnection in AP mode When sta_remove of mac80211 is called, driver iterates through list of existing stations to get the station id. Then peer notify is prepared and send to firmare. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 56 +++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index b1c08491c754..be10d508d740 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -1254,21 +1254,55 @@ static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw, { struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + struct ieee80211_bss_conf *bss = &vif->bss_conf; + struct rsi_sta *rsta; + + rsi_dbg(INFO_ZONE, "Station Remove: %pM\n", sta->addr); mutex_lock(&common->mutex); - /* Resetting all the fields to default values */ - common->bitrate_mask[NL80211_BAND_2GHZ] = 0; - common->bitrate_mask[NL80211_BAND_5GHZ] = 0; - common->min_rate = 0xffff; - common->vif_info[0].is_ht = false; - common->vif_info[0].sgi = false; - common->vif_info[0].seq_start = 0; - common->secinfo.ptk_cipher = 0; - common->secinfo.gtk_cipher = 0; + if (vif->type == NL80211_IFTYPE_AP) { + u8 sta_idx, cnt; - rsi_send_rx_filter_frame(common, 0); - + /* Send peer notify to device */ + rsi_dbg(INFO_ZONE, "Indicate bss status to device\n"); + for (sta_idx = 0; sta_idx < common->max_stations; sta_idx++) { + rsta = &common->stations[sta_idx]; + + if (!rsta->sta) + continue; + if (!memcmp(rsta->sta->addr, sta->addr, ETH_ALEN)) { + rsi_inform_bss_status(common, AP_OPMODE, 0, + sta->addr, sta->wme, + sta->aid, sta, sta_idx); + rsta->sta = NULL; + rsta->sta_id = -1; + for (cnt = 0; cnt < IEEE80211_NUM_TIDS; cnt++) + rsta->start_tx_aggr[cnt] = false; + if (common->num_stations > 0) + common->num_stations--; + break; + } + } + if (sta_idx >= common->max_stations) + rsi_dbg(ERR_ZONE, "%s: No station found\n", __func__); + } + + if (vif->type == NL80211_IFTYPE_STATION) { + /* Resetting all the fields to default values */ + memcpy((u8 *)bss->bssid, (u8 *)sta->addr, ETH_ALEN); + bss->qos = sta->wme; + common->bitrate_mask[NL80211_BAND_2GHZ] = 0; + common->bitrate_mask[NL80211_BAND_5GHZ] = 0; + common->min_rate = 0xffff; + common->vif_info[0].is_ht = false; + common->vif_info[0].sgi = false; + common->vif_info[0].seq_start = 0; + common->secinfo.ptk_cipher = 0; + common->secinfo.gtk_cipher = 0; + if (!common->iface_down) + rsi_send_rx_filter_frame(common, 0); + } mutex_unlock(&common->mutex); return 0; -- cgit v1.2.3 From 19844c0a9a19ab6e9139b5e988449de9d2f559e1 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:14 +0530 Subject: rsi: data and managemet path changes for AP mode Station id needs to be get for data and management frames to fill in the descruptor for AP mode. Few other changes related to AP mode are covered here. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_core.c | 55 ++++++++++++++++++++++------ drivers/net/wireless/rsi/rsi_91x_hal.c | 64 ++++++++++++++++++++++++--------- drivers/net/wireless/rsi/rsi_common.h | 1 + drivers/net/wireless/rsi/rsi_hal.h | 2 +- drivers/net/wireless/rsi/rsi_mgmt.h | 1 + 5 files changed, 96 insertions(+), 27 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index 6cfda8626cfe..2b0516d2f63d 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -320,6 +320,20 @@ void rsi_core_qos_processor(struct rsi_common *common) } } +struct rsi_sta *rsi_find_sta(struct rsi_common *common, u8 *mac_addr) +{ + int i; + + for (i = 0; i < common->max_stations; i++) { + if (!common->stations[i].sta) + continue; + if (!(memcmp(common->stations[i].sta->addr, + mac_addr, ETH_ALEN))) + return &common->stations[i]; + } + return NULL; +} + /** * rsi_core_xmit() - This function transmits the packets received from mac80211 * @common: Pointer to the driver private structure. @@ -332,39 +346,60 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb) struct rsi_hw *adapter = common->priv; struct ieee80211_tx_info *info; struct skb_info *tx_params; - struct ieee80211_hdr *tmp_hdr = NULL; + struct ieee80211_hdr *wh; + struct ieee80211_vif *vif = adapter->vifs[0]; u8 q_num, tid = 0; + struct rsi_sta *rsta = NULL; if ((!skb) || (!skb->len)) { rsi_dbg(ERR_ZONE, "%s: Null skb/zero Length packet\n", __func__); goto xmit_fail; } - info = IEEE80211_SKB_CB(skb); - tx_params = (struct skb_info *)info->driver_data; - tmp_hdr = (struct ieee80211_hdr *)&skb->data[0]; - if (common->fsm_state != FSM_MAC_INIT_DONE) { rsi_dbg(ERR_ZONE, "%s: FSM state not open\n", __func__); goto xmit_fail; } - if ((ieee80211_is_mgmt(tmp_hdr->frame_control)) || - (ieee80211_is_ctl(tmp_hdr->frame_control)) || - (ieee80211_is_qos_nullfunc(tmp_hdr->frame_control))) { + info = IEEE80211_SKB_CB(skb); + tx_params = (struct skb_info *)info->driver_data; + wh = (struct ieee80211_hdr *)&skb->data[0]; + tx_params->sta_id = 0; + + if ((ieee80211_is_mgmt(wh->frame_control)) || + (ieee80211_is_ctl(wh->frame_control)) || + (ieee80211_is_qos_nullfunc(wh->frame_control))) { q_num = MGMT_SOFT_Q; skb->priority = q_num; } else { - if (ieee80211_is_data_qos(tmp_hdr->frame_control)) { + if (ieee80211_is_data_qos(wh->frame_control)) { tid = (skb->data[24] & IEEE80211_QOS_TID); skb->priority = TID_TO_WME_AC(tid); } else { tid = IEEE80211_NONQOS_TID; skb->priority = BE_Q; } + q_num = skb->priority; tx_params->tid = tid; - tx_params->sta_id = 0; + + if ((vif->type == NL80211_IFTYPE_AP) && + (!is_broadcast_ether_addr(wh->addr1)) && + (!is_multicast_ether_addr(wh->addr1))) { + rsta = rsi_find_sta(common, wh->addr1); + if (!rsta) + goto xmit_fail; + tx_params->sta_id = rsta->sta_id; + } + + if (rsta) { + /* Start aggregation if not done for this tid */ + if (!rsta->start_tx_aggr[tid]) { + rsta->start_tx_aggr[tid] = true; + ieee80211_start_tx_ba_session(rsta->sta, + tid, 0); + } + } } if ((q_num < MGMT_SOFT_Q) && diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index 1ed73320e19f..070dfd68bb83 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -18,6 +18,7 @@ #include "rsi_mgmt.h" #include "rsi_hal.h" #include "rsi_sdio.h" +#include "rsi_common.h" /* FLASH Firmware */ static struct ta_metadata metadata_flash_content[] = { @@ -41,7 +42,7 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) struct ieee80211_hdr *wh = NULL; struct ieee80211_tx_info *info; struct ieee80211_conf *conf = &adapter->hw->conf; - struct ieee80211_vif *vif = NULL; + struct ieee80211_vif *vif = adapter->vifs[0]; struct rsi_mgmt_desc *mgmt_desc; struct skb_info *tx_params; struct ieee80211_bss_conf *bss = NULL; @@ -49,6 +50,11 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) u8 header_size; u32 dword_align_bytes = 0; + if (skb->len > MAX_MGMT_PKT_SIZE) { + rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); + return -EINVAL; + } + info = IEEE80211_SKB_CB(skb); tx_params = (struct skb_info *)info->driver_data; @@ -74,15 +80,10 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) memset(&skb->data[0], 0, header_size); bss = &info->control.vif->bss_conf; wh = (struct ieee80211_hdr *)&skb->data[header_size]; - vif = adapter->vifs[0]; mgmt_desc = (struct rsi_mgmt_desc *)skb->data; xtend_desc = (struct xtended_desc *)&skb->data[FRAME_DESC_SZ]; - if (skb->len > MAX_MGMT_PKT_SIZE) { - rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); - return -EINVAL; - } rsi_set_len_qno(&mgmt_desc->len_qno, (skb->len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q); mgmt_desc->frame_type = TX_DOT11_MGMT; @@ -113,6 +114,22 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) } } + if (ieee80211_is_probe_resp(wh->frame_control)) { + mgmt_desc->misc_flags |= (RSI_ADD_DELTA_TSF_VAP_ID | + RSI_FETCH_RETRY_CNT_FRM_HST); +#define PROBE_RESP_RETRY_CNT 3 + xtend_desc->retry_cnt = PROBE_RESP_RETRY_CNT; + } + + if ((vif->type == NL80211_IFTYPE_AP) && + (ieee80211_is_action(wh->frame_control))) { + struct rsi_sta *rsta = rsi_find_sta(common, wh->addr1); + + if (rsta) + mgmt_desc->sta_id = tx_params->sta_id; + else + return -EINVAL; + } return 0; } @@ -157,7 +174,7 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) xtend_desc = (struct xtended_desc *)&skb->data[FRAME_DESC_SZ]; wh = (struct ieee80211_hdr *)&skb->data[header_size]; - seq_num = (le16_to_cpu(wh->seq_ctrl) >> 4); + seq_num = IEEE80211_SEQ_TO_SN(le16_to_cpu(wh->seq_ctrl)); vif = adapter->vifs[0]; data_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; @@ -191,12 +208,11 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) if (conf_is_ht40(&common->priv->hw->conf)) data_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE); - if (common->vif_info[0].sgi) { - if (common->min_rate & 0x100) /* Only MCS rates */ - data_desc->rate_info |= - cpu_to_le16(ENABLE_SHORTGI_RATE); + if ((common->vif_info[0].sgi) && (common->min_rate & 0x100)) { + /* Only MCS rates */ + data_desc->rate_info |= + cpu_to_le16(ENABLE_SHORTGI_RATE); } - } if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { @@ -223,7 +239,17 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); data_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT); data_desc->sta_id = vap_id; + + if (vif->type == NL80211_IFTYPE_AP) { + if (common->band == NL80211_BAND_5GHZ) + data_desc->rate_info = cpu_to_le16(RSI_RATE_6); + else + data_desc->rate_info = cpu_to_le16(RSI_RATE_1); + } } + if ((vif->type == NL80211_IFTYPE_AP) && + (ieee80211_has_moredata(wh->frame_control))) + data_desc->frame_info |= cpu_to_le16(MORE_DATA_PRESENT); return 0; } @@ -232,17 +258,23 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) { struct rsi_hw *adapter = common->priv; + struct ieee80211_vif *vif = adapter->vifs[0]; struct ieee80211_tx_info *info; struct ieee80211_bss_conf *bss; - int status = -EIO; + int status = -EINVAL; + + if (!skb) + return 0; + if (common->iface_down) + goto err; info = IEEE80211_SKB_CB(skb); + if (!info->control.vif) + goto err; bss = &info->control.vif->bss_conf; - if (!bss->assoc) { - status = -EINVAL; + if ((vif->type == NL80211_IFTYPE_STATION) && (!bss->assoc)) goto err; - } status = rsi_prepare_data_desc(common, skb); if (status) diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 44349696f5de..e579d694d13c 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -83,4 +83,5 @@ u16 rsi_get_connected_channel(struct rsi_hw *adapter); struct rsi_hw *rsi_91x_init(void); void rsi_91x_deinit(struct rsi_hw *adapter); int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len); +struct rsi_sta *rsi_find_sta(struct rsi_common *common, u8 *mac_addr); #endif diff --git a/drivers/net/wireless/rsi/rsi_hal.h b/drivers/net/wireless/rsi/rsi_hal.h index 297f4ce2c39e..7c145053da6d 100644 --- a/drivers/net/wireless/rsi/rsi_hal.h +++ b/drivers/net/wireless/rsi/rsi_hal.h @@ -126,7 +126,7 @@ struct rsi_mgmt_desc { __le16 bbp_info; __le16 seq_ctrl; u8 reserved2; - u8 vap_info; + u8 sta_id; } __packed; struct rsi_data_desc { diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index a2e377f3519b..9c59250eb4de 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -63,6 +63,7 @@ #define BBP_REG_WRITE 0 #define RF_RESET_ENABLE BIT(3) #define RATE_INFO_ENABLE BIT(0) +#define MORE_DATA_PRESENT BIT(1) #define RSI_BROADCAST_PKT BIT(9) #define RSI_DESC_REQUIRE_CFM_TO_HOST BIT(2) #define RSI_ADD_DELTA_TSF_VAP_ID BIT(3) -- cgit v1.2.3 From 6572f054e9648ec242b673270b4d199d959d7cc7 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:15 +0530 Subject: rsi: use common descriptor for auto rate frame TX command frame auto rate request is modified to use common descriptor struture. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 18 +++++++----------- drivers/net/wireless/rsi/rsi_mgmt.h | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 7c0f27a21699..c5d48cd42cbe 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1164,8 +1164,9 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) u32 rate_bitmap = common->bitrate_mask[band]; u16 *selected_rates, min_rate; + u16 frame_len = sizeof(struct rsi_auto_rate); - skb = dev_alloc_skb(sizeof(struct rsi_auto_rate)); + skb = dev_alloc_skb(frame_len); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", __func__); @@ -1180,8 +1181,6 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) return -ENOMEM; } - memset(skb->data, 0, sizeof(struct rsi_auto_rate)); - auto_rate = (struct rsi_auto_rate *)skb->data; auto_rate->aarf_rssi = cpu_to_le16(((u16)3 << 6) | (u16)(18 & 0x3f)); @@ -1190,10 +1189,10 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) auto_rate->initial_boundary = cpu_to_le16(3); auto_rate->max_threshold_limt = cpu_to_le16(27); - auto_rate->desc_word[1] = cpu_to_le16(AUTO_RATE_IND); + auto_rate->desc.desc_dword0.frame_type = AUTO_RATE_IND; if (common->channel_width == BW_40MHZ) - auto_rate->desc_word[7] |= cpu_to_le16(1); + auto_rate->desc.desc_dword3.qid_tid = BW_40MHZ; if (band == NL80211_BAND_2GHZ) { min_rate = RSI_RATE_1; @@ -1259,15 +1258,12 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2); auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2); - auto_rate->desc_word[7] |= cpu_to_le16(0 << 8); num_supported_rates *= 2; - auto_rate->desc_word[0] = cpu_to_le16((sizeof(*auto_rate) - - FRAME_DESC_SZ) | - (RSI_WIFI_MGMT_Q << 12)); + rsi_set_len_qno(&auto_rate->desc.desc_dword0.len_qno, + (frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q); - skb_put(skb, - sizeof(struct rsi_auto_rate)); + skb_put(skb, frame_len); kfree(selected_rates); return rsi_send_internal_mgmt_frame(common, skb); diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 9c59250eb4de..c50153dcffb4 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -425,7 +425,7 @@ struct rsi_set_key { } __packed; struct rsi_auto_rate { - __le16 desc_word[8]; + struct rsi_cmd_desc desc; __le16 failure_limit; __le16 initial_boundary; __le16 max_threshold_limt; -- cgit v1.2.3 From 8a1ff83f2bf83b22e7d803ee2dbb103a3fc057ec Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:16 +0530 Subject: rsi: update tx auto rate command frame for AP mode Auto rate frame is sent to firmware when a new station is connected. Station id and station's ht capabilities are updated in auto rate command frame. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 44 +++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index c5d48cd42cbe..8762d13dd6d9 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1152,8 +1152,11 @@ static bool rsi_map_rates(u16 rate, int *offset) * * Return: 0 on success, corresponding error code on failure. */ -static int rsi_send_auto_rate_request(struct rsi_common *common) +static int rsi_send_auto_rate_request(struct rsi_common *common, + struct ieee80211_sta *sta, + u16 sta_id) { + struct ieee80211_vif *vif = common->priv->vifs[0]; struct sk_buff *skb; struct rsi_auto_rate *auto_rate; int ii = 0, jj = 0, kk = 0; @@ -1161,11 +1164,14 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) u8 band = hw->conf.chandef.chan->band; u8 num_supported_rates = 0; u8 rate_table_offset, rate_offset = 0; - u32 rate_bitmap = common->bitrate_mask[band]; - + u32 rate_bitmap; u16 *selected_rates, min_rate; + bool is_ht = false, is_sgi = false; u16 frame_len = sizeof(struct rsi_auto_rate); + rsi_dbg(MGMT_TX_ZONE, + "%s: Sending auto rate request frame\n", __func__); + skb = dev_alloc_skb(frame_len); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", @@ -1193,12 +1199,31 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) if (common->channel_width == BW_40MHZ) auto_rate->desc.desc_dword3.qid_tid = BW_40MHZ; + auto_rate->desc.desc_dword3.sta_id = sta_id; + + if (vif->type == NL80211_IFTYPE_STATION) { + rate_bitmap = common->bitrate_mask[band]; + is_ht = common->vif_info[0].is_ht; + is_sgi = common->vif_info[0].sgi; + } else { + rate_bitmap = sta->supp_rates[band]; + is_ht = sta->ht_cap.ht_supported; + if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) || + (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)) + is_sgi = true; + } if (band == NL80211_BAND_2GHZ) { - min_rate = RSI_RATE_1; + if ((rate_bitmap == 0) && (is_ht)) + min_rate = RSI_RATE_MCS0; + else + min_rate = RSI_RATE_1; rate_table_offset = 0; } else { - min_rate = RSI_RATE_6; + if ((rate_bitmap == 0) && (is_ht)) + min_rate = RSI_RATE_MCS0; + else + min_rate = RSI_RATE_6; rate_table_offset = 4; } @@ -1212,7 +1237,7 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) } num_supported_rates = jj; - if (common->vif_info[0].is_ht) { + if (is_ht) { for (ii = 0; ii < ARRAY_SIZE(mcs); ii++) selected_rates[jj++] = mcs[ii]; num_supported_rates += ARRAY_SIZE(mcs); @@ -1233,11 +1258,10 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) } /* loading HT rates in the bottom half of the auto rate table */ - if (common->vif_info[0].is_ht) { + if (is_ht) { for (ii = rate_offset, kk = ARRAY_SIZE(rsi_mcsrates) - 1; ii < rate_offset + 2 * ARRAY_SIZE(rsi_mcsrates); ii++) { - if (common->vif_info[0].sgi || - conf_is_ht40(&common->priv->hw->conf)) + if (is_sgi || conf_is_ht40(&common->priv->hw->conf)) auto_rate->supported_rates[ii++] = cpu_to_le16(rsi_mcsrates[kk] | BIT(9)); else @@ -1300,7 +1324,7 @@ void rsi_inform_bss_status(struct rsi_common *common, qos_enable, aid, sta_id); if (common->min_rate == 0xffff) - rsi_send_auto_rate_request(common); + rsi_send_auto_rate_request(common, sta, sta_id); if (opmode == STA_OPMODE) { if (!rsi_send_block_unblock_frame(common, false)) common->hw_data_qs_blocked = false; -- cgit v1.2.3 From 32be57a666a51c2c987e816a744afb343b22e199 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:17 +0530 Subject: rsi: aggregation parameters frame for AP mode TX command frame ampdu aggregation parameters is updated to include sta_id for AP mode. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 43 +++++++++++++++++++++++------ drivers/net/wireless/rsi/rsi_91x_mgmt.c | 6 ++-- drivers/net/wireless/rsi/rsi_mgmt.h | 3 +- 3 files changed, 39 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index be10d508d740..f2cb61f2b268 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -866,9 +866,11 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, int status = -EOPNOTSUPP; struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; - u16 seq_no = 0; + struct rsi_sta *rsta = NULL; + u16 seq_no = 0, seq_start = 0; u8 ii = 0; struct ieee80211_sta *sta = params->sta; + u8 sta_id = 0; enum ieee80211_ampdu_mlme_action action = params->action; u16 tid = params->tid; u16 *ssn = ¶ms->ssn; @@ -880,17 +882,31 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, } mutex_lock(&common->mutex); - rsi_dbg(INFO_ZONE, "%s: AMPDU action %d called\n", __func__, action); + if (ssn != NULL) seq_no = *ssn; + if (vif->type == NL80211_IFTYPE_AP) { + rsta = rsi_find_sta(common, sta->addr); + if (!rsta) { + rsi_dbg(ERR_ZONE, "No station mapped\n"); + return 0; + } + sta_id = rsta->sta_id; + } + + rsi_dbg(INFO_ZONE, + "%s: AMPDU action tid=%d ssn=0x%x, buf_size=%d sta_id=%d\n", + __func__, tid, seq_no, buf_size, sta_id); + switch (action) { case IEEE80211_AMPDU_RX_START: status = rsi_send_aggregation_params_frame(common, tid, seq_no, buf_size, - STA_RX_ADDBA_DONE); + STA_RX_ADDBA_DONE, + sta_id); break; case IEEE80211_AMPDU_RX_STOP: @@ -898,11 +914,15 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, tid, 0, buf_size, - STA_RX_DELBA); + STA_RX_DELBA, + sta_id); break; case IEEE80211_AMPDU_TX_START: - common->vif_info[ii].seq_start = seq_no; + if (vif->type == NL80211_IFTYPE_STATION) + common->vif_info[ii].seq_start = seq_no; + else if (vif->type == NL80211_IFTYPE_AP) + rsta->seq_start[tid] = seq_no; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); status = 0; break; @@ -914,18 +934,23 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, tid, seq_no, buf_size, - STA_TX_DELBA); + STA_TX_DELBA, + sta_id); if (!status) ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: + if (vif->type == NL80211_IFTYPE_STATION) + seq_start = common->vif_info[ii].seq_start; + else if (vif->type == NL80211_IFTYPE_AP) + seq_start = rsta->seq_start[tid]; status = rsi_send_aggregation_params_frame(common, tid, - common->vif_info[ii] - .seq_start, + seq_start, buf_size, - STA_TX_ADDBA_DONE); + STA_TX_ADDBA_DONE, + sta_id); break; default: diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 8762d13dd6d9..2d3cae561be2 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -537,11 +537,11 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid, u16 ssn, u8 buf_size, - u8 event) + u8 event, + u8 sta_id) { struct sk_buff *skb = NULL; struct rsi_aggr_params *aggr_params; - u8 peer_id = 0; u16 frame_len = sizeof(struct rsi_aggr_params); skb = dev_alloc_skb(frame_len); @@ -561,7 +561,7 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, aggr_params->desc_dword0.frame_type = AMPDU_IND; aggr_params->aggr_params = tid & RSI_AGGR_PARAMS_TID_MASK; - aggr_params->peer_id = peer_id; + aggr_params->peer_id = sta_id; if (event == STA_TX_ADDBA_DONE) { aggr_params->seq_start = cpu_to_le16(ssn); aggr_params->baw_size = cpu_to_le16(buf_size); diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index c50153dcffb4..2d827330130d 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -609,7 +609,8 @@ int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg); int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode, u8 *mac_addr, u8 vap_id, u8 vap_status); int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid, - u16 ssn, u8 buf_size, u8 event); + u16 ssn, u8 buf_size, u8 event, + u8 sta_id); int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len, u8 key_type, u8 key_id, u32 cipher); int rsi_set_channel(struct rsi_common *common, -- cgit v1.2.3 From 38ef62353acbaa0eea062a9f047b33aebd7d52ce Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Wed, 16 Aug 2017 18:43:18 +0530 Subject: rsi: security enhancements for AP mode Station id should be set in load key frame configured to device. For WEP mode, key is configured once from mac80211. This key is saved and configured to device every time a station is connected. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 60 +++++++++++++++++++++-------- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 10 ++++- drivers/net/wireless/rsi/rsi_main.h | 1 + drivers/net/wireless/rsi/rsi_mgmt.h | 3 +- 4 files changed, 56 insertions(+), 18 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index f2cb61f2b268..8b983d03f2da 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -758,11 +758,14 @@ static int rsi_mac80211_conf_tx(struct ieee80211_hw *hw, */ static int rsi_hal_key_config(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_key_conf *key) + struct ieee80211_key_conf *key, + struct ieee80211_sta *sta) { struct rsi_hw *adapter = hw->priv; + struct rsi_sta *rsta = NULL; int status; u8 key_type; + s16 sta_id = 0; if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) key_type = RSI_PAIRWISE_KEY; @@ -772,23 +775,35 @@ static int rsi_hal_key_config(struct ieee80211_hw *hw, rsi_dbg(ERR_ZONE, "%s: Cipher 0x%x key_type: %d key_len: %d\n", __func__, key->cipher, key_type, key->keylen); - if ((key->cipher == WLAN_CIPHER_SUITE_WEP104) || - (key->cipher == WLAN_CIPHER_SUITE_WEP40)) { - status = rsi_hal_load_key(adapter->priv, - key->key, - key->keylen, - RSI_PAIRWISE_KEY, - key->keyidx, - key->cipher); - if (status) - return status; + if (vif->type == NL80211_IFTYPE_AP) { + if (sta) { + rsta = rsi_find_sta(adapter->priv, sta->addr); + if (rsta) + sta_id = rsta->sta_id; + } + adapter->priv->key = key; + } else { + if ((key->cipher == WLAN_CIPHER_SUITE_WEP104) || + (key->cipher == WLAN_CIPHER_SUITE_WEP40)) { + status = rsi_hal_load_key(adapter->priv, + key->key, + key->keylen, + RSI_PAIRWISE_KEY, + key->keyidx, + key->cipher, + sta_id); + if (status) + return status; + } } + return rsi_hal_load_key(adapter->priv, key->key, key->keylen, key_type, key->keyidx, - key->cipher); + key->cipher, + sta_id); } /** @@ -816,7 +831,7 @@ static int rsi_mac80211_set_key(struct ieee80211_hw *hw, switch (cmd) { case SET_KEY: secinfo->security_enable = true; - status = rsi_hal_key_config(hw, vif, key); + status = rsi_hal_key_config(hw, vif, key, sta); if (status) { mutex_unlock(&common->mutex); return status; @@ -834,10 +849,11 @@ static int rsi_mac80211_set_key(struct ieee80211_hw *hw, break; case DISABLE_KEY: - secinfo->security_enable = false; + if (vif->type == NL80211_IFTYPE_STATION) + secinfo->security_enable = false; rsi_dbg(ERR_ZONE, "%s: RSI del key\n", __func__); memset(key, 0, sizeof(struct ieee80211_key_conf)); - status = rsi_hal_key_config(hw, vif, key); + status = rsi_hal_key_config(hw, vif, key, sta); break; default: @@ -1242,6 +1258,20 @@ static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, rsi_inform_bss_status(common, AP_OPMODE, 1, sta->addr, sta->wme, sta->aid, sta, sta_idx); + if (common->key) { + struct ieee80211_key_conf *key = common->key; + + if ((key->cipher == WLAN_CIPHER_SUITE_WEP104) || + (key->cipher == WLAN_CIPHER_SUITE_WEP40)) + rsi_hal_load_key(adapter->priv, + key->key, + key->keylen, + RSI_PAIRWISE_KEY, + key->keyidx, + key->cipher, + sta_idx); + } + common->num_stations++; } } diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 2d3cae561be2..f7b550f900c4 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -715,8 +715,10 @@ int rsi_hal_load_key(struct rsi_common *common, u16 key_len, u8 key_type, u8 key_id, - u32 cipher) + u32 cipher, + s16 sta_id) { + struct ieee80211_vif *vif = common->priv->vifs[0]; struct sk_buff *skb = NULL; struct rsi_set_key *set_key; u16 key_descriptor = 0; @@ -734,8 +736,11 @@ int rsi_hal_load_key(struct rsi_common *common, memset(skb->data, 0, frame_len); set_key = (struct rsi_set_key *)skb->data; - if (key_type == RSI_GROUP_KEY) + if (key_type == RSI_GROUP_KEY) { key_descriptor = RSI_KEY_TYPE_BROADCAST; + if (vif->type == NL80211_IFTYPE_AP) + key_descriptor |= RSI_KEY_MODE_AP; + } if ((cipher == WLAN_CIPHER_SUITE_WEP40) || (cipher == WLAN_CIPHER_SUITE_WEP104)) { key_id = 0; @@ -754,6 +759,7 @@ int rsi_hal_load_key(struct rsi_common *common, (frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q); set_key->desc_dword0.frame_type = SET_KEY_REQ; set_key->key_desc = cpu_to_le16(key_descriptor); + set_key->sta_id = sta_id; if (data) { if ((cipher == WLAN_CIPHER_SUITE_WEP40) || diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 169e2f9e3c5e..2c18dde633ea 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -269,6 +269,7 @@ struct rsi_common { struct rsi_sta stations[RSI_MAX_ASSOC_STAS + 1]; int num_stations; int max_stations; + struct ieee80211_key_conf *key; }; enum host_intf { diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 2d827330130d..c6e1fa669a27 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -409,6 +409,7 @@ struct rsi_dynamic_s { #define RSI_WEP_KEY_104 BIT(3) #define RSI_CIPHER_WPA BIT(4) #define RSI_CIPHER_TKIP BIT(5) +#define RSI_KEY_MODE_AP BIT(7) #define RSI_PROTECT_DATA_FRAMES BIT(13) #define RSI_KEY_ID_MASK 0xC0 #define RSI_KEY_ID_OFFSET 14 @@ -612,7 +613,7 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid, u16 ssn, u8 buf_size, u8 event, u8 sta_id); int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len, - u8 key_type, u8 key_id, u32 cipher); + u8 key_type, u8 key_id, u32 cipher, s16 sta_id); int rsi_set_channel(struct rsi_common *common, struct ieee80211_channel *channel); int rsi_send_vap_dynamic_update(struct rsi_common *common); -- cgit v1.2.3 From 93345c06b74513c1e1c7933aef146e1d03420079 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 17 Aug 2017 09:19:30 +0100 Subject: liquidio: fix spelling mistake: "interuupt" -> "interrupt" Trivial fix to spelling mistake in dev_info message Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 08aa06c90d46..a63ddf07f168 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -412,7 +412,7 @@ lio_irq_reallocate_irqs(struct octeon_device *oct, uint32_t num_ioqs) kfree(oct->irq_name_storage); oct->irq_name_storage = NULL; if (octeon_setup_interrupt(oct, num_ioqs)) { - dev_info(&oct->pci_dev->dev, "Setup interuupt failed\n"); + dev_info(&oct->pci_dev->dev, "Setup interrupt failed\n"); return 1; } -- cgit v1.2.3 From fd07a62d58c16b1fdd46f4099791c5bd242908c3 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 17 Aug 2017 10:01:07 +0100 Subject: net: hns3: ensure media_type is unitialized Media type is only set if h->ae_algo->ops->get_media_type is called so there is a possibility that media_type is uninitialized when it is used a switch statement. Fix this by initializing media_type to HNAE3_MEDIA_TYPE_UNKNOWN. Detected by CoverityScan, CID#1452624("Uninitialized scalar variable") Fixes: 496d03e960ae ("net: hns3: Add Ethtool support to HNS3 driver") Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c index 0ad65e47c77e..53cab3ad4cda 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c @@ -304,7 +304,7 @@ static int hns3_get_link_ksettings(struct net_device *netdev, struct hnae3_handle *h = priv->ae_handle; u32 supported_caps; u32 advertised_caps; - u8 media_type; + u8 media_type = HNAE3_MEDIA_TYPE_UNKNOWN; u8 link_stat; u8 auto_neg; u8 duplex; -- cgit v1.2.3 From 71450804c689e4c601cb2a3f9c0ff7182cde84e6 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 17 Aug 2017 18:52:53 +0530 Subject: net: ibm: ibmveth: constify vio_device_id vio_device_id are not supposed to change at runtime. All functions working with vio_device_id provided by work with const vio_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmveth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index d17c2b03f580..f210398200ec 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1897,7 +1897,7 @@ static int ibmveth_resume(struct device *dev) return 0; } -static struct vio_device_id ibmveth_device_table[] = { +static const struct vio_device_id ibmveth_device_table[] = { { "network", "IBM,l-lan"}, { "", "" } }; -- cgit v1.2.3 From 8c37bc677af3458dd5598d22467fb913f31c5bb2 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 17 Aug 2017 18:52:54 +0530 Subject: net: ibm: ibmvnic: constify vio_device_id vio_device_id are not supposed to change at runtime. All functions working with vio_device_id provided by work with const vio_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 5ac873173b2e..cb8182f4fdfa 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -4022,7 +4022,7 @@ static int ibmvnic_resume(struct device *dev) return 0; } -static struct vio_device_id ibmvnic_device_table[] = { +static const struct vio_device_id ibmvnic_device_table[] = { {"network", "IBM,vnic"}, {"", "" } }; -- cgit v1.2.3 From 64511df45cbeec02670a83b666ff584dd1d767a2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 16 Jul 2017 16:22:08 +0300 Subject: iwlwifi: mvm: remove the corunning support The corunning block was supposed to help in coex scenarios. It required the driver to configure the firmware based on the coupling between the two antennas of the devices. This was never in use and the configuration sent by the driver has always been blank. Remove all that code. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/coex.h | 13 - .../net/wireless/intel/iwlwifi/fw/api/commands.h | 12 - drivers/net/wireless/intel/iwlwifi/fw/api/config.h | 8 - drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 268 --------------------- drivers/net/wireless/intel/iwlwifi/mvm/constants.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 5 - drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 9 - drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 - 8 files changed, 320 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h index df4ecec59b40..2ba3ea4fa999 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h @@ -76,7 +76,6 @@ enum iwl_bt_coex_lut_type { BT_COEX_INVALID_LUT = 0xff, }; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */ -#define BT_COEX_CORUN_LUT_SIZE (32) #define BT_REDUCED_TX_POWER_BIT BIT(7) enum iwl_bt_coex_mode { @@ -106,18 +105,6 @@ struct iwl_bt_coex_cmd { __le32 enabled_modules; } __packed; /* BT_COEX_CMD_API_S_VER_6 */ -/** - * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut - * @corun_lut20: co-running 20 MHz LUT configuration - * @corun_lut40: co-running 40 MHz LUT configuration - * - * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command. - */ -struct iwl_bt_coex_corun_lut_update_cmd { - __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE]; - __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE]; -} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */ - /** * struct iwl_bt_coex_reduced_txp_update_cmd * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 0eb35b119ae9..074868394427 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -135,12 +135,6 @@ enum iwl_legacy_cmds { */ DBG_CFG = 0x9, - /** - * @ANTENNA_COUPLING_NOTIFICATION: - * Antenna coupling data, &struct iwl_mvm_antenna_coupling_notif - */ - ANTENNA_COUPLING_NOTIFICATION = 0xa, - /** * @SCAN_ITERATION_COMPLETE_UMAC: * Firmware indicates a scan iteration completed, using @@ -523,12 +517,6 @@ enum iwl_legacy_cmds { */ BT_CONFIG = 0x9b, - /** - * @BT_COEX_UPDATE_CORUN_LUT: - * &struct iwl_bt_coex_corun_lut_update_cmd - */ - BT_COEX_UPDATE_CORUN_LUT = 0x5b, - /** * @BT_COEX_UPDATE_REDUCED_TXP: * &struct iwl_bt_coex_reduced_txp_update_cmd diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/config.h b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h index ee1bd45b7021..7f645b62804e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/config.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h @@ -181,12 +181,4 @@ struct iwl_dc2dc_config_resp { __le32 dc2dc_freq_tune1; } __packed; /* DC2DC_CONFIG_RESP_API_S_VER_1 */ -/** - * struct iwl_mvm_antenna_coupling_notif - antenna coupling notification - * @isolation: antenna isolation value - */ -struct iwl_mvm_antenna_coupling_notif { - __le32 isolation; -} __packed; - #endif /* __iwl_fw_api_config_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 0b4486114ddc..890dbfff3a06 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -148,215 +148,6 @@ static const __le64 iwl_ci_mask[][3] = { }, }; -struct corunning_block_luts { - u8 range; - __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; -}; - -/* - * Ranges for the antenna coupling calibration / co-running block LUT: - * LUT0: [ 0, 12[ - * LUT1: [12, 20[ - * LUT2: [20, 21[ - * LUT3: [21, 23[ - * LUT4: [23, 27[ - * LUT5: [27, 30[ - * LUT6: [30, 32[ - * LUT7: [32, 33[ - * LUT8: [33, - [ - */ -static const struct corunning_block_luts antenna_coupling_ranges[] = { - { - .range = 0, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 12, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 20, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 21, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 23, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 27, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 30, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 32, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 33, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, -}; - static enum iwl_bt_coex_lut_type iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) { @@ -437,9 +228,6 @@ int iwl_mvm_send_bt_init_conf(struct iwl_mvm *mvm) bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED); - if (iwl_mvm_bt_is_plcr_supported(mvm)) - bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED); - if (iwl_mvm_is_mplut_supported(mvm)) bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED); @@ -908,59 +696,3 @@ void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) { iwl_mvm_bt_coex_notif_handle(mvm); } - -void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_antenna_coupling_notif *notif = (void *)pkt->data; - u32 ant_isolation = le32_to_cpu(notif->isolation); - struct iwl_bt_coex_corun_lut_update_cmd cmd = {}; - u8 __maybe_unused lower_bound, upper_bound; - u8 lut; - - if (!iwl_mvm_bt_is_plcr_supported(mvm)) - return; - - lockdep_assert_held(&mvm->mutex); - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - if (ant_isolation == mvm->last_ant_isol) - return; - - for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++) - if (ant_isolation < antenna_coupling_ranges[lut + 1].range) - break; - - lower_bound = antenna_coupling_ranges[lut].range; - - if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1) - upper_bound = antenna_coupling_ranges[lut + 1].range; - else - upper_bound = antenna_coupling_ranges[lut].range; - - IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n", - ant_isolation, lower_bound, upper_bound, lut); - - mvm->last_ant_isol = ant_isolation; - - if (mvm->last_corun_lut == lut) - return; - - mvm->last_corun_lut = lut; - - /* For the moment, use the same LUT for 20GHz and 40GHz */ - memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20, - sizeof(cmd.corun_lut20)); - - memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20, - sizeof(cmd.corun_lut40)); - - if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0, - sizeof(cmd), &cmd)) - IWL_ERR(mvm, - "failed to send BT_COEX_UPDATE_CORUN_LUT command\n"); -} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index a922a351c916..753d4138e30f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -95,7 +95,6 @@ #define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62 #define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65 #define IWL_MVM_BT_COEX_SYNC2SCO 1 -#define IWL_MVM_BT_COEX_CORUNNING 0 #define IWL_MVM_BT_COEX_MPLUT 1 #define IWL_MVM_BT_COEX_RRC 1 #define IWL_MVM_BT_COEX_TTC 1 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index ba2745a3b537..3d7b5bd7a6b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -565,9 +565,6 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, pos += scnprintf(buf + pos, bufsz - pos, "bt_activity_grading = %d\n", le32_to_cpu(notif->bt_activity_grading)); - pos += scnprintf(buf + pos, bufsz - pos, - "antenna isolation = %d CORUN LUT index = %d\n", - mvm->last_ant_isol, mvm->last_corun_lut); pos += scnprintf(buf + pos, bufsz - pos, "bt_rrc = %d\n", notif->rrc_status & 0xF); pos += scnprintf(buf + pos, bufsz - pos, "bt_ttc = %d\n", @@ -577,8 +574,6 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, IWL_MVM_BT_COEX_SYNC2SCO); pos += scnprintf(buf + pos, bufsz - pos, "mplut = %d\n", IWL_MVM_BT_COEX_MPLUT); - pos += scnprintf(buf + pos, bufsz - pos, "corunning = %d\n", - IWL_MVM_BT_COEX_CORUNNING); mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 8ff74fbb2562..54e5c8c6736b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -924,8 +924,6 @@ struct iwl_mvm { struct iwl_bt_coex_profile_notif last_bt_notif; struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; - u32 last_ant_isol; - u8 last_corun_lut; u8 bt_tx_prio; enum iwl_bt_force_ant_mode bt_force_ant_mode; @@ -1175,13 +1173,6 @@ static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC); } -static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_BT_COEX_PLCR) && - IWL_MVM_BT_COEX_CORUNNING; -} - static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm) { return fw_has_capa(&mvm->fw->ucode_capa, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 29a21a11c7f8..640881c8d703 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -256,8 +256,6 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_ASYNC_LOCKED), RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, RX_HANDLER_ASYNC_LOCKED), - RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION, - iwl_mvm_rx_ant_coupling_notif, RX_HANDLER_ASYNC_LOCKED), RX_HANDLER(BA_WINDOW_STATUS_NOTIFICATION_ID, iwl_mvm_window_status_notif, RX_HANDLER_SYNC), @@ -325,7 +323,6 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { HCMD_NAME(INIT_COMPLETE_NOTIF), HCMD_NAME(PHY_CONTEXT_CMD), HCMD_NAME(DBG_CFG), - HCMD_NAME(ANTENNA_COUPLING_NOTIFICATION), HCMD_NAME(SCAN_CFG_CMD), HCMD_NAME(SCAN_REQ_UMAC), HCMD_NAME(SCAN_ABORT_UMAC), @@ -357,7 +354,6 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { HCMD_NAME(SCAN_OFFLOAD_ABORT_CMD), HCMD_NAME(HOT_SPOT_CMD), HCMD_NAME(SCAN_OFFLOAD_PROFILES_QUERY_CMD), - HCMD_NAME(BT_COEX_UPDATE_CORUN_LUT), HCMD_NAME(BT_COEX_UPDATE_REDUCED_TXP), HCMD_NAME(BT_COEX_CI), HCMD_NAME(PHY_CONFIGURATION_CMD), -- cgit v1.2.3 From 00e0c6c8fe8833acfebebf0af53b48a965084fd4 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 24 Jul 2017 14:47:52 +0300 Subject: iwlwifi: mvm: consider RFKILL during INIT as success There's no need to differentiate an INIT that ended early because of RFKILL from one that succeded. Additionally, if INIT fails later, during calibration, due to RFKILL, we can just return success and continue as if we were already in RFKILL to start with. Remove this unnecessary differentiation and do some other small clean-ups while at it. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 38 +++++++++++++--------------- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 - 2 files changed, 17 insertions(+), 22 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 0099050f6e2b..ed18479a7b8c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -475,13 +475,13 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT); if (ret) { IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret); - goto error; + goto remove_notif; } if (mvm->cfg->device_family < IWL_DEVICE_FAMILY_8000) { ret = iwl_mvm_send_bt_init_conf(mvm); if (ret) - goto error; + goto remove_notif; } /* Read the NVM only at driver load time, no need to do this twice */ @@ -490,7 +490,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_nvm_init(mvm, true); if (ret) { IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); - goto error; + goto remove_notif; } } @@ -498,8 +498,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) if (mvm->nvm_file_name) iwl_mvm_load_nvm_to_nic(mvm); - ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); - WARN_ON(ret); + WARN_ON(iwl_nvm_check_version(mvm->nvm_data, mvm->trans)); /* * abort after reading the nvm in case RF Kill is on, we will complete @@ -508,9 +507,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) if (iwl_mvm_is_radio_hw_killed(mvm)) { IWL_DEBUG_RF_KILL(mvm, "jump over all phy activities due to RF kill\n"); - iwl_remove_notification(&mvm->notif_wait, &calib_wait); - ret = 1; - goto out; + goto remove_notif; } mvm->calibrating = true; @@ -518,17 +515,13 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) /* Send TX valid antennas before triggering calibrations */ ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) - goto error; + goto remove_notif; - /* - * Send phy configurations command to init uCode - * to start the 16.0 uCode init image internal calibrations. - */ ret = iwl_send_phy_cfg_cmd(mvm); if (ret) { IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n", ret); - goto error; + goto remove_notif; } /* @@ -536,15 +529,21 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) * just wait for the calibration complete notification. */ ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait, - MVM_UCODE_CALIB_TIMEOUT); + MVM_UCODE_CALIB_TIMEOUT); + if (!ret) + goto out; - if (ret && iwl_mvm_is_radio_hw_killed(mvm)) { + if (iwl_mvm_is_radio_hw_killed(mvm)) { IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n"); - ret = 1; + ret = 0; + } else { + IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n", + ret); } + goto out; -error: +remove_notif: iwl_remove_notification(&mvm->notif_wait, &calib_wait); out: mvm->calibrating = false; @@ -1043,9 +1042,6 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) if (ret) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); - /* this can't happen */ - if (WARN_ON(ret > 0)) - ret = -ERFKILL; return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 640881c8d703..76d256a9ad71 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -751,7 +751,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_mvm_stop_device(mvm); iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE); mutex_unlock(&mvm->mutex); - /* returns 0 if successful, 1 if success but in rfkill */ if (err < 0) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); goto out_free; -- cgit v1.2.3 From d98d94952ab8421909159e6a7e4a9c8ad2204753 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 24 Jul 2017 16:04:47 +0300 Subject: iwlwifi: call iwl_remove_notification from iwl_wait_notification The iwl_wait_notification() function removes the wait entry from the list. To make it clearer that it's doing the same thing as iwl_remove_notification(), call the latter instead of having duplicate code. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/notif-wait.c | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.c b/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.c index 29bb92e3df59..1096c945a68b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.c @@ -6,7 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -32,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -161,6 +162,15 @@ iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, } IWL_EXPORT_SYMBOL(iwl_init_notification_wait); +void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, + struct iwl_notification_wait *wait_entry) +{ + spin_lock_bh(¬if_wait->notif_wait_lock); + list_del(&wait_entry->list); + spin_unlock_bh(¬if_wait->notif_wait_lock); +} +IWL_EXPORT_SYMBOL(iwl_remove_notification); + int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, struct iwl_notification_wait *wait_entry, unsigned long timeout) @@ -171,9 +181,7 @@ int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, wait_entry->triggered || wait_entry->aborted, timeout); - spin_lock_bh(¬if_wait->notif_wait_lock); - list_del(&wait_entry->list); - spin_unlock_bh(¬if_wait->notif_wait_lock); + iwl_remove_notification(notif_wait, wait_entry); if (wait_entry->aborted) return -EIO; @@ -184,12 +192,3 @@ int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, return 0; } IWL_EXPORT_SYMBOL(iwl_wait_notification); - -void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, - struct iwl_notification_wait *wait_entry) -{ - spin_lock_bh(¬if_wait->notif_wait_lock); - list_del(&wait_entry->list); - spin_unlock_bh(¬if_wait->notif_wait_lock); -} -IWL_EXPORT_SYMBOL(iwl_remove_notification); -- cgit v1.2.3 From fbfe378fe4e86fd3ff5ca491448da78c26a940ea Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 23 Jul 2017 13:59:47 +0300 Subject: iwlwifi: mvm: support new Coex firmware API The firmware now adds more information about time sharing with the Bluetooth core. Adapt the API structures and add the new fields in the debugfs hooks. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/coex.h | 31 ++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/file.h | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 33 +++++++++++++++++++----- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 13 +++++++--- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 6 +++++ 5 files changed, 77 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h index 2ba3ea4fa999..d9a74db01f90 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h @@ -178,6 +178,7 @@ enum iwl_bt_mxbox_dw3 { BT_MBOX(3, ACL_STATE, 3, 1), BT_MBOX(3, MSTR_STATE, 4, 1), BT_MBOX(3, OBX_STATE, 5, 1), + BT_MBOX(3, A2DP_SRC, 6, 1), BT_MBOX(3, OPEN_CON_2, 8, 2), BT_MBOX(3, TRAFFIC_LOAD, 10, 2), BT_MBOX(3, CHL_SEQN_LSB, 12, 1), @@ -187,6 +188,11 @@ enum iwl_bt_mxbox_dw3 { BT_MBOX(3, UPDATE_REQUEST, 21, 1), }; +enum iwl_bt_mxbox_dw4 { + BT_MBOX(4, ATS_BT_INTERVAL, 0, 7), + BT_MBOX(4, ATS_BT_ACTIVE_MAX_TH, 7, 7), +}; + #define BT_MBOX_MSG(_notif, _num, _field) \ ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ >> BT_MBOX##_num##_##_field##_POS) @@ -220,6 +226,31 @@ enum iwl_bt_ci_compliance { * @reserved: reserved */ struct iwl_bt_coex_profile_notif { + __le32 mbox_msg[8]; + __le32 msg_idx; + __le32 bt_ci_compliance; + + __le32 primary_ch_lut; + __le32 secondary_ch_lut; + __le32 bt_activity_grading; + u8 ttc_status; + u8 rrc_status; + __le16 reserved; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_5 */ + +/** + * struct iwl_bt_coex_profile_notif - notification about BT coex + * @mbox_msg: message from BT to WiFi + * @msg_idx: the index of the message + * @bt_ci_compliance: enum %iwl_bt_ci_compliance + * @primary_ch_lut: LUT used for primary channel &enum iwl_bt_coex_lut_type + * @secondary_ch_lut: LUT used for secondary channel &enum iwl_bt_coex_lut_type + * @bt_activity_grading: the activity of BT &enum iwl_bt_activity_grading + * @ttc_status: is TTC enabled - one bit per PHY + * @rrc_status: is RRC enabled - one bit per PHY + * @reserved: reserved + */ +struct iwl_bt_coex_profile_notif_v4 { __le32 mbox_msg[4]; __le32 msg_idx; __le32 bt_ci_compliance; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index d933aa324ffe..a1cd2f41b026 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -246,6 +246,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * @IWL_UCODE_TLV_API_STA_TYPE: This ucode supports station type assignement. * @IWL_UCODE_TLV_API_NAN2_VER2: This ucode supports NAN API version 2 * @IWL_UCODE_TLV_API_NEW_RX_STATS: should new RX STATISTICS API be used + * @IWL_UCODE_TLV_API_ATS_COEX_EXTERNAL: the coex notification is enlared to + * include information about ACL time sharing. * * @NUM_IWL_UCODE_TLV_API: number of bits used */ @@ -262,6 +264,7 @@ enum iwl_ucode_tlv_api { /* API Set 1 */ IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE = (__force iwl_ucode_tlv_api_t)34, IWL_UCODE_TLV_API_NEW_RX_STATS = (__force iwl_ucode_tlv_api_t)35, + IWL_UCODE_TLV_API_COEX_ATS_EXTERNAL = (__force iwl_ucode_tlv_api_t)37, NUM_IWL_UCODE_TLV_API #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 890dbfff3a06..79c80f181f7d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -7,6 +7,7 @@ * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2017 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -512,17 +514,36 @@ void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; + if (!iwl_mvm_has_new_ats_coex_api(mvm)) { + struct iwl_bt_coex_profile_notif_v4 *v4 = (void *)pkt->data; + + mvm->last_bt_notif.mbox_msg[0] = v4->mbox_msg[0]; + mvm->last_bt_notif.mbox_msg[1] = v4->mbox_msg[1]; + mvm->last_bt_notif.mbox_msg[2] = v4->mbox_msg[2]; + mvm->last_bt_notif.mbox_msg[3] = v4->mbox_msg[3]; + mvm->last_bt_notif.msg_idx = v4->msg_idx; + mvm->last_bt_notif.bt_ci_compliance = v4->bt_ci_compliance; + mvm->last_bt_notif.primary_ch_lut = v4->primary_ch_lut; + mvm->last_bt_notif.secondary_ch_lut = v4->secondary_ch_lut; + mvm->last_bt_notif.bt_activity_grading = + v4->bt_activity_grading; + mvm->last_bt_notif.ttc_status = v4->ttc_status; + mvm->last_bt_notif.rrc_status = v4->rrc_status; + } else { + /* save this notification for future use: rssi fluctuations */ + memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); + } + IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); - IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); + IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", + mvm->last_bt_notif.bt_ci_compliance); IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", - le32_to_cpu(notif->primary_ch_lut)); + le32_to_cpu(mvm->last_bt_notif.primary_ch_lut)); IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", - le32_to_cpu(notif->secondary_ch_lut)); + le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut)); IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", - le32_to_cpu(notif->bt_activity_grading)); + le32_to_cpu(mvm->last_bt_notif.bt_activity_grading)); - /* remember this notification for future use: rssi fluctuations */ - memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); iwl_mvm_bt_coex_notif_handle(mvm); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 3d7b5bd7a6b7..c88a37397075 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -482,7 +482,8 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf, true ? "\n" : ", "); static -int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf, +int iwl_mvm_coex_dump_mbox(struct iwl_mvm *mvm, + struct iwl_bt_coex_profile_notif *notif, char *buf, int pos, int bufsz) { pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n"); @@ -526,6 +527,7 @@ int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf, BT_MBOX_PRINT(3, SCO_STATE, false); BT_MBOX_PRINT(3, SNIFF_STATE, false); BT_MBOX_PRINT(3, A2DP_STATE, false); + BT_MBOX_PRINT(3, A2DP_SRC, false); BT_MBOX_PRINT(3, ACL_STATE, false); BT_MBOX_PRINT(3, MSTR_STATE, false); BT_MBOX_PRINT(3, OBX_STATE, false); @@ -535,7 +537,12 @@ int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf, BT_MBOX_PRINT(3, INBAND_P, false); BT_MBOX_PRINT(3, MSG_TYPE_2, false); BT_MBOX_PRINT(3, SSN_2, false); - BT_MBOX_PRINT(3, UPDATE_REQUEST, true); + BT_MBOX_PRINT(3, UPDATE_REQUEST, !iwl_mvm_has_new_ats_coex_api(mvm)); + + if (iwl_mvm_has_new_ats_coex_api(mvm)) { + BT_MBOX_PRINT(4, ATS_BT_INTERVAL, false); + BT_MBOX_PRINT(4, ATS_BT_ACTIVE_MAX_TH, true); + } return pos; } @@ -554,7 +561,7 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, mutex_lock(&mvm->mutex); - pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz); + pos += iwl_mvm_coex_dump_mbox(mvm, notif, buf, pos, bufsz); pos += scnprintf(buf + pos, bufsz - pos, "bt_ci_compliance = %d\n", notif->bt_ci_compliance); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 54e5c8c6736b..d58de9b80886 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1242,6 +1242,12 @@ static inline bool iwl_mvm_has_new_rx_stats_api(struct iwl_mvm *mvm) IWL_UCODE_TLV_API_NEW_RX_STATS); } +static inline bool iwl_mvm_has_new_ats_coex_api(struct iwl_mvm *mvm) +{ + return fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_COEX_ATS_EXTERNAL); +} + static inline struct agg_tx_status * iwl_mvm_get_agg_status(struct iwl_mvm *mvm, void *tx_resp) { -- cgit v1.2.3 From 4ecab5616023e742b70493cf0e90fc97e828d353 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 16 Jul 2017 12:28:05 +0300 Subject: iwlwifi: pcie: support short Tx queues for A000 device family This allows to modify TFD_TX_CMD_SLOTS to a power of 2 which is smaller than 256. Note that we still need to set values to wrap at 256 into the scheduler's write pointer, but all the rest of the code can use shorter transmit queues. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/pcie/ctxt-info.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 13 +++---- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 41 +++++++++++----------- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 13 +++---- 6 files changed, 37 insertions(+), 36 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index eddaca76d514..3fc4343581ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -244,7 +244,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, ctxt_info->hcmd_cfg.cmd_queue_addr = cpu_to_le64(trans_pcie->txq[trans_pcie->cmd_queue]->dma_addr); ctxt_info->hcmd_cfg.cmd_queue_size = - TFD_QUEUE_CB_SIZE(TFD_QUEUE_SIZE_MAX); + TFD_QUEUE_CB_SIZE(TFD_CMD_SLOTS); /* allocate ucode sections in dram and set addresses */ ret = iwl_pcie_ctxt_info_init_fw_sec(trans, fw, ctxt_info); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index f46871840fd2..79020cf8c79c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -661,10 +661,16 @@ static inline void iwl_pcie_sw_reset(struct iwl_trans *trans) usleep_range(5000, 6000); } +static inline u8 iwl_pcie_get_cmd_index(struct iwl_txq *q, u32 index) +{ + return index & (q->n_window - 1); +} + static inline void *iwl_pcie_get_tfd(struct iwl_trans_pcie *trans_pcie, struct iwl_txq *txq, int idx) { - return txq->tfds + trans_pcie->tfd_size * idx; + return txq->tfds + trans_pcie->tfd_size * iwl_pcie_get_cmd_index(txq, + idx); } static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) @@ -726,11 +732,6 @@ static inline bool iwl_queue_used(const struct iwl_txq *q, int i) !(i < q->read_ptr && i >= q->write_ptr); } -static inline u8 get_cmd_index(struct iwl_txq *q, u32 index) -{ - return index & (q->n_window - 1); -} - static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 351c4423125a..e5d2bf0bde37 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1176,7 +1176,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, sequence = le16_to_cpu(pkt->hdr.sequence); index = SEQ_TO_INDEX(sequence); - cmd_index = get_cmd_index(txq, index); + cmd_index = iwl_pcie_get_cmd_index(txq, index); if (rxq->id == 0) iwl_op_mode_rx(trans->op_mode, &rxq->napi, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 382d7c251066..3ecafa2ad922 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2835,7 +2835,7 @@ static struct iwl_trans_dump_data spin_lock_bh(&cmdq->lock); ptr = cmdq->write_ptr; for (i = 0; i < cmdq->n_window; i++) { - u8 idx = get_cmd_index(cmdq, ptr); + u8 idx = iwl_pcie_get_cmd_index(cmdq, ptr); u32 caplen, cmdlen; cmdlen = iwl_trans_pcie_get_cmdlen(trans, cmdq->tfds + diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 4db45e56b6ba..d74613fcb756 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -88,14 +88,14 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_txq *txq, u16 byte_cnt, int num_tbs) { struct iwlagn_scd_bc_tbl *scd_bc_tbl = txq->bc_tbl.addr; - int write_ptr = txq->write_ptr; + int idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); u8 filled_tfd_size, num_fetch_chunks; u16 len = byte_cnt; __le16 bc_ent; len = DIV_ROUND_UP(len, 4); - if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX)) + if (WARN_ON(len > 0xFFF || idx >= txq->n_window)) return; filled_tfd_size = offsetof(struct iwl_tfh_tfd, tbs) + @@ -111,7 +111,7 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_txq *txq, u16 byte_cnt, num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1; bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12)); - scd_bc_tbl->tfd_offset[write_ptr] = bc_ent; + scd_bc_tbl->tfd_offset[idx] = bc_ent; } /* @@ -176,16 +176,12 @@ static void iwl_pcie_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and * idx is bounded by n_window */ - int rd_ptr = txq->read_ptr; - int idx = get_cmd_index(txq, rd_ptr); + int idx = iwl_pcie_get_cmd_index(txq, txq->read_ptr); lockdep_assert_held(&txq->lock); - /* We have only q->n_window txq->entries, but we use - * TFD_QUEUE_SIZE_MAX tfds - */ iwl_pcie_gen2_tfd_unmap(trans, &txq->entries[idx].meta, - iwl_pcie_get_tfd(trans_pcie, txq, rd_ptr)); + iwl_pcie_get_tfd(trans_pcie, txq, idx)); /* free SKB */ if (txq->entries) { @@ -373,8 +369,9 @@ struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); struct iwl_tfh_tfd *tfd = - iwl_pcie_get_tfd(trans_pcie, txq, txq->write_ptr); + iwl_pcie_get_tfd(trans_pcie, txq, idx); dma_addr_t tb_phys; bool amsdu; int i, len, tb1_len, tb2_len, hdr_len; @@ -386,10 +383,10 @@ struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, (*ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_A_MSDU_PRESENT); - tb_phys = iwl_pcie_get_first_tb_dma(txq, txq->write_ptr); + tb_phys = iwl_pcie_get_first_tb_dma(txq, idx); /* The first TB points to bi-directional DMA data */ if (!amsdu) - memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr, + memcpy(&txq->first_tb_bufs[idx], &dev_cmd->hdr, IWL_FIRST_TB_SIZE); iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, IWL_FIRST_TB_SIZE); @@ -431,7 +428,7 @@ struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, * building the A-MSDU might have changed this data, so memcpy * it now */ - memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr, + memcpy(&txq->first_tb_bufs[idx], &dev_cmd->hdr, IWL_FIRST_TB_SIZE); return tfd; } @@ -484,6 +481,7 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_tx_cmd_gen2 *tx_cmd = (void *)dev_cmd->payload; struct iwl_cmd_meta *out_meta; struct iwl_txq *txq = trans_pcie->txq[txq_id]; + int idx; void *tfd; if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used), @@ -497,16 +495,18 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, spin_lock(&txq->lock); + idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); + /* Set up driver data for this TFD */ - txq->entries[txq->write_ptr].skb = skb; - txq->entries[txq->write_ptr].cmd = dev_cmd; + txq->entries[idx].skb = skb; + txq->entries[idx].cmd = dev_cmd; dev_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | - INDEX_TO_SEQ(txq->write_ptr))); + INDEX_TO_SEQ(idx))); /* Set up first empty entry in queue's array of Tx/cmd buffers */ - out_meta = &txq->entries[txq->write_ptr].meta; + out_meta = &txq->entries[idx].meta; out_meta->flags = 0; tfd = iwl_pcie_gen2_build_tfd(trans, txq, dev_cmd, skb, out_meta); @@ -562,7 +562,7 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, unsigned long flags; void *dup_buf = NULL; dma_addr_t phys_addr; - int idx, i, cmd_pos; + int i, cmd_pos, idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); u16 copy_size, cmd_size, tb0_size; bool had_nocopy = false; u8 group_id = iwl_cmd_groupid(cmd->id); @@ -651,7 +651,6 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, goto free_dup_buf; } - idx = get_cmd_index(txq, txq->write_ptr); out_cmd = txq->entries[idx].cmd; out_meta = &txq->entries[idx].meta; @@ -938,7 +937,7 @@ void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) txq_id, txq->read_ptr); if (txq_id != trans_pcie->cmd_queue) { - int idx = get_cmd_index(txq, txq->read_ptr); + int idx = iwl_pcie_get_cmd_index(txq, txq->read_ptr); struct sk_buff *skb = txq->entries[idx].skb; if (WARN_ON_ONCE(!skb)) @@ -1070,7 +1069,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, cmd->tfdq_addr = cpu_to_le64(txq->dma_addr); cmd->byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma); - cmd->cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(TFD_QUEUE_SIZE_MAX)); + cmd->cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(TFD_TX_CMD_SLOTS)); ret = iwl_trans_send_cmd(trans, &hcmd); if (ret) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index c893f9088f9d..c645d10d3707 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -106,7 +106,7 @@ static int iwl_queue_init(struct iwl_txq *q, int slots_num) q->n_window = slots_num; /* slots_num must be power-of-two size, otherwise - * get_cmd_index is broken. */ + * iwl_pcie_get_cmd_index is broken. */ if (WARN_ON(!is_power_of_2(slots_num))) return -EINVAL; @@ -428,7 +428,7 @@ void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) * idx is bounded by n_window */ int rd_ptr = txq->read_ptr; - int idx = get_cmd_index(txq, rd_ptr); + int idx = iwl_pcie_get_cmd_index(txq, rd_ptr); lockdep_assert_held(&txq->lock); @@ -1100,7 +1100,8 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, for (; txq->read_ptr != tfd_num; txq->read_ptr = iwl_queue_inc_wrap(txq->read_ptr)) { - struct sk_buff *skb = txq->entries[txq->read_ptr].skb; + int idx = iwl_pcie_get_cmd_index(txq, txq->read_ptr); + struct sk_buff *skb = txq->entries[idx].skb; if (WARN_ON_ONCE(!skb)) continue; @@ -1109,7 +1110,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, __skb_queue_tail(skbs, skb); - txq->entries[txq->read_ptr].skb = NULL; + txq->entries[idx].skb = NULL; if (!trans->cfg->use_tfh) iwl_pcie_txq_inval_byte_cnt_tbl(trans, txq); @@ -1559,7 +1560,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, goto free_dup_buf; } - idx = get_cmd_index(txq, txq->write_ptr); + idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); out_cmd = txq->entries[idx].cmd; out_meta = &txq->entries[idx].meta; @@ -1751,7 +1752,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, spin_lock_bh(&txq->lock); - cmd_index = get_cmd_index(txq, index); + cmd_index = iwl_pcie_get_cmd_index(txq, index); cmd = txq->entries[cmd_index].cmd; meta = &txq->entries[cmd_index].meta; group_id = cmd->hdr.group_id; -- cgit v1.2.3 From 3e73aa3bf96619b51c80c73f06512f07fb2b5659 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 27 Jul 2017 09:40:16 +0300 Subject: iwlwifi: mvm: add command name for FRAME_RELEASE This name was missing in the list. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 76d256a9ad71..231878969332 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -384,6 +384,7 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC), HCMD_NAME(REPLY_RX_PHY_CMD), HCMD_NAME(REPLY_RX_MPDU_CMD), + HCMD_NAME(FRAME_RELEASE), HCMD_NAME(BA_NOTIF), HCMD_NAME(MCC_UPDATE_CMD), HCMD_NAME(MCC_CHUB_UPDATE_CMD), -- cgit v1.2.3 From 790e663433d839c5664408d3b1f066450e85c923 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 27 Jul 2017 09:45:10 +0300 Subject: iwlwifi: mvm: include more debug data when we get an unexpected baid When we get a valid baid in a received frame, we need to check that we are aware of this baid. If not, we check that the OLD_SN bit set. If that's not the case, we issue a WARNING. Print more data when that happens. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 6b8e57b7234a..4fbf102b3a98 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -636,8 +636,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, baid_data = rcu_dereference(mvm->baid_map[baid]); if (!baid_data) { WARN(!(reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN), - "Received baid %d, but no data exists for this BAID\n", - baid); + "Received baid %d, but no data exists for this BAID - reorder data 0x%x\n", + baid, reorder); return false; } @@ -758,7 +758,9 @@ static void iwl_mvm_agg_rx_received(struct iwl_mvm *mvm, data = rcu_dereference(mvm->baid_map[baid]); if (!data) { - WARN_ON(!(reorder_data & IWL_RX_MPDU_REORDER_BA_OLD_SN)); + WARN(!(reorder_data & IWL_RX_MPDU_REORDER_BA_OLD_SN), + "OLD_SN isn't set, but no data exists for baid %d - reorder data 0x%x\n", + baid, reorder_data); goto out; } -- cgit v1.2.3 From 18f1755db781165bcda6b3c8d93b909af96bb5ab Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 26 Jul 2017 15:20:18 +0300 Subject: iwlwifi: mvm: group all dummy SAR function declarations together We have some of the SAR dummy functions when ACPI is not set declared in mvm.h and some declared in fw.c. Group them all together in fw.c for consistency and to avoid static/non-static issues. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 11 +++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 14 -------------- 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index ed18479a7b8c..90ae50f7768a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -996,6 +996,17 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) { return 0; } + +int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, + int prof_b) +{ + return -ENOENT; +} + +int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) +{ + return -ENOENT; +} #endif /* CONFIG_ACPI */ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index d58de9b80886..74fdd33fd9fb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1825,21 +1825,7 @@ int iwl_mvm_send_lqm_cmd(struct ieee80211_vif *vif, u32 duration, u32 timeout); bool iwl_mvm_lqm_active(struct iwl_mvm *mvm); -#ifdef CONFIG_ACPI int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b); int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm); -#else -static inline -int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) -{ - return -ENOENT; -} - -static inline -int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) -{ - return -ENOENT; -} -#endif /* CONFIG_ACPI */ #endif /* __IWL_MVM_H__ */ -- cgit v1.2.3 From 7ccb498ca86c3700f391131b7c5eda9318b3b7db Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 1 Aug 2017 09:01:34 +0300 Subject: iwlwifi: mvm: use mvmsta consistently in rs.c We use mvmsta for the sta->drv_priv in mvm, but in rs.c we have a bunch of instances using sta_priv, which is probably due to it being copied from dvm. Change all occurrences to mvmsta for consistency with the rest of the driver Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 65beca3a457a..cdf10ce9dbea 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1673,14 +1673,14 @@ static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_scale_tbl_info *tbl, enum rs_action scale_action) { - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); if ((!is_vht(&tbl->rate) && !is_ht(&tbl->rate)) || tbl->rate.index < IWL_RATE_MCS_5_INDEX || scale_action == RS_ACTION_DOWNSCALE) - sta_priv->tlc_amsdu = false; + mvmsta->tlc_amsdu = false; else - sta_priv->tlc_amsdu = true; + mvmsta->tlc_amsdu = true; } /* @@ -2228,11 +2228,11 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, u16 high_low; s32 sr; u8 prev_agg = lq_sta->is_agg; - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data; struct rs_rate *rate; - lq_sta->is_agg = !!sta_priv->agg_tids; + lq_sta->is_agg = !!mvmsta->agg_tids; /* * Select rate-scale / modulation-mode table to work with in @@ -2491,7 +2491,7 @@ lq_update: IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n"); if (tid != IWL_MAX_TID_COUNT) { - tid_data = &sta_priv->tid_data[tid]; + tid_data = &mvmsta->tid_data[tid]; if (tid_data->state != IWL_AGG_OFF) { IWL_DEBUG_RATE(mvm, "Stop aggregation on tid %d\n", @@ -2507,7 +2507,7 @@ lq_update: if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && (lq_sta->tx_agg_tid_en & (1 << tid)) && (tid != IWL_MAX_TID_COUNT)) { - tid_data = &sta_priv->tid_data[tid]; + tid_data = &mvmsta->tid_data[tid]; if (tid_data->state == IWL_AGG_OFF && !ndp) { IWL_DEBUG_RATE(mvm, "try to aggregate tid %d\n", @@ -2900,10 +2900,10 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, gfp_t gfp) { - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate; struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; + struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta; IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); @@ -2917,7 +2917,7 @@ static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal)); lq_sta->pers.last_rssi = S8_MIN; - return &sta_priv->lq_sta; + return &mvmsta->lq_sta; } static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, @@ -3109,8 +3109,8 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct ieee80211_hw *hw = mvm->hw; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); - struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta; struct ieee80211_supported_band *sband; unsigned long supp; /* must be unsigned long for for_each_set_bit */ @@ -3119,8 +3119,8 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, sband = hw->wiphy->bands[band]; - lq_sta->lq.sta_id = sta_priv->sta_id; - sta_priv->tlc_amsdu = false; + lq_sta->lq.sta_id = mvmsta->sta_id; + mvmsta->tlc_amsdu = false; for (j = 0; j < LQ_SIZE; j++) rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]); @@ -3130,7 +3130,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, IWL_DEBUG_RATE(mvm, "LQ: *** rate scale station global init for station %d ***\n", - sta_priv->sta_id); + mvmsta->sta_id); /* TODO: what is a good starting rate for STA? About middle? Maybe not * the lowest or the highest rate.. Could consider using RSSI from * previous packets? Need to have IEEE 802.1X auth succeed immediately -- cgit v1.2.3 From f9cd3e0871b54e70d3ecfa89b76b2e9a7ac172af Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 2 Aug 2017 09:29:33 +0300 Subject: iwlwifi: mvm: update the firmware API in TX The firmware team is now re-using a bit that hasn't been used for a few generations. Re-use for TX_ON_AIR drop. This bit will be set by the firmware to indicate that a frame in an A-MPDU was dropped but not because of the already mapped reasons. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/tx.h | 9 +++------ drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index 4928310ddd31..14ad9fb895f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -409,7 +409,8 @@ enum iwl_tx_status { * @AGG_TX_STATE_BT_PRIO: * @AGG_TX_STATE_FEW_BYTES: * @AGG_TX_STATE_ABORT: - * @AGG_TX_STATE_LAST_SENT_TTL: + * @AGG_TX_STATE_TX_ON_AIR_DROP: TX_ON_AIR signal drop without underrun or + * BT detection * @AGG_TX_STATE_LAST_SENT_TRY_CNT: * @AGG_TX_STATE_LAST_SENT_BT_KILL: * @AGG_TX_STATE_SCD_QUERY: @@ -433,7 +434,7 @@ enum iwl_tx_agg_status { AGG_TX_STATE_BT_PRIO = 0x002, AGG_TX_STATE_FEW_BYTES = 0x004, AGG_TX_STATE_ABORT = 0x008, - AGG_TX_STATE_LAST_SENT_TTL = 0x010, + AGG_TX_STATE_TX_ON_AIR_DROP = 0x010, AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020, AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040, AGG_TX_STATE_SCD_QUERY = 0x080, @@ -445,10 +446,6 @@ enum iwl_tx_agg_status { AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS, }; -#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL| \ - AGG_TX_STATE_LAST_SENT_TRY_CNT| \ - AGG_TX_STATE_LAST_SENT_BT_KILL) - /* * The mask below describes a status where we are absolutely sure that the MPDU * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 6d7d1a66af81..48f028366d51 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1515,7 +1515,7 @@ static const char *iwl_get_agg_tx_status(u16 status) AGG_TX_STATE_(BT_PRIO); AGG_TX_STATE_(FEW_BYTES); AGG_TX_STATE_(ABORT); - AGG_TX_STATE_(LAST_SENT_TTL); + AGG_TX_STATE_(TX_ON_AIR_DROP); AGG_TX_STATE_(LAST_SENT_TRY_CNT); AGG_TX_STATE_(LAST_SENT_BT_KILL); AGG_TX_STATE_(SCD_QUERY); -- cgit v1.2.3 From 3edfb5f44b942f04b4fbbddb24d8866bbfb5d1a4 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 2 Aug 2017 12:13:20 +0300 Subject: iwlwifi: mvm: remove session protection to allow channel switch If a time event is already scheduled when trying to schedule one for channel switch, the code assumes the channel switch is already scheduled and no further action is required. However, it is possible that the scheduled time event is actually for session protection (e.g. when the first beacon after association contains the CSA IE). In this case the channel switch will not be scheduled which will finally lead to disconnection. Fix this by removing the old time event and schduling a new one for the channel switch. Signed-off-by: Avraham Stern Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 +- .../net/wireless/intel/iwlwifi/mvm/time-event.c | 34 ++++++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 2d1404c9fbf4..01143c491e53 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2023,8 +2023,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, * We received a beacon from the associated AP so * remove the session protection. */ - iwl_mvm_remove_time_event(mvm, mvmvif, - &mvmvif->time_event_data); + iwl_mvm_stop_session_protection(mvm, vif); iwl_mvm_sf_update(mvm, vif, false); WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 65d8299108d5..4d0314912e94 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2017 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -726,8 +728,21 @@ void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; + u32 id; lockdep_assert_held(&mvm->mutex); + + spin_lock_bh(&mvm->time_event_lock); + id = te_data->id; + spin_unlock_bh(&mvm->time_event_lock); + + if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) { + IWL_DEBUG_TE(mvm, + "don't remove TE with id=%u (not session protection)\n", + id); + return; + } + iwl_mvm_remove_time_event(mvm, mvmvif, te_data); } @@ -859,8 +874,23 @@ int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); if (te_data->running) { - IWL_DEBUG_TE(mvm, "CS period is already scheduled\n"); - return -EBUSY; + u32 id; + + spin_lock_bh(&mvm->time_event_lock); + id = te_data->id; + spin_unlock_bh(&mvm->time_event_lock); + + if (id == TE_CHANNEL_SWITCH_PERIOD) { + IWL_DEBUG_TE(mvm, "CS period is already scheduled\n"); + return -EBUSY; + } + + /* + * Remove the session protection time event to allow the + * channel switch. If we got here, we just heard a beacon so + * the session protection is not needed anymore anyway. + */ + iwl_mvm_remove_time_event(mvm, mvmvif, te_data); } time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); -- cgit v1.2.3 From 114db230d35442e6d3382adc8ac34c944b5cd0eb Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 6 Aug 2017 13:19:05 +0300 Subject: iwlwifi: mvm: don't send BAR on flushed frames When we flush a queue, the packets will have a 'failed' status but we shouldn't send a BAR. This check was missing. Because of that, when we got an ampdu_action with IEEE80211_AMPDU_TX_STOP_FLUSH, we started the following ping pong with the firmware: 1) Set the station as 'draining' 2) Get a failed Tx status (DRAINED) 3) Send a BAR because of the failed Tx status (loop of 2 and 3) This loop wasn't endless since the BAR isn't sent on a queue that would trigger a "nested" BAR. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 48f028366d51..52f9e8a76124 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1331,6 +1331,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, while (!skb_queue_empty(&skbs)) { struct sk_buff *skb = __skb_dequeue(&skbs); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + bool flushed = false; skb_freed++; @@ -1344,6 +1345,10 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, case TX_STATUS_DIRECT_DONE: info->flags |= IEEE80211_TX_STAT_ACK; break; + case TX_STATUS_FAIL_FIFO_FLUSHED: + case TX_STATUS_FAIL_DRAIN_FLOW: + flushed = true; + break; case TX_STATUS_FAIL_DEST_PS: /* the FW should have stopped the queue and not * return this status @@ -1366,7 +1371,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, /* Single frame failure in an AMPDU queue => send BAR */ if (info->flags & IEEE80211_TX_CTL_AMPDU && !(info->flags & IEEE80211_TX_STAT_ACK) && - !(info->flags & IEEE80211_TX_STAT_TX_FILTERED)) + !(info->flags & IEEE80211_TX_STAT_TX_FILTERED) && !flushed) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; info->flags &= ~IEEE80211_TX_CTL_AMPDU; -- cgit v1.2.3 From d91c3fd0ceba90ed66949fccc856b1e8119a9ccd Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Sun, 6 Aug 2017 11:06:44 +0300 Subject: iwlwifi: move BT_MBOX_PRINT macro to common header Move the BT_MBOX_PRINT() macro from mvm/debugfs.c to fw/api/coex.h so it can be reused and remove duplicate definition of BT_MBOX_MSG(), keeping only the one already in coex.h. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/coex.h | 6 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 12 ------------ 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h index d9a74db01f90..d09555afe2c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h @@ -197,6 +197,12 @@ enum iwl_bt_mxbox_dw4 { ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ >> BT_MBOX##_num##_##_field##_POS) +#define BT_MBOX_PRINT(_num, _field, _end) \ + pos += scnprintf(buf + pos, bufsz - pos, \ + "\t%s: %d%s", \ + #_field, \ + BT_MBOX_MSG(notif, _num, _field), \ + true ? "\n" : ", "); enum iwl_bt_activity_grading { BT_OFF = 0, BT_ON_NO_CONNECTION = 1, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index c88a37397075..e97904c2c4d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -469,18 +469,6 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf, return ret ?: count; } -#define BT_MBOX_MSG(_notif, _num, _field) \ - ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ - >> BT_MBOX##_num##_##_field##_POS) - - -#define BT_MBOX_PRINT(_num, _field, _end) \ - pos += scnprintf(buf + pos, bufsz - pos, \ - "\t%s: %d%s", \ - #_field, \ - BT_MBOX_MSG(notif, _num, _field), \ - true ? "\n" : ", "); - static int iwl_mvm_coex_dump_mbox(struct iwl_mvm *mvm, struct iwl_bt_coex_profile_notif *notif, char *buf, -- cgit v1.2.3 From 5f5537ac3f0f7bd527d332aa166a009be833dfae Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Thu, 3 Aug 2017 07:47:28 -0700 Subject: iwlwifi: Demote messages about fw flags size to info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These messages are not reporting a real error, just the fact that the firmware knows about more flags than the driver. Currently these messages are presented to the user during boot if there is no bootsplash covering the console, even when booting the kernel with "quiet". Demoting it to the warn level helps having a clean boot process. Signed-off-by: João Paulo Rechi Vita Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index cdb765656115..bd3902f888e0 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -487,9 +487,9 @@ static void iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, int i; if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) { - IWL_ERR(drv, - "api flags index %d larger than supported by driver\n", - api_index); + IWL_WARN(drv, + "api flags index %d larger than supported by driver\n", + api_index); return; } @@ -508,9 +508,9 @@ static void iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, int i; if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) { - IWL_ERR(drv, - "capa flags index %d larger than supported by driver\n", - api_index); + IWL_WARN(drv, + "capa flags index %d larger than supported by driver\n", + api_index); return; } -- cgit v1.2.3 From f5d8f50f271d1f80c2afd7eada1c91a863c87a06 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 8 Aug 2017 14:56:58 +0300 Subject: iwlwifi: mvm: Fix channel switch in case of count <= 1 The code did not consider the case that the channel switch counter is <= 1, which would result with an inaccurate calculation of the time event apply time. As the specification states that in case of counter == 0 the switch occurs at any time after the reception the frame, and for counter == 1 the switch would happens before the next TBTT, schedule the time event immediately. Signed-off-by: Ilan Peer Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 01143c491e53..cfabe302c9c7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3875,11 +3875,16 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, /* Schedule the time event to a bit before beacon 1, * to make sure we're in the new channel when the - * GO/AP arrives. + * GO/AP arrives. In case count <= 1 immediately schedule the + * TE (this might result with some packet loss or connection + * loss). */ - apply_time = chsw->device_timestamp + - ((vif->bss_conf.beacon_int * (chsw->count - 1) - - IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024); + if (chsw->count <= 1) + apply_time = 0; + else + apply_time = chsw->device_timestamp + + ((vif->bss_conf.beacon_int * (chsw->count - 1) - + IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024); if (chsw->block_tx) iwl_mvm_csa_client_absent(mvm, vif); -- cgit v1.2.3 From 5f19d6dd811b1bd4ceb0823d22ec00bbc479e379 Mon Sep 17 00:00:00 2001 From: Tzipi Peres Date: Tue, 25 Jul 2017 13:04:46 +0300 Subject: iwlwifi: distinguish different RF modules in A000 devices Newer versions of A000 devices come with two diffenent RF modules. The PCI_ID, the subsystem ID and the RF ID are identical in these two cases, so we need to differentiate them by using the CSR_HW_RF_ID register- in order to load the appropriate firmware. Signed-off-by: Tzipi Peres Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/cfg/a000.c | 34 ++++++++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 4 ++- drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 7 ++++- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 23 ++++++++++++----- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- 5 files changed, 58 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/a000.c b/drivers/net/wireless/intel/iwlwifi/cfg/a000.c index 40d67a5a2635..dcd35b5c9d24 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/a000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/a000.c @@ -76,13 +76,19 @@ #define IWL_A000_HR_FW_PRE "iwlwifi-Qu-a0-hr-a0-" #define IWL_A000_HR_CDB_FW_PRE "iwlwifi-QuIcp-z0-hrcdb-a0-" #define IWL_A000_HR_F0_FW_PRE "iwlwifi-QuQnj-f0-hr-a0-" +#define IWL_A000_JF_B0_FW_PRE "iwlwifi-QuQnj-a0-jf-b0-" +#define IWL_A000_HR_A0_FW_PRE "iwlwifi-QuQnj-a0-hr-a0-" #define IWL_A000_HR_MODULE_FIRMWARE(api) \ IWL_A000_HR_FW_PRE "-" __stringify(api) ".ucode" #define IWL_A000_JF_MODULE_FIRMWARE(api) \ IWL_A000_JF_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_A000_HR_QNJ_MODULE_FIRMWARE(api) \ +#define IWL_A000_HR_F0_QNJ_MODULE_FIRMWARE(api) \ IWL_A000_HR_F0_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_A000_JF_B0_QNJ_MODULE_FIRMWARE(api) \ + IWL_A000_JF_B0_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_A000_HR_A0_QNJ_MODULE_FIRMWARE(api) \ + IWL_A000_HR_A0_FW_PRE "-" __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_A000 10 @@ -171,7 +177,7 @@ const struct iwl_cfg iwla000_2ax_cfg_hr = { .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, }; -const struct iwl_cfg iwla000_2ax_cfg_qnj_hr = { +const struct iwl_cfg iwla000_2ax_cfg_qnj_hr_f0 = { .name = "Intel(R) Dual Band Wireless AX a000", .fw_name_pre = IWL_A000_HR_F0_FW_PRE, IWL_DEVICE_A000, @@ -181,6 +187,28 @@ const struct iwl_cfg iwla000_2ax_cfg_qnj_hr = { .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, }; +const struct iwl_cfg iwla000_2ax_cfg_qnj_jf_b0 = { + .name = "Intel(R) Dual Band Wireless AX a000", + .fw_name_pre = IWL_A000_JF_B0_FW_PRE, + IWL_DEVICE_A000, + .ht_params = &iwl_a000_ht_params, + .nvm_ver = IWL_A000_NVM_VERSION, + .nvm_calib_ver = IWL_A000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + +const struct iwl_cfg iwla000_2ax_cfg_qnj_hr_a0 = { + .name = "Intel(R) Dual Band Wireless AX a000", + .fw_name_pre = IWL_A000_HR_A0_FW_PRE, + IWL_DEVICE_A000, + .ht_params = &iwl_a000_ht_params, + .nvm_ver = IWL_A000_NVM_VERSION, + .nvm_calib_ver = IWL_A000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + MODULE_FIRMWARE(IWL_A000_HR_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_A000_JF_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_A000_HR_QNJ_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_A000_HR_F0_QNJ_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_A000_JF_B0_QNJ_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_A000_HR_A0_QNJ_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 573dbeed3fbf..b82a3d0f64b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -463,7 +463,9 @@ extern const struct iwl_cfg iwla000_2ac_cfg_hr; extern const struct iwl_cfg iwla000_2ac_cfg_hr_cdb; extern const struct iwl_cfg iwla000_2ac_cfg_jf; extern const struct iwl_cfg iwla000_2ax_cfg_hr; -extern const struct iwl_cfg iwla000_2ax_cfg_qnj_hr; +extern const struct iwl_cfg iwla000_2ax_cfg_qnj_hr_f0; +extern const struct iwl_cfg iwla000_2ax_cfg_qnj_jf_b0; +extern const struct iwl_cfg iwla000_2ax_cfg_qnj_hr_a0; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 7d468ad7cb6a..b03e0f975b5a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -354,11 +354,16 @@ enum { #define CSR_HW_REV_TYPE_135 (0x0000120) #define CSR_HW_REV_TYPE_7265D (0x0000210) #define CSR_HW_REV_TYPE_NONE (0x00001F0) +#define CSR_HW_REV_TYPE_QNJ (0x0000360) +#define CSR_HW_REV_TYPE_HR_CDB (0x0000340) /* RF_ID value */ #define CSR_HW_RF_ID_TYPE_JF (0x00105100) #define CSR_HW_RF_ID_TYPE_HR (0x0010A000) -#define CSR_HW_RF_ID_TYPE_HRCDB (0x00109000) +#define CSR_HW_RF_ID_TYPE_HRCDB (0x00109F00) + +/* HW_RF CHIP ID */ +#define CSR_HW_RF_ID_TYPE_CHIP_ID(_val) (((_val) >> 12) & 0xFFF) /* EEPROM REG */ #define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 87712aeac31f..27d1eec1c899 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -690,12 +690,23 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_trans->cfg = cfg_7265d; } - if (iwl_trans->cfg->rf_id && cfg == &iwla000_2ac_cfg_hr_cdb) { - if (iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_JF) - cfg = &iwla000_2ac_cfg_jf; - else if (iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_HR) - cfg = &iwla000_2ac_cfg_hr; - + if (iwl_trans->cfg->rf_id && cfg == &iwla000_2ac_cfg_hr_cdb && + iwl_trans->hw_rev != CSR_HW_REV_TYPE_HR_CDB) { + u32 rf_id_chp = CSR_HW_RF_ID_TYPE_CHIP_ID(iwl_trans->hw_rf_id); + u32 jf_chp_id = CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_JF); + u32 hr_chp_id = CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR); + + if (rf_id_chp == jf_chp_id) { + if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QNJ) + cfg = &iwla000_2ax_cfg_qnj_jf_b0; + else + cfg = &iwla000_2ac_cfg_jf; + } else if (rf_id_chp == hr_chp_id) { + if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QNJ) + cfg = &iwla000_2ax_cfg_qnj_hr_a0; + else + cfg = &iwla000_2ac_cfg_hr; + } iwl_trans->cfg = cfg; } #endif diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 3ecafa2ad922..58873cc27396 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -3145,7 +3145,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS); if (hw_status & UMAG_GEN_HW_IS_FPGA) - trans->cfg = &iwla000_2ax_cfg_qnj_hr; + trans->cfg = &iwla000_2ax_cfg_qnj_hr_f0; else trans->cfg = &iwla000_2ac_cfg_hr; } -- cgit v1.2.3 From 3f7a5e13e85026b6e460bbd6e87f87379421d272 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 16 Aug 2017 08:47:38 +0300 Subject: iwlwifi: pci: add new PCI ID for 7265D We have a new PCI subsystem ID for 7265D. Add it to the list. Cc: stable@vger.kernel.org Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 27d1eec1c899..5398a0917f06 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -430,6 +430,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9E10, iwl7265_2ac_cfg)}, /* 8000 Series */ {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, -- cgit v1.2.3 From b823cf3bae8111341212e698b94ab7293e7fb9f9 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 16 Aug 2017 10:46:52 +0300 Subject: iwlwifi: update channel flags parser There are some new flags in the channel flags that we don't know about. Also, the "WIDE" flag is very confusing, because it actually means 20MHz bandwidth, which is not very wide. Add the new flags and rename the confusing one. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 51 +++++++++++++--------- 1 file changed, 30 insertions(+), 21 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 5c08f4d40f6a..1172e4572a82 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -183,22 +183,26 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = { * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS * on same channel on 2.4 or same UNII band on 5.2 - * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?) - * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) - * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) - * @NVM_CHANNEL_160MHZ: 160 MHz channel okay (?) + * @NVM_CHANNEL_UNIFORM: uniform spreading required + * @NVM_CHANNEL_20MHZ: 20 MHz channel okay + * @NVM_CHANNEL_40MHZ: 40 MHz channel okay + * @NVM_CHANNEL_80MHZ: 80 MHz channel okay + * @NVM_CHANNEL_160MHZ: 160 MHz channel okay + * @NVM_CHANNEL_DC_HIGH: DC HIGH required/allowed (?) */ enum iwl_nvm_channel_flags { - NVM_CHANNEL_VALID = BIT(0), - NVM_CHANNEL_IBSS = BIT(1), - NVM_CHANNEL_ACTIVE = BIT(3), - NVM_CHANNEL_RADAR = BIT(4), - NVM_CHANNEL_INDOOR_ONLY = BIT(5), - NVM_CHANNEL_GO_CONCURRENT = BIT(6), - NVM_CHANNEL_WIDE = BIT(8), - NVM_CHANNEL_40MHZ = BIT(9), - NVM_CHANNEL_80MHZ = BIT(10), - NVM_CHANNEL_160MHZ = BIT(11), + NVM_CHANNEL_VALID = BIT(0), + NVM_CHANNEL_IBSS = BIT(1), + NVM_CHANNEL_ACTIVE = BIT(3), + NVM_CHANNEL_RADAR = BIT(4), + NVM_CHANNEL_INDOOR_ONLY = BIT(5), + NVM_CHANNEL_GO_CONCURRENT = BIT(6), + NVM_CHANNEL_UNIFORM = BIT(7), + NVM_CHANNEL_20MHZ = BIT(8), + NVM_CHANNEL_40MHZ = BIT(9), + NVM_CHANNEL_80MHZ = BIT(10), + NVM_CHANNEL_160MHZ = BIT(11), + NVM_CHANNEL_DC_HIGH = BIT(12), }; #define CHECK_AND_PRINT_I(x) \ @@ -327,7 +331,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, channel->flags = 0; IWL_DEBUG_EEPROM(dev, - "Ch. %d [%sGHz] flags 0x%x %s%s%s%s%s%s%s%s%s%s(%ddBm): Ad-Hoc %ssupported\n", + "Ch. %d [%sGHz] flags 0x%x %s%s%s%s%s%s%s%s%s%s%s%s(%ddBm): Ad-Hoc %ssupported\n", channel->hw_value, is_5ghz ? "5.2" : "2.4", ch_flags, @@ -337,10 +341,12 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, CHECK_AND_PRINT_I(RADAR), CHECK_AND_PRINT_I(INDOOR_ONLY), CHECK_AND_PRINT_I(GO_CONCURRENT), - CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(UNIFORM), + CHECK_AND_PRINT_I(20MHZ), CHECK_AND_PRINT_I(40MHZ), CHECK_AND_PRINT_I(80MHZ), CHECK_AND_PRINT_I(160MHZ), + CHECK_AND_PRINT_I(DC_HIGH), channel->max_power, ((ch_flags & NVM_CHANNEL_IBSS) && !(ch_flags & NVM_CHANNEL_RADAR)) @@ -865,22 +871,25 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, prev_center_freq = center_freq; IWL_DEBUG_DEV(dev, IWL_DL_LAR, - "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n", + "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s%s%s%s(0x%02x): %s\n", center_freq, band == NL80211_BAND_5GHZ ? "5.2" : "2.4", CHECK_AND_PRINT_I(VALID), + CHECK_AND_PRINT_I(IBSS), CHECK_AND_PRINT_I(ACTIVE), CHECK_AND_PRINT_I(RADAR), - CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(INDOOR_ONLY), + CHECK_AND_PRINT_I(GO_CONCURRENT), + CHECK_AND_PRINT_I(UNIFORM), + CHECK_AND_PRINT_I(20MHZ), CHECK_AND_PRINT_I(40MHZ), CHECK_AND_PRINT_I(80MHZ), CHECK_AND_PRINT_I(160MHZ), - CHECK_AND_PRINT_I(INDOOR_ONLY), - CHECK_AND_PRINT_I(GO_CONCURRENT), + CHECK_AND_PRINT_I(DC_HIGH), ch_flags, ((ch_flags & NVM_CHANNEL_ACTIVE) && !(ch_flags & NVM_CHANNEL_RADAR)) - ? "" : "not "); + ? "Ad-Hoc" : ""); } regd->n_reg_rules = valid_rules; -- cgit v1.2.3 From 482e48440a0e0a6260d026b92de99034ac4d7b52 Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Tue, 15 Aug 2017 12:27:01 +0300 Subject: iwlwifi: mvm: change open and close criteria of a BA session Tx BA session should be started according to the current throughput without any dependence on the internal rate scaling state. The criteria for opening a BA session will be 10 frames per second. Sending frequent del BAs can cause inter-op issues with some APs. We'll not close a BA session until we receive an explicit del BA from the peer. Signed-off-by: Gregory Greenman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/constants.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 117 ++++++++++++--------- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 7 ++ 3 files changed, 73 insertions(+), 52 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index 753d4138e30f..976640fed334 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -136,6 +136,7 @@ #define IWL_MVM_RS_SR_NO_DECREASE 85 /* percent */ #define IWL_MVM_RS_AGG_TIME_LIMIT 4000 /* 4 msecs. valid 100-8000 */ #define IWL_MVM_RS_AGG_DISABLE_START 3 +#define IWL_MVM_RS_AGG_START_THRESHOLD 10 /* num frames per second */ #define IWL_MVM_RS_TPC_SR_FORCE_INCREASE 75 /* percent */ #define IWL_MVM_RS_TPC_SR_NO_INCREASE 85 /* percent */ #define IWL_MVM_RS_TPC_TX_POWER_STEP 3 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index cdf10ce9dbea..44c873082a31 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -622,7 +622,9 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); - ret = ieee80211_start_tx_ba_session(sta, tid, 5000); + + /* start BA session until the peer sends del BA */ + ret = ieee80211_start_tx_ba_session(sta, tid, 0); if (ret == -EAGAIN) { /* * driver and mac80211 is out of sync @@ -636,15 +638,31 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, return ret; } -static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, u8 tid, - struct iwl_lq_sta *lq_data, +static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, + u8 tid, struct iwl_lq_sta *lq_sta, struct ieee80211_sta *sta) { - if (tid < IWL_MAX_TID_COUNT) - rs_tl_turn_on_agg_for_tid(mvm, lq_data, tid, sta); - else + struct iwl_mvm_tid_data *tid_data; + + /* + * In AP mode, tid can be equal to IWL_MAX_TID_COUNT + * when the frame is not QoS + */ + if (WARN_ON_ONCE(tid > IWL_MAX_TID_COUNT)) { IWL_ERR(mvm, "tid exceeds max TID count: %d/%d\n", tid, IWL_MAX_TID_COUNT); + return; + } else if (tid == IWL_MAX_TID_COUNT) { + return; + } + + tid_data = &mvmsta->tid_data[tid]; + if ((tid_data->state == IWL_AGG_OFF) && + (lq_sta->tx_agg_tid_en & BIT(tid)) && + (tid_data->tx_count_last >= IWL_MVM_RS_AGG_START_THRESHOLD)) { + IWL_DEBUG_RATE(mvm, "try to aggregate tid %d\n", tid); + rs_tl_turn_on_agg_for_tid(mvm, lq_sta, tid, sta); + } } static inline int get_num_of_ant_from_rate(u32 rate_n_flags) @@ -753,8 +771,38 @@ static int rs_collect_tpc_data(struct iwl_mvm *mvm, window); } +static void rs_update_tid_tpt_stats(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, + u8 tid, int successes) +{ + struct iwl_mvm_tid_data *tid_data; + + if (tid >= IWL_MAX_TID_COUNT) + return; + + tid_data = &mvmsta->tid_data[tid]; + + /* + * Measure if there're enough successful transmits per second. + * These statistics are used only to decide if we can start a + * BA session, so it should be updated only when A-MPDU is + * off. + */ + if (tid_data->state != IWL_AGG_OFF) + return; + + if (time_is_before_jiffies(tid_data->tpt_meas_start + HZ) || + (tid_data->tx_count >= IWL_MVM_RS_AGG_START_THRESHOLD)) { + tid_data->tx_count_last = tid_data->tx_count; + tid_data->tx_count = 0; + tid_data->tpt_meas_start = jiffies; + } else { + tid_data->tx_count += successes; + } +} + static int rs_collect_tlc_data(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, + struct iwl_mvm_sta *mvmsta, u8 tid, struct iwl_scale_tbl_info *tbl, int scale_index, int attempts, int successes) { @@ -764,12 +812,14 @@ static int rs_collect_tlc_data(struct iwl_mvm *mvm, return -EINVAL; if (tbl->column != RS_COLUMN_INVALID) { - struct lq_sta_pers *pers = &lq_sta->pers; + struct lq_sta_pers *pers = &mvmsta->lq_sta.pers; pers->tx_stats[tbl->column][scale_index].total += attempts; pers->tx_stats[tbl->column][scale_index].success += successes; } + rs_update_tid_tpt_stats(mvm, mvmsta, tid, successes); + /* Select window for current tx bit rate */ window = &(tbl->win[scale_index]); return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, @@ -1211,12 +1261,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (time_after(jiffies, (unsigned long)(lq_sta->last_tx + (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { - int t; - IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); - for (t = 0; t < IWL_MAX_TID_COUNT; t++) - ieee80211_stop_tx_ba_session(sta, t); - iwl_mvm_rs_rate_init(mvm, sta, info->band, false); return; } @@ -1312,7 +1357,8 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (info->status.ampdu_ack_len == 0) info->status.ampdu_len = 1; - rs_collect_tlc_data(mvm, lq_sta, curr_tbl, lq_rate.index, + rs_collect_tlc_data(mvm, mvmsta, tid, curr_tbl, + lq_rate.index, info->status.ampdu_len, info->status.ampdu_ack_len); @@ -1351,7 +1397,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_rate.index, 1, i < retries ? 0 : legacy_success, reduced_txp); - rs_collect_tlc_data(mvm, lq_sta, tmp_tbl, + rs_collect_tlc_data(mvm, mvmsta, tid, tmp_tbl, lq_rate.index, 1, i < retries ? 0 : legacy_success); } @@ -2229,7 +2275,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, s32 sr; u8 prev_agg = lq_sta->is_agg; struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data; struct rs_rate *rate; lq_sta->is_agg = !!mvmsta->agg_tids; @@ -2480,44 +2525,12 @@ lq_update: } } + if (!ndp) + rs_tl_turn_on_agg(mvm, mvmsta, tid, lq_sta, sta); + if (done_search && lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_ENDED) { - /* If the "active" (non-search) mode was legacy, - * and we've tried switching antennas, - * but we haven't been able to try HT modes (not available), - * stay with best antenna legacy modulation for a while - * before next round of mode comparisons. */ tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); - if (is_legacy(&tbl1->rate)) { - IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n"); - - if (tid != IWL_MAX_TID_COUNT) { - tid_data = &mvmsta->tid_data[tid]; - if (tid_data->state != IWL_AGG_OFF) { - IWL_DEBUG_RATE(mvm, - "Stop aggregation on tid %d\n", - tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - } - rs_set_stay_in_table(mvm, 1, lq_sta); - } else { - /* If we're in an HT mode, and all 3 mode switch actions - * have been tried and compared, stay in this best modulation - * mode for a while before next round of mode comparisons. */ - if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && - (lq_sta->tx_agg_tid_en & (1 << tid)) && - (tid != IWL_MAX_TID_COUNT)) { - tid_data = &mvmsta->tid_data[tid]; - if (tid_data->state == IWL_AGG_OFF && !ndp) { - IWL_DEBUG_RATE(mvm, - "try to aggregate tid %d\n", - tid); - rs_tl_turn_on_agg(mvm, tid, - lq_sta, sta); - } - } - rs_set_stay_in_table(mvm, 0, lq_sta); - } + rs_set_stay_in_table(mvm, is_legacy(&tbl1->rate), lq_sta); } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 005037aa3122..d13893806513 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -316,6 +316,10 @@ enum iwl_mvm_agg_state { * @is_tid_active: has this TID sent traffic in the last * %IWL_MVM_DQA_QUEUE_TIMEOUT time period. If %txq_id is invalid, this * field should be ignored. + * @tpt_meas_start: time of the throughput measurements start, is reset every HZ + * @tx_count_last: number of frames transmitted during the last second + * @tx_count: counts the number of frames transmitted since the last reset of + * tpt_meas_start */ struct iwl_mvm_tid_data { struct sk_buff_head deferred_tx_frames; @@ -330,6 +334,9 @@ struct iwl_mvm_tid_data { u16 ssn; u16 tx_time; bool is_tid_active; + unsigned long tpt_meas_start; + u32 tx_count_last; + u32 tx_count; }; struct iwl_mvm_key_pn { -- cgit v1.2.3 From 01a9c948a09348950515bf2abb6113ed83e696d8 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 15 Aug 2017 20:48:41 +0300 Subject: iwlwifi: add workaround to disable wide channels in 5GHz The OTP in some SKUs have erroneously allowed 40MHz and 80MHz channels in the 5.2GHz band. The firmware has been modified to not allow this in those SKUs, so the driver needs to do the same otherwise the firmware will assert when we try to use it. Cc: stable@vger.kernel.org Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/nvm.c | 3 +- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 62 ++++++++++++++++++---- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h | 3 +- 3 files changed, 56 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/nvm.c b/drivers/net/wireless/intel/iwlwifi/fw/nvm.c index ae03d0f5564f..e81f6dd3744e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/nvm.c @@ -148,7 +148,8 @@ struct iwl_nvm_data *iwl_fw_get_nvm(struct iwl_fw_runtime *fwrt) rsp->regulatory.channel_profile, nvm->valid_tx_ant & fwrt->fw->valid_tx_ant, nvm->valid_rx_ant & fwrt->fw->valid_rx_ant, - rsp->regulatory.lar_enabled && lar_fw_supported); + rsp->regulatory.lar_enabled && lar_fw_supported, + false); iwl_free_resp(&hcmd); return nvm; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 1172e4572a82..ea165b3e6dd3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -79,6 +79,7 @@ /* NVM offsets (in words) definitions */ enum wkp_nvm_offsets { /* NVM HW-Section offset (in words) definitions */ + SUBSYSTEM_ID = 0x0A, HW_ADDR = 0x15, /* NVM SW-Section offset (in words) definitions */ @@ -258,13 +259,12 @@ static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz, static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 * const nvm_ch_flags, - bool lar_supported) + bool lar_supported, bool no_wide_in_5ghz) { int ch_idx; int n_channels = 0; struct ieee80211_channel *channel; u16 ch_flags; - bool is_5ghz; int num_of_ch, num_2ghz_channels; const u8 *nvm_chan; @@ -279,12 +279,20 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, } for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { + bool is_5ghz = (ch_idx >= num_2ghz_channels); + ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); - if (ch_idx >= num_2ghz_channels && - !data->sku_cap_band_52GHz_enable) + if (is_5ghz && !data->sku_cap_band_52GHz_enable) continue; + /* workaround to disable wide channels in 5GHz */ + if (no_wide_in_5ghz && is_5ghz) { + ch_flags &= ~(NVM_CHANNEL_40MHZ | + NVM_CHANNEL_80MHZ | + NVM_CHANNEL_160MHZ); + } + if (ch_flags & NVM_CHANNEL_160MHZ) data->vht160_supported = true; @@ -307,8 +315,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, n_channels++; channel->hw_value = nvm_chan[ch_idx]; - channel->band = (ch_idx < num_2ghz_channels) ? - NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; + channel->band = is_5ghz ? + NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; channel->center_freq = ieee80211_channel_to_frequency( channel->hw_value, channel->band); @@ -320,7 +328,6 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, * is not used in mvm, and is used for backwards compatibility */ channel->max_power = IWL_DEFAULT_MAX_TX_POWER; - is_5ghz = channel->band == NL80211_BAND_5GHZ; /* don't put limitations in case we're using LAR */ if (!lar_supported) @@ -438,14 +445,15 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *nvm_ch_flags, - u8 tx_chains, u8 rx_chains, bool lar_supported) + u8 tx_chains, u8 rx_chains, bool lar_supported, + bool no_wide_in_5ghz) { int n_channels; int n_used = 0; struct ieee80211_supported_band *sband; n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags, - lar_supported); + lar_supported, no_wide_in_5ghz); sband = &data->bands[NL80211_BAND_2GHZ]; sband->band = NL80211_BAND_2GHZ; sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; @@ -651,6 +659,39 @@ static int iwl_set_hw_address(struct iwl_trans *trans, return 0; } +static bool +iwl_nvm_no_wide_in_5ghz(struct device *dev, const struct iwl_cfg *cfg, + const __le16 *nvm_hw) +{ + /* + * Workaround a bug in Indonesia SKUs where the regulatory in + * some 7000-family OTPs erroneously allow wide channels in + * 5GHz. To check for Indonesia, we take the SKU value from + * bits 1-4 in the subsystem ID and check if it is either 5 or + * 9. In those cases, we need to force-disable wide channels + * in 5GHz otherwise the FW will throw a sysassert when we try + * to use them. + */ + if (cfg->device_family == IWL_DEVICE_FAMILY_7000) { + /* + * Unlike the other sections in the NVM, the hw + * section uses big-endian. + */ + u16 subsystem_id = be16_to_cpup((const __be16 *)nvm_hw + + SUBSYSTEM_ID); + u8 sku = (subsystem_id & 0x1e) >> 1; + + if (sku == 5 || sku == 9) { + IWL_DEBUG_EEPROM(dev, + "disabling wide channels in 5GHz (0x%0x %d)\n", + subsystem_id, sku); + return true; + } + } + + return false; +} + struct iwl_nvm_data * iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, @@ -661,6 +702,7 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, struct device *dev = trans->dev; struct iwl_nvm_data *data; bool lar_enabled; + bool no_wide_in_5ghz = iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw); u32 sku, radio_cfg; u16 lar_config; const __le16 *ch_section; @@ -731,7 +773,7 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, } iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains, - lar_fw_supported && lar_enabled); + lar_fw_supported && lar_enabled, no_wide_in_5ghz); data->calib_version = 255; return data; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 3fd6506a02ab..50d9b3eaa4f8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -93,7 +93,8 @@ void iwl_set_hw_address_from_csr(struct iwl_trans *trans, */ void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *nvm_ch_flags, - u8 tx_chains, u8 rx_chains, bool lar_supported); + u8 tx_chains, u8 rx_chains, bool lar_supported, + bool no_wide_in_5ghz); /** * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW -- cgit v1.2.3 From 8a0d53ce10053ce39b9512a054ab4c0bef2e2cc3 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 15 Aug 2017 23:12:39 +0300 Subject: iwlwifi: fw: fix lar_enabled endian problem in iwl_fw_get_nvm We read the regulatory.lar_enabled field in iwl_fw_get_nvm() and store it in nvm->lar_enabled, taking care of endianness. But then later we read it again to pass the value to iwl_init_sbands() without handling endianness. To solve this, simply reuse nvm->lar_enabled when calling that function. Fixes: e9e1ba3dbf00 ("iwlwifi: mvm: support getting nvm data from firmware") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/nvm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/nvm.c b/drivers/net/wireless/intel/iwlwifi/fw/nvm.c index e81f6dd3744e..bd2e1fb43f5a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/nvm.c @@ -148,8 +148,7 @@ struct iwl_nvm_data *iwl_fw_get_nvm(struct iwl_fw_runtime *fwrt) rsp->regulatory.channel_profile, nvm->valid_tx_ant & fwrt->fw->valid_tx_ant, nvm->valid_rx_ant & fwrt->fw->valid_rx_ant, - rsp->regulatory.lar_enabled && lar_fw_supported, - false); + nvm->lar_enabled, false); iwl_free_resp(&hcmd); return nvm; -- cgit v1.2.3 From 5bd1d2c1ea9d0f8ca7ad0457c3a24fb91c9ca756 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 15 Aug 2017 18:46:44 +0300 Subject: iwlwifi: mvm: remove useless argument in iwl_nvm_init() We always call iwl_nvm_init() with read_nvm_from_nic == true, so this argument is useless. Remove it. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 5 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 98 ++++++++++++++-------------- 3 files changed, 51 insertions(+), 54 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 90ae50f7768a..3d65ab49a8a6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -388,7 +388,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) } if (IWL_MVM_PARSE_NVM && read_nvm) { - ret = iwl_nvm_init(mvm, true); + ret = iwl_nvm_init(mvm); if (ret) { IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); goto error; @@ -486,8 +486,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) /* Read the NVM only at driver load time, no need to do this twice */ if (read_nvm) { - /* Read nvm */ - ret = iwl_nvm_init(mvm, true); + ret = iwl_nvm_init(mvm); if (ret) { IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); goto remove_notif; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 74fdd33fd9fb..83303bac0e4b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1373,7 +1373,7 @@ int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear); void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm); /* NVM */ -int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); +int iwl_nvm_init(struct iwl_mvm *mvm); int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 08020386c3d4..5a6916f0b9ec 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -546,7 +546,7 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) return ret; } -int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) +int iwl_nvm_init(struct iwl_mvm *mvm) { int ret, section; u32 size_read = 0; @@ -557,63 +557,61 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) return -EINVAL; /* load NVM values from nic */ - if (read_nvm_from_nic) { - /* Read From FW NVM */ - IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); - - nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, - GFP_KERNEL); - if (!nvm_buffer) - return -ENOMEM; - for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { - /* we override the constness for initial read */ - ret = iwl_nvm_read_section(mvm, section, nvm_buffer, - size_read); - if (ret < 0) - continue; - size_read += ret; - temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); - if (!temp) { - ret = -ENOMEM; - break; - } + /* Read From FW NVM */ + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); + + nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, + GFP_KERNEL); + if (!nvm_buffer) + return -ENOMEM; + for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { + /* we override the constness for initial read */ + ret = iwl_nvm_read_section(mvm, section, nvm_buffer, + size_read); + if (ret < 0) + continue; + size_read += ret; + temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + break; + } - iwl_mvm_nvm_fixups(mvm, section, temp, ret); + iwl_mvm_nvm_fixups(mvm, section, temp, ret); - mvm->nvm_sections[section].data = temp; - mvm->nvm_sections[section].length = ret; + mvm->nvm_sections[section].data = temp; + mvm->nvm_sections[section].length = ret; #ifdef CONFIG_IWLWIFI_DEBUGFS - switch (section) { - case NVM_SECTION_TYPE_SW: - mvm->nvm_sw_blob.data = temp; - mvm->nvm_sw_blob.size = ret; - break; - case NVM_SECTION_TYPE_CALIBRATION: - mvm->nvm_calib_blob.data = temp; - mvm->nvm_calib_blob.size = ret; - break; - case NVM_SECTION_TYPE_PRODUCTION: - mvm->nvm_prod_blob.data = temp; - mvm->nvm_prod_blob.size = ret; - break; - case NVM_SECTION_TYPE_PHY_SKU: - mvm->nvm_phy_sku_blob.data = temp; - mvm->nvm_phy_sku_blob.size = ret; + switch (section) { + case NVM_SECTION_TYPE_SW: + mvm->nvm_sw_blob.data = temp; + mvm->nvm_sw_blob.size = ret; + break; + case NVM_SECTION_TYPE_CALIBRATION: + mvm->nvm_calib_blob.data = temp; + mvm->nvm_calib_blob.size = ret; + break; + case NVM_SECTION_TYPE_PRODUCTION: + mvm->nvm_prod_blob.data = temp; + mvm->nvm_prod_blob.size = ret; + break; + case NVM_SECTION_TYPE_PHY_SKU: + mvm->nvm_phy_sku_blob.data = temp; + mvm->nvm_phy_sku_blob.size = ret; + break; + default: + if (section == mvm->cfg->nvm_hw_section_num) { + mvm->nvm_hw_blob.data = temp; + mvm->nvm_hw_blob.size = ret; break; - default: - if (section == mvm->cfg->nvm_hw_section_num) { - mvm->nvm_hw_blob.data = temp; - mvm->nvm_hw_blob.size = ret; - break; - } } -#endif } - if (!size_read) - IWL_ERR(mvm, "OTP is blank\n"); - kfree(nvm_buffer); +#endif } + if (!size_read) + IWL_ERR(mvm, "OTP is blank\n"); + kfree(nvm_buffer); /* Only if PNVM selected in the mod param - load external NVM */ if (mvm->nvm_file_name) { -- cgit v1.2.3 From ee4fe54024f071976120dce71aefe056dfab4eb7 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 15 Aug 2017 19:24:59 +0300 Subject: iwlwifi: mvm: remove useless check for mvm->cfg in iwl_parse_nvm_section() At this point we have already copied the cfg pointer to mvm and we have been dereferencing this pointer many times before, so it will never be NULL or we would have crashed. Remove the useless check. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 5a6916f0b9ec..b05673e4a193 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -326,9 +326,6 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) } } - if (WARN_ON(!mvm->cfg)) - return NULL; - hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; -- cgit v1.2.3 From 8fe34b060a4b11be35ffd9383acdd0bc7bee72e2 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Thu, 17 Aug 2017 18:51:56 +0300 Subject: iwlwifi: use big-endian for the hw section of the nvm Unlike the other sections of the NVM, the hw section is in big-endian. To read a value from it, we had to cast it to __be16. Fix that by using __be16 * for the entire section. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 11 +++++------ drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 5 +++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index ea165b3e6dd3..aa382f719988 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -582,7 +582,7 @@ static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *mac_override, - const __le16 *nvm_hw) + const __be16 *nvm_hw) { const u8 *hw_addr; @@ -629,7 +629,7 @@ static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, static int iwl_set_hw_address(struct iwl_trans *trans, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, const __le16 *nvm_hw, + struct iwl_nvm_data *data, const __be16 *nvm_hw, const __le16 *mac_override) { if (cfg->mac_addr_from_csr) { @@ -661,7 +661,7 @@ static int iwl_set_hw_address(struct iwl_trans *trans, static bool iwl_nvm_no_wide_in_5ghz(struct device *dev, const struct iwl_cfg *cfg, - const __le16 *nvm_hw) + const __be16 *nvm_hw) { /* * Workaround a bug in Indonesia SKUs where the regulatory in @@ -677,8 +677,7 @@ iwl_nvm_no_wide_in_5ghz(struct device *dev, const struct iwl_cfg *cfg, * Unlike the other sections in the NVM, the hw * section uses big-endian. */ - u16 subsystem_id = be16_to_cpup((const __be16 *)nvm_hw - + SUBSYSTEM_ID); + u16 subsystem_id = be16_to_cpup(nvm_hw + SUBSYSTEM_ID); u8 sku = (subsystem_id & 0x1e) >> 1; if (sku == 5 || sku == 9) { @@ -694,7 +693,7 @@ iwl_nvm_no_wide_in_5ghz(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data * iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, - const __le16 *nvm_hw, const __le16 *nvm_sw, + const __be16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, const __le16 *mac_override, const __le16 *phy_sku, u8 tx_chains, u8 rx_chains, bool lar_fw_supported) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 50d9b3eaa4f8..2d1a24dd8410 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -77,7 +77,7 @@ */ struct iwl_nvm_data * iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, - const __le16 *nvm_hw, const __le16 *nvm_sw, + const __be16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, const __le16 *mac_override, const __le16 *phy_sku, u8 tx_chains, u8 rx_chains, bool lar_fw_supported); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index b05673e4a193..422aa6be9932 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -292,7 +292,8 @@ static struct iwl_nvm_data * iwl_parse_nvm_sections(struct iwl_mvm *mvm) { struct iwl_nvm_section *sections = mvm->nvm_sections; - const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; + const __be16 *hw; + const __le16 *sw, *calib, *regulatory, *mac_override, *phy_sku; bool lar_enabled; /* Checking for required sections */ @@ -326,7 +327,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) } } - hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; + hw = (const __be16 *)sections[mvm->cfg->nvm_hw_section_num].data; sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; -- cgit v1.2.3 From 70535350e26f9bf8c21de0300728f17f61cdcf77 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Thu, 17 Aug 2017 23:11:25 -0700 Subject: liquidio: with embedded f/w, don't reload f/w, issue pf flr at exit 1. Add support for PF FLR when exiting (enables CORE_DRV_ACTIVE upon next driver init) 2. Skip some initialization (don't try to load f/w, activate consoles). Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 70 ++++++++++++++++--------- 1 file changed, 46 insertions(+), 24 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 0eea6a2d0200..bd67980b5462 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1121,6 +1121,33 @@ static bool fw_type_is_none(void) sizeof(LIO_FW_NAME_TYPE_NONE)) == 0; } +/** + * \brief PCI FLR for each Octeon device. + * @param oct octeon device + */ +static void octeon_pci_flr(struct octeon_device *oct) +{ + int rc; + + pci_save_state(oct->pci_dev); + + pci_cfg_access_lock(oct->pci_dev); + + /* Quiesce the device completely */ + pci_write_config_word(oct->pci_dev, PCI_COMMAND, + PCI_COMMAND_INTX_DISABLE); + + rc = __pci_reset_function_locked(oct->pci_dev); + + if (rc != 0) + dev_err(&oct->pci_dev->dev, "Error %d resetting PCI function %d\n", + rc, oct->pf_num); + + pci_cfg_access_unlock(oct->pci_dev); + + pci_restore_state(oct->pci_dev); +} + /** *\brief Destroy resources associated with octeon device * @param pdev PCI device structure @@ -1269,14 +1296,16 @@ static void octeon_destroy_resources(struct octeon_device *oct) case OCT_DEV_PCI_MAP_DONE: refcount = octeon_deregister_device(oct); - if (!fw_type_is_none()) { - /* Soft reset the octeon device before exiting. - * Implementation note: here, we reset the device - * if it is a CN6XXX OR the last CN23XX device. - */ - if (OCTEON_CN6XXX(oct) || !refcount) - oct->fn_list.soft_reset(oct); - } + /* Soft reset the octeon device before exiting. + * However, if fw was loaded from card (i.e. autoboot), + * perform an FLR instead. + * Implementation note: only soft-reset the device + * if it is a CN6XXX OR the LAST CN23XX device. + */ + if (fw_type_is_none()) + octeon_pci_flr(oct); + else if (OCTEON_CN6XXX(oct) || !refcount) + oct->fn_list.soft_reset(oct); octeon_unmap_pci_barx(oct, 0); octeon_unmap_pci_barx(oct, 1); @@ -1912,11 +1941,6 @@ static int load_firmware(struct octeon_device *oct) char fw_name[LIO_MAX_FW_FILENAME_LEN]; char *tmp_fw_type; - if (fw_type_is_none()) { - dev_info(&oct->pci_dev->dev, "Skipping firmware load\n"); - return ret; - } - if (fw_type[0] == '\0') tmp_fw_type = LIO_FW_NAME_TYPE_NIC; else @@ -3900,18 +3924,16 @@ static int octeon_device_init(struct octeon_device *octeon_dev) octeon_dev->app_mode = CVM_DRV_INVALID_APP; if (OCTEON_CN23XX_PF(octeon_dev)) { - if (!cn23xx_fw_loaded(octeon_dev)) { + if (!cn23xx_fw_loaded(octeon_dev) && !fw_type_is_none()) { fw_loaded = 0; - if (!fw_type_is_none()) { - /* Do a soft reset of the Octeon device. */ - if (octeon_dev->fn_list.soft_reset(octeon_dev)) - return 1; - /* things might have changed */ - if (!cn23xx_fw_loaded(octeon_dev)) - fw_loaded = 0; - else - fw_loaded = 1; - } + /* Do a soft reset of the Octeon device. */ + if (octeon_dev->fn_list.soft_reset(octeon_dev)) + return 1; + /* things might have changed */ + if (!cn23xx_fw_loaded(octeon_dev)) + fw_loaded = 0; + else + fw_loaded = 1; } else { fw_loaded = 1; } -- cgit v1.2.3 From 3c57f61501c4006f558fc804e0bed11705a3ed45 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Thu, 17 Aug 2017 23:11:30 -0700 Subject: liquidio: with embedded f/w, issue droq credits before enablement 1. Issue credits BEFORE enabling DROQ's; this prevents PKTPF_ERR interrupt. Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index bd67980b5462..268ba5215bdd 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -4049,6 +4049,18 @@ static int octeon_device_init(struct octeon_device *octeon_dev) atomic_set(&octeon_dev->status, OCT_DEV_INTR_SET_DONE); + /* Send Credit for Octeon Output queues. Credits are always sent BEFORE + * the output queue is enabled. + * This ensures that we'll receive the f/w CORE DRV_ACTIVE message in + * case we've configured CN23XX_SLI_GBL_CONTROL[NOPTR_D] = 0. + * Otherwise, it is possible that the DRV_ACTIVE message will be sent + * before any credits have been issued, causing the ring to be reset + * (and the f/w appear to never have started). + */ + for (j = 0; j < octeon_dev->num_oqs; j++) + writel(octeon_dev->droq[j]->max_count, + octeon_dev->droq[j]->pkts_credit_reg); + /* Enable the input and output queues for this Octeon device */ ret = octeon_dev->fn_list.enable_io_queues(octeon_dev); if (ret) { @@ -4133,14 +4145,6 @@ static int octeon_device_init(struct octeon_device *octeon_dev) atomic_set(&octeon_dev->status, OCT_DEV_HOST_OK); - /* Send Credit for Octeon Output queues. Credits are always sent after - * the output queue is enabled. - */ - for (j = 0; j < octeon_dev->num_oqs; j++) - writel(octeon_dev->droq[j]->max_count, - octeon_dev->droq[j]->pkts_credit_reg); - - /* Packets can start arriving on the output queues from this point. */ return 0; } -- cgit v1.2.3 From 7f3b39dafc6234dc1565fafe6adb15a6c4932182 Mon Sep 17 00:00:00 2001 From: Chris Mi Date: Fri, 18 Aug 2017 07:24:20 -0400 Subject: net/sched: Fix the logic error to decide the ingress qdisc The offending commit used a newly added helper function. But the logic is wrong. Without this fix, the affected NICs can't do HW offload. Error -EOPNOTSUPP will be returned directly. Fixes: a2e8da9378cc ("net/sched: use newly added classid identity helpers") Signed-off-by: Chris Mi Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 2 +- drivers/net/ethernet/netronome/nfp/bpf/main.c | 2 +- drivers/net/ethernet/netronome/nfp/flower/offload.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 77538cd8184a..e55a9299547a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2892,7 +2892,7 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate) static int cxgb_setup_tc_cls_u32(struct net_device *dev, struct tc_cls_u32_offload *cls_u32) { - if (is_classid_clsact_ingress(cls_u32->common.classid) || + if (!is_classid_clsact_ingress(cls_u32->common.classid) || cls_u32->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index f9fd8d8f1bef..56d7ef014d0d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9230,7 +9230,7 @@ static int ixgbe_setup_tc_cls_u32(struct net_device *dev, { struct ixgbe_adapter *adapter = netdev_priv(dev); - if (is_classid_clsact_ingress(cls_u32->common.classid) || + if (!is_classid_clsact_ingress(cls_u32->common.classid) || cls_u32->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 8633ca5af6ed..2fc3832bc2f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3031,7 +3031,7 @@ static int mlx5e_setup_tc_cls_flower(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); - if (is_classid_clsact_ingress(cls_flower->common.classid) || + if (!is_classid_clsact_ingress(cls_flower->common.classid) || cls_flower->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index f34c00fbf78c..7a9f53f74976 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -657,7 +657,7 @@ mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); - if (is_classid_clsact_ingress(cls_flower->common.classid) || + if (!is_classid_clsact_ingress(cls_flower->common.classid) || cls_flower->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index 0e6864922d5c..f4de3a7377b0 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -127,7 +127,7 @@ static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, struct nfp_net *nn = netdev_priv(netdev); if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) || - is_classid_clsact_ingress(cls_bpf->common.classid) || + !is_classid_clsact_ingress(cls_bpf->common.classid) || cls_bpf->common.protocol != htons(ETH_P_ALL) || cls_bpf->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 3ad5aaa210a4..d868a5700e01 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -390,7 +390,7 @@ int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, struct tc_cls_flower_offload *cls_flower = type_data; if (type != TC_SETUP_CLSFLOWER || - is_classid_clsact_ingress(cls_flower->common.classid) || + !is_classid_clsact_ingress(cls_flower->common.classid) || !eth_proto_is_802_3(cls_flower->common.protocol) || cls_flower->common.chain_index) return -EOPNOTSUPP; -- cgit v1.2.3 From d36d36ceac11e514f1c313ea6ca7568e2463b6f2 Mon Sep 17 00:00:00 2001 From: Salil Date: Fri, 18 Aug 2017 12:31:37 +0100 Subject: net: hns3: Fixes the missing u64_stats_fetch_begin_irq in 64-bit stats fetch This patch fixes the missing u64_stats_fetch_begin_irq() while trying to atomically do 64-bit RX/TX fetch. We did not get any error during test as our SoC is 64-bit so all of these seq/lock operations results in NOOP. As such, this seq lock supports has been added for the sake of completion if this code ever runs on 32-bit platform and we are trying to do 64-bit stats fetch. Fixes: 76ad4f0ee747 ("net: hns3: Add support of HNS3 Ethernet Driver for hip08 SoC") Reported-by: Dan Carpenter Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index 9589b7e1d24c..b12730a23c25 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1054,6 +1054,7 @@ hns3_nic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) /* fetch the tx stats */ ring = priv->ring_data[idx].ring; do { + start = u64_stats_fetch_begin_irq(&ring->syncp); tx_bytes += ring->stats.tx_bytes; tx_pkts += ring->stats.tx_pkts; } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); @@ -1061,6 +1062,7 @@ hns3_nic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) /* fetch the rx stats */ ring = priv->ring_data[idx + queue_num].ring; do { + start = u64_stats_fetch_begin_irq(&ring->syncp); rx_bytes += ring->stats.rx_bytes; rx_pkts += ring->stats.rx_pkts; } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); -- cgit v1.2.3 From 9db85f33c0d724ce9251ad80dff49aa7bdbd95af Mon Sep 17 00:00:00 2001 From: Salil Date: Fri, 18 Aug 2017 12:31:38 +0100 Subject: net: hns3: Fixes the static checker error warning in hns3_get_link_ksettings() This patch fixes the static check error warning in hns3_get_link_ksettings() function by re-arranging the code. Fixes: 496d03e960ae ("net: hns3: Add Ethtool support to HNS3 Driver") Reported-by: Dan Carpenter Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- .../ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c | 85 ++++++++++++---------- 1 file changed, 48 insertions(+), 37 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c index 53cab3ad4cda..d636399232fb 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c @@ -313,7 +313,7 @@ static int hns3_get_link_ksettings(struct net_device *netdev, if (!h->ae_algo || !h->ae_algo->ops) return -EOPNOTSUPP; - /* 1.auto_neg&speed&duplex from cmd */ + /* 1.auto_neg & speed & duplex from cmd */ if (h->ae_algo->ops->get_ksettings_an_result) { h->ae_algo->ops->get_ksettings_an_result(h, &auto_neg, &speed, &duplex); @@ -329,50 +329,61 @@ static int hns3_get_link_ksettings(struct net_device *netdev, } /* 2.media_type get from bios parameter block */ - if (h->ae_algo->ops->get_media_type) + if (h->ae_algo->ops->get_media_type) { h->ae_algo->ops->get_media_type(h, &media_type); - switch (media_type) { - case HNAE3_MEDIA_TYPE_FIBER: - cmd->base.port = PORT_FIBRE; - supported_caps = HNS3_LM_FIBRE_BIT | HNS3_LM_AUTONEG_BIT | - HNS3_LM_PAUSE_BIT | HNS3_LM_1000BASET_FULL_BIT; + switch (media_type) { + case HNAE3_MEDIA_TYPE_FIBER: + cmd->base.port = PORT_FIBRE; + supported_caps = HNS3_LM_FIBRE_BIT | + HNS3_LM_AUTONEG_BIT | + HNS3_LM_PAUSE_BIT | + HNS3_LM_1000BASET_FULL_BIT; + + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_COPPER: + cmd->base.port = PORT_TP; + supported_caps = HNS3_LM_TP_BIT | + HNS3_LM_AUTONEG_BIT | + HNS3_LM_PAUSE_BIT | + HNS3_LM_1000BASET_FULL_BIT | + HNS3_LM_100BASET_FULL_BIT | + HNS3_LM_100BASET_HALF_BIT | + HNS3_LM_10BASET_FULL_BIT | + HNS3_LM_10BASET_HALF_BIT; + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_BACKPLANE: + cmd->base.port = PORT_NONE; + supported_caps = HNS3_LM_BACKPLANE_BIT | + HNS3_LM_PAUSE_BIT | + HNS3_LM_AUTONEG_BIT | + HNS3_LM_1000BASET_FULL_BIT | + HNS3_LM_100BASET_FULL_BIT | + HNS3_LM_100BASET_HALF_BIT | + HNS3_LM_10BASET_FULL_BIT | + HNS3_LM_10BASET_HALF_BIT; + + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_UNKNOWN: + default: + cmd->base.port = PORT_OTHER; + supported_caps = 0; + advertised_caps = 0; + break; + } - advertised_caps = supported_caps; - break; - case HNAE3_MEDIA_TYPE_COPPER: - cmd->base.port = PORT_TP; - supported_caps = HNS3_LM_TP_BIT | HNS3_LM_AUTONEG_BIT | - HNS3_LM_PAUSE_BIT | HNS3_LM_1000BASET_FULL_BIT | - HNS3_LM_100BASET_FULL_BIT | HNS3_LM_100BASET_HALF_BIT | - HNS3_LM_10BASET_FULL_BIT | HNS3_LM_10BASET_HALF_BIT; - advertised_caps = supported_caps; - break; - case HNAE3_MEDIA_TYPE_BACKPLANE: - cmd->base.port = PORT_NONE; - supported_caps = HNS3_LM_BACKPLANE_BIT | HNS3_LM_PAUSE_BIT | - HNS3_LM_AUTONEG_BIT | HNS3_LM_1000BASET_FULL_BIT | - HNS3_LM_100BASET_FULL_BIT | HNS3_LM_100BASET_HALF_BIT | - HNS3_LM_10BASET_FULL_BIT | HNS3_LM_10BASET_HALF_BIT; - - advertised_caps = supported_caps; - break; - case HNAE3_MEDIA_TYPE_UNKNOWN: - default: - cmd->base.port = PORT_OTHER; - supported_caps = 0; - advertised_caps = 0; - break; + /* now, map driver link modes to ethtool link modes */ + hns3_driv_to_eth_caps(supported_caps, cmd, false); + hns3_driv_to_eth_caps(advertised_caps, cmd, true); } - /* now, map driver link modes to ethtool link modes */ - hns3_driv_to_eth_caps(supported_caps, cmd, false); - hns3_driv_to_eth_caps(advertised_caps, cmd, true); - /* 3.mdix_ctrl&mdix get from phy reg */ if (h->ae_algo->ops->get_mdix_mode) h->ae_algo->ops->get_mdix_mode(h, &cmd->base.eth_tp_mdix_ctrl, - &cmd->base.eth_tp_mdix); + &cmd->base.eth_tp_mdix); /* 4.mdio_support */ cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22; -- cgit v1.2.3 From 1898d4e404a5f0095071fa3ae178a1d066360fbb Mon Sep 17 00:00:00 2001 From: Salil Date: Fri, 18 Aug 2017 12:31:39 +0100 Subject: net: hns3: Fixes the static check warning due to missing unsupp L3 proto check This patch fixes the static check warning due to missing handling leg of unsupported L3 protocol type in the hns3_get_l4_protocol() function. Fixes: 76ad4f0ee747 ("net: hns3: Add support of HNS3 Ethernet Driver for hip08 SoC") Reported-by: Dan Carpenter Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index b12730a23c25..e731f87f3c46 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -436,8 +436,8 @@ static int hns3_set_tso(struct sk_buff *skb, u32 *paylen, return 0; } -static void hns3_get_l4_protocol(struct sk_buff *skb, u8 *ol4_proto, - u8 *il4_proto) +static int hns3_get_l4_protocol(struct sk_buff *skb, u8 *ol4_proto, + u8 *il4_proto) { union { struct iphdr *v4; @@ -461,6 +461,8 @@ static void hns3_get_l4_protocol(struct sk_buff *skb, u8 *ol4_proto, &l4_proto_tmp, &frag_off); } else if (skb->protocol == htons(ETH_P_IP)) { l4_proto_tmp = l3.v4->protocol; + } else { + return -EINVAL; } *ol4_proto = l4_proto_tmp; @@ -468,7 +470,7 @@ static void hns3_get_l4_protocol(struct sk_buff *skb, u8 *ol4_proto, /* tunnel packet */ if (!skb->encapsulation) { *il4_proto = 0; - return; + return 0; } /* find inner header point */ @@ -486,6 +488,8 @@ static void hns3_get_l4_protocol(struct sk_buff *skb, u8 *ol4_proto, } *il4_proto = l4_proto_tmp; + + return 0; } static void hns3_set_l2l3l4_len(struct sk_buff *skb, u8 ol4_proto, @@ -757,7 +761,9 @@ static int hns3_fill_desc(struct hns3_enet_ring *ring, void *priv, protocol = vlan_get_protocol(skb); skb->protocol = protocol; } - hns3_get_l4_protocol(skb, &ol4_proto, &il4_proto); + ret = hns3_get_l4_protocol(skb, &ol4_proto, &il4_proto); + if (ret) + return ret; hns3_set_l2l3l4_len(skb, ol4_proto, il4_proto, &type_cs_vlan_tso, &ol_type_vlan_len_msec); -- cgit v1.2.3 From d2896116dbc7be7cabd5db414e008aef4a5e0a00 Mon Sep 17 00:00:00 2001 From: Intiyaz Basha Date: Fri, 18 Aug 2017 13:07:19 -0700 Subject: liquidio: fix Smatch error Fix Smatch error by not dereferencing iq pointer if it's NULL. See http://marc.info/?l=kernel-janitors&m=150296723301129&w=2 Also, remove unnecessary parentheses. Fixes: d314ac222829 ("liquidio: moved liquidio_napi_poll to lio_core.c") Reported-by: Dan Carpenter Signed-off-by: Intiyaz Basha Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index d4f0646084b7..0e7896cdb295 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -668,8 +668,8 @@ static int liquidio_napi_poll(struct napi_struct *napi, int budget) #define MAX_REG_CNT 2000000U /* force enable interrupt if reg cnts are high to avoid wraparound */ - if (((work_done < budget) && (tx_done)) || - (iq->pkt_in_done >= MAX_REG_CNT) || + if ((work_done < budget && tx_done) || + (iq && iq->pkt_in_done >= MAX_REG_CNT) || (droq->pkt_count >= MAX_REG_CNT)) { tx_done = 1; napi_complete_done(napi, work_done); -- cgit v1.2.3 From f21e5077010acda73a60d85e416d6887d537e506 Mon Sep 17 00:00:00 2001 From: Dimitris Michailidis Date: Wed, 16 Aug 2017 14:34:46 -0700 Subject: macvlan: add offload features for encapsulation Currently macvlan devices do not set their hw_enc_features making encapsulated Tx packets resort to SW fallbacks. Add encapsulation GSO offloads to ->features as is done for the other GSOs and set ->hw_enc_features. Signed-off-by: Dimitris Michailidis Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index ca35c6ba7947..d2aea961e0f4 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -835,7 +835,7 @@ static struct lock_class_key macvlan_netdev_addr_lock_key; #define ALWAYS_ON_OFFLOADS \ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | \ - NETIF_F_GSO_ROBUST) + NETIF_F_GSO_ROBUST | NETIF_F_GSO_ENCAP_ALL) #define ALWAYS_ON_FEATURES (ALWAYS_ON_OFFLOADS | NETIF_F_LLTX) @@ -874,6 +874,7 @@ static int macvlan_init(struct net_device *dev) dev->hw_features |= NETIF_F_LRO; dev->vlan_features = lowerdev->vlan_features & MACVLAN_FEATURES; dev->vlan_features |= ALWAYS_ON_OFFLOADS; + dev->hw_enc_features |= dev->features; dev->gso_max_size = lowerdev->gso_max_size; dev->gso_max_segs = lowerdev->gso_max_segs; dev->hard_header_len = lowerdev->hard_header_len; -- cgit v1.2.3 From 3de42f5617dca6d2e5c8bbc4a07b4cfe270f8764 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Wed, 16 Aug 2017 18:30:13 -0700 Subject: liquidio: remove support for deprecated f/w cmd OCTNET_CMD_RESET_PF Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 9 --------- drivers/net/ethernet/cavium/liquidio/liquidio_common.h | 1 - 2 files changed, 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 268ba5215bdd..89d4bbc81707 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1438,15 +1438,6 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) if (atomic_read(&lio->ifstate) & LIO_IFSTATE_RUNNING) liquidio_stop(netdev); - if (fw_type_is_none()) { - struct octnic_ctrl_pkt nctrl; - - memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); - nctrl.ncmd.s.cmd = OCTNET_CMD_RESET_PF; - nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; - octnet_send_nic_ctrl_pkt(oct, &nctrl); - } - if (oct->props[lio->ifidx].napi_enabled == 1) { list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) napi_disable(napi); diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 18d29550e2f8..906e30aadadc 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -189,7 +189,6 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry, #define OCTNET_CMD_Q 0 /* NIC Command types */ -#define OCTNET_CMD_RESET_PF 0x0 #define OCTNET_CMD_CHANGE_MTU 0x1 #define OCTNET_CMD_CHANGE_MACADDR 0x2 #define OCTNET_CMD_CHANGE_DEVFLAGS 0x3 -- cgit v1.2.3 From d2cee2e5d0106abb5da25f393b3c50e0bb01f7f9 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Thu, 17 Aug 2017 18:22:32 +0200 Subject: ixgbe: change ndo_xdp_xmit return code on xmit errors Use errno -ENOSPC ("No space left on device") when the XDP xmit have no space left on the TX ring buffer, instead of -ENOMEM. Signed-off-by: Jesper Dangaard Brouer Acked-by: John Fastabend Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 56d7ef014d0d..8d3224ad6434 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9860,7 +9860,7 @@ static int ixgbe_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp) err = ixgbe_xmit_xdp_ring(adapter, xdp); if (err != IXGBE_XDP_TX) - return -ENOMEM; + return -ENOSPC; return 0; } -- cgit v1.2.3 From 1547f538c14581346c2eb82f8af0071ee6ec2b30 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 18 Aug 2017 14:49:25 +0100 Subject: mlx5: ensure 0 is returned when vport is zero Currently, if vport is zero then then an uninialized return status in err is returned. Since the only return status at the end of the function esw_add_uc_addr is zero for the current set of return paths we may as well just return 0 rather than err to fix this issue. Detected by CoverityScan, CID#1452698 ("Uninitialized scalar variable") Fixes: eeb66cdb6826 ("net/mlx5: Separate between E-Switch and MPFS") Signed-off-by: Colin Ian King Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index fd51f0ea8df9..6b84c1113301 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -401,7 +401,7 @@ fdb_add: esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM fr(%p)\n", vport, mac, vaddr->flow_rule); - return err; + return 0; } static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) -- cgit v1.2.3 From 3b1ded4e0b5118040d44b3b85391aa8865b0d05c Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:02:18 -0500 Subject: amd-xgbe: Set the MDIO mode for 10000Base-T configuration Currently the MDIO mode is set to none for the 10000Base-T, which is incorrect. The MDIO mode should for this configuration should be clause 45. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 04b5c149caca..81c45fa65e0a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -2921,7 +2921,7 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) phy_data->start_mode = XGBE_MODE_KR; } - phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; + phy_data->phydev_mode = XGBE_MDIO_MODE_CL45; break; /* 10GBase-R support */ -- cgit v1.2.3 From f087b506ff5c38e0f2873651dba4e2b73a1b2d67 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:02:27 -0500 Subject: amd-xgbe: Set the MII control width for the MAC interface When running in SGMII mode at speeds below 1000Mbps, the auto-negotition control register must set the MII control width for the MAC interface to be 8-bits wide. By default the width is 4-bits. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 1 + drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 9795419aac2d..d07edf9eaa69 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -1339,6 +1339,7 @@ #define XGBE_AN_CL37_PCS_MODE_BASEX 0x00 #define XGBE_AN_CL37_PCS_MODE_SGMII 0x04 #define XGBE_AN_CL37_TX_CONFIG_MASK 0x08 +#define XGBE_AN_CL37_MII_CTRL_8BIT 0x0100 /* Bit setting and getting macros * The get macro will extract the current bit field value from within diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 80684914dd8a..2222bbf8d0a4 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -982,6 +982,8 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata) break; } + reg |= XGBE_AN_CL37_MII_CTRL_8BIT; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); netif_dbg(pdata, link, pdata->netdev, "CL37 AN (%s) initialized\n", -- cgit v1.2.3 From 25ff96a9dc9c6a5a21778c36cb1e7175128d2071 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:02:40 -0500 Subject: amd-xgbe: Be sure driver shuts down cleanly on module removal Sometimes when the driver is being unloaded while the devices are still up the driver can issue errors. This is based on timing and the double invocation of some routines. The phy_exit() call needs to be run after the network device has been closed and unregistered from the system. Also, the phy_exit() does not need to invoke phy_stop() since that will be called as part of the device closing, so remove that call. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 4 ++-- drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 500147d9e3c8..53a425cc7a2d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -458,6 +458,8 @@ void xgbe_deconfig_netdev(struct xgbe_prv_data *pdata) if (IS_REACHABLE(CONFIG_PTP_1588_CLOCK)) xgbe_ptp_unregister(pdata); + unregister_netdev(netdev); + pdata->phy_if.phy_exit(pdata); flush_workqueue(pdata->an_workqueue); @@ -465,8 +467,6 @@ void xgbe_deconfig_netdev(struct xgbe_prv_data *pdata) flush_workqueue(pdata->dev_workqueue); destroy_workqueue(pdata->dev_workqueue); - - unregister_netdev(netdev); } static int __init xgbe_mod_init(void) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 2222bbf8d0a4..24092020f119 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -1533,8 +1533,6 @@ static int xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata) static void xgbe_phy_exit(struct xgbe_prv_data *pdata) { - xgbe_phy_stop(pdata); - pdata->phy_if.phy_impl.exit(pdata); } -- cgit v1.2.3 From 1b631424e8244722419ebf6ba1fa5a7d72e675c2 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:02:49 -0500 Subject: amd-xgbe: Update TSO packet statistics accuracy When transmitting a TSO packet, the driver only increments the TSO packet statistic by one rather than the number of total packets that were sent. Update the driver to record the total number of packets that resulted from TSO transmit. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 06f953e1e9b2..bb60507d3d9b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1740,7 +1740,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TCPHDRLEN, packet->tcp_header_len / 4); - pdata->ext_stats.tx_tso_packets++; + pdata->ext_stats.tx_tso_packets += packet->tx_packets; } else { /* Enable CRC and Pad Insertion */ XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CPC, 0); -- cgit v1.2.3 From efbaa828330aed8cfa9529451abfea4a465303ec Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:02:57 -0500 Subject: amd-xgbe: Add support to handle device renaming Many of the names used by the driver are based upon the name of the device found during device probe. Move the formatting of the names into the device open function so that any renaming that occurs before the device is brought up will be accounted for. This also means moving the creation of some named workqueues into the device open path. Add support to register for net events so that if a device is renamed the corresponding debugfs directory can be renamed. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c | 25 ++++++++++ drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 44 +++++++++++++++-- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 72 ++++++++++++---------------- drivers/net/ethernet/amd/xgbe/xgbe.h | 5 +- 4 files changed, 100 insertions(+), 46 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c index 7546b660d6b5..7d128be61310 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c @@ -527,3 +527,28 @@ void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) debugfs_remove_recursive(pdata->xgbe_debugfs); pdata->xgbe_debugfs = NULL; } + +void xgbe_debugfs_rename(struct xgbe_prv_data *pdata) +{ + struct dentry *pfile; + char *buf; + + if (!pdata->xgbe_debugfs) + return; + + buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name); + if (!buf) + return; + + if (!strcmp(pdata->xgbe_debugfs->d_name.name, buf)) + goto out; + + pfile = debugfs_rename(pdata->xgbe_debugfs->d_parent, + pdata->xgbe_debugfs, + pdata->xgbe_debugfs->d_parent, buf); + if (!pfile) + netdev_err(pdata->netdev, "debugfs_rename failed\n"); + +out: + kfree(buf); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 2fd9b80b39b0..d6d29d8c58a7 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -887,7 +887,7 @@ static int xgbe_request_irqs(struct xgbe_prv_data *pdata) (unsigned long)pdata); ret = devm_request_irq(pdata->dev, pdata->dev_irq, xgbe_isr, 0, - netdev->name, pdata); + netdev_name(netdev), pdata); if (ret) { netdev_alert(netdev, "error requesting irq %d\n", pdata->dev_irq); @@ -1589,16 +1589,42 @@ static int xgbe_open(struct net_device *netdev) DBGPR("-->xgbe_open\n"); + /* Create the various names based on netdev name */ + snprintf(pdata->an_name, sizeof(pdata->an_name) - 1, "%s-pcs", + netdev_name(netdev)); + + snprintf(pdata->ecc_name, sizeof(pdata->ecc_name) - 1, "%s-ecc", + netdev_name(netdev)); + + snprintf(pdata->i2c_name, sizeof(pdata->i2c_name) - 1, "%s-i2c", + netdev_name(netdev)); + + /* Create workqueues */ + pdata->dev_workqueue = + create_singlethread_workqueue(netdev_name(netdev)); + if (!pdata->dev_workqueue) { + netdev_err(netdev, "device workqueue creation failed\n"); + return -ENOMEM; + } + + pdata->an_workqueue = + create_singlethread_workqueue(pdata->an_name); + if (!pdata->an_workqueue) { + netdev_err(netdev, "phy workqueue creation failed\n"); + ret = -ENOMEM; + goto err_dev_wq; + } + /* Reset the phy settings */ ret = xgbe_phy_reset(pdata); if (ret) - return ret; + goto err_an_wq; /* Enable the clocks */ ret = clk_prepare_enable(pdata->sysclk); if (ret) { netdev_alert(netdev, "dma clk_prepare_enable failed\n"); - return ret; + goto err_an_wq; } ret = clk_prepare_enable(pdata->ptpclk); @@ -1651,6 +1677,12 @@ err_ptpclk: err_sysclk: clk_disable_unprepare(pdata->sysclk); +err_an_wq: + destroy_workqueue(pdata->an_workqueue); + +err_dev_wq: + destroy_workqueue(pdata->dev_workqueue); + return ret; } @@ -1674,6 +1706,12 @@ static int xgbe_close(struct net_device *netdev) clk_disable_unprepare(pdata->ptpclk); clk_disable_unprepare(pdata->sysclk); + flush_workqueue(pdata->an_workqueue); + destroy_workqueue(pdata->an_workqueue); + + flush_workqueue(pdata->dev_workqueue); + destroy_workqueue(pdata->dev_workqueue); + set_bit(XGBE_DOWN, &pdata->dev_state); DBGPR("<--xgbe_close\n"); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 53a425cc7a2d..c5ff385d51ae 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -120,6 +120,7 @@ #include #include #include +#include #include "xgbe.h" #include "xgbe-common.h" @@ -399,35 +400,6 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) return ret; } - /* Create the PHY/ANEG name based on netdev name */ - snprintf(pdata->an_name, sizeof(pdata->an_name) - 1, "%s-pcs", - netdev_name(netdev)); - - /* Create the ECC name based on netdev name */ - snprintf(pdata->ecc_name, sizeof(pdata->ecc_name) - 1, "%s-ecc", - netdev_name(netdev)); - - /* Create the I2C name based on netdev name */ - snprintf(pdata->i2c_name, sizeof(pdata->i2c_name) - 1, "%s-i2c", - netdev_name(netdev)); - - /* Create workqueues */ - pdata->dev_workqueue = - create_singlethread_workqueue(netdev_name(netdev)); - if (!pdata->dev_workqueue) { - netdev_err(netdev, "device workqueue creation failed\n"); - ret = -ENOMEM; - goto err_netdev; - } - - pdata->an_workqueue = - create_singlethread_workqueue(pdata->an_name); - if (!pdata->an_workqueue) { - netdev_err(netdev, "phy workqueue creation failed\n"); - ret = -ENOMEM; - goto err_wq; - } - if (IS_REACHABLE(CONFIG_PTP_1588_CLOCK)) xgbe_ptp_register(pdata); @@ -439,14 +411,6 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) pdata->rx_ring_count); return 0; - -err_wq: - destroy_workqueue(pdata->dev_workqueue); - -err_netdev: - unregister_netdev(netdev); - - return ret; } void xgbe_deconfig_netdev(struct xgbe_prv_data *pdata) @@ -461,18 +425,42 @@ void xgbe_deconfig_netdev(struct xgbe_prv_data *pdata) unregister_netdev(netdev); pdata->phy_if.phy_exit(pdata); +} - flush_workqueue(pdata->an_workqueue); - destroy_workqueue(pdata->an_workqueue); +static int xgbe_netdev_event(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(data); + struct xgbe_prv_data *pdata = netdev_priv(netdev); - flush_workqueue(pdata->dev_workqueue); - destroy_workqueue(pdata->dev_workqueue); + if (netdev->netdev_ops != xgbe_get_netdev_ops()) + goto out; + + switch (event) { + case NETDEV_CHANGENAME: + xgbe_debugfs_rename(pdata); + break; + + default: + break; + } + +out: + return NOTIFY_DONE; } +static struct notifier_block xgbe_netdev_notifier = { + .notifier_call = xgbe_netdev_event, +}; + static int __init xgbe_mod_init(void) { int ret; + ret = register_netdevice_notifier(&xgbe_netdev_notifier); + if (ret) + return ret; + ret = xgbe_platform_init(); if (ret) return ret; @@ -489,6 +477,8 @@ static void __exit xgbe_mod_exit(void) xgbe_pci_exit(); xgbe_platform_exit(); + + unregister_netdevice_notifier(&xgbe_netdev_notifier); } module_init(xgbe_mod_init); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index e9282c924621..9a80f20adc1c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -130,6 +130,7 @@ #include #include #include +#include #define XGBE_DRV_NAME "amd-xgbe" #define XGBE_DRV_VERSION "1.0.3" @@ -1172,7 +1173,6 @@ struct xgbe_prv_data { struct tasklet_struct tasklet_i2c; struct tasklet_struct tasklet_an; -#ifdef CONFIG_DEBUG_FS struct dentry *xgbe_debugfs; unsigned int debugfs_xgmac_reg; @@ -1183,7 +1183,6 @@ struct xgbe_prv_data { unsigned int debugfs_xprop_reg; unsigned int debugfs_xi2c_reg; -#endif }; /* Function prototypes*/ @@ -1232,9 +1231,11 @@ void xgbe_init_tx_coalesce(struct xgbe_prv_data *); #ifdef CONFIG_DEBUG_FS void xgbe_debugfs_init(struct xgbe_prv_data *); void xgbe_debugfs_exit(struct xgbe_prv_data *); +void xgbe_debugfs_rename(struct xgbe_prv_data *pdata); #else static inline void xgbe_debugfs_init(struct xgbe_prv_data *pdata) {} static inline void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) {} +static inline void xgbe_debugfs_rename(struct xgbe_prv_data *pdata) {} #endif /* CONFIG_DEBUG_FS */ /* NOTE: Uncomment for function trace log messages in KERNEL LOG */ -- cgit v1.2.3 From 40452f0ec84a3b7082a934404783a121d378b990 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:03:08 -0500 Subject: amd-xgbe: Add additional dynamic debug messages Add some additional dynamic debug message to the driver. The new messages will provide additional information about the PCS window calculation. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index 1e56ad7bd9a5..3e5833cf1fab 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -292,6 +292,10 @@ static int xgbe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pdata->xpcs_window_size = 1 << (pdata->xpcs_window_size + 7); pdata->xpcs_window_mask = pdata->xpcs_window_size - 1; if (netif_msg_probe(pdata)) { + dev_dbg(dev, "xpcs window def = %#010x\n", + pdata->xpcs_window_def_reg); + dev_dbg(dev, "xpcs window sel = %#010x\n", + pdata->xpcs_window_sel_reg); dev_dbg(dev, "xpcs window = %#010x\n", pdata->xpcs_window); dev_dbg(dev, "xpcs window size = %#010x\n", -- cgit v1.2.3 From caa575afad73ce1a40848543461667c57a1ad989 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:03:17 -0500 Subject: amd-xgbe: Optimize DMA channel interrupt enablement Currently whenever the driver needs to enable or disable interrupts for a DMA channel it reads the interrupt enable register (IER), updates the value and then writes the new value back to the IER. Since the hardware does not change the IER, software can track this value and elimiate the need to read it each time. Add the IER value to the channel related data structure and use that as the base for enabling and disabling interrupts, thus removing the need for the MMIO read. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 77 +++++++++++++++----------------- drivers/net/ethernet/amd/xgbe/xgbe.h | 4 +- 2 files changed, 37 insertions(+), 44 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index bb60507d3d9b..75a479caac03 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -605,7 +605,6 @@ static void xgbe_config_flow_control(struct xgbe_prv_data *pdata) static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) { struct xgbe_channel *channel; - unsigned int dma_ch_isr, dma_ch_ier; unsigned int i; /* Set the interrupt mode if supported */ @@ -617,20 +616,20 @@ static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) channel = pdata->channel[i]; /* Clear all the interrupts which are set */ - dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR); - XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr); + XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, + XGMAC_DMA_IOREAD(channel, DMA_CH_SR)); /* Clear all interrupt enable bits */ - dma_ch_ier = 0; + channel->curr_ier = 0; /* Enable following interrupts * NIE - Normal Interrupt Summary Enable * AIE - Abnormal Interrupt Summary Enable * FBEE - Fatal Bus Error Enable */ - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, NIE, 1); - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, AIE, 1); - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, NIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, AIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 1); if (channel->tx_ring) { /* Enable the following Tx interrupts @@ -639,7 +638,8 @@ static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) * mode) */ if (!pdata->per_channel_irq || pdata->channel_irq_mode) - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 1); + XGMAC_SET_BITS(channel->curr_ier, + DMA_CH_IER, TIE, 1); } if (channel->rx_ring) { /* Enable following Rx interrupts @@ -648,12 +648,13 @@ static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) * per channel interrupts in edge triggered * mode) */ - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RBUE, 1); if (!pdata->per_channel_irq || pdata->channel_irq_mode) - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 1); + XGMAC_SET_BITS(channel->curr_ier, + DMA_CH_IER, RIE, 1); } - XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier); + XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, channel->curr_ier); } } @@ -1964,44 +1965,40 @@ static int xgbe_is_last_desc(struct xgbe_ring_desc *rdesc) static int xgbe_enable_int(struct xgbe_channel *channel, enum xgbe_int int_id) { - unsigned int dma_ch_ier; - - dma_ch_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER); - switch (int_id) { case XGMAC_INT_DMA_CH_SR_TI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 1); break; case XGMAC_INT_DMA_CH_SR_TPS: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TXSE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TXSE, 1); break; case XGMAC_INT_DMA_CH_SR_TBU: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TBUE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TBUE, 1); break; case XGMAC_INT_DMA_CH_SR_RI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 1); break; case XGMAC_INT_DMA_CH_SR_RBU: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RBUE, 1); break; case XGMAC_INT_DMA_CH_SR_RPS: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RSE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RSE, 1); break; case XGMAC_INT_DMA_CH_SR_TI_RI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 1); - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 1); break; case XGMAC_INT_DMA_CH_SR_FBE: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 1); break; case XGMAC_INT_DMA_ALL: - dma_ch_ier |= channel->saved_ier; + channel->curr_ier |= channel->saved_ier; break; default: return -1; } - XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier); + XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, channel->curr_ier); return 0; } @@ -2009,45 +2006,41 @@ static int xgbe_enable_int(struct xgbe_channel *channel, static int xgbe_disable_int(struct xgbe_channel *channel, enum xgbe_int int_id) { - unsigned int dma_ch_ier; - - dma_ch_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER); - switch (int_id) { case XGMAC_INT_DMA_CH_SR_TI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 0); break; case XGMAC_INT_DMA_CH_SR_TPS: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TXSE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TXSE, 0); break; case XGMAC_INT_DMA_CH_SR_TBU: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TBUE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TBUE, 0); break; case XGMAC_INT_DMA_CH_SR_RI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 0); break; case XGMAC_INT_DMA_CH_SR_RBU: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RBUE, 0); break; case XGMAC_INT_DMA_CH_SR_RPS: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RSE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RSE, 0); break; case XGMAC_INT_DMA_CH_SR_TI_RI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 0); - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 0); break; case XGMAC_INT_DMA_CH_SR_FBE: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 0); break; case XGMAC_INT_DMA_ALL: - channel->saved_ier = dma_ch_ier & XGBE_DMA_INTERRUPT_MASK; - dma_ch_ier &= ~XGBE_DMA_INTERRUPT_MASK; + channel->saved_ier = channel->curr_ier; + channel->curr_ier = 0; break; default: return -1; } - XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier); + XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, channel->curr_ier); return 0; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 9a80f20adc1c..58bb455bf1a1 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -182,8 +182,6 @@ #define XGBE_IRQ_MODE_EDGE 0 #define XGBE_IRQ_MODE_LEVEL 1 -#define XGBE_DMA_INTERRUPT_MASK 0x31c7 - #define XGMAC_MIN_PACKET 60 #define XGMAC_STD_PACKET_MTU 1500 #define XGMAC_MAX_STD_PACKET 1518 @@ -462,6 +460,8 @@ struct xgbe_channel { /* Netdev related settings */ struct napi_struct napi; + /* Per channel interrupt enablement tracker */ + unsigned int curr_ier; unsigned int saved_ier; unsigned int tx_timer_active; -- cgit v1.2.3 From 3be95872e89eeabc83ddd6011c988d85c94df050 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:03:26 -0500 Subject: amd-xgbe: Add hardware features debug output Use the dynamic debug support to output information about the hardware features reported by the device. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 78 ++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index d6d29d8c58a7..7498bb81f918 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -732,8 +732,6 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) unsigned int mac_hfr0, mac_hfr1, mac_hfr2; struct xgbe_hw_features *hw_feat = &pdata->hw_feat; - DBGPR("-->xgbe_get_all_hw_features\n"); - mac_hfr0 = XGMAC_IOREAD(pdata, MAC_HWF0R); mac_hfr1 = XGMAC_IOREAD(pdata, MAC_HWF1R); mac_hfr2 = XGMAC_IOREAD(pdata, MAC_HWF2R); @@ -828,7 +826,81 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) hw_feat->rx_fifo_size = 1 << (hw_feat->rx_fifo_size + 7); hw_feat->tx_fifo_size = 1 << (hw_feat->tx_fifo_size + 7); - DBGPR("<--xgbe_get_all_hw_features\n"); + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "Hardware features:\n"); + + /* Hardware feature register 0 */ + dev_dbg(pdata->dev, " 1GbE support : %s\n", + hw_feat->gmii ? "yes" : "no"); + dev_dbg(pdata->dev, " VLAN hash filter : %s\n", + hw_feat->vlhash ? "yes" : "no"); + dev_dbg(pdata->dev, " MDIO interface : %s\n", + hw_feat->sma ? "yes" : "no"); + dev_dbg(pdata->dev, " Wake-up packet support : %s\n", + hw_feat->rwk ? "yes" : "no"); + dev_dbg(pdata->dev, " Magic packet support : %s\n", + hw_feat->mgk ? "yes" : "no"); + dev_dbg(pdata->dev, " Management counters : %s\n", + hw_feat->mmc ? "yes" : "no"); + dev_dbg(pdata->dev, " ARP offload : %s\n", + hw_feat->aoe ? "yes" : "no"); + dev_dbg(pdata->dev, " IEEE 1588-2008 Timestamp : %s\n", + hw_feat->ts ? "yes" : "no"); + dev_dbg(pdata->dev, " Energy Efficient Ethernet : %s\n", + hw_feat->eee ? "yes" : "no"); + dev_dbg(pdata->dev, " TX checksum offload : %s\n", + hw_feat->tx_coe ? "yes" : "no"); + dev_dbg(pdata->dev, " RX checksum offload : %s\n", + hw_feat->rx_coe ? "yes" : "no"); + dev_dbg(pdata->dev, " Additional MAC addresses : %u\n", + hw_feat->addn_mac); + dev_dbg(pdata->dev, " Timestamp source : %s\n", + (hw_feat->ts_src == 1) ? "internal" : + (hw_feat->ts_src == 2) ? "external" : + (hw_feat->ts_src == 3) ? "internal/external" : "n/a"); + dev_dbg(pdata->dev, " SA/VLAN insertion : %s\n", + hw_feat->sa_vlan_ins ? "yes" : "no"); + + /* Hardware feature register 1 */ + dev_dbg(pdata->dev, " RX fifo size : %u\n", + hw_feat->rx_fifo_size); + dev_dbg(pdata->dev, " TX fifo size : %u\n", + hw_feat->tx_fifo_size); + dev_dbg(pdata->dev, " IEEE 1588 high word : %s\n", + hw_feat->adv_ts_hi ? "yes" : "no"); + dev_dbg(pdata->dev, " DMA width : %u\n", + hw_feat->dma_width); + dev_dbg(pdata->dev, " Data Center Bridging : %s\n", + hw_feat->dcb ? "yes" : "no"); + dev_dbg(pdata->dev, " Split header : %s\n", + hw_feat->sph ? "yes" : "no"); + dev_dbg(pdata->dev, " TCP Segmentation Offload : %s\n", + hw_feat->tso ? "yes" : "no"); + dev_dbg(pdata->dev, " Debug memory interface : %s\n", + hw_feat->dma_debug ? "yes" : "no"); + dev_dbg(pdata->dev, " Receive Side Scaling : %s\n", + hw_feat->rss ? "yes" : "no"); + dev_dbg(pdata->dev, " Traffic Class count : %u\n", + hw_feat->tc_cnt); + dev_dbg(pdata->dev, " Hash table size : %u\n", + hw_feat->hash_table_size); + dev_dbg(pdata->dev, " L3/L4 Filters : %u\n", + hw_feat->l3l4_filter_num); + + /* Hardware feature register 2 */ + dev_dbg(pdata->dev, " RX queue count : %u\n", + hw_feat->rx_q_cnt); + dev_dbg(pdata->dev, " TX queue count : %u\n", + hw_feat->tx_q_cnt); + dev_dbg(pdata->dev, " RX DMA channel count : %u\n", + hw_feat->rx_ch_cnt); + dev_dbg(pdata->dev, " TX DMA channel count : %u\n", + hw_feat->rx_ch_cnt); + dev_dbg(pdata->dev, " PPS outputs : %u\n", + hw_feat->pps_out_num); + dev_dbg(pdata->dev, " Auxiliary snapshot inputs : %u\n", + hw_feat->aux_snap_num); + } } static void xgbe_napi_enable(struct xgbe_prv_data *pdata, unsigned int add) -- cgit v1.2.3 From 80a788c94e6c0aa29dfbb43eae4a0d4144992c5d Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:03:35 -0500 Subject: amd-xgbe: Add per queue Tx and Rx statistics Add per queue Tx and Rx packet and byte counts. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 23 ++++++++++++++++------- drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 26 +++++++++++++++++++++++++- drivers/net/ethernet/amd/xgbe/xgbe.h | 5 +++++ 3 files changed, 46 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 75a479caac03..a9784084202a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1609,6 +1609,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) struct xgbe_ring_data *rdata; struct xgbe_ring_desc *rdesc; struct xgbe_packet_data *packet = &ring->packet_data; + unsigned int tx_packets, tx_bytes; unsigned int csum, tso, vlan; unsigned int tso_context, vlan_context; unsigned int tx_set_ic; @@ -1618,6 +1619,9 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) DBGPR("-->xgbe_dev_xmit\n"); + tx_packets = packet->tx_packets; + tx_bytes = packet->tx_bytes; + csum = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, CSUM_ENABLE); tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, @@ -1645,13 +1649,12 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) * - Addition of Tx frame count to the frame count since the * last interrupt was set does not exceed the frame count setting */ - ring->coalesce_count += packet->tx_packets; + ring->coalesce_count += tx_packets; if (!pdata->tx_frames) tx_set_ic = 0; - else if (packet->tx_packets > pdata->tx_frames) + else if (tx_packets > pdata->tx_frames) tx_set_ic = 1; - else if ((ring->coalesce_count % pdata->tx_frames) < - packet->tx_packets) + else if ((ring->coalesce_count % pdata->tx_frames) < tx_packets) tx_set_ic = 1; else tx_set_ic = 0; @@ -1741,7 +1744,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TCPHDRLEN, packet->tcp_header_len / 4); - pdata->ext_stats.tx_tso_packets += packet->tx_packets; + pdata->ext_stats.tx_tso_packets += tx_packets; } else { /* Enable CRC and Pad Insertion */ XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CPC, 0); @@ -1789,8 +1792,11 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 1); /* Save the Tx info to report back during cleanup */ - rdata->tx.packets = packet->tx_packets; - rdata->tx.bytes = packet->tx_bytes; + rdata->tx.packets = tx_packets; + rdata->tx.bytes = tx_bytes; + + pdata->ext_stats.txq_packets[channel->queue_index] += tx_packets; + pdata->ext_stats.txq_bytes[channel->queue_index] += tx_bytes; /* In case the Tx DMA engine is running, make sure everything * is written to the descriptor(s) before setting the OWN bit @@ -1944,6 +1950,9 @@ static int xgbe_dev_read(struct xgbe_channel *channel) FRAME, 1); } + pdata->ext_stats.rxq_packets[channel->queue_index]++; + pdata->ext_stats.rxq_bytes[channel->queue_index] += rdata->rx.len; + DBGPR("<--xgbe_dev_read: %s - descriptor=%u (cur=%d)\n", channel->name, ring->cur & (ring->rdesc_count - 1), ring->cur); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 67a2e52ad25d..f80b186779b7 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -186,6 +186,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { + struct xgbe_prv_data *pdata = netdev_priv(netdev); int i; switch (stringset) { @@ -195,6 +196,18 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) ETH_GSTRING_LEN); data += ETH_GSTRING_LEN; } + for (i = 0; i < pdata->tx_ring_count; i++) { + sprintf(data, "txq_%u_packets", i); + data += ETH_GSTRING_LEN; + sprintf(data, "txq_%u_bytes", i); + data += ETH_GSTRING_LEN; + } + for (i = 0; i < pdata->rx_ring_count; i++) { + sprintf(data, "rxq_%u_packets", i); + data += ETH_GSTRING_LEN; + sprintf(data, "rxq_%u_bytes", i); + data += ETH_GSTRING_LEN; + } break; } } @@ -211,15 +224,26 @@ static void xgbe_get_ethtool_stats(struct net_device *netdev, stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset; *data++ = *(u64 *)stat; } + for (i = 0; i < pdata->tx_ring_count; i++) { + *data++ = pdata->ext_stats.txq_packets[i]; + *data++ = pdata->ext_stats.txq_bytes[i]; + } + for (i = 0; i < pdata->rx_ring_count; i++) { + *data++ = pdata->ext_stats.rxq_packets[i]; + *data++ = pdata->ext_stats.rxq_bytes[i]; + } } static int xgbe_get_sset_count(struct net_device *netdev, int stringset) { + struct xgbe_prv_data *pdata = netdev_priv(netdev); int ret; switch (stringset) { case ETH_SS_STATS: - ret = XGBE_STATS_COUNT; + ret = XGBE_STATS_COUNT + + (pdata->tx_ring_count * 2) + + (pdata->rx_ring_count * 2); break; default: diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 58bb455bf1a1..0e93155bc2d5 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -668,6 +668,11 @@ struct xgbe_ext_stats { u64 tx_tso_packets; u64 rx_split_header_packets; u64 rx_buffer_unavailable; + + u64 txq_packets[XGBE_MAX_DMA_CHANNELS]; + u64 txq_bytes[XGBE_MAX_DMA_CHANNELS]; + u64 rxq_packets[XGBE_MAX_DMA_CHANNELS]; + u64 rxq_bytes[XGBE_MAX_DMA_CHANNELS]; }; struct xgbe_hw_if { -- cgit v1.2.3 From 85f9feb64bd5cfd6412258be15f90b517b4b40fc Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:03:55 -0500 Subject: amd-xgbe: Convert to using the new link mode settings Convert from using the old u32 supported, advertising, etc. link settings to the new link mode settings that support bit positions / settings greater than 32 bits. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 56 +++-- drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 77 +++--- drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c | 54 +++-- drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 350 +++++++++++++++------------ drivers/net/ethernet/amd/xgbe/xgbe.h | 50 +++- 5 files changed, 345 insertions(+), 242 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index f80b186779b7..cea25acd5de2 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -267,6 +267,7 @@ static int xgbe_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct ethtool_link_ksettings *lks = &pdata->phy.lks; int ret = 0; if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) { @@ -279,16 +280,21 @@ static int xgbe_set_pauseparam(struct net_device *netdev, pdata->phy.tx_pause = pause->tx_pause; pdata->phy.rx_pause = pause->rx_pause; - pdata->phy.advertising &= ~ADVERTISED_Pause; - pdata->phy.advertising &= ~ADVERTISED_Asym_Pause; + XGBE_CLR_ADV(lks, Pause); + XGBE_CLR_ADV(lks, Asym_Pause); if (pause->rx_pause) { - pdata->phy.advertising |= ADVERTISED_Pause; - pdata->phy.advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_ADV(lks, Pause); + XGBE_SET_ADV(lks, Asym_Pause); } - if (pause->tx_pause) - pdata->phy.advertising ^= ADVERTISED_Asym_Pause; + if (pause->tx_pause) { + /* Equivalent to XOR of Asym_Pause */ + if (XGBE_ADV(lks, Asym_Pause)) + XGBE_CLR_ADV(lks, Asym_Pause); + else + XGBE_SET_ADV(lks, Asym_Pause); + } if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); @@ -300,22 +306,20 @@ static int xgbe_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) { struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct ethtool_link_ksettings *lks = &pdata->phy.lks; cmd->base.phy_address = pdata->phy.address; - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, - pdata->phy.supported); - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, - pdata->phy.advertising); - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising, - pdata->phy.lp_advertising); - cmd->base.autoneg = pdata->phy.autoneg; cmd->base.speed = pdata->phy.speed; cmd->base.duplex = pdata->phy.duplex; cmd->base.port = PORT_NONE; + XGBE_LM_COPY(cmd, supported, lks, supported); + XGBE_LM_COPY(cmd, advertising, lks, advertising); + XGBE_LM_COPY(cmd, lp_advertising, lks, lp_advertising); + return 0; } @@ -323,7 +327,8 @@ static int xgbe_set_link_ksettings(struct net_device *netdev, const struct ethtool_link_ksettings *cmd) { struct xgbe_prv_data *pdata = netdev_priv(netdev); - u32 advertising; + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); u32 speed; int ret; @@ -355,15 +360,17 @@ static int xgbe_set_link_ksettings(struct net_device *netdev, } } - ethtool_convert_link_mode_to_legacy_u32(&advertising, - cmd->link_modes.advertising); - netif_dbg(pdata, link, netdev, - "requested advertisement %#x, phy supported %#x\n", - advertising, pdata->phy.supported); + "requested advertisement 0x%*pb, phy supported 0x%*pb\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, cmd->link_modes.advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS, lks->link_modes.supported); + + bitmap_and(advertising, + cmd->link_modes.advertising, lks->link_modes.supported, + __ETHTOOL_LINK_MODE_MASK_NBITS); - advertising &= pdata->phy.supported; - if ((cmd->base.autoneg == AUTONEG_ENABLE) && !advertising) { + if ((cmd->base.autoneg == AUTONEG_ENABLE) && + bitmap_empty(advertising, __ETHTOOL_LINK_MODE_MASK_NBITS)) { netdev_err(netdev, "unsupported requested advertisement\n"); return -EINVAL; @@ -373,12 +380,13 @@ static int xgbe_set_link_ksettings(struct net_device *netdev, pdata->phy.autoneg = cmd->base.autoneg; pdata->phy.speed = speed; pdata->phy.duplex = cmd->base.duplex; - pdata->phy.advertising = advertising; + bitmap_copy(lks->link_modes.advertising, advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS); if (cmd->base.autoneg == AUTONEG_ENABLE) - pdata->phy.advertising |= ADVERTISED_Autoneg; + XGBE_SET_ADV(lks, Autoneg); else - pdata->phy.advertising &= ~ADVERTISED_Autoneg; + XGBE_CLR_ADV(lks, Autoneg); if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 24092020f119..072b9f664597 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -615,12 +615,14 @@ static enum xgbe_an xgbe_an73_page_received(struct xgbe_prv_data *pdata) static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + /* Be sure we aren't looping trying to negotiate */ if (xgbe_in_kr_mode(pdata)) { pdata->kr_state = XGBE_RX_ERROR; - if (!(pdata->phy.advertising & ADVERTISED_1000baseKX_Full) && - !(pdata->phy.advertising & ADVERTISED_2500baseX_Full)) + if (!XGBE_ADV(lks, 1000baseKX_Full) && + !XGBE_ADV(lks, 2500baseX_Full)) return XGBE_AN_NO_LINK; if (pdata->kx_state != XGBE_RX_BPA) @@ -628,7 +630,7 @@ static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata) } else { pdata->kx_state = XGBE_RX_ERROR; - if (!(pdata->phy.advertising & ADVERTISED_10000baseKR_Full)) + if (!XGBE_ADV(lks, 10000baseKR_Full)) return XGBE_AN_NO_LINK; if (pdata->kr_state != XGBE_RX_BPA) @@ -944,18 +946,19 @@ static void xgbe_an_state_machine(struct work_struct *work) static void xgbe_an37_init(struct xgbe_prv_data *pdata) { - unsigned int advertising, reg; + struct ethtool_link_ksettings lks; + unsigned int reg; - advertising = pdata->phy_if.phy_impl.an_advertising(pdata); + pdata->phy_if.phy_impl.an_advertising(pdata, &lks); /* Set up Advertisement register */ reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); - if (advertising & ADVERTISED_Pause) + if (XGBE_ADV(&lks, Pause)) reg |= 0x100; else reg &= ~0x100; - if (advertising & ADVERTISED_Asym_Pause) + if (XGBE_ADV(&lks, Asym_Pause)) reg |= 0x80; else reg &= ~0x80; @@ -992,13 +995,14 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata) static void xgbe_an73_init(struct xgbe_prv_data *pdata) { - unsigned int advertising, reg; + struct ethtool_link_ksettings lks; + unsigned int reg; - advertising = pdata->phy_if.phy_impl.an_advertising(pdata); + pdata->phy_if.phy_impl.an_advertising(pdata, &lks); /* Set up Advertisement register 3 first */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); - if (advertising & ADVERTISED_10000baseR_FEC) + if (XGBE_ADV(&lks, 10000baseR_FEC)) reg |= 0xc000; else reg &= ~0xc000; @@ -1007,13 +1011,13 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) /* Set up Advertisement register 2 next */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); - if (advertising & ADVERTISED_10000baseKR_Full) + if (XGBE_ADV(&lks, 10000baseKR_Full)) reg |= 0x80; else reg &= ~0x80; - if ((advertising & ADVERTISED_1000baseKX_Full) || - (advertising & ADVERTISED_2500baseX_Full)) + if (XGBE_ADV(&lks, 1000baseKX_Full) || + XGBE_ADV(&lks, 2500baseX_Full)) reg |= 0x20; else reg &= ~0x20; @@ -1022,12 +1026,12 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) /* Set up Advertisement register 1 last */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - if (advertising & ADVERTISED_Pause) + if (XGBE_ADV(&lks, Pause)) reg |= 0x400; else reg &= ~0x400; - if (advertising & ADVERTISED_Asym_Pause) + if (XGBE_ADV(&lks, Asym_Pause)) reg |= 0x800; else reg &= ~0x800; @@ -1283,9 +1287,10 @@ static enum xgbe_mode xgbe_phy_status_aneg(struct xgbe_prv_data *pdata) static void xgbe_phy_status_result(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; enum xgbe_mode mode; - pdata->phy.lp_advertising = 0; + XGBE_ZERO_LP_ADV(lks); if ((pdata->phy.autoneg != AUTONEG_ENABLE) || pdata->parallel_detect) mode = xgbe_cur_mode(pdata); @@ -1515,17 +1520,21 @@ static void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) static int xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata) { - if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + if (XGBE_ADV(lks, 10000baseKR_Full)) return SPEED_10000; - else if (pdata->phy.advertising & ADVERTISED_10000baseT_Full) + else if (XGBE_ADV(lks, 10000baseT_Full)) return SPEED_10000; - else if (pdata->phy.advertising & ADVERTISED_2500baseX_Full) + else if (XGBE_ADV(lks, 2500baseX_Full)) return SPEED_2500; - else if (pdata->phy.advertising & ADVERTISED_1000baseKX_Full) + else if (XGBE_ADV(lks, 2500baseT_Full)) + return SPEED_2500; + else if (XGBE_ADV(lks, 1000baseKX_Full)) return SPEED_1000; - else if (pdata->phy.advertising & ADVERTISED_1000baseT_Full) + else if (XGBE_ADV(lks, 1000baseT_Full)) return SPEED_1000; - else if (pdata->phy.advertising & ADVERTISED_100baseT_Full) + else if (XGBE_ADV(lks, 100baseT_Full)) return SPEED_100; return SPEED_UNKNOWN; @@ -1538,6 +1547,7 @@ static void xgbe_phy_exit(struct xgbe_prv_data *pdata) static int xgbe_phy_init(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; int ret; mutex_init(&pdata->an_mutex); @@ -1555,11 +1565,13 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) ret = pdata->phy_if.phy_impl.init(pdata); if (ret) return ret; - pdata->phy.advertising = pdata->phy.supported; + + /* Copy supported link modes to advertising link modes */ + XGBE_LM_COPY(lks, advertising, lks, supported); pdata->phy.address = 0; - if (pdata->phy.advertising & ADVERTISED_Autoneg) { + if (XGBE_ADV(lks, Autoneg)) { pdata->phy.autoneg = AUTONEG_ENABLE; pdata->phy.speed = SPEED_UNKNOWN; pdata->phy.duplex = DUPLEX_UNKNOWN; @@ -1576,16 +1588,21 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) pdata->phy.rx_pause = pdata->rx_pause; /* Fix up Flow Control advertising */ - pdata->phy.advertising &= ~ADVERTISED_Pause; - pdata->phy.advertising &= ~ADVERTISED_Asym_Pause; + XGBE_CLR_ADV(lks, Pause); + XGBE_CLR_ADV(lks, Asym_Pause); if (pdata->rx_pause) { - pdata->phy.advertising |= ADVERTISED_Pause; - pdata->phy.advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_ADV(lks, Pause); + XGBE_SET_ADV(lks, Asym_Pause); } - if (pdata->tx_pause) - pdata->phy.advertising ^= ADVERTISED_Asym_Pause; + if (pdata->tx_pause) { + /* Equivalent to XOR of Asym_Pause */ + if (XGBE_ADV(lks, Asym_Pause)) + XGBE_CLR_ADV(lks, Asym_Pause); + else + XGBE_SET_ADV(lks, Asym_Pause); + } if (netif_msg_drv(pdata)) xgbe_dump_phy_registers(pdata); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c index c75edcac5e0a..d16eae415f72 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c @@ -231,20 +231,21 @@ static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata) static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; enum xgbe_mode mode; unsigned int ad_reg, lp_reg; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_Backplane; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, Backplane); /* Compare Advertisement and Link Partner register 1 */ ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); if (lp_reg & 0x400) - pdata->phy.lp_advertising |= ADVERTISED_Pause; + XGBE_SET_LP_ADV(lks, Pause); if (lp_reg & 0x800) - pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_LP_ADV(lks, Asym_Pause); if (pdata->phy.pause_autoneg) { /* Set flow control based on auto-negotiation result */ @@ -266,12 +267,12 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); if (lp_reg & 0x80) - pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_LP_ADV(lks, 10000baseKR_Full); if (lp_reg & 0x20) { if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) - pdata->phy.lp_advertising |= ADVERTISED_2500baseX_Full; + XGBE_SET_LP_ADV(lks, 2500baseX_Full); else - pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_LP_ADV(lks, 1000baseKX_Full); } ad_reg &= lp_reg; @@ -290,14 +291,17 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); if (lp_reg & 0xc000) - pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + XGBE_SET_LP_ADV(lks, 10000baseR_FEC); return mode; } -static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata) +static void xgbe_phy_an_advertising(struct xgbe_prv_data *pdata, + struct ethtool_link_ksettings *dlks) { - return pdata->phy.advertising; + struct ethtool_link_ksettings *slks = &pdata->phy.lks; + + XGBE_LM_COPY(dlks, advertising, slks, advertising); } static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) @@ -565,11 +569,10 @@ static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) } static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, - enum xgbe_mode mode, u32 advert) + enum xgbe_mode mode, bool advert) { if (pdata->phy.autoneg == AUTONEG_ENABLE) { - if (pdata->phy.advertising & advert) - return true; + return advert; } else { enum xgbe_mode cur_mode; @@ -583,16 +586,18 @@ static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_KX_1000: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseKX_Full); + XGBE_ADV(lks, 1000baseKX_Full)); case XGBE_MODE_KX_2500: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_2500baseX_Full); + XGBE_ADV(lks, 2500baseX_Full)); case XGBE_MODE_KR: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseKR_Full); + XGBE_ADV(lks, 10000baseKR_Full)); default: return false; } @@ -672,6 +677,7 @@ static void xgbe_phy_exit(struct xgbe_prv_data *pdata) static int xgbe_phy_init(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data; int ret; @@ -790,21 +796,23 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) } /* Initialize supported features */ - pdata->phy.supported = SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_Backplane; - pdata->phy.supported |= SUPPORTED_10000baseKR_Full; + XGBE_ZERO_SUP(lks); + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); + XGBE_SET_SUP(lks, 10000baseKR_Full); switch (phy_data->speed_set) { case XGBE_SPEEDSET_1000_10000: - pdata->phy.supported |= SUPPORTED_1000baseKX_Full; + XGBE_SET_SUP(lks, 1000baseKX_Full); break; case XGBE_SPEEDSET_2500_10000: - pdata->phy.supported |= SUPPORTED_2500baseX_Full; + XGBE_SET_SUP(lks, 2500baseX_Full); break; } if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) - pdata->phy.supported |= SUPPORTED_10000baseR_FEC; + XGBE_SET_SUP(lks, 10000baseR_FEC); pdata->phy_data = phy_data; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 81c45fa65e0a..3304a291aa96 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -709,18 +709,13 @@ static int xgbe_phy_mii_read(struct mii_bus *mii, int addr, int reg) static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; if (!phy_data->sfp_mod_absent && !phy_data->sfp_changed) return; - pdata->phy.supported &= ~SUPPORTED_Autoneg; - pdata->phy.supported &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause); - pdata->phy.supported &= ~SUPPORTED_TP; - pdata->phy.supported &= ~SUPPORTED_FIBRE; - pdata->phy.supported &= ~SUPPORTED_100baseT_Full; - pdata->phy.supported &= ~SUPPORTED_1000baseT_Full; - pdata->phy.supported &= ~SUPPORTED_10000baseT_Full; + XGBE_ZERO_SUP(lks); if (phy_data->sfp_mod_absent) { pdata->phy.speed = SPEED_UNKNOWN; @@ -728,18 +723,13 @@ static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) pdata->phy.autoneg = AUTONEG_ENABLE; pdata->phy.pause_autoneg = AUTONEG_ENABLE; - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; - pdata->phy.supported |= SUPPORTED_FIBRE; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) - pdata->phy.supported |= SUPPORTED_100baseT_Full; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) - pdata->phy.supported |= SUPPORTED_1000baseT_Full; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) - pdata->phy.supported |= SUPPORTED_10000baseT_Full; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); + XGBE_SET_SUP(lks, FIBRE); - pdata->phy.advertising = pdata->phy.supported; + XGBE_LM_COPY(lks, advertising, lks, supported); return; } @@ -753,8 +743,18 @@ static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) pdata->phy.duplex = DUPLEX_UNKNOWN; pdata->phy.autoneg = AUTONEG_ENABLE; pdata->phy.pause_autoneg = AUTONEG_ENABLE; - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + if (phy_data->sfp_base == XGBE_SFP_BASE_1000_T) { + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) + XGBE_SET_SUP(lks, 100baseT_Full); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + XGBE_SET_SUP(lks, 1000baseT_Full); + } else { + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + XGBE_SET_SUP(lks, 1000baseX_Full); + } break; case XGBE_SFP_BASE_10000_SR: case XGBE_SFP_BASE_10000_LR: @@ -765,6 +765,27 @@ static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) pdata->phy.duplex = DUPLEX_FULL; pdata->phy.autoneg = AUTONEG_DISABLE; pdata->phy.pause_autoneg = AUTONEG_DISABLE; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_10000_SR: + XGBE_SET_SUP(lks, 10000baseSR_Full); + break; + case XGBE_SFP_BASE_10000_LR: + XGBE_SET_SUP(lks, 10000baseLR_Full); + break; + case XGBE_SFP_BASE_10000_LRM: + XGBE_SET_SUP(lks, 10000baseLRM_Full); + break; + case XGBE_SFP_BASE_10000_ER: + XGBE_SET_SUP(lks, 10000baseER_Full); + break; + case XGBE_SFP_BASE_10000_CR: + XGBE_SET_SUP(lks, 10000baseCR_Full); + break; + default: + break; + } + } break; default: pdata->phy.speed = SPEED_UNKNOWN; @@ -778,38 +799,14 @@ static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) case XGBE_SFP_BASE_1000_T: case XGBE_SFP_BASE_1000_CX: case XGBE_SFP_BASE_10000_CR: - pdata->phy.supported |= SUPPORTED_TP; + XGBE_SET_SUP(lks, TP); break; default: - pdata->phy.supported |= SUPPORTED_FIBRE; - } - - switch (phy_data->sfp_speed) { - case XGBE_SFP_SPEED_100_1000: - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) - pdata->phy.supported |= SUPPORTED_100baseT_Full; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) - pdata->phy.supported |= SUPPORTED_1000baseT_Full; - break; - case XGBE_SFP_SPEED_1000: - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, FIBRE); break; - case XGBE_SFP_SPEED_10000: - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) - pdata->phy.supported |= SUPPORTED_10000baseT_Full; - break; - default: - /* Choose the fastest supported speed */ - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) - pdata->phy.supported |= SUPPORTED_10000baseT_Full; - else if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) - pdata->phy.supported |= SUPPORTED_1000baseT_Full; - else if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) - pdata->phy.supported |= SUPPORTED_100baseT_Full; } - pdata->phy.advertising = pdata->phy.supported; + XGBE_LM_COPY(lks, advertising, lks, supported); } static bool xgbe_phy_sfp_bit_rate(struct xgbe_sfp_eeprom *sfp_eeprom, @@ -886,8 +883,10 @@ static void xgbe_phy_external_phy_quirks(struct xgbe_prv_data *pdata) static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; struct phy_device *phydev; + u32 advertising; int ret; /* If we already have a PHY, just return */ @@ -943,7 +942,10 @@ static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata) phy_data->phydev = phydev; xgbe_phy_external_phy_quirks(pdata); - phydev->advertising &= pdata->phy.advertising; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + lks->link_modes.advertising); + phydev->advertising &= advertising; phy_start_aneg(phy_data->phydev); @@ -1277,6 +1279,7 @@ put: static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; u16 lcl_adv = 0, rmt_adv = 0; u8 fc; @@ -1293,11 +1296,11 @@ static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata) lcl_adv |= ADVERTISE_PAUSE_ASYM; if (phy_data->phydev->pause) { - pdata->phy.lp_advertising |= ADVERTISED_Pause; + XGBE_SET_LP_ADV(lks, Pause); rmt_adv |= LPA_PAUSE_CAP; } if (phy_data->phydev->asym_pause) { - pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_LP_ADV(lks, Asym_Pause); rmt_adv |= LPA_PAUSE_ASYM; } @@ -1310,10 +1313,11 @@ static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata) static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; enum xgbe_mode mode; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_TP; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, TP); /* Use external PHY to determine flow control */ if (pdata->phy.pause_autoneg) @@ -1322,21 +1326,21 @@ static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) switch (pdata->an_status & XGBE_SGMII_AN_LINK_SPEED) { case XGBE_SGMII_AN_LINK_SPEED_100: if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) { - pdata->phy.lp_advertising |= ADVERTISED_100baseT_Full; + XGBE_SET_LP_ADV(lks, 100baseT_Full); mode = XGBE_MODE_SGMII_100; } else { /* Half-duplex not supported */ - pdata->phy.lp_advertising |= ADVERTISED_100baseT_Half; + XGBE_SET_LP_ADV(lks, 100baseT_Half); mode = XGBE_MODE_UNKNOWN; } break; case XGBE_SGMII_AN_LINK_SPEED_1000: if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) { - pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Full; + XGBE_SET_LP_ADV(lks, 1000baseT_Full); mode = XGBE_MODE_SGMII_1000; } else { /* Half-duplex not supported */ - pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Half; + XGBE_SET_LP_ADV(lks, 1000baseT_Half); mode = XGBE_MODE_UNKNOWN; } break; @@ -1349,19 +1353,20 @@ static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; enum xgbe_mode mode; unsigned int ad_reg, lp_reg; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_FIBRE; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, FIBRE); /* Compare Advertisement and Link Partner register */ ad_reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); lp_reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_LP_ABILITY); if (lp_reg & 0x100) - pdata->phy.lp_advertising |= ADVERTISED_Pause; + XGBE_SET_LP_ADV(lks, Pause); if (lp_reg & 0x80) - pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_LP_ADV(lks, Asym_Pause); if (pdata->phy.pause_autoneg) { /* Set flow control based on auto-negotiation result */ @@ -1379,10 +1384,8 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) } } - if (lp_reg & 0x40) - pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Half; if (lp_reg & 0x20) - pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Full; + XGBE_SET_LP_ADV(lks, 1000baseX_Full); /* Half duplex is not supported */ ad_reg &= lp_reg; @@ -1393,12 +1396,13 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; enum xgbe_mode mode; unsigned int ad_reg, lp_reg; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_Backplane; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, Backplane); /* Use external PHY to determine flow control */ if (pdata->phy.pause_autoneg) @@ -1408,9 +1412,9 @@ static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); if (lp_reg & 0x80) - pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_LP_ADV(lks, 10000baseKR_Full); if (lp_reg & 0x20) - pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_LP_ADV(lks, 1000baseKX_Full); ad_reg &= lp_reg; if (ad_reg & 0x80) { @@ -1463,26 +1467,27 @@ static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); if (lp_reg & 0xc000) - pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + XGBE_SET_LP_ADV(lks, 10000baseR_FEC); return mode; } static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; enum xgbe_mode mode; unsigned int ad_reg, lp_reg; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_Backplane; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, Backplane); /* Compare Advertisement and Link Partner register 1 */ ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); if (lp_reg & 0x400) - pdata->phy.lp_advertising |= ADVERTISED_Pause; + XGBE_SET_LP_ADV(lks, Pause); if (lp_reg & 0x800) - pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_LP_ADV(lks, Asym_Pause); if (pdata->phy.pause_autoneg) { /* Set flow control based on auto-negotiation result */ @@ -1504,9 +1509,9 @@ static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); if (lp_reg & 0x80) - pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_LP_ADV(lks, 10000baseKR_Full); if (lp_reg & 0x20) - pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_LP_ADV(lks, 1000baseKX_Full); ad_reg &= lp_reg; if (ad_reg & 0x80) @@ -1520,7 +1525,7 @@ static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); if (lp_reg & 0xc000) - pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + XGBE_SET_LP_ADV(lks, 10000baseR_FEC); return mode; } @@ -1541,41 +1546,43 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) } } -static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata) +static void xgbe_phy_an_advertising(struct xgbe_prv_data *pdata, + struct ethtool_link_ksettings *dlks) { + struct ethtool_link_ksettings *slks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; - unsigned int advertising; + + XGBE_LM_COPY(dlks, advertising, slks, advertising); /* Without a re-driver, just return current advertising */ if (!phy_data->redrv) - return pdata->phy.advertising; + return; /* With the KR re-driver we need to advertise a single speed */ - advertising = pdata->phy.advertising; - advertising &= ~ADVERTISED_1000baseKX_Full; - advertising &= ~ADVERTISED_10000baseKR_Full; + XGBE_CLR_ADV(dlks, 1000baseKX_Full); + XGBE_CLR_ADV(dlks, 10000baseKR_Full); switch (phy_data->port_mode) { case XGBE_PORT_MODE_BACKPLANE: - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); break; case XGBE_PORT_MODE_BACKPLANE_2500: - advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_ADV(dlks, 1000baseKX_Full); break; case XGBE_PORT_MODE_1000BASE_T: case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_NBASE_T: - advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_ADV(dlks, 1000baseKX_Full); break; case XGBE_PORT_MODE_10GBASE_T: if (phy_data->phydev && (phy_data->phydev->speed == SPEED_10000)) - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); else - advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_ADV(dlks, 1000baseKX_Full); break; case XGBE_PORT_MODE_10GBASE_R: - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); break; case XGBE_PORT_MODE_SFP: switch (phy_data->sfp_base) { @@ -1583,24 +1590,24 @@ static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata) case XGBE_SFP_BASE_1000_SX: case XGBE_SFP_BASE_1000_LX: case XGBE_SFP_BASE_1000_CX: - advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_ADV(dlks, 1000baseKX_Full); break; default: - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); break; } break; default: - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); break; } - - return advertising; } static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; + u32 advertising; int ret; ret = xgbe_phy_find_phy_device(pdata); @@ -1610,9 +1617,12 @@ static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) if (!phy_data->phydev) return 0; + ethtool_convert_link_mode_to_legacy_u32(&advertising, + lks->link_modes.advertising); + phy_data->phydev->autoneg = pdata->phy.autoneg; phy_data->phydev->advertising = phy_data->phydev->supported & - pdata->phy.advertising; + advertising; if (pdata->phy.autoneg != AUTONEG_ENABLE) { phy_data->phydev->speed = pdata->phy.speed; @@ -2073,11 +2083,10 @@ static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) } static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, - enum xgbe_mode mode, u32 advert) + enum xgbe_mode mode, bool advert) { if (pdata->phy.autoneg == AUTONEG_ENABLE) { - if (pdata->phy.advertising & advert) - return true; + return advert; } else { enum xgbe_mode cur_mode; @@ -2092,13 +2101,15 @@ static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_basex_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_X: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseT_Full); + XGBE_ADV(lks, 1000baseX_Full)); case XGBE_MODE_KR: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseT_Full); + XGBE_ADV(lks, 10000baseKR_Full)); default: return false; } @@ -2107,19 +2118,21 @@ static bool xgbe_phy_use_basex_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_baset_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_SGMII_100: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_100baseT_Full); + XGBE_ADV(lks, 100baseT_Full)); case XGBE_MODE_SGMII_1000: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseT_Full); + XGBE_ADV(lks, 1000baseT_Full)); case XGBE_MODE_KX_2500: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_2500baseX_Full); + XGBE_ADV(lks, 2500baseT_Full)); case XGBE_MODE_KR: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseT_Full); + XGBE_ADV(lks, 10000baseT_Full)); default: return false; } @@ -2128,6 +2141,7 @@ static bool xgbe_phy_use_baset_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; switch (mode) { @@ -2135,22 +2149,26 @@ static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata, if (phy_data->sfp_base == XGBE_SFP_BASE_1000_T) return false; return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseT_Full); + XGBE_ADV(lks, 1000baseX_Full)); case XGBE_MODE_SGMII_100: if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) return false; return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_100baseT_Full); + XGBE_ADV(lks, 100baseT_Full)); case XGBE_MODE_SGMII_1000: if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) return false; return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseT_Full); + XGBE_ADV(lks, 1000baseT_Full)); case XGBE_MODE_SFI: if (phy_data->sfp_mod_absent) return true; return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseT_Full); + XGBE_ADV(lks, 10000baseSR_Full) || + XGBE_ADV(lks, 10000baseLR_Full) || + XGBE_ADV(lks, 10000baseLRM_Full) || + XGBE_ADV(lks, 10000baseER_Full) || + XGBE_ADV(lks, 10000baseCR_Full)); default: return false; } @@ -2159,10 +2177,12 @@ static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_bp_2500_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_KX_2500: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_2500baseX_Full); + XGBE_ADV(lks, 2500baseX_Full)); default: return false; } @@ -2171,13 +2191,15 @@ static bool xgbe_phy_use_bp_2500_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_bp_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_KX_1000: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseKX_Full); + XGBE_ADV(lks, 1000baseKX_Full)); case XGBE_MODE_KR: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseKR_Full); + XGBE_ADV(lks, 10000baseKR_Full)); default: return false; } @@ -2744,6 +2766,7 @@ static void xgbe_phy_exit(struct xgbe_prv_data *pdata) static int xgbe_phy_init(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data; struct mii_bus *mii; unsigned int reg; @@ -2823,32 +2846,33 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) phy_data->cur_mode = XGBE_MODE_UNKNOWN; /* Initialize supported features */ - pdata->phy.supported = 0; + XGBE_ZERO_SUP(lks); switch (phy_data->port_mode) { /* Backplane support */ case XGBE_PORT_MODE_BACKPLANE: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_Backplane; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseKX_Full; + XGBE_SET_SUP(lks, 1000baseKX_Full); phy_data->start_mode = XGBE_MODE_KX_1000; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { - pdata->phy.supported |= SUPPORTED_10000baseKR_Full; + XGBE_SET_SUP(lks, 10000baseKR_Full); if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) - pdata->phy.supported |= - SUPPORTED_10000baseR_FEC; + XGBE_SET_SUP(lks, 10000baseR_FEC); phy_data->start_mode = XGBE_MODE_KR; } phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; break; case XGBE_PORT_MODE_BACKPLANE_2500: - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_Backplane; - pdata->phy.supported |= SUPPORTED_2500baseX_Full; + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); + XGBE_SET_SUP(lks, 2500baseX_Full); phy_data->start_mode = XGBE_MODE_KX_2500; phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; @@ -2856,15 +2880,16 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* MDIO 1GBase-T support */ case XGBE_PORT_MODE_1000BASE_T: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { - pdata->phy.supported |= SUPPORTED_100baseT_Full; + XGBE_SET_SUP(lks, 100baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_100; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, 1000baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_1000; } @@ -2873,10 +2898,11 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* MDIO Base-X support */ case XGBE_PORT_MODE_1000BASE_X: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_FIBRE; - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, FIBRE); + XGBE_SET_SUP(lks, 1000baseX_Full); phy_data->start_mode = XGBE_MODE_X; phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; @@ -2884,19 +2910,20 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* MDIO NBase-T support */ case XGBE_PORT_MODE_NBASE_T: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { - pdata->phy.supported |= SUPPORTED_100baseT_Full; + XGBE_SET_SUP(lks, 100baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_100; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, 1000baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_1000; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500) { - pdata->phy.supported |= SUPPORTED_2500baseX_Full; + XGBE_SET_SUP(lks, 2500baseT_Full); phy_data->start_mode = XGBE_MODE_KX_2500; } @@ -2905,19 +2932,20 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* 10GBase-T support */ case XGBE_PORT_MODE_10GBASE_T: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { - pdata->phy.supported |= SUPPORTED_100baseT_Full; + XGBE_SET_SUP(lks, 100baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_100; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, 1000baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_1000; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { - pdata->phy.supported |= SUPPORTED_10000baseT_Full; + XGBE_SET_SUP(lks, 10000baseT_Full); phy_data->start_mode = XGBE_MODE_KR; } @@ -2926,12 +2954,16 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* 10GBase-R support */ case XGBE_PORT_MODE_10GBASE_R: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; - pdata->phy.supported |= SUPPORTED_10000baseT_Full; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, FIBRE); + XGBE_SET_SUP(lks, 10000baseSR_Full); + XGBE_SET_SUP(lks, 10000baseLR_Full); + XGBE_SET_SUP(lks, 10000baseLRM_Full); + XGBE_SET_SUP(lks, 10000baseER_Full); if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) - pdata->phy.supported |= SUPPORTED_10000baseR_FEC; + XGBE_SET_SUP(lks, 10000baseR_FEC); phy_data->start_mode = XGBE_MODE_SFI; phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; @@ -2939,22 +2971,17 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* SFP support */ case XGBE_PORT_MODE_SFP: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; - pdata->phy.supported |= SUPPORTED_FIBRE; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { - pdata->phy.supported |= SUPPORTED_100baseT_Full; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); + XGBE_SET_SUP(lks, FIBRE); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) phy_data->start_mode = XGBE_MODE_SGMII_100; - } - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) phy_data->start_mode = XGBE_MODE_SGMII_1000; - } - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { - pdata->phy.supported |= SUPPORTED_10000baseT_Full; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) phy_data->start_mode = XGBE_MODE_SFI; - } phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; @@ -2965,8 +2992,9 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) } if (netif_msg_probe(pdata)) - dev_dbg(pdata->dev, "phy supported=%#x\n", - pdata->phy.supported); + dev_dbg(pdata->dev, "phy supported=0x%*pb\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, + lks->link_modes.supported); if ((phy_data->conn_type & XGBE_CONN_TYPE_MDIO) && (phy_data->phydev_mode != XGBE_MDIO_MODE_NONE)) { diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 0e93155bc2d5..48a46a70bf5e 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -131,6 +131,7 @@ #include #include #include +#include #define XGBE_DRV_NAME "amd-xgbe" #define XGBE_DRV_VERSION "1.0.3" @@ -296,6 +297,48 @@ /* MDIO port types */ #define XGMAC_MAX_C22_PORT 3 +/* Link mode bit operations */ +#define XGBE_ZERO_SUP(_ls) \ + ethtool_link_ksettings_zero_link_mode((_ls), supported) + +#define XGBE_SET_SUP(_ls, _mode) \ + ethtool_link_ksettings_add_link_mode((_ls), supported, _mode) + +#define XGBE_CLR_SUP(_ls, _mode) \ + ethtool_link_ksettings_del_link_mode((_ls), supported, _mode) + +#define XGBE_IS_SUP(_ls, _mode) \ + ethtool_link_ksettings_test_link_mode((_ls), supported, _mode) + +#define XGBE_ZERO_ADV(_ls) \ + ethtool_link_ksettings_zero_link_mode((_ls), advertising) + +#define XGBE_SET_ADV(_ls, _mode) \ + ethtool_link_ksettings_add_link_mode((_ls), advertising, _mode) + +#define XGBE_CLR_ADV(_ls, _mode) \ + ethtool_link_ksettings_del_link_mode((_ls), advertising, _mode) + +#define XGBE_ADV(_ls, _mode) \ + ethtool_link_ksettings_test_link_mode((_ls), advertising, _mode) + +#define XGBE_ZERO_LP_ADV(_ls) \ + ethtool_link_ksettings_zero_link_mode((_ls), lp_advertising) + +#define XGBE_SET_LP_ADV(_ls, _mode) \ + ethtool_link_ksettings_add_link_mode((_ls), lp_advertising, _mode) + +#define XGBE_CLR_LP_ADV(_ls, _mode) \ + ethtool_link_ksettings_del_link_mode((_ls), lp_advertising, _mode) + +#define XGBE_LP_ADV(_ls, _mode) \ + ethtool_link_ksettings_test_link_mode((_ls), lp_advertising, _mode) + +#define XGBE_LM_COPY(_dst, _dname, _src, _sname) \ + bitmap_copy((_dst)->link_modes._dname, \ + (_src)->link_modes._sname, \ + __ETHTOOL_LINK_MODE_MASK_NBITS) + struct xgbe_prv_data; struct xgbe_packet_data { @@ -563,9 +606,7 @@ enum xgbe_mdio_mode { }; struct xgbe_phy { - u32 supported; - u32 advertising; - u32 lp_advertising; + struct ethtool_link_ksettings lks; int address; @@ -817,7 +858,8 @@ struct xgbe_phy_impl_if { int (*an_config)(struct xgbe_prv_data *); /* Set/override auto-negotiation advertisement settings */ - unsigned int (*an_advertising)(struct xgbe_prv_data *); + void (*an_advertising)(struct xgbe_prv_data *, + struct ethtool_link_ksettings *); /* Process results of auto-negotiation */ enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); -- cgit v1.2.3 From 1a510ccf5869a95c0ff324c61518e42e95712844 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:04:04 -0500 Subject: amd-xgbe: Add support for VXLAN offload capabilities The hardware has the capability to perform checksum offload support (both Tx and Rx) and TSO support for VXLAN packets. Add the support required to enable this. The hardware can only support a single VXLAN port for offload. If more than one VXLAN port is added then the offload capabilities have to be disabled and can no longer be advertised. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 24 ++ drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 92 ++++++- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 365 +++++++++++++++++++++++++++- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 23 ++ drivers/net/ethernet/amd/xgbe/xgbe.h | 22 ++ 5 files changed, 520 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index d07edf9eaa69..943133034879 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -298,6 +298,7 @@ #define MAC_RWKPFR 0x00c4 #define MAC_LPICSR 0x00d0 #define MAC_LPITCR 0x00d4 +#define MAC_TIR 0x00e0 #define MAC_VR 0x0110 #define MAC_DR 0x0114 #define MAC_HWF0R 0x011c @@ -364,6 +365,8 @@ #define MAC_HWF0R_TXCOESEL_WIDTH 1 #define MAC_HWF0R_VLHASH_INDEX 4 #define MAC_HWF0R_VLHASH_WIDTH 1 +#define MAC_HWF0R_VXN_INDEX 29 +#define MAC_HWF0R_VXN_WIDTH 1 #define MAC_HWF1R_ADDR64_INDEX 14 #define MAC_HWF1R_ADDR64_WIDTH 2 #define MAC_HWF1R_ADVTHWORD_INDEX 13 @@ -448,6 +451,8 @@ #define MAC_PFR_PR_WIDTH 1 #define MAC_PFR_VTFE_INDEX 16 #define MAC_PFR_VTFE_WIDTH 1 +#define MAC_PFR_VUCC_INDEX 22 +#define MAC_PFR_VUCC_WIDTH 1 #define MAC_PMTCSR_MGKPKTEN_INDEX 1 #define MAC_PMTCSR_MGKPKTEN_WIDTH 1 #define MAC_PMTCSR_PWRDWN_INDEX 0 @@ -510,6 +515,12 @@ #define MAC_TCR_SS_WIDTH 2 #define MAC_TCR_TE_INDEX 0 #define MAC_TCR_TE_WIDTH 1 +#define MAC_TCR_VNE_INDEX 24 +#define MAC_TCR_VNE_WIDTH 1 +#define MAC_TCR_VNM_INDEX 25 +#define MAC_TCR_VNM_WIDTH 1 +#define MAC_TIR_TNID_INDEX 0 +#define MAC_TIR_TNID_WIDTH 16 #define MAC_TSCR_AV8021ASMEN_INDEX 28 #define MAC_TSCR_AV8021ASMEN_WIDTH 1 #define MAC_TSCR_SNAPTYPSEL_INDEX 16 @@ -1153,11 +1164,17 @@ #define RX_PACKET_ATTRIBUTES_RSS_HASH_WIDTH 1 #define RX_PACKET_ATTRIBUTES_FIRST_INDEX 7 #define RX_PACKET_ATTRIBUTES_FIRST_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_TNP_INDEX 8 +#define RX_PACKET_ATTRIBUTES_TNP_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_TNPCSUM_DONE_INDEX 9 +#define RX_PACKET_ATTRIBUTES_TNPCSUM_DONE_WIDTH 1 #define RX_NORMAL_DESC0_OVT_INDEX 0 #define RX_NORMAL_DESC0_OVT_WIDTH 16 #define RX_NORMAL_DESC2_HL_INDEX 0 #define RX_NORMAL_DESC2_HL_WIDTH 10 +#define RX_NORMAL_DESC2_TNP_INDEX 11 +#define RX_NORMAL_DESC2_TNP_WIDTH 1 #define RX_NORMAL_DESC3_CDA_INDEX 27 #define RX_NORMAL_DESC3_CDA_WIDTH 1 #define RX_NORMAL_DESC3_CTXT_INDEX 30 @@ -1184,9 +1201,11 @@ #define RX_DESC3_L34T_IPV4_TCP 1 #define RX_DESC3_L34T_IPV4_UDP 2 #define RX_DESC3_L34T_IPV4_ICMP 3 +#define RX_DESC3_L34T_IPV4_UNKNOWN 7 #define RX_DESC3_L34T_IPV6_TCP 9 #define RX_DESC3_L34T_IPV6_UDP 10 #define RX_DESC3_L34T_IPV6_ICMP 11 +#define RX_DESC3_L34T_IPV6_UNKNOWN 15 #define RX_CONTEXT_DESC3_TSA_INDEX 4 #define RX_CONTEXT_DESC3_TSA_WIDTH 1 @@ -1201,6 +1220,8 @@ #define TX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH 1 #define TX_PACKET_ATTRIBUTES_PTP_INDEX 3 #define TX_PACKET_ATTRIBUTES_PTP_WIDTH 1 +#define TX_PACKET_ATTRIBUTES_VXLAN_INDEX 4 +#define TX_PACKET_ATTRIBUTES_VXLAN_WIDTH 1 #define TX_CONTEXT_DESC2_MSS_INDEX 0 #define TX_CONTEXT_DESC2_MSS_WIDTH 15 @@ -1241,8 +1262,11 @@ #define TX_NORMAL_DESC3_TCPPL_WIDTH 18 #define TX_NORMAL_DESC3_TSE_INDEX 18 #define TX_NORMAL_DESC3_TSE_WIDTH 1 +#define TX_NORMAL_DESC3_VNP_INDEX 23 +#define TX_NORMAL_DESC3_VNP_WIDTH 3 #define TX_NORMAL_DESC2_VLAN_INSERT 0x2 +#define TX_NORMAL_DESC3_VXLAN_PACKET 0x3 /* MDIO undefined or vendor specific registers */ #ifndef MDIO_PMA_10GBR_PMD_CTRL diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index a9784084202a..1bf671e02c0a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -479,6 +479,50 @@ static bool xgbe_is_pfc_queue(struct xgbe_prv_data *pdata, return false; } +static void xgbe_set_vxlan_id(struct xgbe_prv_data *pdata) +{ + /* Program the VXLAN port */ + XGMAC_IOWRITE_BITS(pdata, MAC_TIR, TNID, pdata->vxlan_port); + + netif_dbg(pdata, drv, pdata->netdev, "VXLAN tunnel id set to %hx\n", + pdata->vxlan_port); +} + +static void xgbe_enable_vxlan(struct xgbe_prv_data *pdata) +{ + if (!pdata->hw_feat.vxn) + return; + + /* Program the VXLAN port */ + xgbe_set_vxlan_id(pdata); + + /* Allow for IPv6/UDP zero-checksum VXLAN packets */ + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VUCC, 1); + + /* Enable VXLAN tunneling mode */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNM, 0); + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNE, 1); + + netif_dbg(pdata, drv, pdata->netdev, "VXLAN acceleration enabled\n"); +} + +static void xgbe_disable_vxlan(struct xgbe_prv_data *pdata) +{ + if (!pdata->hw_feat.vxn) + return; + + /* Disable tunneling mode */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNE, 0); + + /* Clear IPv6/UDP zero-checksum VXLAN packets setting */ + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VUCC, 0); + + /* Clear the VXLAN port */ + XGMAC_IOWRITE_BITS(pdata, MAC_TIR, TNID, 0); + + netif_dbg(pdata, drv, pdata->netdev, "VXLAN acceleration disabled\n"); +} + static int xgbe_disable_tx_flow_control(struct xgbe_prv_data *pdata) { unsigned int max_q_count, q_count; @@ -1610,7 +1654,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) struct xgbe_ring_desc *rdesc; struct xgbe_packet_data *packet = &ring->packet_data; unsigned int tx_packets, tx_bytes; - unsigned int csum, tso, vlan; + unsigned int csum, tso, vlan, vxlan; unsigned int tso_context, vlan_context; unsigned int tx_set_ic; int start_index = ring->cur; @@ -1628,6 +1672,8 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) TSO_ENABLE); vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, VLAN_CTAG); + vxlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + VXLAN); if (tso && (packet->mss != ring->tx.cur_mss)) tso_context = 1; @@ -1759,6 +1805,10 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) packet->length); } + if (vxlan) + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, VNP, + TX_NORMAL_DESC3_VXLAN_PACKET); + for (i = cur_index - start_index + 1; i < packet->rdesc_count; i++) { cur_index++; rdata = XGBE_GET_DESC_DATA(ring, cur_index); @@ -1920,9 +1970,27 @@ static int xgbe_dev_read(struct xgbe_channel *channel) rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL); /* Set checksum done indicator as appropriate */ - if (netdev->features & NETIF_F_RXCSUM) + if (netdev->features & NETIF_F_RXCSUM) { XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, CSUM_DONE, 1); + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 1); + } + + /* Set the tunneled packet indicator */ + if (XGMAC_GET_BITS_LE(rdesc->desc2, RX_NORMAL_DESC2, TNP)) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNP, 1); + + l34t = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, L34T); + switch (l34t) { + case RX_DESC3_L34T_IPV4_UNKNOWN: + case RX_DESC3_L34T_IPV6_UNKNOWN: + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 0); + break; + } + } /* Check for errors (only valid in last descriptor) */ err = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, ES); @@ -1942,12 +2010,23 @@ static int xgbe_dev_read(struct xgbe_channel *channel) packet->vlan_ctag); } } else { - if ((etlt == 0x05) || (etlt == 0x06)) + unsigned int tnp = XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, TNP); + + if ((etlt == 0x05) || (etlt == 0x06)) { XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, CSUM_DONE, 0); - else + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 0); + } else if (tnp && ((etlt == 0x09) || (etlt == 0x0a))) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + CSUM_DONE, 0); + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 0); + } else { XGMAC_SET_BITS(packet->errors, RX_PACKET_ERRORS, FRAME, 1); + } } pdata->ext_stats.rxq_packets[channel->queue_index]++; @@ -3536,5 +3615,10 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if) hw_if->disable_ecc_ded = xgbe_disable_ecc_ded; hw_if->disable_ecc_sec = xgbe_disable_ecc_sec; + /* For VXLAN */ + hw_if->enable_vxlan = xgbe_enable_vxlan; + hw_if->disable_vxlan = xgbe_disable_vxlan; + hw_if->set_vxlan_id = xgbe_set_vxlan_id; + DBGPR("<--xgbe_init_function_ptrs\n"); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 7498bb81f918..608693d11bd7 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -124,6 +124,7 @@ #include #include #include +#include #include "xgbe.h" #include "xgbe-common.h" @@ -756,6 +757,7 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) ADDMACADRSEL); hw_feat->ts_src = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TSSTSSEL); hw_feat->sa_vlan_ins = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, SAVLANINS); + hw_feat->vxn = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, VXN); /* Hardware feature register 1 */ hw_feat->rx_fifo_size = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, @@ -860,6 +862,8 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) (hw_feat->ts_src == 3) ? "internal/external" : "n/a"); dev_dbg(pdata->dev, " SA/VLAN insertion : %s\n", hw_feat->sa_vlan_ins ? "yes" : "no"); + dev_dbg(pdata->dev, " VXLAN/NVGRE support : %s\n", + hw_feat->vxn ? "yes" : "no"); /* Hardware feature register 1 */ dev_dbg(pdata->dev, " RX fifo size : %u\n", @@ -903,6 +907,116 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) } } +static void xgbe_disable_vxlan_offloads(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + + if (!pdata->vxlan_offloads_set) + return; + + netdev_info(netdev, "disabling VXLAN offloads\n"); + + netdev->hw_enc_features &= ~(NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_GRO | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM); + + netdev->features &= ~(NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM); + + pdata->vxlan_offloads_set = 0; +} + +static void xgbe_disable_vxlan_hw(struct xgbe_prv_data *pdata) +{ + if (!pdata->vxlan_port_set) + return; + + pdata->hw_if.disable_vxlan(pdata); + + pdata->vxlan_port_set = 0; + pdata->vxlan_port = 0; +} + +static void xgbe_disable_vxlan_accel(struct xgbe_prv_data *pdata) +{ + xgbe_disable_vxlan_offloads(pdata); + + xgbe_disable_vxlan_hw(pdata); +} + +static void xgbe_enable_vxlan_offloads(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + + if (pdata->vxlan_offloads_set) + return; + + netdev_info(netdev, "enabling VXLAN offloads\n"); + + netdev->hw_enc_features |= NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_GRO | + pdata->vxlan_features; + + netdev->features |= pdata->vxlan_features; + + pdata->vxlan_offloads_set = 1; +} + +static void xgbe_enable_vxlan_hw(struct xgbe_prv_data *pdata) +{ + struct xgbe_vxlan_data *vdata; + + if (pdata->vxlan_port_set) + return; + + if (list_empty(&pdata->vxlan_ports)) + return; + + vdata = list_first_entry(&pdata->vxlan_ports, + struct xgbe_vxlan_data, list); + + pdata->vxlan_port_set = 1; + pdata->vxlan_port = be16_to_cpu(vdata->port); + + pdata->hw_if.enable_vxlan(pdata); +} + +static void xgbe_enable_vxlan_accel(struct xgbe_prv_data *pdata) +{ + /* VXLAN acceleration desired? */ + if (!pdata->vxlan_features) + return; + + /* VXLAN acceleration possible? */ + if (pdata->vxlan_force_disable) + return; + + xgbe_enable_vxlan_hw(pdata); + + xgbe_enable_vxlan_offloads(pdata); +} + +static void xgbe_reset_vxlan_accel(struct xgbe_prv_data *pdata) +{ + xgbe_disable_vxlan_hw(pdata); + + if (pdata->vxlan_features) + xgbe_enable_vxlan_offloads(pdata); + + pdata->vxlan_force_disable = 0; +} + static void xgbe_napi_enable(struct xgbe_prv_data *pdata, unsigned int add) { struct xgbe_channel *channel; @@ -1226,6 +1340,8 @@ static int xgbe_start(struct xgbe_prv_data *pdata) hw_if->enable_tx(pdata); hw_if->enable_rx(pdata); + udp_tunnel_get_rx_info(netdev); + netif_tx_start_all_queues(netdev); xgbe_start_timers(pdata); @@ -1267,6 +1383,8 @@ static void xgbe_stop(struct xgbe_prv_data *pdata) xgbe_stop_timers(pdata); flush_workqueue(pdata->dev_workqueue); + xgbe_reset_vxlan_accel(pdata); + hw_if->disable_tx(pdata); hw_if->disable_rx(pdata); @@ -1555,10 +1673,18 @@ static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet) if (ret) return ret; - packet->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb); - packet->tcp_header_len = tcp_hdrlen(skb); + if (XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, VXLAN)) { + packet->header_len = skb_inner_transport_offset(skb) + + inner_tcp_hdrlen(skb); + packet->tcp_header_len = inner_tcp_hdrlen(skb); + } else { + packet->header_len = skb_transport_offset(skb) + + tcp_hdrlen(skb); + packet->tcp_header_len = tcp_hdrlen(skb); + } packet->tcp_payload_len = skb->len - packet->header_len; packet->mss = skb_shinfo(skb)->gso_size; + DBGPR(" packet->header_len=%u\n", packet->header_len); DBGPR(" packet->tcp_header_len=%u, packet->tcp_payload_len=%u\n", packet->tcp_header_len, packet->tcp_payload_len); @@ -1573,6 +1699,49 @@ static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet) return 0; } +static bool xgbe_is_vxlan(struct xgbe_prv_data *pdata, struct sk_buff *skb) +{ + struct xgbe_vxlan_data *vdata; + + if (pdata->vxlan_force_disable) + return false; + + if (!skb->encapsulation) + return false; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return false; + + switch (skb->protocol) { + case htons(ETH_P_IP): + if (ip_hdr(skb)->protocol != IPPROTO_UDP) + return false; + break; + + case htons(ETH_P_IPV6): + if (ipv6_hdr(skb)->nexthdr != IPPROTO_UDP) + return false; + break; + + default: + return false; + } + + /* See if we have the UDP port in our list */ + list_for_each_entry(vdata, &pdata->vxlan_ports, list) { + if ((skb->protocol == htons(ETH_P_IP)) && + (vdata->sa_family == AF_INET) && + (vdata->port == udp_hdr(skb)->dest)) + return true; + else if ((skb->protocol == htons(ETH_P_IPV6)) && + (vdata->sa_family == AF_INET6) && + (vdata->port == udp_hdr(skb)->dest)) + return true; + } + + return false; +} + static int xgbe_is_tso(struct sk_buff *skb) { if (skb->ip_summed != CHECKSUM_PARTIAL) @@ -1621,6 +1790,10 @@ static void xgbe_packet_info(struct xgbe_prv_data *pdata, XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, CSUM_ENABLE, 1); + if (xgbe_is_vxlan(pdata, skb)) + XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + VXLAN, 1); + if (skb_vlan_tag_present(skb)) { /* VLAN requires an extra descriptor if tag is different */ if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag) @@ -2050,18 +2223,83 @@ static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, return 0; } +static netdev_features_t xgbe_fix_features(struct net_device *netdev, + netdev_features_t features) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + netdev_features_t vxlan_base, vxlan_mask; + + vxlan_base = NETIF_F_GSO_UDP_TUNNEL | NETIF_F_RX_UDP_TUNNEL_PORT; + vxlan_mask = vxlan_base | NETIF_F_GSO_UDP_TUNNEL_CSUM; + + pdata->vxlan_features = features & vxlan_mask; + + /* Only fix VXLAN-related features */ + if (!pdata->vxlan_features) + return features; + + /* If VXLAN isn't supported then clear any features: + * This is needed because NETIF_F_RX_UDP_TUNNEL_PORT gets + * automatically set if ndo_udp_tunnel_add is set. + */ + if (!pdata->hw_feat.vxn) + return features & ~vxlan_mask; + + /* VXLAN CSUM requires VXLAN base */ + if ((features & NETIF_F_GSO_UDP_TUNNEL_CSUM) && + !(features & NETIF_F_GSO_UDP_TUNNEL)) { + netdev_notice(netdev, + "forcing tx udp tunnel support\n"); + features |= NETIF_F_GSO_UDP_TUNNEL; + } + + /* Can't do one without doing the other */ + if ((features & vxlan_base) != vxlan_base) { + netdev_notice(netdev, + "forcing both tx and rx udp tunnel support\n"); + features |= vxlan_base; + } + + if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { + if (!(features & NETIF_F_GSO_UDP_TUNNEL_CSUM)) { + netdev_notice(netdev, + "forcing tx udp tunnel checksumming on\n"); + features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; + } + } else { + if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM) { + netdev_notice(netdev, + "forcing tx udp tunnel checksumming off\n"); + features &= ~NETIF_F_GSO_UDP_TUNNEL_CSUM; + } + } + + pdata->vxlan_features = features & vxlan_mask; + + /* Adjust UDP Tunnel based on current state */ + if (pdata->vxlan_force_disable) { + netdev_notice(netdev, + "VXLAN acceleration disabled, turning off udp tunnel features\n"); + features &= ~vxlan_mask; + } + + return features; +} + static int xgbe_set_features(struct net_device *netdev, netdev_features_t features) { struct xgbe_prv_data *pdata = netdev_priv(netdev); struct xgbe_hw_if *hw_if = &pdata->hw_if; netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter; + netdev_features_t udp_tunnel; int ret = 0; rxhash = pdata->netdev_features & NETIF_F_RXHASH; rxcsum = pdata->netdev_features & NETIF_F_RXCSUM; rxvlan = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX; rxvlan_filter = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER; + udp_tunnel = pdata->netdev_features & NETIF_F_GSO_UDP_TUNNEL; if ((features & NETIF_F_RXHASH) && !rxhash) ret = hw_if->enable_rss(pdata); @@ -2085,6 +2323,11 @@ static int xgbe_set_features(struct net_device *netdev, else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter) hw_if->disable_rx_vlan_filtering(pdata); + if ((features & NETIF_F_GSO_UDP_TUNNEL) && !udp_tunnel) + xgbe_enable_vxlan_accel(pdata); + else if (!(features & NETIF_F_GSO_UDP_TUNNEL) && udp_tunnel) + xgbe_disable_vxlan_accel(pdata); + pdata->netdev_features = features; DBGPR("<--xgbe_set_features\n"); @@ -2092,6 +2335,111 @@ static int xgbe_set_features(struct net_device *netdev, return 0; } +static void xgbe_udp_tunnel_add(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_vxlan_data *vdata; + + if (!pdata->hw_feat.vxn) + return; + + if (ti->type != UDP_TUNNEL_TYPE_VXLAN) + return; + + pdata->vxlan_port_count++; + + netif_dbg(pdata, drv, netdev, + "adding VXLAN tunnel, family=%hx/port=%hx\n", + ti->sa_family, be16_to_cpu(ti->port)); + + if (pdata->vxlan_force_disable) + return; + + vdata = kzalloc(sizeof(*vdata), GFP_ATOMIC); + if (!vdata) { + /* Can no longer properly track VXLAN ports */ + pdata->vxlan_force_disable = 1; + netif_dbg(pdata, drv, netdev, + "internal error, disabling VXLAN accelerations\n"); + + xgbe_disable_vxlan_accel(pdata); + + return; + } + vdata->sa_family = ti->sa_family; + vdata->port = ti->port; + + list_add_tail(&vdata->list, &pdata->vxlan_ports); + + /* First port added? */ + if (pdata->vxlan_port_count == 1) { + xgbe_enable_vxlan_accel(pdata); + + return; + } +} + +static void xgbe_udp_tunnel_del(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_vxlan_data *vdata; + + if (!pdata->hw_feat.vxn) + return; + + if (ti->type != UDP_TUNNEL_TYPE_VXLAN) + return; + + netif_dbg(pdata, drv, netdev, + "deleting VXLAN tunnel, family=%hx/port=%hx\n", + ti->sa_family, be16_to_cpu(ti->port)); + + /* Don't need safe version since loop terminates with deletion */ + list_for_each_entry(vdata, &pdata->vxlan_ports, list) { + if (vdata->sa_family != ti->sa_family) + continue; + + if (vdata->port != ti->port) + continue; + + list_del(&vdata->list); + kfree(vdata); + + break; + } + + pdata->vxlan_port_count--; + if (!pdata->vxlan_port_count) { + xgbe_reset_vxlan_accel(pdata); + + return; + } + + if (pdata->vxlan_force_disable) + return; + + /* See if VXLAN tunnel id needs to be changed */ + vdata = list_first_entry(&pdata->vxlan_ports, + struct xgbe_vxlan_data, list); + if (pdata->vxlan_port == be16_to_cpu(vdata->port)) + return; + + pdata->vxlan_port = be16_to_cpu(vdata->port); + pdata->hw_if.set_vxlan_id(pdata); +} + +static netdev_features_t xgbe_features_check(struct sk_buff *skb, + struct net_device *netdev, + netdev_features_t features) +{ + features = vlan_features_check(skb, features); + features = vxlan_features_check(skb, features); + + return features; +} + static const struct net_device_ops xgbe_netdev_ops = { .ndo_open = xgbe_open, .ndo_stop = xgbe_close, @@ -2109,7 +2457,11 @@ static const struct net_device_ops xgbe_netdev_ops = { .ndo_poll_controller = xgbe_poll_controller, #endif .ndo_setup_tc = xgbe_setup_tc, + .ndo_fix_features = xgbe_fix_features, .ndo_set_features = xgbe_set_features, + .ndo_udp_tunnel_add = xgbe_udp_tunnel_add, + .ndo_udp_tunnel_del = xgbe_udp_tunnel_del, + .ndo_features_check = xgbe_features_check, }; const struct net_device_ops *xgbe_get_netdev_ops(void) @@ -2421,6 +2773,15 @@ skip_data: RX_PACKET_ATTRIBUTES, CSUM_DONE)) skb->ip_summed = CHECKSUM_UNNECESSARY; + if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, TNP)) { + skb->encapsulation = 1; + + if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, TNPCSUM_DONE)) + skb->csum_level = 1; + } + if (XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, VLAN_CTAG)) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index c5ff385d51ae..d91fa595be98 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -193,6 +193,7 @@ struct xgbe_prv_data *xgbe_alloc_pdata(struct device *dev) mutex_init(&pdata->i2c_mutex); init_completion(&pdata->i2c_complete); init_completion(&pdata->mdio_complete); + INIT_LIST_HEAD(&pdata->vxlan_ports); pdata->msg_enable = netif_msg_init(debug, default_msg_level); @@ -374,6 +375,28 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) if (pdata->hw_feat.rss) netdev->hw_features |= NETIF_F_RXHASH; + if (pdata->hw_feat.vxn) { + netdev->hw_enc_features = NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_GRO | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_RX_UDP_TUNNEL_PORT; + + netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_RX_UDP_TUNNEL_PORT; + + pdata->vxlan_offloads_set = 1; + pdata->vxlan_features = NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_RX_UDP_TUNNEL_PORT; + } + netdev->vlan_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 48a46a70bf5e..db155fe7f74d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -132,6 +132,7 @@ #include #include #include +#include #define XGBE_DRV_NAME "amd-xgbe" #define XGBE_DRV_VERSION "1.0.3" @@ -817,6 +818,11 @@ struct xgbe_hw_if { /* For ECC */ void (*disable_ecc_ded)(struct xgbe_prv_data *); void (*disable_ecc_sec)(struct xgbe_prv_data *, enum xgbe_ecc_sec); + + /* For VXLAN */ + void (*enable_vxlan)(struct xgbe_prv_data *); + void (*disable_vxlan)(struct xgbe_prv_data *); + void (*set_vxlan_id)(struct xgbe_prv_data *); }; /* This structure represents implementation specific routines for an @@ -941,6 +947,7 @@ struct xgbe_hw_features { unsigned int addn_mac; /* Additional MAC Addresses */ unsigned int ts_src; /* Timestamp Source */ unsigned int sa_vlan_ins; /* Source Address or VLAN Insertion */ + unsigned int vxn; /* VXLAN/NVGRE */ /* HW Feature Register1 */ unsigned int rx_fifo_size; /* MTL Receive FIFO Size */ @@ -979,6 +986,12 @@ struct xgbe_version_data { unsigned int rx_desc_prefetch; }; +struct xgbe_vxlan_data { + struct list_head list; + sa_family_t sa_family; + __be16 port; +}; + struct xgbe_prv_data { struct net_device *netdev; struct pci_dev *pcidev; @@ -1120,6 +1133,15 @@ struct xgbe_prv_data { u32 rss_table[XGBE_RSS_MAX_TABLE_SIZE]; u32 rss_options; + /* VXLAN settings */ + unsigned int vxlan_port_set; + unsigned int vxlan_offloads_set; + unsigned int vxlan_force_disable; + unsigned int vxlan_port_count; + struct list_head vxlan_ports; + u16 vxlan_port; + netdev_features_t vxlan_features; + /* Netdev related settings */ unsigned char mac_addr[ETH_ALEN]; netdev_features_t netdev_features; -- cgit v1.2.3 From 3010608d033c7cae73d1662493c85b4c3b27015a Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Fri, 18 Aug 2017 09:04:14 -0500 Subject: amd-xgbe: Add additional ethtool statistics Add some additional statistics for tracking VXLAN packets and checksum errors. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 8 +++++++- drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 4 ++++ drivers/net/ethernet/amd/xgbe/xgbe.h | 5 +++++ 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 1bf671e02c0a..671203dbea3d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1805,10 +1805,13 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) packet->length); } - if (vxlan) + if (vxlan) { XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, VNP, TX_NORMAL_DESC3_VXLAN_PACKET); + pdata->ext_stats.tx_vxlan_packets += packet->tx_packets; + } + for (i = cur_index - start_index + 1; i < packet->rdesc_count; i++) { cur_index++; rdata = XGBE_GET_DESC_DATA(ring, cur_index); @@ -1981,6 +1984,7 @@ static int xgbe_dev_read(struct xgbe_channel *channel) if (XGMAC_GET_BITS_LE(rdesc->desc2, RX_NORMAL_DESC2, TNP)) { XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, TNP, 1); + pdata->ext_stats.rx_vxlan_packets++; l34t = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, L34T); switch (l34t) { @@ -2018,11 +2022,13 @@ static int xgbe_dev_read(struct xgbe_channel *channel) CSUM_DONE, 0); XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, TNPCSUM_DONE, 0); + pdata->ext_stats.rx_csum_errors++; } else if (tnp && ((etlt == 0x09) || (etlt == 0x0a))) { XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, CSUM_DONE, 0); XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, TNPCSUM_DONE, 0); + pdata->ext_stats.rx_vxlan_csum_errors++; } else { XGMAC_SET_BITS(packet->errors, RX_PACKET_ERRORS, FRAME, 1); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index cea25acd5de2..ff397bb25042 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -146,6 +146,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { XGMAC_MMC_STAT("tx_broadcast_packets", txbroadcastframes_gb), XGMAC_MMC_STAT("tx_multicast_packets", txmulticastframes_gb), XGMAC_MMC_STAT("tx_vlan_packets", txvlanframes_g), + XGMAC_EXT_STAT("tx_vxlan_packets", tx_vxlan_packets), XGMAC_EXT_STAT("tx_tso_packets", tx_tso_packets), XGMAC_MMC_STAT("tx_64_byte_packets", tx64octets_gb), XGMAC_MMC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb), @@ -162,6 +163,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { XGMAC_MMC_STAT("rx_broadcast_packets", rxbroadcastframes_g), XGMAC_MMC_STAT("rx_multicast_packets", rxmulticastframes_g), XGMAC_MMC_STAT("rx_vlan_packets", rxvlanframes_gb), + XGMAC_EXT_STAT("rx_vxlan_packets", rx_vxlan_packets), XGMAC_MMC_STAT("rx_64_byte_packets", rx64octets_gb), XGMAC_MMC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb), XGMAC_MMC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb), @@ -177,6 +179,8 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { XGMAC_MMC_STAT("rx_out_of_range_errors", rxoutofrangetype), XGMAC_MMC_STAT("rx_fifo_overflow_errors", rxfifooverflow), XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror), + XGMAC_EXT_STAT("rx_csum_errors", rx_csum_errors), + XGMAC_EXT_STAT("rx_vxlan_csum_errors", rx_vxlan_csum_errors), XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes), XGMAC_EXT_STAT("rx_split_header_packets", rx_split_header_packets), XGMAC_EXT_STAT("rx_buffer_unavailable", rx_buffer_unavailable), diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index db155fe7f74d..ad102c8bac7b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -715,6 +715,11 @@ struct xgbe_ext_stats { u64 txq_bytes[XGBE_MAX_DMA_CHANNELS]; u64 rxq_packets[XGBE_MAX_DMA_CHANNELS]; u64 rxq_bytes[XGBE_MAX_DMA_CHANNELS]; + + u64 tx_vxlan_packets; + u64 rx_vxlan_packets; + u64 rx_csum_errors; + u64 rx_vxlan_csum_errors; }; struct xgbe_hw_if { -- cgit v1.2.3 From 718ad681eff47d3d04879ff5f5290bdab0b8bad6 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:24 -0700 Subject: net: drop unused attribute argument from sysfs queue funcs The show and store functions don't need/use the attribute. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 2 +- include/linux/netdevice.h | 5 ++--- net/core/net-sysfs.c | 37 +++++++++++-------------------------- 3 files changed, 14 insertions(+), 30 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 4302f313d9a7..52ae78ca3d38 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2376,7 +2376,7 @@ err: #ifdef CONFIG_SYSFS static ssize_t mergeable_rx_buffer_size_show(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attribute, char *buf) + char *buf) { struct virtnet_info *vi = netdev_priv(queue->dev); unsigned int queue_index = get_netdev_rx_queue_index(queue); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b0c928598dab..c5475b37a631 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -694,10 +694,9 @@ struct netdev_rx_queue { */ struct rx_queue_attribute { struct attribute attr; - ssize_t (*show)(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attr, char *buf); + ssize_t (*show)(struct netdev_rx_queue *queue, char *buf); ssize_t (*store)(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attr, const char *buf, size_t len); + const char *buf, size_t len); }; #ifdef CONFIG_XPS diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 76ec74d4a65b..48714c8024f3 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -661,7 +661,7 @@ static ssize_t rx_queue_attr_show(struct kobject *kobj, struct attribute *attr, if (!attribute->show) return -EIO; - return attribute->show(queue, attribute, buf); + return attribute->show(queue, buf); } static ssize_t rx_queue_attr_store(struct kobject *kobj, struct attribute *attr, @@ -673,7 +673,7 @@ static ssize_t rx_queue_attr_store(struct kobject *kobj, struct attribute *attr, if (!attribute->store) return -EIO; - return attribute->store(queue, attribute, buf, count); + return attribute->store(queue, buf, count); } static const struct sysfs_ops rx_queue_sysfs_ops = { @@ -682,8 +682,7 @@ static const struct sysfs_ops rx_queue_sysfs_ops = { }; #ifdef CONFIG_RPS -static ssize_t show_rps_map(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attribute, char *buf) +static ssize_t show_rps_map(struct netdev_rx_queue *queue, char *buf) { struct rps_map *map; cpumask_var_t mask; @@ -706,8 +705,7 @@ static ssize_t show_rps_map(struct netdev_rx_queue *queue, } static ssize_t store_rps_map(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attribute, - const char *buf, size_t len) + const char *buf, size_t len) { struct rps_map *old_map, *map; cpumask_var_t mask; @@ -765,7 +763,6 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue, } static ssize_t show_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attr, char *buf) { struct rps_dev_flow_table *flow_table; @@ -788,8 +785,7 @@ static void rps_dev_flow_table_release(struct rcu_head *rcu) } static ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attr, - const char *buf, size_t len) + const char *buf, size_t len) { unsigned long mask, count; struct rps_dev_flow_table *table, *old_table; @@ -975,10 +971,9 @@ net_rx_queue_update_kobjects(struct net_device *dev, int old_num, int new_num) */ struct netdev_queue_attribute { struct attribute attr; - ssize_t (*show)(struct netdev_queue *queue, - struct netdev_queue_attribute *attr, char *buf); + ssize_t (*show)(struct netdev_queue *queue, char *buf); ssize_t (*store)(struct netdev_queue *queue, - struct netdev_queue_attribute *attr, const char *buf, size_t len); + const char *buf, size_t len); }; #define to_netdev_queue_attr(_attr) container_of(_attr, \ struct netdev_queue_attribute, attr) @@ -994,7 +989,7 @@ static ssize_t netdev_queue_attr_show(struct kobject *kobj, if (!attribute->show) return -EIO; - return attribute->show(queue, attribute, buf); + return attribute->show(queue, buf); } static ssize_t netdev_queue_attr_store(struct kobject *kobj, @@ -1007,7 +1002,7 @@ static ssize_t netdev_queue_attr_store(struct kobject *kobj, if (!attribute->store) return -EIO; - return attribute->store(queue, attribute, buf, count); + return attribute->store(queue, buf, count); } static const struct sysfs_ops netdev_queue_sysfs_ops = { @@ -1016,7 +1011,6 @@ static const struct sysfs_ops netdev_queue_sysfs_ops = { }; static ssize_t show_trans_timeout(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, char *buf) { unsigned long trans_timeout; @@ -1040,7 +1034,6 @@ static unsigned int get_netdev_queue_index(struct netdev_queue *queue) } static ssize_t show_traffic_class(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, char *buf) { struct net_device *dev = queue->dev; @@ -1055,14 +1048,12 @@ static ssize_t show_traffic_class(struct netdev_queue *queue, #ifdef CONFIG_XPS static ssize_t show_tx_maxrate(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, char *buf) { return sprintf(buf, "%lu\n", queue->tx_maxrate); } static ssize_t set_tx_maxrate(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, const char *buf, size_t len) { struct net_device *dev = queue->dev; @@ -1130,7 +1121,6 @@ static ssize_t bql_set(const char *buf, const size_t count, } static ssize_t bql_show_hold_time(struct netdev_queue *queue, - struct netdev_queue_attribute *attr, char *buf) { struct dql *dql = &queue->dql; @@ -1139,7 +1129,6 @@ static ssize_t bql_show_hold_time(struct netdev_queue *queue, } static ssize_t bql_set_hold_time(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, const char *buf, size_t len) { struct dql *dql = &queue->dql; @@ -1160,7 +1149,6 @@ static struct netdev_queue_attribute bql_hold_time_attribute = bql_set_hold_time); static ssize_t bql_show_inflight(struct netdev_queue *queue, - struct netdev_queue_attribute *attr, char *buf) { struct dql *dql = &queue->dql; @@ -1173,14 +1161,12 @@ static struct netdev_queue_attribute bql_inflight_attribute = #define BQL_ATTR(NAME, FIELD) \ static ssize_t bql_show_ ## NAME(struct netdev_queue *queue, \ - struct netdev_queue_attribute *attr, \ char *buf) \ { \ return bql_show(buf, queue->dql.FIELD); \ } \ \ static ssize_t bql_set_ ## NAME(struct netdev_queue *queue, \ - struct netdev_queue_attribute *attr, \ const char *buf, size_t len) \ { \ return bql_set(buf, len, &queue->dql.FIELD); \ @@ -1211,7 +1197,7 @@ static const struct attribute_group dql_group = { #ifdef CONFIG_XPS static ssize_t show_xps_map(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, char *buf) + char *buf) { struct net_device *dev = queue->dev; int cpu, len, num_tc = 1, tc = 0; @@ -1258,8 +1244,7 @@ static ssize_t show_xps_map(struct netdev_queue *queue, } static ssize_t store_xps_map(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, - const char *buf, size_t len) + const char *buf, size_t len) { struct net_device *dev = queue->dev; unsigned long index; -- cgit v1.2.3 From 06726f30364c38f12519e2c6c046f19b2aa509bc Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:11 -0700 Subject: nfp: link basic ethtool ops to representors Start linking ethtool ops to representors. Begin by adding a separate ops structure and providing link state. Next patches will convert appropriate functions to only use nfp_port, which will make them reusable on representors. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 4 ++++ drivers/net/ethernet/netronome/nfp/nfp_net_repr.c | 2 ++ drivers/net/ethernet/netronome/nfp/nfp_port.h | 1 + 3 files changed, 7 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 6e31355c3567..3edc5d62ad5b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -939,6 +939,10 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .set_link_ksettings = nfp_net_set_link_ksettings, }; +const struct ethtool_ops nfp_port_ethtool_ops = { + .get_link = ethtool_op_get_link, +}; + void nfp_net_set_ethtool_ops(struct net_device *netdev) { netdev->ethtool_ops = &nfp_net_ethtool_ops; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 47daad30756c..50f7cc057cc9 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -320,6 +320,8 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev, repr->dst->u.port_info.lower_dev = pf_netdev; netdev->netdev_ops = &nfp_repr_netdev_ops; + netdev->ethtool_ops = &nfp_port_ethtool_ops; + SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops); if (nfp_app_has_tc(app)) { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index c88e376dcf0f..784d82c2f32c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -106,6 +106,7 @@ struct nfp_port { struct list_head port_list; }; +extern const struct ethtool_ops nfp_port_ethtool_ops; extern const struct switchdev_ops nfp_port_switchdev_ops; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, -- cgit v1.2.3 From 9e4c2cfc67816276a80da5888a1d9430b60c2183 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:12 -0700 Subject: nfp: provide ethtool_drvinfo on representors Extend representors' ethtool ops to show basic info like firmware version, driver version, and driver name. While at it don't set drvinfo.n_stats and drvinfo.regdump_len, core will invoke appropriate handlers to get those. A helper is added to turn a netdev into nfp_app for convenience. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_app.c | 20 ++++++++++ drivers/net/ethernet/netronome/nfp/nfp_app.h | 2 + .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 44 ++++++++++++++++------ 3 files changed, 54 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.c b/drivers/net/ethernet/netronome/nfp/nfp_app.c index c704c022574f..505e63f47419 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.c @@ -38,6 +38,7 @@ #include "nfpcore/nfp_nffw.h" #include "nfp_app.h" #include "nfp_main.h" +#include "nfp_net.h" #include "nfp_net_repr.h" static const struct nfp_app_type *apps[] = { @@ -48,6 +49,25 @@ static const struct nfp_app_type *apps[] = { #endif }; +struct nfp_app *nfp_app_from_netdev(struct net_device *netdev) +{ + if (nfp_netdev_is_nfp_net(netdev)) { + struct nfp_net *nn = netdev_priv(netdev); + + return nn->app; + } + + if (nfp_netdev_is_nfp_repr(netdev)) { + struct nfp_repr *repr = netdev_priv(netdev); + + return repr->app; + } + + WARN(1, "Unknown netdev type for nfp_app\n"); + + return NULL; +} + const char *nfp_app_mip_name(struct nfp_app *app) { if (!app || !app->pf->mip) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index f34e8778fae2..c13b9bbe7e62 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -293,6 +293,8 @@ static inline struct net_device *nfp_app_repr_get(struct nfp_app *app, u32 id) return app->type->repr_get(app, id); } +struct nfp_app *nfp_app_from_netdev(struct net_device *netdev); + struct nfp_reprs * nfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type, struct nfp_reprs *reprs); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 3edc5d62ad5b..3c34c8b27dcf 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -147,34 +147,53 @@ static void nfp_net_get_nspinfo(struct nfp_app *app, char *version) if (IS_ERR(nsp)) return; - snprintf(version, ETHTOOL_FWVERS_LEN, "sp:%hu.%hu", + snprintf(version, ETHTOOL_FWVERS_LEN, "%hu.%hu", nfp_nsp_get_abi_ver_major(nsp), nfp_nsp_get_abi_ver_minor(nsp)); nfp_nsp_close(nsp); } -static void nfp_net_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) +static void +nfp_get_drvinfo(struct nfp_app *app, struct pci_dev *pdev, + const char *vnic_version, struct ethtool_drvinfo *drvinfo) { char nsp_version[ETHTOOL_FWVERS_LEN] = {}; - struct nfp_net *nn = netdev_priv(netdev); - strlcpy(drvinfo->driver, nn->pdev->driver->name, - sizeof(drvinfo->driver)); + strlcpy(drvinfo->driver, pdev->driver->name, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, nfp_driver_version, sizeof(drvinfo->version)); - nfp_net_get_nspinfo(nn->app, nsp_version); + nfp_net_get_nspinfo(app, nsp_version); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%d.%d.%d.%d %s %s %s", + "%s %s %s %s", vnic_version, nsp_version, + nfp_app_mip_name(app), nfp_app_name(app)); +} + +static void +nfp_net_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) +{ + char vnic_version[ETHTOOL_FWVERS_LEN] = {}; + struct nfp_net *nn = netdev_priv(netdev); + + snprintf(vnic_version, sizeof(vnic_version), "%d.%d.%d.%d", nn->fw_ver.resv, nn->fw_ver.class, - nn->fw_ver.major, nn->fw_ver.minor, nsp_version, - nfp_app_mip_name(nn->app), nfp_app_name(nn->app)); + nn->fw_ver.major, nn->fw_ver.minor); strlcpy(drvinfo->bus_info, pci_name(nn->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = NN_ET_STATS_LEN; - drvinfo->regdump_len = NFP_NET_CFG_BAR_SZ; + nfp_get_drvinfo(nn->app, nn->pdev, vnic_version, drvinfo); +} + +static void +nfp_app_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) +{ + struct nfp_app *app; + + app = nfp_app_from_netdev(netdev); + if (!app) + return; + + nfp_get_drvinfo(app, app->pdev, "*", drvinfo); } /** @@ -940,6 +959,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { }; const struct ethtool_ops nfp_port_ethtool_ops = { + .get_drvinfo = nfp_app_get_drvinfo, .get_link = ethtool_op_get_link, }; -- cgit v1.2.3 From a2f4c3d9bd4a8a85d7cdd8221f7995fdbc98aa4a Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:13 -0700 Subject: nfp: allow retreiving management FW logs on representors Users should be able to dump the management FW logs on any of the driver's netdevs. Make the code only depend on the nfp_app and share it between vNICs and representors. Storing the dump flag is simply dropped for now, since we only support the argument being set to 0. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 2 -- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 33 +++++++++++----------- 2 files changed, 17 insertions(+), 18 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index b1fa77bd708b..d51d8237b984 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -573,7 +573,6 @@ struct nfp_net_dp { * @tx_bar: Pointer to mapped TX queues * @rx_bar: Pointer to mapped FL/RX queues * @debugfs_dir: Device directory in debugfs - * @ethtool_dump_flag: Ethtool dump flag * @vnic_list: Entry on device vNIC list * @pdev: Backpointer to PCI device * @app: APP handle if available @@ -640,7 +639,6 @@ struct nfp_net { u8 __iomem *rx_bar; struct dentry *debugfs_dir; - u32 ethtool_dump_flag; struct list_head vnic_list; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 3c34c8b27dcf..289fa318a651 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -727,18 +727,18 @@ static int nfp_net_get_coalesce(struct net_device *netdev, /* Other debug dumps */ static int -nfp_dump_nsp_diag(struct nfp_net *nn, struct ethtool_dump *dump, void *buffer) +nfp_dump_nsp_diag(struct nfp_app *app, struct ethtool_dump *dump, void *buffer) { struct nfp_resource *res; int ret; - if (!nn->app) + if (!app) return -EOPNOTSUPP; dump->version = 1; dump->flag = NFP_DUMP_NSP_DIAG; - res = nfp_resource_acquire(nn->app->cpp, NFP_RESOURCE_NSP_DIAG); + res = nfp_resource_acquire(app->cpp, NFP_RESOURCE_NSP_DIAG); if (IS_ERR(res)) return PTR_ERR(res); @@ -748,7 +748,7 @@ nfp_dump_nsp_diag(struct nfp_net *nn, struct ethtool_dump *dump, void *buffer) goto exit_release; } - ret = nfp_cpp_read(nn->app->cpp, nfp_resource_cpp_id(res), + ret = nfp_cpp_read(app->cpp, nfp_resource_cpp_id(res), nfp_resource_address(res), buffer, dump->len); if (ret != dump->len) @@ -765,32 +765,30 @@ exit_release: return ret; } -static int nfp_net_set_dump(struct net_device *netdev, struct ethtool_dump *val) +static int nfp_app_set_dump(struct net_device *netdev, struct ethtool_dump *val) { - struct nfp_net *nn = netdev_priv(netdev); + struct nfp_app *app = nfp_app_from_netdev(netdev); - if (!nn->app) + if (!app) return -EOPNOTSUPP; if (val->flag != NFP_DUMP_NSP_DIAG) return -EINVAL; - nn->ethtool_dump_flag = val->flag; - return 0; } static int -nfp_net_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) +nfp_app_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) { - return nfp_dump_nsp_diag(netdev_priv(netdev), dump, NULL); + return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, NULL); } static int -nfp_net_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, +nfp_app_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, void *buffer) { - return nfp_dump_nsp_diag(netdev_priv(netdev), dump, buffer); + return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, buffer); } static int nfp_net_set_coalesce(struct net_device *netdev, @@ -947,9 +945,9 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .set_rxfh = nfp_net_set_rxfh, .get_regs_len = nfp_net_get_regs_len, .get_regs = nfp_net_get_regs, - .set_dump = nfp_net_set_dump, - .get_dump_flag = nfp_net_get_dump_flag, - .get_dump_data = nfp_net_get_dump_data, + .set_dump = nfp_app_set_dump, + .get_dump_flag = nfp_app_get_dump_flag, + .get_dump_data = nfp_app_get_dump_data, .get_coalesce = nfp_net_get_coalesce, .set_coalesce = nfp_net_set_coalesce, .get_channels = nfp_net_get_channels, @@ -961,6 +959,9 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { const struct ethtool_ops nfp_port_ethtool_ops = { .get_drvinfo = nfp_app_get_drvinfo, .get_link = ethtool_op_get_link, + .set_dump = nfp_app_set_dump, + .get_dump_flag = nfp_app_get_dump_flag, + .get_dump_data = nfp_app_get_dump_data, }; void nfp_net_set_ethtool_ops(struct net_device *netdev) -- cgit v1.2.3 From 1cfcc97bb1575446016e0c061c0629b1e6572523 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:14 -0700 Subject: nfp: don't report standard netdev statistics in ethtool We have been recently called out as a bad example for reporting standard netdev statistics as part of ethtool. Fix that :) Removing standard statistics allows us to simplify the structure holding definitions since we no longer have to mux different types of statistics. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 109 ++++++--------------- 1 file changed, 32 insertions(+), 77 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 289fa318a651..1753bfbc8b47 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -59,74 +59,44 @@ enum nfp_dump_diag { NFP_DUMP_NSP_DIAG = 0, }; -/* Support for stats. Returns netdev, driver, and device stats */ -enum { NETDEV_ET_STATS, NFP_NET_DRV_ET_STATS, NFP_NET_DEV_ET_STATS }; -struct _nfp_net_et_stats { +struct nfp_et_stat { char name[ETH_GSTRING_LEN]; - int type; - int sz; int off; }; -#define NN_ET_NETDEV_STAT(m) NETDEV_ET_STATS, \ - FIELD_SIZEOF(struct net_device_stats, m), \ - offsetof(struct net_device_stats, m) -/* For stats in the control BAR (other than Q stats) */ -#define NN_ET_DEV_STAT(m) NFP_NET_DEV_ET_STATS, \ - sizeof(u64), \ - (m) -static const struct _nfp_net_et_stats nfp_net_et_stats[] = { - /* netdev stats */ - {"rx_packets", NN_ET_NETDEV_STAT(rx_packets)}, - {"tx_packets", NN_ET_NETDEV_STAT(tx_packets)}, - {"rx_bytes", NN_ET_NETDEV_STAT(rx_bytes)}, - {"tx_bytes", NN_ET_NETDEV_STAT(tx_bytes)}, - {"rx_errors", NN_ET_NETDEV_STAT(rx_errors)}, - {"tx_errors", NN_ET_NETDEV_STAT(tx_errors)}, - {"rx_dropped", NN_ET_NETDEV_STAT(rx_dropped)}, - {"tx_dropped", NN_ET_NETDEV_STAT(tx_dropped)}, - {"multicast", NN_ET_NETDEV_STAT(multicast)}, - {"collisions", NN_ET_NETDEV_STAT(collisions)}, - {"rx_over_errors", NN_ET_NETDEV_STAT(rx_over_errors)}, - {"rx_crc_errors", NN_ET_NETDEV_STAT(rx_crc_errors)}, - {"rx_frame_errors", NN_ET_NETDEV_STAT(rx_frame_errors)}, - {"rx_fifo_errors", NN_ET_NETDEV_STAT(rx_fifo_errors)}, - {"rx_missed_errors", NN_ET_NETDEV_STAT(rx_missed_errors)}, - {"tx_aborted_errors", NN_ET_NETDEV_STAT(tx_aborted_errors)}, - {"tx_carrier_errors", NN_ET_NETDEV_STAT(tx_carrier_errors)}, - {"tx_fifo_errors", NN_ET_NETDEV_STAT(tx_fifo_errors)}, +static const struct nfp_et_stat nfp_net_et_stats[] = { /* Stats from the device */ - {"dev_rx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_DISCARDS)}, - {"dev_rx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_ERRORS)}, - {"dev_rx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_OCTETS)}, - {"dev_rx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_UC_OCTETS)}, - {"dev_rx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_OCTETS)}, - {"dev_rx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_OCTETS)}, - {"dev_rx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_FRAMES)}, - {"dev_rx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_FRAMES)}, - {"dev_rx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_FRAMES)}, - - {"dev_tx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_DISCARDS)}, - {"dev_tx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_ERRORS)}, - {"dev_tx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_OCTETS)}, - {"dev_tx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_UC_OCTETS)}, - {"dev_tx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_OCTETS)}, - {"dev_tx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_OCTETS)}, - {"dev_tx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_FRAMES)}, - {"dev_tx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_FRAMES)}, - {"dev_tx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_FRAMES)}, - - {"bpf_pass_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP0_FRAMES)}, - {"bpf_pass_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP0_BYTES)}, + { "dev_rx_discards", NFP_NET_CFG_STATS_RX_DISCARDS }, + { "dev_rx_errors", NFP_NET_CFG_STATS_RX_ERRORS }, + { "dev_rx_bytes", NFP_NET_CFG_STATS_RX_OCTETS }, + { "dev_rx_uc_bytes", NFP_NET_CFG_STATS_RX_UC_OCTETS }, + { "dev_rx_mc_bytes", NFP_NET_CFG_STATS_RX_MC_OCTETS }, + { "dev_rx_bc_bytes", NFP_NET_CFG_STATS_RX_BC_OCTETS }, + { "dev_rx_pkts", NFP_NET_CFG_STATS_RX_FRAMES }, + { "dev_rx_mc_pkts", NFP_NET_CFG_STATS_RX_MC_FRAMES }, + { "dev_rx_bc_pkts", NFP_NET_CFG_STATS_RX_BC_FRAMES }, + + { "dev_tx_discards", NFP_NET_CFG_STATS_TX_DISCARDS }, + { "dev_tx_errors", NFP_NET_CFG_STATS_TX_ERRORS }, + { "dev_tx_bytes", NFP_NET_CFG_STATS_TX_OCTETS }, + { "dev_tx_uc_bytes", NFP_NET_CFG_STATS_TX_UC_OCTETS }, + { "dev_tx_mc_bytes", NFP_NET_CFG_STATS_TX_MC_OCTETS }, + { "dev_tx_bc_bytes", NFP_NET_CFG_STATS_TX_BC_OCTETS }, + { "dev_tx_pkts", NFP_NET_CFG_STATS_TX_FRAMES }, + { "dev_tx_mc_pkts", NFP_NET_CFG_STATS_TX_MC_FRAMES }, + { "dev_tx_bc_pkts", NFP_NET_CFG_STATS_TX_BC_FRAMES }, + + { "bpf_pass_pkts", NFP_NET_CFG_STATS_APP0_FRAMES }, + { "bpf_pass_bytes", NFP_NET_CFG_STATS_APP0_BYTES }, /* see comments in outro functions in nfp_bpf_jit.c to find out * how different BPF modes use app-specific counters */ - {"bpf_app1_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP1_FRAMES)}, - {"bpf_app1_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP1_BYTES)}, - {"bpf_app2_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP2_FRAMES)}, - {"bpf_app2_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP2_BYTES)}, - {"bpf_app3_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP3_FRAMES)}, - {"bpf_app3_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP3_BYTES)}, + { "bpf_app1_pkts", NFP_NET_CFG_STATS_APP1_FRAMES }, + { "bpf_app1_bytes", NFP_NET_CFG_STATS_APP1_BYTES }, + { "bpf_app2_pkts", NFP_NET_CFG_STATS_APP2_FRAMES }, + { "bpf_app2_bytes", NFP_NET_CFG_STATS_APP2_BYTES }, + { "bpf_app3_pkts", NFP_NET_CFG_STATS_APP3_FRAMES }, + { "bpf_app3_bytes", NFP_NET_CFG_STATS_APP3_BYTES }, }; #define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats) @@ -421,28 +391,13 @@ static void nfp_net_get_stats(struct net_device *netdev, { u64 gathered_stats[NN_ET_RVEC_GATHER_STATS] = {}; struct nfp_net *nn = netdev_priv(netdev); - struct rtnl_link_stats64 *netdev_stats; - struct rtnl_link_stats64 temp = {}; u64 tmp[NN_ET_RVEC_GATHER_STATS]; u8 __iomem *io_p; int i, j, k; - u8 *p; - - netdev_stats = dev_get_stats(netdev, &temp); for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) { - switch (nfp_net_et_stats[i].type) { - case NETDEV_ET_STATS: - p = (char *)netdev_stats + nfp_net_et_stats[i].off; - data[i] = nfp_net_et_stats[i].sz == sizeof(u64) ? - *(u64 *)p : *(u32 *)p; - break; - - case NFP_NET_DEV_ET_STATS: - io_p = nn->dp.ctrl_bar + nfp_net_et_stats[i].off; - data[i] = readq(io_p); - break; - } + io_p = nn->dp.ctrl_bar + nfp_net_et_stats[i].off; + data[i] = readq(io_p); } for (j = 0; j < nn->dp.num_r_vecs; j++) { unsigned int start; -- cgit v1.2.3 From 634287ba7544f10e81d2ffcb58e934b2a3514ed2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:15 -0700 Subject: nfp: add helper for printing ethtool strings Add a helper for printing ethtool strings and advancing the pointer correctly. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 65 +++++++++++----------- 1 file changed, 32 insertions(+), 33 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 1753bfbc8b47..ba1c28b8791b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -335,53 +335,52 @@ static int nfp_net_set_ringparam(struct net_device *netdev, return nfp_net_set_ring_size(nn, rxd_cnt, txd_cnt); } +static __printf(2, 3) u8 *nfp_pr_et(u8 *data, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsnprintf(data, ETH_GSTRING_LEN, fmt, args); + va_end(args); + + return data + ETH_GSTRING_LEN; +} + static void nfp_net_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { struct nfp_net *nn = netdev_priv(netdev); - u8 *p = data; int i; switch (stringset) { case ETH_SS_STATS: - for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) { - memcpy(p, nfp_net_et_stats[i].name, ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - } + for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) + data = nfp_pr_et(data, nfp_net_et_stats[i].name); + for (i = 0; i < nn->dp.num_r_vecs; i++) { - sprintf(p, "rvec_%u_rx_pkts", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rvec_%u_tx_pkts", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rvec_%u_tx_busy", i); - p += ETH_GSTRING_LEN; + data = nfp_pr_et(data, "rvec_%u_rx_pkts", i); + data = nfp_pr_et(data, "rvec_%u_tx_pkts", i); + data = nfp_pr_et(data, "rvec_%u_tx_busy", i); } - strncpy(p, "hw_rx_csum_ok", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "hw_rx_csum_inner_ok", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "hw_rx_csum_err", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "hw_tx_csum", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "hw_tx_inner_csum", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "tx_gather", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "tx_lso", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; + + data = nfp_pr_et(data, "hw_rx_csum_ok"); + data = nfp_pr_et(data, "hw_rx_csum_inner_ok"); + data = nfp_pr_et(data, "hw_rx_csum_err"); + data = nfp_pr_et(data, "hw_tx_csum"); + data = nfp_pr_et(data, "hw_tx_inner_csum"); + data = nfp_pr_et(data, "tx_gather"); + data = nfp_pr_et(data, "tx_lso"); + for (i = 0; i < nn->dp.num_tx_rings; i++) { - sprintf(p, "txq_%u_pkts", i); - p += ETH_GSTRING_LEN; - sprintf(p, "txq_%u_bytes", i); - p += ETH_GSTRING_LEN; + data = nfp_pr_et(data, "txq_%u_pkts", i); + data = nfp_pr_et(data, "txq_%u_bytes", i); } + for (i = 0; i < nn->dp.num_rx_rings; i++) { - sprintf(p, "rxq_%u_pkts", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rxq_%u_bytes", i); - p += ETH_GSTRING_LEN; + data = nfp_pr_et(data, "rxq_%u_pkts", i); + data = nfp_pr_et(data, "rxq_%u_bytes", i); } + break; } } -- cgit v1.2.3 From 325945ede6d49fd94b79c8c243fc3c0f2db2fcdb Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:16 -0700 Subject: nfp: split software and hardware vNIC statistics In preparation for reporting vNIC HW stats on representors split handling of the SW and HW stats in ethtool -S. Representors don't have SW stats (since vNIC is assigned to the VM). Remove the questionable defines which assume nn variable exists in the scope. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 192 +++++++++++++-------- 1 file changed, 120 insertions(+), 72 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index ba1c28b8791b..169f3e3714fd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -100,11 +100,7 @@ static const struct nfp_et_stat nfp_net_et_stats[] = { }; #define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats) -#define NN_ET_RVEC_STATS_LEN (nn->dp.num_r_vecs * 3) #define NN_ET_RVEC_GATHER_STATS 7 -#define NN_ET_QUEUE_STATS_LEN ((nn->dp.num_tx_rings + nn->dp.num_rx_rings) * 2) -#define NN_ET_STATS_LEN (NN_ET_GLOBAL_STATS_LEN + NN_ET_RVEC_GATHER_STATS + \ - NN_ET_RVEC_STATS_LEN + NN_ET_QUEUE_STATS_LEN) static void nfp_net_get_nspinfo(struct nfp_app *app, char *version) { @@ -346,96 +342,146 @@ static __printf(2, 3) u8 *nfp_pr_et(u8 *data, const char *fmt, ...) return data + ETH_GSTRING_LEN; } -static void nfp_net_get_strings(struct net_device *netdev, - u32 stringset, u8 *data) +static unsigned int nfp_vnic_get_sw_stats_count(struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); - int i; - switch (stringset) { - case ETH_SS_STATS: - for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) - data = nfp_pr_et(data, nfp_net_et_stats[i].name); + return NN_ET_RVEC_GATHER_STATS + nn->dp.num_r_vecs * 3; +} - for (i = 0; i < nn->dp.num_r_vecs; i++) { - data = nfp_pr_et(data, "rvec_%u_rx_pkts", i); - data = nfp_pr_et(data, "rvec_%u_tx_pkts", i); - data = nfp_pr_et(data, "rvec_%u_tx_busy", i); - } +static u8 *nfp_vnic_get_sw_stats_strings(struct net_device *netdev, u8 *data) +{ + struct nfp_net *nn = netdev_priv(netdev); + int i; - data = nfp_pr_et(data, "hw_rx_csum_ok"); - data = nfp_pr_et(data, "hw_rx_csum_inner_ok"); - data = nfp_pr_et(data, "hw_rx_csum_err"); - data = nfp_pr_et(data, "hw_tx_csum"); - data = nfp_pr_et(data, "hw_tx_inner_csum"); - data = nfp_pr_et(data, "tx_gather"); - data = nfp_pr_et(data, "tx_lso"); - - for (i = 0; i < nn->dp.num_tx_rings; i++) { - data = nfp_pr_et(data, "txq_%u_pkts", i); - data = nfp_pr_et(data, "txq_%u_bytes", i); - } + for (i = 0; i < nn->dp.num_r_vecs; i++) { + data = nfp_pr_et(data, "rvec_%u_rx_pkts", i); + data = nfp_pr_et(data, "rvec_%u_tx_pkts", i); + data = nfp_pr_et(data, "rvec_%u_tx_busy", i); + } - for (i = 0; i < nn->dp.num_rx_rings; i++) { - data = nfp_pr_et(data, "rxq_%u_pkts", i); - data = nfp_pr_et(data, "rxq_%u_bytes", i); - } + data = nfp_pr_et(data, "hw_rx_csum_ok"); + data = nfp_pr_et(data, "hw_rx_csum_inner_ok"); + data = nfp_pr_et(data, "hw_rx_csum_err"); + data = nfp_pr_et(data, "hw_tx_csum"); + data = nfp_pr_et(data, "hw_tx_inner_csum"); + data = nfp_pr_et(data, "tx_gather"); + data = nfp_pr_et(data, "tx_lso"); - break; - } + return data; } -static void nfp_net_get_stats(struct net_device *netdev, - struct ethtool_stats *stats, u64 *data) +static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data) { u64 gathered_stats[NN_ET_RVEC_GATHER_STATS] = {}; struct nfp_net *nn = netdev_priv(netdev); u64 tmp[NN_ET_RVEC_GATHER_STATS]; - u8 __iomem *io_p; - int i, j, k; + unsigned int i, j; - for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) { - io_p = nn->dp.ctrl_bar + nfp_net_et_stats[i].off; - data[i] = readq(io_p); - } - for (j = 0; j < nn->dp.num_r_vecs; j++) { + for (i = 0; i < nn->dp.num_r_vecs; i++) { unsigned int start; do { - start = u64_stats_fetch_begin(&nn->r_vecs[j].rx_sync); - data[i++] = nn->r_vecs[j].rx_pkts; - tmp[0] = nn->r_vecs[j].hw_csum_rx_ok; - tmp[1] = nn->r_vecs[j].hw_csum_rx_inner_ok; - tmp[2] = nn->r_vecs[j].hw_csum_rx_error; - } while (u64_stats_fetch_retry(&nn->r_vecs[j].rx_sync, start)); + start = u64_stats_fetch_begin(&nn->r_vecs[i].rx_sync); + *data++ = nn->r_vecs[i].rx_pkts; + tmp[0] = nn->r_vecs[i].hw_csum_rx_ok; + tmp[1] = nn->r_vecs[i].hw_csum_rx_inner_ok; + tmp[2] = nn->r_vecs[i].hw_csum_rx_error; + } while (u64_stats_fetch_retry(&nn->r_vecs[i].rx_sync, start)); do { - start = u64_stats_fetch_begin(&nn->r_vecs[j].tx_sync); - data[i++] = nn->r_vecs[j].tx_pkts; - data[i++] = nn->r_vecs[j].tx_busy; - tmp[3] = nn->r_vecs[j].hw_csum_tx; - tmp[4] = nn->r_vecs[j].hw_csum_tx_inner; - tmp[5] = nn->r_vecs[j].tx_gather; - tmp[6] = nn->r_vecs[j].tx_lso; - } while (u64_stats_fetch_retry(&nn->r_vecs[j].tx_sync, start)); - - for (k = 0; k < NN_ET_RVEC_GATHER_STATS; k++) - gathered_stats[k] += tmp[k]; + start = u64_stats_fetch_begin(&nn->r_vecs[i].tx_sync); + *data++ = nn->r_vecs[i].tx_pkts; + *data++ = nn->r_vecs[i].tx_busy; + tmp[3] = nn->r_vecs[i].hw_csum_tx; + tmp[4] = nn->r_vecs[i].hw_csum_tx_inner; + tmp[5] = nn->r_vecs[i].tx_gather; + tmp[6] = nn->r_vecs[i].tx_lso; + } while (u64_stats_fetch_retry(&nn->r_vecs[i].tx_sync, start)); + + for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++) + gathered_stats[j] += tmp[j]; } + for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++) - data[i++] = gathered_stats[j]; - for (j = 0; j < nn->dp.num_tx_rings; j++) { - io_p = nn->dp.ctrl_bar + NFP_NET_CFG_TXR_STATS(j); - data[i++] = readq(io_p); - io_p = nn->dp.ctrl_bar + NFP_NET_CFG_TXR_STATS(j) + 8; - data[i++] = readq(io_p); + *data++ = gathered_stats[j]; + + return data; +} + +static unsigned int +nfp_vnic_get_hw_stats_count(unsigned int rx_rings, unsigned int tx_rings) +{ + return NN_ET_GLOBAL_STATS_LEN + (rx_rings + tx_rings) * 2; +} + +static u8 * +nfp_vnic_get_hw_stats_strings(u8 *data, unsigned int rx_rings, + unsigned int tx_rings) +{ + int i; + + for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) + data = nfp_pr_et(data, nfp_net_et_stats[i].name); + + for (i = 0; i < tx_rings; i++) { + data = nfp_pr_et(data, "txq_%u_pkts", i); + data = nfp_pr_et(data, "txq_%u_bytes", i); } - for (j = 0; j < nn->dp.num_rx_rings; j++) { - io_p = nn->dp.ctrl_bar + NFP_NET_CFG_RXR_STATS(j); - data[i++] = readq(io_p); - io_p = nn->dp.ctrl_bar + NFP_NET_CFG_RXR_STATS(j) + 8; - data[i++] = readq(io_p); + + for (i = 0; i < rx_rings; i++) { + data = nfp_pr_et(data, "rxq_%u_pkts", i); + data = nfp_pr_et(data, "rxq_%u_bytes", i); } + + return data; +} + +static u64 * +nfp_vnic_get_hw_stats(u64 *data, u8 __iomem *mem, + unsigned int rx_rings, unsigned int tx_rings) +{ + unsigned int i; + + for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) + *data++ = readq(mem + nfp_net_et_stats[i].off); + + for (i = 0; i < tx_rings; i++) { + *data++ = readq(mem + NFP_NET_CFG_TXR_STATS(i)); + *data++ = readq(mem + NFP_NET_CFG_TXR_STATS(i) + 8); + } + + for (i = 0; i < rx_rings; i++) { + *data++ = readq(mem + NFP_NET_CFG_RXR_STATS(i)); + *data++ = readq(mem + NFP_NET_CFG_RXR_STATS(i) + 8); + } + + return data; +} + +static void nfp_net_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + struct nfp_net *nn = netdev_priv(netdev); + + switch (stringset) { + case ETH_SS_STATS: + data = nfp_vnic_get_sw_stats_strings(netdev, data); + data = nfp_vnic_get_hw_stats_strings(data, nn->dp.num_rx_rings, + nn->dp.num_tx_rings); + break; + } +} + +static void +nfp_net_get_stats(struct net_device *netdev, struct ethtool_stats *stats, + u64 *data) +{ + struct nfp_net *nn = netdev_priv(netdev); + + data = nfp_vnic_get_sw_stats(netdev, data); + data = nfp_vnic_get_hw_stats(data, nn->dp.ctrl_bar, + nn->dp.num_rx_rings, nn->dp.num_tx_rings); } static int nfp_net_get_sset_count(struct net_device *netdev, int sset) @@ -444,7 +490,9 @@ static int nfp_net_get_sset_count(struct net_device *netdev, int sset) switch (sset) { case ETH_SS_STATS: - return NN_ET_STATS_LEN; + return nfp_vnic_get_sw_stats_count(netdev) + + nfp_vnic_get_hw_stats_count(nn->dp.num_rx_rings, + nn->dp.num_tx_rings); default: return -EOPNOTSUPP; } -- cgit v1.2.3 From 7344bea1401627cd1b02d19252baa1c40c0fb5e9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:17 -0700 Subject: nfp: store pointer to MAC statistics in nfp_port Store pointer to device memory containing MAC statistics in nfp_port. This simplifies representor code and will be used to dump those statistics in ethtool as well. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_repr.c | 12 ++++-------- drivers/net/ethernet/netronome/nfp/nfp_port.c | 3 +++ drivers/net/ethernet/netronome/nfp/nfp_port.h | 2 ++ 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 50f7cc057cc9..28e932eab812 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -78,12 +78,10 @@ void nfp_repr_inc_rx_stats(struct net_device *netdev, unsigned int len) } static void -nfp_repr_phy_port_get_stats64(const struct nfp_app *app, u8 phy_port, +nfp_repr_phy_port_get_stats64(struct nfp_port *port, struct rtnl_link_stats64 *stats) { - u8 __iomem *mem; - - mem = app->pf->mac_stats_mem + phy_port * NFP_MAC_STATS_SIZE; + u8 __iomem *mem = port->eth_stats; /* TX and RX stats are flipped as we are returning the stats as seen * at the switch port corresponding to the phys port. @@ -141,7 +139,6 @@ static void nfp_repr_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) { struct nfp_repr *repr = netdev_priv(netdev); - struct nfp_eth_table_port *eth_port; struct nfp_app *app = repr->app; if (WARN_ON(!repr->port)) @@ -149,10 +146,9 @@ nfp_repr_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) switch (repr->port->type) { case NFP_PORT_PHYS_PORT: - eth_port = __nfp_port_get_eth_port(repr->port); - if (!eth_port) + if (!__nfp_port_get_eth_port(repr->port)) break; - nfp_repr_phy_port_get_stats64(app, eth_port->index, stats); + nfp_repr_phy_port_get_stats64(repr->port, stats); break; case NFP_PORT_PF_PORT: nfp_repr_pf_get_stats64(app, repr->port->pf_id, stats); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index 0cf65e57addb..34a6e035fe9a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -225,6 +225,9 @@ int nfp_port_init_phy_port(struct nfp_pf *pf, struct nfp_app *app, port->eth_port = &pf->eth_tbl->ports[id]; port->eth_id = pf->eth_tbl->ports[id].index; + if (pf->mac_stats_mem) + port->eth_stats = + pf->mac_stats_mem + port->eth_id * NFP_MAC_STATS_SIZE; return 0; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 784d82c2f32c..cf71ac065e85 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -76,6 +76,7 @@ enum nfp_port_flags { * @dl_port: devlink port structure * @eth_id: for %NFP_PORT_PHYS_PORT port ID in NFP enumeration scheme * @eth_port: for %NFP_PORT_PHYS_PORT translated ETH Table port entry + * @eth_stats: for %NFP_PORT_PHYS_PORT MAC stats if available * @pf_id: for %NFP_PORT_PF_PORT, %NFP_PORT_VF_PORT ID of the PCI PF (0-3) * @vf_id: for %NFP_PORT_VF_PORT ID of the PCI VF within @pf_id * @port_list: entry on pf's list of ports @@ -95,6 +96,7 @@ struct nfp_port { struct { unsigned int eth_id; struct nfp_eth_table_port *eth_port; + u8 __iomem *eth_stats; }; /* NFP_PORT_PF_PORT, NFP_PORT_VF_PORT */ struct { -- cgit v1.2.3 From 098ce840c9ef7b474437a766a0f9759b5c3e1ae6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:18 -0700 Subject: nfp: report MAC statistics in ethtool Add reporting of MAC statistics in ethtool. MAC statistics are read out from the MAC IP and accumulated by application FW, therefore their presence depends on the application FW. Add missing defines and string names for the statistics and dump them in ethtool -S. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 126 ++++++++++++++++++++- drivers/net/ethernet/netronome/nfp/nfp_port.h | 44 ++++--- 2 files changed, 153 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 169f3e3714fd..f33c341844be 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -99,6 +99,86 @@ static const struct nfp_et_stat nfp_net_et_stats[] = { { "bpf_app3_bytes", NFP_NET_CFG_STATS_APP3_BYTES }, }; +static const struct nfp_et_stat nfp_mac_et_stats[] = { + { "rx_octets", NFP_MAC_STATS_RX_IN_OCTETS, }, + { "rx_frame_too_long_errors", + NFP_MAC_STATS_RX_FRAME_TOO_LONG_ERRORS, }, + { "rx_range_length_errors", NFP_MAC_STATS_RX_RANGE_LENGTH_ERRORS, }, + { "rx_vlan_reveive_ok", NFP_MAC_STATS_RX_VLAN_REVEIVE_OK, }, + { "rx_errors", NFP_MAC_STATS_RX_IN_ERRORS, }, + { "rx_broadcast_pkts", NFP_MAC_STATS_RX_IN_BROADCAST_PKTS, }, + { "rx_drop_events", NFP_MAC_STATS_RX_DROP_EVENTS, }, + { "rx_alignment_errors", NFP_MAC_STATS_RX_ALIGNMENT_ERRORS, }, + { "rx_pause_mac_ctrl_frames", + NFP_MAC_STATS_RX_PAUSE_MAC_CTRL_FRAMES, }, + { "rx_frames_received_ok", NFP_MAC_STATS_RX_FRAMES_RECEIVED_OK, }, + { "rx_frame_check_sequence_errors", + NFP_MAC_STATS_RX_FRAME_CHECK_SEQUENCE_ERRORS, }, + { "rx_unicast_pkts", NFP_MAC_STATS_RX_UNICAST_PKTS, }, + { "rx_multicast_pkts", NFP_MAC_STATS_RX_MULTICAST_PKTS, }, + { "rx_pkts", NFP_MAC_STATS_RX_PKTS, }, + { "rx_undersize_pkts", NFP_MAC_STATS_RX_UNDERSIZE_PKTS, }, + { "rx_pkts_64_octets", NFP_MAC_STATS_RX_PKTS_64_OCTETS, }, + { "rx_pkts_65_to_127_octets", + NFP_MAC_STATS_RX_PKTS_65_TO_127_OCTETS, }, + { "rx_pkts_128_to_255_octets", + NFP_MAC_STATS_RX_PKTS_128_TO_255_OCTETS, }, + { "rx_pkts_256_to_511_octets", + NFP_MAC_STATS_RX_PKTS_256_TO_511_OCTETS, }, + { "rx_pkts_512_to_1023_octets", + NFP_MAC_STATS_RX_PKTS_512_TO_1023_OCTETS, }, + { "rx_pkts_1024_to_1518_octets", + NFP_MAC_STATS_RX_PKTS_1024_TO_1518_OCTETS, }, + { "rx_pkts_1519_to_max_octets", + NFP_MAC_STATS_RX_PKTS_1519_TO_MAX_OCTETS, }, + { "rx_jabbers", NFP_MAC_STATS_RX_JABBERS, }, + { "rx_fragments", NFP_MAC_STATS_RX_FRAGMENTS, }, + { "rx_oversize_pkts", NFP_MAC_STATS_RX_OVERSIZE_PKTS, }, + { "rx_pause_frames_class0", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS0, }, + { "rx_pause_frames_class1", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS1, }, + { "rx_pause_frames_class2", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS2, }, + { "rx_pause_frames_class3", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS3, }, + { "rx_pause_frames_class4", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS4, }, + { "rx_pause_frames_class5", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS5, }, + { "rx_pause_frames_class6", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS6, }, + { "rx_pause_frames_class7", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS7, }, + { "rx_mac_ctrl_frames_received", + NFP_MAC_STATS_RX_MAC_CTRL_FRAMES_RECEIVED, }, + { "rx_mac_head_drop", NFP_MAC_STATS_RX_MAC_HEAD_DROP, }, + { "tx_queue_drop", NFP_MAC_STATS_TX_QUEUE_DROP, }, + { "tx_octets", NFP_MAC_STATS_TX_OUT_OCTETS, }, + { "tx_vlan_transmitted_ok", NFP_MAC_STATS_TX_VLAN_TRANSMITTED_OK, }, + { "tx_errors", NFP_MAC_STATS_TX_OUT_ERRORS, }, + { "tx_broadcast_pkts", NFP_MAC_STATS_TX_BROADCAST_PKTS, }, + { "tx_pause_mac_ctrl_frames", + NFP_MAC_STATS_TX_PAUSE_MAC_CTRL_FRAMES, }, + { "tx_frames_transmitted_ok", + NFP_MAC_STATS_TX_FRAMES_TRANSMITTED_OK, }, + { "tx_unicast_pkts", NFP_MAC_STATS_TX_UNICAST_PKTS, }, + { "tx_multicast_pkts", NFP_MAC_STATS_TX_MULTICAST_PKTS, }, + { "tx_pkts_64_octets", NFP_MAC_STATS_TX_PKTS_64_OCTETS, }, + { "tx_pkts_65_to_127_octets", + NFP_MAC_STATS_TX_PKTS_65_TO_127_OCTETS, }, + { "tx_pkts_128_to_255_octets", + NFP_MAC_STATS_TX_PKTS_128_TO_255_OCTETS, }, + { "tx_pkts_256_to_511_octets", + NFP_MAC_STATS_TX_PKTS_256_TO_511_OCTETS, }, + { "tx_pkts_512_to_1023_octets", + NFP_MAC_STATS_TX_PKTS_512_TO_1023_OCTETS, }, + { "tx_pkts_1024_to_1518_octets", + NFP_MAC_STATS_TX_PKTS_1024_TO_1518_OCTETS, }, + { "tx_pkts_1519_to_max_octets", + NFP_MAC_STATS_TX_PKTS_1519_TO_MAX_OCTETS, }, + { "tx_pause_frames_class0", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS0, }, + { "tx_pause_frames_class1", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS1, }, + { "tx_pause_frames_class2", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS2, }, + { "tx_pause_frames_class3", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS3, }, + { "tx_pause_frames_class4", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS4, }, + { "tx_pause_frames_class5", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS5, }, + { "tx_pause_frames_class6", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS6, }, + { "tx_pause_frames_class7", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS7, }, +}; + #define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats) #define NN_ET_RVEC_GATHER_STATS 7 @@ -459,6 +539,47 @@ nfp_vnic_get_hw_stats(u64 *data, u8 __iomem *mem, return data; } +static unsigned int nfp_mac_get_stats_count(struct net_device *netdev) +{ + struct nfp_port *port; + + port = nfp_port_from_netdev(netdev); + if (!__nfp_port_get_eth_port(port) || !port->eth_stats) + return 0; + + return ARRAY_SIZE(nfp_mac_et_stats); +} + +static u8 *nfp_mac_get_stats_strings(struct net_device *netdev, u8 *data) +{ + struct nfp_port *port; + unsigned int i; + + port = nfp_port_from_netdev(netdev); + if (!__nfp_port_get_eth_port(port) || !port->eth_stats) + return data; + + for (i = 0; i < ARRAY_SIZE(nfp_mac_et_stats); i++) + data = nfp_pr_et(data, "mac.%s", nfp_mac_et_stats[i].name); + + return data; +} + +static u64 *nfp_mac_get_stats(struct net_device *netdev, u64 *data) +{ + struct nfp_port *port; + unsigned int i; + + port = nfp_port_from_netdev(netdev); + if (!__nfp_port_get_eth_port(port) || !port->eth_stats) + return data; + + for (i = 0; i < ARRAY_SIZE(nfp_mac_et_stats); i++) + *data++ = readq(port->eth_stats + nfp_mac_et_stats[i].off); + + return data; +} + static void nfp_net_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { @@ -469,6 +590,7 @@ static void nfp_net_get_strings(struct net_device *netdev, data = nfp_vnic_get_sw_stats_strings(netdev, data); data = nfp_vnic_get_hw_stats_strings(data, nn->dp.num_rx_rings, nn->dp.num_tx_rings); + data = nfp_mac_get_stats_strings(netdev, data); break; } } @@ -482,6 +604,7 @@ nfp_net_get_stats(struct net_device *netdev, struct ethtool_stats *stats, data = nfp_vnic_get_sw_stats(netdev, data); data = nfp_vnic_get_hw_stats(data, nn->dp.ctrl_bar, nn->dp.num_rx_rings, nn->dp.num_tx_rings); + data = nfp_mac_get_stats(netdev, data); } static int nfp_net_get_sset_count(struct net_device *netdev, int sset) @@ -492,7 +615,8 @@ static int nfp_net_get_sset_count(struct net_device *netdev, int sset) case ETH_SS_STATS: return nfp_vnic_get_sw_stats_count(netdev) + nfp_vnic_get_hw_stats_count(nn->dp.num_rx_rings, - nn->dp.num_tx_rings); + nn->dp.num_tx_rings) + + nfp_mac_get_stats_count(netdev); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index cf71ac065e85..de8ec609b57e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -147,31 +147,32 @@ void nfp_devlink_port_unregister(struct nfp_port *port); #define NFP_MAC_STATS_SIZE 0x0200 #define NFP_MAC_STATS_RX_IN_OCTETS (NFP_MAC_STATS_BASE + 0x000) + /* unused 0x008 */ #define NFP_MAC_STATS_RX_FRAME_TOO_LONG_ERRORS (NFP_MAC_STATS_BASE + 0x010) #define NFP_MAC_STATS_RX_RANGE_LENGTH_ERRORS (NFP_MAC_STATS_BASE + 0x018) #define NFP_MAC_STATS_RX_VLAN_REVEIVE_OK (NFP_MAC_STATS_BASE + 0x020) #define NFP_MAC_STATS_RX_IN_ERRORS (NFP_MAC_STATS_BASE + 0x028) #define NFP_MAC_STATS_RX_IN_BROADCAST_PKTS (NFP_MAC_STATS_BASE + 0x030) -#define NFP_MAC_STATS_RX_STATS_DROP_EVENTS (NFP_MAC_STATS_BASE + 0x038) +#define NFP_MAC_STATS_RX_DROP_EVENTS (NFP_MAC_STATS_BASE + 0x038) #define NFP_MAC_STATS_RX_ALIGNMENT_ERRORS (NFP_MAC_STATS_BASE + 0x040) #define NFP_MAC_STATS_RX_PAUSE_MAC_CTRL_FRAMES (NFP_MAC_STATS_BASE + 0x048) #define NFP_MAC_STATS_RX_FRAMES_RECEIVED_OK (NFP_MAC_STATS_BASE + 0x050) #define NFP_MAC_STATS_RX_FRAME_CHECK_SEQUENCE_ERRORS (NFP_MAC_STATS_BASE + 0x058) #define NFP_MAC_STATS_RX_UNICAST_PKTS (NFP_MAC_STATS_BASE + 0x060) #define NFP_MAC_STATS_RX_MULTICAST_PKTS (NFP_MAC_STATS_BASE + 0x068) -#define NFP_MAC_STATS_RX_STATS_PKTS (NFP_MAC_STATS_BASE + 0x070) -#define NFP_MAC_STATS_RX_STATS_UNDERSIZE_PKTS (NFP_MAC_STATS_BASE + 0x078) -#define NFP_MAC_STATS_RX_STATS_PKTS_64_OCTETS (NFP_MAC_STATS_BASE + 0x080) -#define NFP_MAC_STATS_RX_STATS_PKTS_65_TO_127_OCTETS (NFP_MAC_STATS_BASE + 0x088) -#define NFP_MAC_STATS_RX_STATS_PKTS_512_TO_1023_OCTETS (NFP_MAC_STATS_BASE + 0x090) -#define NFP_MAC_STATS_RX_STATS_PKTS_1024_TO_1518_OCTETS (NFP_MAC_STATS_BASE + 0x098) -#define NFP_MAC_STATS_RX_STATS_JABBERS (NFP_MAC_STATS_BASE + 0x0a0) -#define NFP_MAC_STATS_RX_STATS_FRAGMENTS (NFP_MAC_STATS_BASE + 0x0a8) +#define NFP_MAC_STATS_RX_PKTS (NFP_MAC_STATS_BASE + 0x070) +#define NFP_MAC_STATS_RX_UNDERSIZE_PKTS (NFP_MAC_STATS_BASE + 0x078) +#define NFP_MAC_STATS_RX_PKTS_64_OCTETS (NFP_MAC_STATS_BASE + 0x080) +#define NFP_MAC_STATS_RX_PKTS_65_TO_127_OCTETS (NFP_MAC_STATS_BASE + 0x088) +#define NFP_MAC_STATS_RX_PKTS_512_TO_1023_OCTETS (NFP_MAC_STATS_BASE + 0x090) +#define NFP_MAC_STATS_RX_PKTS_1024_TO_1518_OCTETS (NFP_MAC_STATS_BASE + 0x098) +#define NFP_MAC_STATS_RX_JABBERS (NFP_MAC_STATS_BASE + 0x0a0) +#define NFP_MAC_STATS_RX_FRAGMENTS (NFP_MAC_STATS_BASE + 0x0a8) #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS2 (NFP_MAC_STATS_BASE + 0x0b0) #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS3 (NFP_MAC_STATS_BASE + 0x0b8) -#define NFP_MAC_STATS_RX_STATS_PKTS_128_TO_255_OCTETS (NFP_MAC_STATS_BASE + 0x0c0) -#define NFP_MAC_STATS_RX_STATS_PKTS_256_TO_511_OCTETS (NFP_MAC_STATS_BASE + 0x0c8) -#define NFP_MAC_STATS_RX_STATS_PKTS_1519_TO_MAX_OCTETS (NFP_MAC_STATS_BASE + 0x0d0) +#define NFP_MAC_STATS_RX_PKTS_128_TO_255_OCTETS (NFP_MAC_STATS_BASE + 0x0c0) +#define NFP_MAC_STATS_RX_PKTS_256_TO_511_OCTETS (NFP_MAC_STATS_BASE + 0x0c8) +#define NFP_MAC_STATS_RX_PKTS_1519_TO_MAX_OCTETS (NFP_MAC_STATS_BASE + 0x0d0) #define NFP_MAC_STATS_RX_OVERSIZE_PKTS (NFP_MAC_STATS_BASE + 0x0d8) #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS0 (NFP_MAC_STATS_BASE + 0x0e0) #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS1 (NFP_MAC_STATS_BASE + 0x0e8) @@ -181,9 +182,12 @@ void nfp_devlink_port_unregister(struct nfp_port *port); #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS7 (NFP_MAC_STATS_BASE + 0x108) #define NFP_MAC_STATS_RX_MAC_CTRL_FRAMES_RECEIVED (NFP_MAC_STATS_BASE + 0x110) #define NFP_MAC_STATS_RX_MAC_HEAD_DROP (NFP_MAC_STATS_BASE + 0x118) - + /* unused 0x120 */ + /* unused 0x128 */ + /* unused 0x130 */ #define NFP_MAC_STATS_TX_QUEUE_DROP (NFP_MAC_STATS_BASE + 0x138) #define NFP_MAC_STATS_TX_OUT_OCTETS (NFP_MAC_STATS_BASE + 0x140) + /* unused 0x148 */ #define NFP_MAC_STATS_TX_VLAN_TRANSMITTED_OK (NFP_MAC_STATS_BASE + 0x150) #define NFP_MAC_STATS_TX_OUT_ERRORS (NFP_MAC_STATS_BASE + 0x158) #define NFP_MAC_STATS_TX_BROADCAST_PKTS (NFP_MAC_STATS_BASE + 0x160) @@ -195,8 +199,16 @@ void nfp_devlink_port_unregister(struct nfp_port *port); #define NFP_MAC_STATS_TX_UNICAST_PKTS (NFP_MAC_STATS_BASE + 0x190) #define NFP_MAC_STATS_TX_MULTICAST_PKTS (NFP_MAC_STATS_BASE + 0x198) #define NFP_MAC_STATS_TX_PKTS_65_TO_127_OCTETS (NFP_MAC_STATS_BASE + 0x1a0) -#define NFP_MAC_STATS_TX_PKTS_127_TO_512_OCTETS (NFP_MAC_STATS_BASE + 0x1a8) -#define NFP_MAC_STATS_TX_PKTS_128_TO_1518_OCTETS (NFP_MAC_STATS_BASE + 0x1b0) -#define NFP_MAC_STATS_TX_PKTS_1518_TO_MAX_OCTETS (NFP_MAC_STATS_BASE + 0x1b8) +#define NFP_MAC_STATS_TX_PKTS_128_TO_255_OCTETS (NFP_MAC_STATS_BASE + 0x1a8) +#define NFP_MAC_STATS_TX_PKTS_1024_TO_1518_OCTETS (NFP_MAC_STATS_BASE + 0x1b0) +#define NFP_MAC_STATS_TX_PKTS_1519_TO_MAX_OCTETS (NFP_MAC_STATS_BASE + 0x1b8) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS0 (NFP_MAC_STATS_BASE + 0x1c0) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS1 (NFP_MAC_STATS_BASE + 0x1c8) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS4 (NFP_MAC_STATS_BASE + 0x1d0) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS5 (NFP_MAC_STATS_BASE + 0x1d8) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS2 (NFP_MAC_STATS_BASE + 0x1e0) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS3 (NFP_MAC_STATS_BASE + 0x1e8) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS6 (NFP_MAC_STATS_BASE + 0x1f0) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS7 (NFP_MAC_STATS_BASE + 0x1f8) #endif -- cgit v1.2.3 From ef0ec676a7181d418bb9051ccfe1cd13a1f47ab2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:19 -0700 Subject: nfp: add pointer to vNIC config memory to nfp_port structure Simplify the statistics handling code by keeping pointer to vNIC's config memory in nfp_port. Note that this is referring to the representor side of vNICs, vNIC side has the pointer in nfp_net. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/flower/main.c | 8 +++- drivers/net/ethernet/netronome/nfp/nfp_net_repr.c | 45 +++++------------------ drivers/net/ethernet/netronome/nfp/nfp_port.h | 2 + 3 files changed, 18 insertions(+), 37 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index 3088e959f2a3..126a6b5233bf 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -159,12 +159,18 @@ nfp_flower_spawn_vnic_reprs(struct nfp_app *app, goto err_reprs_clean; } + /* For now we only support 1 PF */ + WARN_ON(repr_type == NFP_REPR_TYPE_PF && i); + port = nfp_port_alloc(app, port_type, reprs->reprs[i]); if (repr_type == NFP_REPR_TYPE_PF) { port->pf_id = i; + port->vnic = priv->nn->dp.ctrl_bar; } else { - port->pf_id = 0; /* For now we only support 1 PF */ + port->pf_id = 0; port->vf_id = i; + port->vnic = + app->pf->vf_cfg_mem + i * NFP_NET_CFG_BAR_SZ; } eth_hw_addr_random(reprs->reprs[i]); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 28e932eab812..0f9878d1bf40 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -96,50 +96,25 @@ nfp_repr_phy_port_get_stats64(struct nfp_port *port, } static void -nfp_repr_vf_get_stats64(const struct nfp_app *app, u8 vf, - struct rtnl_link_stats64 *stats) +nfp_repr_vnic_get_stats64(struct nfp_port *port, + struct rtnl_link_stats64 *stats) { - u8 __iomem *mem; - - mem = app->pf->vf_cfg_mem + vf * NFP_NET_CFG_BAR_SZ; - /* TX and RX stats are flipped as we are returning the stats as seen * at the switch port corresponding to the VF. */ - stats->tx_packets = readq(mem + NFP_NET_CFG_STATS_RX_FRAMES); - stats->tx_bytes = readq(mem + NFP_NET_CFG_STATS_RX_OCTETS); - stats->tx_dropped = readq(mem + NFP_NET_CFG_STATS_RX_DISCARDS); + stats->tx_packets = readq(port->vnic + NFP_NET_CFG_STATS_RX_FRAMES); + stats->tx_bytes = readq(port->vnic + NFP_NET_CFG_STATS_RX_OCTETS); + stats->tx_dropped = readq(port->vnic + NFP_NET_CFG_STATS_RX_DISCARDS); - stats->rx_packets = readq(mem + NFP_NET_CFG_STATS_TX_FRAMES); - stats->rx_bytes = readq(mem + NFP_NET_CFG_STATS_TX_OCTETS); - stats->rx_dropped = readq(mem + NFP_NET_CFG_STATS_TX_DISCARDS); -} - -static void -nfp_repr_pf_get_stats64(const struct nfp_app *app, u8 pf, - struct rtnl_link_stats64 *stats) -{ - u8 __iomem *mem; - - if (pf) - return; - - mem = nfp_cpp_area_iomem(app->pf->data_vnic_bar); - - stats->tx_packets = readq(mem + NFP_NET_CFG_STATS_RX_FRAMES); - stats->tx_bytes = readq(mem + NFP_NET_CFG_STATS_RX_OCTETS); - stats->tx_dropped = readq(mem + NFP_NET_CFG_STATS_RX_DISCARDS); - - stats->rx_packets = readq(mem + NFP_NET_CFG_STATS_TX_FRAMES); - stats->rx_bytes = readq(mem + NFP_NET_CFG_STATS_TX_OCTETS); - stats->rx_dropped = readq(mem + NFP_NET_CFG_STATS_TX_DISCARDS); + stats->rx_packets = readq(port->vnic + NFP_NET_CFG_STATS_TX_FRAMES); + stats->rx_bytes = readq(port->vnic + NFP_NET_CFG_STATS_TX_OCTETS); + stats->rx_dropped = readq(port->vnic + NFP_NET_CFG_STATS_TX_DISCARDS); } static void nfp_repr_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) { struct nfp_repr *repr = netdev_priv(netdev); - struct nfp_app *app = repr->app; if (WARN_ON(!repr->port)) return; @@ -151,10 +126,8 @@ nfp_repr_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) nfp_repr_phy_port_get_stats64(repr->port, stats); break; case NFP_PORT_PF_PORT: - nfp_repr_pf_get_stats64(app, repr->port->pf_id, stats); - break; case NFP_PORT_VF_PORT: - nfp_repr_vf_get_stats64(app, repr->port->vf_id, stats); + nfp_repr_vnic_get_stats64(repr->port, stats); default: break; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index de8ec609b57e..81c034018133 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -79,6 +79,7 @@ enum nfp_port_flags { * @eth_stats: for %NFP_PORT_PHYS_PORT MAC stats if available * @pf_id: for %NFP_PORT_PF_PORT, %NFP_PORT_VF_PORT ID of the PCI PF (0-3) * @vf_id: for %NFP_PORT_VF_PORT ID of the PCI VF within @pf_id + * @vnic: for %NFP_PORT_PF_PORT, %NFP_PORT_VF_PORT vNIC ctrl memory * @port_list: entry on pf's list of ports */ struct nfp_port { @@ -102,6 +103,7 @@ struct nfp_port { struct { unsigned int pf_id; unsigned int vf_id; + u8 __iomem *vnic; }; }; -- cgit v1.2.3 From 899a37ade8c6dd1619d510c1e3b4b99d508272a9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:20 -0700 Subject: nfp: add ethtool statistics for representors Representors may be associated with both VFs or more importantly with physical ports. Allow vNIC and MAC statistics to be read with ethtool -S on representors. In case of vNICs we reuse the vNIC statistic helper, we just need to swap RX and TX to give statistics the "switch perspective." Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 70 ++++++++++++++++++++-- drivers/net/ethernet/netronome/nfp/nfp_port.h | 5 ++ 2 files changed, 71 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index f33c341844be..07969f06df10 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -180,6 +180,7 @@ static const struct nfp_et_stat nfp_mac_et_stats[] = { }; #define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats) +#define NN_ET_SWITCH_STATS_LEN 9 #define NN_ET_RVEC_GATHER_STATS 7 static void nfp_net_get_nspinfo(struct nfp_app *app, char *version) @@ -497,11 +498,24 @@ nfp_vnic_get_hw_stats_count(unsigned int rx_rings, unsigned int tx_rings) static u8 * nfp_vnic_get_hw_stats_strings(u8 *data, unsigned int rx_rings, - unsigned int tx_rings) + unsigned int tx_rings, bool repr) { - int i; + int swap_off, i; - for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) + BUILD_BUG_ON(NN_ET_GLOBAL_STATS_LEN < NN_ET_SWITCH_STATS_LEN * 2); + /* If repr is true first add SWITCH_STATS_LEN and then subtract it + * effectively swapping the RX and TX statistics (giving us the RX + * and TX from perspective of the switch). + */ + swap_off = repr * NN_ET_SWITCH_STATS_LEN; + + for (i = 0; i < NN_ET_SWITCH_STATS_LEN; i++) + data = nfp_pr_et(data, nfp_net_et_stats[i + swap_off].name); + + for (i = NN_ET_SWITCH_STATS_LEN; i < NN_ET_SWITCH_STATS_LEN * 2; i++) + data = nfp_pr_et(data, nfp_net_et_stats[i - swap_off].name); + + for (i = NN_ET_SWITCH_STATS_LEN * 2; i < NN_ET_GLOBAL_STATS_LEN; i++) data = nfp_pr_et(data, nfp_net_et_stats[i].name); for (i = 0; i < tx_rings; i++) { @@ -589,7 +603,8 @@ static void nfp_net_get_strings(struct net_device *netdev, case ETH_SS_STATS: data = nfp_vnic_get_sw_stats_strings(netdev, data); data = nfp_vnic_get_hw_stats_strings(data, nn->dp.num_rx_rings, - nn->dp.num_tx_rings); + nn->dp.num_tx_rings, + false); data = nfp_mac_get_stats_strings(netdev, data); break; } @@ -622,6 +637,50 @@ static int nfp_net_get_sset_count(struct net_device *netdev, int sset) } } +static void nfp_port_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + struct nfp_port *port = nfp_port_from_netdev(netdev); + + switch (stringset) { + case ETH_SS_STATS: + if (nfp_port_is_vnic(port)) + data = nfp_vnic_get_hw_stats_strings(data, 0, 0, true); + else + data = nfp_mac_get_stats_strings(netdev, data); + break; + } +} + +static void +nfp_port_get_stats(struct net_device *netdev, struct ethtool_stats *stats, + u64 *data) +{ + struct nfp_port *port = nfp_port_from_netdev(netdev); + + if (nfp_port_is_vnic(port)) + data = nfp_vnic_get_hw_stats(data, port->vnic, 0, 0); + else + data = nfp_mac_get_stats(netdev, data); +} + +static int nfp_port_get_sset_count(struct net_device *netdev, int sset) +{ + struct nfp_port *port = nfp_port_from_netdev(netdev); + unsigned int count; + + switch (sset) { + case ETH_SS_STATS: + if (nfp_port_is_vnic(port)) + count = nfp_vnic_get_hw_stats_count(0, 0); + else + count = nfp_mac_get_stats_count(netdev); + return count; + default: + return -EOPNOTSUPP; + } +} + /* RX network flow classification (RSS, filters, etc) */ static u32 ethtool_flow_to_nfp_flag(u32 flow_type) @@ -1085,6 +1144,9 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { const struct ethtool_ops nfp_port_ethtool_ops = { .get_drvinfo = nfp_app_get_drvinfo, .get_link = ethtool_op_get_link, + .get_strings = nfp_port_get_strings, + .get_ethtool_stats = nfp_port_get_stats, + .get_sset_count = nfp_port_get_sset_count, .set_dump = nfp_app_set_dump, .get_dump_flag = nfp_app_get_dump_flag, .get_dump_data = nfp_app_get_dump_data, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 81c034018133..51dcb9c603ee 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -116,6 +116,11 @@ extern const struct switchdev_ops nfp_port_switchdev_ops; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, void *type_data); +static inline bool nfp_port_is_vnic(const struct nfp_port *port) +{ + return port->type == NFP_PORT_PF_PORT || port->type == NFP_PORT_VF_PORT; +} + struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); struct nfp_port * nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id); -- cgit v1.2.3 From 825b18ab24a1762a07e8202999cd8eb8eb8d505d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:21 -0700 Subject: nfp: fix copy paste in names and messages regarding vNICs Data and control vNICs currently use the same area name and error message. This could lead to confusion. Make sure the error message says "ctrl" in case of control and the data area is called "nfp.bar0". Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index d5e2361f0e86..acdad6f20251 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -388,7 +388,7 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride) NFP_PF_CSR_SLICE_SIZE, &pf->ctrl_vnic_bar); if (IS_ERR(ctrl_bar)) { - nfp_err(pf->cpp, "Failed to find data vNIC memory symbol\n"); + nfp_err(pf->cpp, "Failed to find ctrl vNIC memory symbol\n"); err = PTR_ERR(ctrl_bar); goto err_app_clean; } @@ -504,7 +504,7 @@ static int nfp_net_pci_map_mem(struct nfp_pf *pf) int err; min_size = pf->max_data_vnics * NFP_PF_CSR_SLICE_SIZE; - mem = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%d_net_bar0", + mem = nfp_net_pf_map_rtsym(pf, "net.bar0", "_pf%d_net_bar0", min_size, &pf->data_vnic_bar); if (IS_ERR(mem)) { nfp_err(pf->cpp, "Failed to find data vNIC memory symbol\n"); -- cgit v1.2.3 From 85d8e2ba7060ced6b23a2b1a2a8dd5cdaf951fb9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 18 Aug 2017 15:48:22 -0700 Subject: nfp: don't reuse pointers in ring dumping We were reusing skb pointer when reading page frag, since ring entries contain a union of a skb and frag pointer. This can be confusing to people reading the code. Refactor the code to read frag pointer directly. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index 40217ece5fcb..cf81cf95d1d8 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -125,7 +125,6 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) struct nfp_net_tx_ring *tx_ring; struct nfp_net_tx_desc *txd; int d_rd_p, d_wr_p, txd_cnt; - struct sk_buff *skb; struct nfp_net *nn; int i; @@ -158,13 +157,15 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) txd->vals[0], txd->vals[1], txd->vals[2], txd->vals[3]); - skb = READ_ONCE(tx_ring->txbufs[i].skb); - if (skb) { - if (tx_ring == r_vec->tx_ring) + if (tx_ring == r_vec->tx_ring) { + struct sk_buff *skb = READ_ONCE(tx_ring->txbufs[i].skb); + + if (skb) seq_printf(file, " skb->head=%p skb->data=%p", skb->head, skb->data); - else - seq_printf(file, " frag=%p", skb); + } else { + seq_printf(file, " frag=%p", + READ_ONCE(tx_ring->txbufs[i].frag)); } if (tx_ring->txbufs[i].dma_addr) -- cgit v1.2.3 From 4ac5bc34fca3d75c470139d2b53ccca251a6031f Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sat, 19 Aug 2017 12:21:43 +0530 Subject: net: 3c509: constify eisa_device_id eisa_device_id are not supposed to change at runtime. All functions working with eisa_device_id provided by work with const eisa_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/3c509.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index 077d01d9f141..b223769d6a5e 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -474,7 +474,7 @@ static int pnp_registered; #endif /* CONFIG_PNP */ #ifdef CONFIG_EISA -static struct eisa_device_id el3_eisa_ids[] = { +static const struct eisa_device_id el3_eisa_ids[] = { { "TCM5090" }, { "TCM5091" }, { "TCM5092" }, -- cgit v1.2.3 From 670af5ed4c55e0d7c9aa295e74867cbebbb29ce5 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sat, 19 Aug 2017 12:22:13 +0530 Subject: net: 3c59x: constify eisa_device_id eisa_device_id are not supposed to change at runtime. All functions working with eisa_device_id provided by work with const eisa_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/3c59x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 3b516ebeeddb..402d9090ad29 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -900,7 +900,7 @@ static const struct dev_pm_ops vortex_pm_ops = { #endif /* !CONFIG_PM */ #ifdef CONFIG_EISA -static struct eisa_device_id vortex_eisa_ids[] = { +static const struct eisa_device_id vortex_eisa_ids[] = { { "TCM5920", CH_3C592 }, { "TCM5970", CH_3C597 }, { "" } -- cgit v1.2.3 From f98dfa4a5f83fdc2eb26d31c25924aeb84739e06 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sat, 19 Aug 2017 12:22:44 +0530 Subject: net: de4x5: constify eisa_device_id eisa_device_id are not supposed to change at runtime. All functions working with eisa_device_id provided by work with const eisa_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/dec/tulip/de4x5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c index 47be5018d35d..0affee9c8aa2 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.c +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -2094,7 +2094,7 @@ static int de4x5_eisa_remove(struct device *device) return 0; } -static struct eisa_device_id de4x5_eisa_ids[] = { +static const struct eisa_device_id de4x5_eisa_ids[] = { { "DEC4250", 0 }, /* 0 is the board name index... */ { "" } }; -- cgit v1.2.3 From 33f14384dde8a848d68c9e0224477e29babb233a Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sat, 19 Aug 2017 12:23:14 +0530 Subject: net: hp100: constify eisa_device_id eisa_device_id are not supposed to change at runtime. All functions working with eisa_device_id provided by work with const eisa_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/hp/hp100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hp/hp100.c b/drivers/net/ethernet/hp/hp100.c index c6164a98f257..c8c7ad2eff77 100644 --- a/drivers/net/ethernet/hp/hp100.c +++ b/drivers/net/ethernet/hp/hp100.c @@ -194,7 +194,7 @@ static const char *hp100_isa_tbl[] = { }; #endif -static struct eisa_device_id hp100_eisa_tbl[] = { +static const struct eisa_device_id hp100_eisa_tbl[] = { { "HWPF180" }, /* HP J2577 rev A */ { "HWP1920" }, /* HP 27248B */ { "HWP1940" }, /* HP J2577 */ -- cgit v1.2.3 From 46d9ceaad0b092ea17c90a7ce2608eba57e9adc3 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sat, 19 Aug 2017 12:23:34 +0530 Subject: net: defxx: constify eisa_device_id eisa_device_id are not supposed to change at runtime. All functions working with eisa_device_id provided by work with const eisa_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Reviewed-by: Maciej W. Rozycki Signed-off-by: David S. Miller --- drivers/net/fddi/defxx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/fddi/defxx.c b/drivers/net/fddi/defxx.c index f4a816cf012a..61fceee73c1b 100644 --- a/drivers/net/fddi/defxx.c +++ b/drivers/net/fddi/defxx.c @@ -3767,7 +3767,7 @@ static void dfx_pci_unregister(struct pci_dev *pdev) #endif /* CONFIG_PCI */ #ifdef CONFIG_EISA -static struct eisa_device_id dfx_eisa_table[] = { +static const struct eisa_device_id dfx_eisa_table[] = { { "DEC3001", DEFEA_PROD_ID_1 }, { "DEC3002", DEFEA_PROD_ID_2 }, { "DEC3003", DEFEA_PROD_ID_3 }, -- cgit v1.2.3 From d7629e748ee4332af91c1c2e2b4d7a92e7d5cde4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 18 Aug 2017 16:30:00 +0100 Subject: net: hns3: fix a handful of spelling mistakes Trival fix to spelling mistakes: firware -> firmware invald -> invalid mutilcast -> multicast Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c | 2 +- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c index bc869842728f..8b511e6e0ce9 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c @@ -334,7 +334,7 @@ int hclge_cmd_init(struct hclge_dev *hdev) } hdev->fw_version = version; - dev_info(&hdev->pdev->dev, "The firware version is %08x\n", version); + dev_info(&hdev->pdev->dev, "The firmware version is %08x\n", version); return 0; err_csq: diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 6fb7648bb2f2..bb45365fb817 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -1952,7 +1952,7 @@ int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex) HCLGE_CFG_SPEED_S, 5); break; default: - dev_err(&hdev->pdev->dev, "invald speed (%d)\n", speed); + dev_err(&hdev->pdev->dev, "invalid speed (%d)\n", speed); return -EINVAL; } @@ -3476,7 +3476,7 @@ int hclge_rm_mc_addr_common(struct hclge_vport *vport, } else { /* This mac addr do not exist, can't delete it */ dev_err(&hdev->pdev->dev, - "Rm mutilcast mac addr failed, ret = %d.\n", + "Rm multicast mac addr failed, ret = %d.\n", status); return -EIO; } -- cgit v1.2.3 From bd76b87962833f6e55264030a227be0f090b1286 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 18 Aug 2017 16:40:00 +0100 Subject: bnxt_en: fix spelling mistake: "swtichdev" -> "switchdev" Trivial fix to spelling mistake in a netdev_info message Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index b05c5d0ee3f9..86cce6f53f78 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -428,7 +428,7 @@ static int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode) case DEVLINK_ESWITCH_MODE_SWITCHDEV: if (pci_num_vf(bp->pdev) == 0) { netdev_info(bp->dev, - "Enable VFs before setting swtichdev mode"); + "Enable VFs before setting switchdev mode"); rc = -EPERM; goto done; } -- cgit v1.2.3 From 63bfd399de55e09369e5ab344d1fb7f86ed73bab Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Sun, 5 Feb 2017 17:57:40 +0200 Subject: net/mlx5e: Send PAOS command on interface up/down Upon interface up/down, driver will send PAOS (Ports Administrative and Operational Status Register) in order to inform the Firmware on the desired status of the port by the driver. Since now we might change physical link status on mlx5e_open/close, logical VF representor should not use mlx5e_open/close ndos as is, and should call the logical version mlx5e_open/closed_locked. Signed-off-by: Eran Ben Elisha Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 7 +++++++ drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 20 +++++++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 2fc3832bc2f3..7c512a4c6d5c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2682,6 +2682,8 @@ int mlx5e_open(struct net_device *netdev) mutex_lock(&priv->state_lock); err = mlx5e_open_locked(netdev); + if (!err) + mlx5_set_port_admin_status(priv->mdev, MLX5_PORT_UP); mutex_unlock(&priv->state_lock); return err; @@ -2716,6 +2718,7 @@ int mlx5e_close(struct net_device *netdev) return -ENODEV; mutex_lock(&priv->state_lock); + mlx5_set_port_admin_status(priv->mdev, MLX5_PORT_DOWN); err = mlx5e_close_locked(netdev); mutex_unlock(&priv->state_lock); @@ -4187,6 +4190,10 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) mlx5e_init_l2_addr(priv); + /* Marking the link as currently not needed by the Driver */ + if (!netif_running(netdev)) + mlx5_set_port_admin_status(mdev, MLX5_PORT_DOWN); + /* MTU range: 68 - hw-specific max */ netdev->min_mtu = ETH_MIN_MTU; mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 7a9f53f74976..45c088c10ee1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -613,15 +613,18 @@ static int mlx5e_rep_open(struct net_device *dev) struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; int err; - err = mlx5e_open(dev); + mutex_lock(&priv->state_lock); + err = mlx5e_open_locked(dev); if (err) - return err; + goto unlock; - err = mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_UP); - if (!err) + if (!mlx5_eswitch_set_vport_state(esw, rep->vport, + MLX5_ESW_VPORT_ADMIN_STATE_UP)) netif_carrier_on(dev); - return 0; +unlock: + mutex_unlock(&priv->state_lock); + return err; } static int mlx5e_rep_close(struct net_device *dev) @@ -630,10 +633,13 @@ static int mlx5e_rep_close(struct net_device *dev) struct mlx5e_rep_priv *rpriv = priv->ppriv; struct mlx5_eswitch_rep *rep = rpriv->rep; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + int ret; + mutex_lock(&priv->state_lock); (void)mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN); - - return mlx5e_close(dev); + ret = mlx5e_close_locked(dev); + mutex_unlock(&priv->state_lock); + return ret; } static int mlx5e_rep_get_phys_port_name(struct net_device *dev, -- cgit v1.2.3 From 1d1c3436119a6fb92adb8ae72d9a1bcb7f5d0d6a Mon Sep 17 00:00:00 2001 From: Feras Daoud Date: Mon, 10 Jul 2017 18:35:06 +0300 Subject: net/mlx5e: IPoIB, Fix driver name retrieved by ethtool Printing an enhanced IPoIB device information using "ethtool -i DEVNAME", prints the low level driver name: mlx5_core. This commit changes the name to mlx5_core [ib_ipoib], to include the ipoib device driver infromation. Fixes: 076b0936e5fb ("net/mlx5e: IPoIB, Add ethtool support") Signed-off-by: Feras Daoud Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c index eb04e97d8765..b080fabfe8de 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c @@ -39,6 +39,8 @@ static void mlx5i_get_drvinfo(struct net_device *dev, struct mlx5e_priv *priv = mlx5i_epriv(dev); mlx5e_ethtool_get_drvinfo(priv, drvinfo); + strlcpy(drvinfo->driver, DRIVER_NAME "[ib_ipoib]", + sizeof(drvinfo->driver)); } static void mlx5i_get_strings(struct net_device *dev, -- cgit v1.2.3 From eb234ee9d5413368c8bef5f4061cd76da0b5dd55 Mon Sep 17 00:00:00 2001 From: Shalom Lagziel Date: Sun, 28 May 2017 16:40:24 +0300 Subject: net/mlx5e: IPoIB, Add support for get_link_ksettings in ethtool Add support for "ethtool DEVNAME" over ipoib ports, Display standard port information for IPoIB netdevices using ethtool For example: $ ethtool ib2 > Settings for ib2: Supported ports: [ ] Supported link modes: Not reported Supported pause frame use: No Supports auto-negotiation: No Advertised link modes: Not reported Advertised pause frame use: No Advertised auto-negotiation: No Speed: 100000Mb/s Duplex: Full Port: Other PHYAD: 0 Transceiver: internal Auto-negotiation: off Link detected: yes Signed-off-by: Shalom Lagziel Signed-off-by: Saeed Mahameed --- .../ethernet/mellanox/mlx5/core/ipoib/ethtool.c | 130 +++++++++++++++++++-- 1 file changed, 118 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c index b080fabfe8de..dd49a59854e5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c @@ -131,17 +131,123 @@ static int mlx5i_flash_device(struct net_device *netdev, return mlx5e_ethtool_flash_device(priv, flash); } +enum mlx5_ptys_width { + MLX5_PTYS_WIDTH_1X = 1 << 0, + MLX5_PTYS_WIDTH_2X = 1 << 1, + MLX5_PTYS_WIDTH_4X = 1 << 2, + MLX5_PTYS_WIDTH_8X = 1 << 3, + MLX5_PTYS_WIDTH_12X = 1 << 4, +}; + +static inline int mlx5_ptys_width_enum_to_int(enum mlx5_ptys_width width) +{ + switch (width) { + case MLX5_PTYS_WIDTH_1X: return 1; + case MLX5_PTYS_WIDTH_2X: return 2; + case MLX5_PTYS_WIDTH_4X: return 4; + case MLX5_PTYS_WIDTH_8X: return 8; + case MLX5_PTYS_WIDTH_12X: return 12; + default: return -1; + } +} + +enum mlx5_ptys_rate { + MLX5_PTYS_RATE_SDR = 1 << 0, + MLX5_PTYS_RATE_DDR = 1 << 1, + MLX5_PTYS_RATE_QDR = 1 << 2, + MLX5_PTYS_RATE_FDR10 = 1 << 3, + MLX5_PTYS_RATE_FDR = 1 << 4, + MLX5_PTYS_RATE_EDR = 1 << 5, + MLX5_PTYS_RATE_HDR = 1 << 6, +}; + +static inline int mlx5_ptys_rate_enum_to_int(enum mlx5_ptys_rate rate) +{ + switch (rate) { + case MLX5_PTYS_RATE_SDR: return 2500; + case MLX5_PTYS_RATE_DDR: return 5000; + case MLX5_PTYS_RATE_QDR: + case MLX5_PTYS_RATE_FDR10: return 10000; + case MLX5_PTYS_RATE_FDR: return 14000; + case MLX5_PTYS_RATE_EDR: return 25000; + case MLX5_PTYS_RATE_HDR: return 50000; + default: return -1; + } +} + +static int mlx5i_get_port_settings(struct net_device *netdev, + u16 *ib_link_width_oper, u16 *ib_proto_oper) +{ + struct mlx5e_priv *priv = mlx5i_epriv(netdev); + struct mlx5_core_dev *mdev = priv->mdev; + u32 out[MLX5_ST_SZ_DW(ptys_reg)] = {0}; + int ret; + + ret = mlx5_query_port_ptys(mdev, out, sizeof(out), MLX5_PTYS_IB, 1); + if (ret) + return ret; + + *ib_link_width_oper = MLX5_GET(ptys_reg, out, ib_link_width_oper); + *ib_proto_oper = MLX5_GET(ptys_reg, out, ib_proto_oper); + + return 0; +} + +static int mlx5i_get_speed_settings(u16 ib_link_width_oper, u16 ib_proto_oper) +{ + int rate, width; + + rate = mlx5_ptys_rate_enum_to_int(ib_proto_oper); + if (rate < 0) + return -EINVAL; + width = mlx5_ptys_width_enum_to_int(ib_link_width_oper); + if (width < 0) + return -EINVAL; + + return rate * width; +} + +static int mlx5i_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *link_ksettings) +{ + u16 ib_link_width_oper; + u16 ib_proto_oper; + int speed, ret; + + ret = mlx5i_get_port_settings(netdev, &ib_link_width_oper, &ib_proto_oper); + if (ret) + return ret; + + ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); + ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); + + speed = mlx5i_get_speed_settings(ib_link_width_oper, ib_proto_oper); + if (speed < 0) + return -EINVAL; + + link_ksettings->base.duplex = DUPLEX_FULL; + link_ksettings->base.port = PORT_OTHER; + + link_ksettings->base.autoneg = AUTONEG_DISABLE; + + link_ksettings->base.speed = speed; + + return 0; +} + const struct ethtool_ops mlx5i_ethtool_ops = { - .get_drvinfo = mlx5i_get_drvinfo, - .get_strings = mlx5i_get_strings, - .get_sset_count = mlx5i_get_sset_count, - .get_ethtool_stats = mlx5i_get_ethtool_stats, - .get_ringparam = mlx5i_get_ringparam, - .set_ringparam = mlx5i_set_ringparam, - .flash_device = mlx5i_flash_device, - .get_channels = mlx5i_get_channels, - .set_channels = mlx5i_set_channels, - .get_coalesce = mlx5i_get_coalesce, - .set_coalesce = mlx5i_set_coalesce, - .get_ts_info = mlx5i_get_ts_info, + .get_drvinfo = mlx5i_get_drvinfo, + .get_strings = mlx5i_get_strings, + .get_sset_count = mlx5i_get_sset_count, + .get_ethtool_stats = mlx5i_get_ethtool_stats, + .get_ringparam = mlx5i_get_ringparam, + .set_ringparam = mlx5i_set_ringparam, + .flash_device = mlx5i_flash_device, + .get_channels = mlx5i_get_channels, + .set_channels = mlx5i_set_channels, + .get_coalesce = mlx5i_get_coalesce, + .set_coalesce = mlx5i_set_coalesce, + .get_ts_info = mlx5i_get_ts_info, + .get_link_ksettings = mlx5i_get_link_ksettings, + .get_link = ethtool_op_get_link, }; -- cgit v1.2.3 From 73e90646a275aeffea263305c8662f8dd0cb41ef Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Thu, 15 Jun 2017 18:29:32 +0300 Subject: net/mlx5e: Add PCIe outbound stalls counters outbound_pci_stalled_rd - The percentage of time within the last second that the NIC had outbound non-posted read requests but could not perform the operation due to insufficient non-posted credits. outbound_pci_stalled_wr - The percentage of time within the last second that the NIC had outbound posted writes requests but could not perform the operation due to insufficient posted credits. outbound_pci_stalled_rd_events - The number of events where outbound_pci_stalled_rd was above the threshold. outbound_pci_stalled_wr_events - The number of events where outbound_pci_stalled_wr was above the threshold. Signed-off-by: Gal Pressman Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 8 ++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 13 ++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 917fade5f5d5..07202f7322fc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -246,6 +246,10 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) strcpy(data + (idx++) * ETH_GSTRING_LEN, pcie_perf_stats_desc[i].format); + for (i = 0; i < NUM_PCIE_PERF_STALL_COUNTERS(priv); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + pcie_perf_stall_stats_desc[i].format); + for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++) sprintf(data + (idx++) * ETH_GSTRING_LEN, @@ -377,6 +381,10 @@ void mlx5e_ethtool_get_ethtool_stats(struct mlx5e_priv *priv, data[idx++] = MLX5E_READ_CTR32_BE(&priv->stats.pcie.pcie_perf_counters, pcie_perf_stats_desc, i); + for (i = 0; i < NUM_PCIE_PERF_STALL_COUNTERS(priv); i++) + data[idx++] = MLX5E_READ_CTR32_BE(&priv->stats.pcie.pcie_perf_counters, + pcie_perf_stall_stats_desc, i); + for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++) data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.per_prio_counters[prio], diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index e65517eafc58..bdddddc46170 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -305,6 +305,13 @@ static const struct counter_desc pcie_perf_stats_desc[] = { { "tx_pci_signal_integrity", PCIE_PERF_OFF(tx_errors) }, }; +static const struct counter_desc pcie_perf_stall_stats_desc[] = { + { "outbound_pci_stalled_rd", PCIE_PERF_OFF(outbound_stalled_reads) }, + { "outbound_pci_stalled_wr", PCIE_PERF_OFF(outbound_stalled_writes) }, + { "outbound_pci_stalled_rd_events", PCIE_PERF_OFF(outbound_stalled_reads_events) }, + { "outbound_pci_stalled_wr_events", PCIE_PERF_OFF(outbound_stalled_writes_events) }, +}; + struct mlx5e_rq_stats { u64 packets; u64 bytes; @@ -397,6 +404,9 @@ static const struct counter_desc sq_stats_desc[] = { #define NUM_PCIE_PERF_COUNTERS(priv) \ (ARRAY_SIZE(pcie_perf_stats_desc) * \ MLX5_CAP_MCAM_FEATURE((priv)->mdev, pcie_performance_group)) +#define NUM_PCIE_PERF_STALL_COUNTERS(priv) \ + (ARRAY_SIZE(pcie_perf_stall_stats_desc) * \ + MLX5_CAP_MCAM_FEATURE((priv)->mdev, pcie_outbound_stalled)) #define NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS \ ARRAY_SIZE(pport_per_prio_traffic_stats_desc) #define NUM_PPORT_PER_PRIO_PFC_COUNTERS \ @@ -407,7 +417,8 @@ static const struct counter_desc sq_stats_desc[] = { NUM_PPORT_PHY_STATISTICAL_COUNTERS(priv) + \ NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS * \ NUM_PPORT_PRIO) -#define NUM_PCIE_COUNTERS(priv) NUM_PCIE_PERF_COUNTERS(priv) +#define NUM_PCIE_COUNTERS(priv) (NUM_PCIE_PERF_COUNTERS(priv) + \ + NUM_PCIE_PERF_STALL_COUNTERS(priv)) #define NUM_RQ_STATS ARRAY_SIZE(rq_stats_desc) #define NUM_SQ_STATS ARRAY_SIZE(sq_stats_desc) -- cgit v1.2.3 From 068aef33bebcb1962720e44525c8d4aff6c2ee3d Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Sun, 18 Jun 2017 14:56:57 +0300 Subject: net/mlx5e: Add RX buffer fullness counters rx_buffer_passed_thres_phy - The number of events where the port RX buffer has passed a fullness threshold. rx_buffer_full_phy - The number of events where the port RX buffer has reached 100% fullness. Signed-off-by: Gal Pressman Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 8 ++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 6 ++++++ drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 17 ++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 07202f7322fc..8c013a521319 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -242,6 +242,10 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) strcpy(data + (idx++) * ETH_GSTRING_LEN, pport_phy_statistical_stats_desc[i].format); + for (i = 0; i < NUM_PPORT_ETH_EXT_COUNTERS(priv); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + pport_eth_ext_stats_desc[i].format); + for (i = 0; i < NUM_PCIE_PERF_COUNTERS(priv); i++) strcpy(data + (idx++) * ETH_GSTRING_LEN, pcie_perf_stats_desc[i].format); @@ -377,6 +381,10 @@ void mlx5e_ethtool_get_ethtool_stats(struct mlx5e_priv *priv, data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.phy_statistical_counters, pport_phy_statistical_stats_desc, i); + for (i = 0; i < NUM_PPORT_ETH_EXT_COUNTERS(priv); i++) + data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.eth_ext_counters, + pport_eth_ext_stats_desc, i); + for (i = 0; i < NUM_PCIE_PERF_COUNTERS(priv); i++) data[idx++] = MLX5E_READ_CTR32_BE(&priv->stats.pcie.pcie_perf_counters, pcie_perf_stats_desc, i); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 7c512a4c6d5c..fdc2b92f020b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -288,6 +288,12 @@ static void mlx5e_update_pport_counters(struct mlx5e_priv *priv, bool full) mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPCNT, 0, 0); } + if (MLX5_CAP_PCAM_FEATURE(mdev, rx_buffer_fullness_counters)) { + out = pstats->eth_ext_counters; + MLX5_SET(ppcnt_reg, in, grp, MLX5_ETHERNET_EXTENDED_COUNTERS_GROUP); + mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPCNT, 0, 0); + } + MLX5_SET(ppcnt_reg, in, grp, MLX5_PER_PRIORITY_COUNTERS_GROUP); for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { out = pstats->per_prio_counters[prio]; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index bdddddc46170..be49df4bedd9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -216,6 +216,12 @@ static const struct counter_desc vport_stats_desc[] = { MLX5_GET64(ppcnt_reg, pstats->per_prio_counters[prio], \ counter_set.eth_per_prio_grp_data_layout.c##_high) #define NUM_PPORT_PRIO 8 +#define PPORT_ETH_EXT_OFF(c) \ + MLX5_BYTE_OFF(ppcnt_reg, \ + counter_set.eth_extended_cntrs_grp_data_layout.c##_high) +#define PPORT_ETH_EXT_GET(pstats, c) \ + MLX5_GET64(ppcnt_reg, (pstats)->eth_ext_counters, \ + counter_set.eth_extended_cntrs_grp_data_layout.c##_high) struct mlx5e_pport_stats { __be64 IEEE_802_3_counters[MLX5_ST_SZ_QW(ppcnt_reg)]; @@ -224,6 +230,7 @@ struct mlx5e_pport_stats { __be64 per_prio_counters[NUM_PPORT_PRIO][MLX5_ST_SZ_QW(ppcnt_reg)]; __be64 phy_counters[MLX5_ST_SZ_QW(ppcnt_reg)]; __be64 phy_statistical_counters[MLX5_ST_SZ_QW(ppcnt_reg)]; + __be64 eth_ext_counters[MLX5_ST_SZ_QW(ppcnt_reg)]; }; static const struct counter_desc pport_802_3_stats_desc[] = { @@ -290,6 +297,10 @@ static const struct counter_desc pport_per_prio_pfc_stats_desc[] = { { "rx_%s_pause_transition", PPORT_PER_PRIO_OFF(rx_pause_transition) }, }; +static const struct counter_desc pport_eth_ext_stats_desc[] = { + { "rx_buffer_passed_thres_phy", PPORT_ETH_EXT_OFF(rx_buffer_almost_full) }, +}; + #define PCIE_PERF_OFF(c) \ MLX5_BYTE_OFF(mpcnt_reg, counter_set.pcie_perf_cntrs_grp_data_layout.c) #define PCIE_PERF_GET(pcie_stats, c) \ @@ -411,12 +422,16 @@ static const struct counter_desc sq_stats_desc[] = { ARRAY_SIZE(pport_per_prio_traffic_stats_desc) #define NUM_PPORT_PER_PRIO_PFC_COUNTERS \ ARRAY_SIZE(pport_per_prio_pfc_stats_desc) +#define NUM_PPORT_ETH_EXT_COUNTERS(priv) \ + (ARRAY_SIZE(pport_eth_ext_stats_desc) * \ + MLX5_CAP_PCAM_FEATURE((priv)->mdev, rx_buffer_fullness_counters)) #define NUM_PPORT_COUNTERS(priv) (NUM_PPORT_802_3_COUNTERS + \ NUM_PPORT_2863_COUNTERS + \ NUM_PPORT_2819_COUNTERS + \ NUM_PPORT_PHY_STATISTICAL_COUNTERS(priv) + \ NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS * \ - NUM_PPORT_PRIO) + NUM_PPORT_PRIO + \ + NUM_PPORT_ETH_EXT_COUNTERS(priv)) #define NUM_PCIE_COUNTERS(priv) (NUM_PCIE_PERF_COUNTERS(priv) + \ NUM_PCIE_PERF_STALL_COUNTERS(priv)) #define NUM_RQ_STATS ARRAY_SIZE(rq_stats_desc) -- cgit v1.2.3 From efae7f78c45ba37bdc23a95d219b59ac85bdd0a7 Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Fri, 12 May 2017 02:47:02 +0300 Subject: net/mlx5e: Add outbound PCI buffer overflow counter Add outbound_pci_buffer_overflow to ethtool output for monitoring the number of packets that were dropped due to lack of PCIe buffers on receive path from NIC port toward the host(s). This counter is valid only in case that tx_overflow_buffer_pkt is supported in MCAM enhanced features. Signed-off-by: Eran Ben Elisha Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 12 ++++++++++-- drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 14 ++++++++++++++ include/linux/mlx5/mlx5_ifc.h | 6 ++++-- 3 files changed, 28 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 8c013a521319..d453a11f41fe 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -250,9 +250,13 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) strcpy(data + (idx++) * ETH_GSTRING_LEN, pcie_perf_stats_desc[i].format); - for (i = 0; i < NUM_PCIE_PERF_STALL_COUNTERS(priv); i++) + for (i = 0; i < NUM_PCIE_PERF_COUNTERS64(priv); i++) strcpy(data + (idx++) * ETH_GSTRING_LEN, - pcie_perf_stall_stats_desc[i].format); + pcie_perf_stats_desc64[i].format); + + for (i = 0; i < NUM_PCIE_PERF_STALL_COUNTERS(priv); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + pcie_perf_stall_stats_desc[i].format); for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++) @@ -389,6 +393,10 @@ void mlx5e_ethtool_get_ethtool_stats(struct mlx5e_priv *priv, data[idx++] = MLX5E_READ_CTR32_BE(&priv->stats.pcie.pcie_perf_counters, pcie_perf_stats_desc, i); + for (i = 0; i < NUM_PCIE_PERF_COUNTERS64(priv); i++) + data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pcie.pcie_perf_counters, + pcie_perf_stats_desc64, i); + for (i = 0; i < NUM_PCIE_PERF_STALL_COUNTERS(priv); i++) data[idx++] = MLX5E_READ_CTR32_BE(&priv->stats.pcie.pcie_perf_counters, pcie_perf_stall_stats_desc, i); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index be49df4bedd9..40b5c73e5e26 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -307,6 +307,12 @@ static const struct counter_desc pport_eth_ext_stats_desc[] = { MLX5_GET(mpcnt_reg, (pcie_stats)->pcie_perf_counters, \ counter_set.pcie_perf_cntrs_grp_data_layout.c) +#define PCIE_PERF_OFF64(c) \ + MLX5_BYTE_OFF(mpcnt_reg, counter_set.pcie_perf_cntrs_grp_data_layout.c##_high) +#define PCIE_PERF_GET64(pcie_stats, c) \ + MLX5_GET64(mpcnt_reg, (pcie_stats)->pcie_perf_counters, \ + counter_set.pcie_perf_cntrs_grp_data_layout.c##_high) + struct mlx5e_pcie_stats { __be64 pcie_perf_counters[MLX5_ST_SZ_QW(mpcnt_reg)]; }; @@ -316,6 +322,10 @@ static const struct counter_desc pcie_perf_stats_desc[] = { { "tx_pci_signal_integrity", PCIE_PERF_OFF(tx_errors) }, }; +static const struct counter_desc pcie_perf_stats_desc64[] = { + { "outbound_pci_buffer_overflow", PCIE_PERF_OFF64(tx_overflow_buffer_pkt) }, +}; + static const struct counter_desc pcie_perf_stall_stats_desc[] = { { "outbound_pci_stalled_rd", PCIE_PERF_OFF(outbound_stalled_reads) }, { "outbound_pci_stalled_wr", PCIE_PERF_OFF(outbound_stalled_writes) }, @@ -415,6 +425,9 @@ static const struct counter_desc sq_stats_desc[] = { #define NUM_PCIE_PERF_COUNTERS(priv) \ (ARRAY_SIZE(pcie_perf_stats_desc) * \ MLX5_CAP_MCAM_FEATURE((priv)->mdev, pcie_performance_group)) +#define NUM_PCIE_PERF_COUNTERS64(priv) \ + (ARRAY_SIZE(pcie_perf_stats_desc64) * \ + MLX5_CAP_MCAM_FEATURE((priv)->mdev, tx_overflow_buffer_pkt)) #define NUM_PCIE_PERF_STALL_COUNTERS(priv) \ (ARRAY_SIZE(pcie_perf_stall_stats_desc) * \ MLX5_CAP_MCAM_FEATURE((priv)->mdev, pcie_outbound_stalled)) @@ -433,6 +446,7 @@ static const struct counter_desc sq_stats_desc[] = { NUM_PPORT_PRIO + \ NUM_PPORT_ETH_EXT_COUNTERS(priv)) #define NUM_PCIE_COUNTERS(priv) (NUM_PCIE_PERF_COUNTERS(priv) + \ + NUM_PCIE_PERF_COUNTERS64(priv) +\ NUM_PCIE_PERF_STALL_COUNTERS(priv)) #define NUM_RQ_STATS ARRAY_SIZE(rq_stats_desc) #define NUM_SQ_STATS ARRAY_SIZE(sq_stats_desc) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index cf7ff52c594e..ae7d09b9c52f 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -1864,7 +1864,9 @@ struct mlx5_ifc_pcie_perf_cntrs_grp_data_layout_bits { u8 crc_error_tlp[0x20]; - u8 reserved_at_140[0x40]; + u8 tx_overflow_buffer_pkt_high[0x20]; + + u8 tx_overflow_buffer_pkt_low[0x20]; u8 outbound_stalled_reads[0x20]; @@ -7767,7 +7769,7 @@ struct mlx5_ifc_pcam_reg_bits { struct mlx5_ifc_mcam_enhanced_features_bits { u8 reserved_at_0[0x7b]; u8 pcie_outbound_stalled[0x1]; - u8 reserved_at_7c[0x1]; + u8 tx_overflow_buffer_pkt[0x1]; u8 mtpps_enh_out_per_adj[0x1]; u8 mtpps_fs[0x1]; u8 pcie_performance_group[0x1]; -- cgit v1.2.3 From 733d6c5149b0fb8628bb0db618edd9bf49b496e6 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 11 Jul 2017 15:44:04 +0300 Subject: net/mlx5: Avoid blank lines after/before open/close brace To fix these checkpatch complaints: CHECK: Blank lines aren't necessary after an open brace '{' CHECK: Blank lines aren't necessary before a close brace '}' Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/sriov.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index d453a11f41fe..a75ac4d11c5b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -176,7 +176,6 @@ static bool mlx5e_query_global_pause_combined(struct mlx5e_priv *priv) int mlx5e_ethtool_get_sset_count(struct mlx5e_priv *priv, int sset) { - switch (sset) { case ETH_SS_STATS: return NUM_SW_COUNTERS + diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c index 5e7ffc9fad78..55b07c5ecd12 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -71,7 +71,6 @@ static int mlx5_device_enable_sriov(struct mlx5_core_dev *dev, int num_vfs) sriov->vfs_ctx[vf].enabled = 1; sriov->enabled_vfs++; mlx5_core_dbg(dev, "successfully enabled VF* %d\n", vf); - } return 0; -- cgit v1.2.3 From ad5b39a95c8339d069be46f13562a0b6a20d5185 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 11 Jul 2017 15:53:29 +0300 Subject: net/mlx5: Add a blank line after declarations To fix these checkpatch complaints: WARNING: Missing a blank line after declarations Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/alloc.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/eq.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index 3c95f7f53802..47239bf7bf43 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -258,6 +258,7 @@ EXPORT_SYMBOL_GPL(mlx5_db_alloc); void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db) { u32 db_per_page = PAGE_SIZE / cache_line_size(); + mutex_lock(&dev->priv.pgdir_mutex); __set_bit(db->index, db->u.pgdir->bitmap); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index de704ff5619a..a08027b8f3ce 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -188,6 +188,7 @@ static enum mlx5_dev_event port_subtype_event(u8 subtype) static void eq_update_ci(struct mlx5_eq *eq, int arm) { __be32 __iomem *addr = eq->doorbell + (arm ? 0 : 2); + u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24); __raw_writel((__force u32)cpu_to_be32(val), addr); /* We still want ordering, just not swabbing, so add a barrier */ -- cgit v1.2.3 From 61bf2125656259dcd0a34b14839f2b157a16bde9 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 11 Jul 2017 15:36:11 +0300 Subject: net/mlx5e: Properly indent within conditional statements To fix these checkpatch complaints: WARNING: suspect code indent for conditional statements (8, 24) + if (eth_proto & (MLX5E_PROT_MASK(MLX5E_10GBASE_SR) [...] + return PORT_FIBRE; Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 31 ++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index a75ac4d11c5b..1f3d87e28618 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -987,24 +987,27 @@ static u8 get_connector_port(u32 eth_proto, u8 connector_type) if (connector_type && connector_type < MLX5E_CONNECTOR_TYPE_NUMBER) return ptys2connector_type[connector_type]; - if (eth_proto & (MLX5E_PROT_MASK(MLX5E_10GBASE_SR) - | MLX5E_PROT_MASK(MLX5E_40GBASE_SR4) - | MLX5E_PROT_MASK(MLX5E_100GBASE_SR4) - | MLX5E_PROT_MASK(MLX5E_1000BASE_CX_SGMII))) { - return PORT_FIBRE; + if (eth_proto & + (MLX5E_PROT_MASK(MLX5E_10GBASE_SR) | + MLX5E_PROT_MASK(MLX5E_40GBASE_SR4) | + MLX5E_PROT_MASK(MLX5E_100GBASE_SR4) | + MLX5E_PROT_MASK(MLX5E_1000BASE_CX_SGMII))) { + return PORT_FIBRE; } - if (eth_proto & (MLX5E_PROT_MASK(MLX5E_40GBASE_CR4) - | MLX5E_PROT_MASK(MLX5E_10GBASE_CR) - | MLX5E_PROT_MASK(MLX5E_100GBASE_CR4))) { - return PORT_DA; + if (eth_proto & + (MLX5E_PROT_MASK(MLX5E_40GBASE_CR4) | + MLX5E_PROT_MASK(MLX5E_10GBASE_CR) | + MLX5E_PROT_MASK(MLX5E_100GBASE_CR4))) { + return PORT_DA; } - if (eth_proto & (MLX5E_PROT_MASK(MLX5E_10GBASE_KX4) - | MLX5E_PROT_MASK(MLX5E_10GBASE_KR) - | MLX5E_PROT_MASK(MLX5E_40GBASE_KR4) - | MLX5E_PROT_MASK(MLX5E_100GBASE_KR4))) { - return PORT_NONE; + if (eth_proto & + (MLX5E_PROT_MASK(MLX5E_10GBASE_KX4) | + MLX5E_PROT_MASK(MLX5E_10GBASE_KR) | + MLX5E_PROT_MASK(MLX5E_40GBASE_KR4) | + MLX5E_PROT_MASK(MLX5E_100GBASE_KR4))) { + return PORT_NONE; } return PORT_OTHER; -- cgit v1.2.3 From 12148f5ab024f1c899e545b8da0d47d3dc9b80fb Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 11 Jul 2017 15:40:30 +0300 Subject: net/mlx5e: Avoid using multiple blank lines To fix these checkpatch complaints: CHECK: Please don't use multiple blank lines Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/main.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 31cbe5e86a01..0ef68a7c051e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -802,7 +802,6 @@ static void cmd_work_handler(struct work_struct *work) bool poll_cmd = ent->polling; int alloc_ret; - sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; down(sem); if (!ent->page_queue) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 7e6e24398926..514c22d21729 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -836,7 +836,6 @@ static int mlx5_core_set_issi(struct mlx5_core_dev *dev) return -EOPNOTSUPP; } - static int mlx5_pci_init(struct mlx5_core_dev *dev, struct mlx5_priv *priv) { struct pci_dev *pdev = dev->pdev; -- cgit v1.2.3 From 1afdb7718fde9bcf59af85e861fa666b9476d927 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 11 Jul 2017 16:10:50 +0300 Subject: net/mlx5e: Place constants on the right side of comparisons To fix these checkpatch complaints: WARNING: Comparisons should place the constant on the right side of the test Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 8e224bcbc6a6..55a6786d3c4c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -509,8 +509,8 @@ static void mlx5e_lro_update_hdr(struct sk_buff *skb, struct mlx5_cqe64 *cqe, u16 tot_len; u8 l4_hdr_type = get_cqe_l4_hdr_type(cqe); - int tcp_ack = ((CQE_L4_HDR_TYPE_TCP_ACK_NO_DATA == l4_hdr_type) || - (CQE_L4_HDR_TYPE_TCP_ACK_AND_DATA == l4_hdr_type)); + int tcp_ack = ((l4_hdr_type == CQE_L4_HDR_TYPE_TCP_ACK_NO_DATA) || + (l4_hdr_type == CQE_L4_HDR_TYPE_TCP_ACK_AND_DATA)); skb->mac_len = ETH_HLEN; proto = __vlan_get_protocol(skb, eth->h_proto, &network_depth); -- cgit v1.2.3 From c045deef64f389f40f135c71b2ded3405c989fba Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Wed, 28 Jun 2017 14:03:36 +0300 Subject: net/mlx5e: Use kernel types instead of uint*_t in ethtool callbacks Fix checkpatch errors: CHECK:PREFER_KERNEL_TYPES: Prefer kernel type 'u32' over 'uint32_t' Signed-off-by: Gal Pressman Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 8 +++----- drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c | 3 +-- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 1f3d87e28618..c30cf6b4736f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -206,7 +206,7 @@ static int mlx5e_get_sset_count(struct net_device *dev, int sset) return mlx5e_ethtool_get_sset_count(priv, sset); } -static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) +static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, u8 *data) { int i, j, tc, prio, idx = 0; unsigned long pfc_combined; @@ -308,8 +308,7 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) priv->channel_tc2txq[i][tc]); } -void mlx5e_ethtool_get_strings(struct mlx5e_priv *priv, - uint32_t stringset, uint8_t *data) +void mlx5e_ethtool_get_strings(struct mlx5e_priv *priv, u32 stringset, u8 *data) { int i; @@ -331,8 +330,7 @@ void mlx5e_ethtool_get_strings(struct mlx5e_priv *priv, } } -static void mlx5e_get_strings(struct net_device *dev, - uint32_t stringset, uint8_t *data) +static void mlx5e_get_strings(struct net_device *dev, u32 stringset, u8 *data) { struct mlx5e_priv *priv = netdev_priv(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c index dd49a59854e5..43c126c63955 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c @@ -43,8 +43,7 @@ static void mlx5i_get_drvinfo(struct net_device *dev, sizeof(drvinfo->driver)); } -static void mlx5i_get_strings(struct net_device *dev, - uint32_t stringset, uint8_t *data) +static void mlx5i_get_strings(struct net_device *dev, u32 stringset, u8 *data) { struct mlx5e_priv *priv = mlx5i_epriv(dev); -- cgit v1.2.3 From 9da5106c5656fdd7626af8abc09677364055f2c9 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Sun, 2 Jul 2017 18:26:21 +0300 Subject: net/mlx5e: Use size_t to store byte offset in statistics descriptors The byte offset of counter descriptors should be stored in size_t variable instead of an integer. Signed-off-by: Gal Pressman Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index 40b5c73e5e26..6761796e803c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -47,7 +47,7 @@ struct counter_desc { char format[ETH_GSTRING_LEN]; - int offset; /* Byte offset */ + size_t offset; /* Byte offset */ }; struct mlx5e_sw_stats { -- cgit v1.2.3 From f00c2987bfdde69c6eb27b5547d222beb5bc7552 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 20 Aug 2017 18:25:02 +0200 Subject: ieee802154: ca8210: Fix a potential NULL pointer dereference 'spi' is known to be NULL, so we dereference a NULL pointer here. Use 'pr_crit()' instead of 'dev_crit()' to report the message. Signed-off-by: Christophe JAILLET Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/ca8210.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index 326243fae7e2..24a1eabbbc9d 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -917,10 +917,7 @@ static int ca8210_spi_transfer( struct cas_control *cas_ctl; if (!spi) { - dev_crit( - &spi->dev, - "NULL spi device passed to ca8210_spi_transfer\n" - ); + pr_crit("NULL spi device passed to %s\n", __func__); return -ENODEV; } -- cgit v1.2.3 From c3168cabe1af2683475d0e3048220c04b7fa4f51 Mon Sep 17 00:00:00 2001 From: Ganesh Goudar Date: Sun, 20 Aug 2017 14:15:51 +0530 Subject: cxgb4/cxgbvf: Handle 32-bit fw port capabilities Implement new 32-bit Firmware Port Capabilities in order to handle new speeds which couldn't be represented in the old 16-bit Firmware Port Capabilities values. Based on the original work of Casey Leedom Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 43 +- drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c | 98 ++-- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 88 ++-- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 580 ++++++++++++++++----- drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h | 175 ++++++- .../net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c | 50 +- drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h | 86 +-- drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c | 456 +++++++++++++--- 8 files changed, 1220 insertions(+), 356 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index b9bff1d9801f..ea72d2d2e1b4 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -104,13 +104,13 @@ enum dev_state { DEV_STATE_ERR }; -enum { +enum cc_pause { PAUSE_RX = 1 << 0, PAUSE_TX = 1 << 1, PAUSE_AUTONEG = 1 << 2 }; -enum { +enum cc_fec { FEC_AUTO = 1 << 0, /* IEEE 802.3 "automatic" */ FEC_RS = 1 << 1, /* Reed-Solomon */ FEC_BASER_RS = 1 << 2 /* BaseR/Reed-Solomon */ @@ -366,6 +366,7 @@ struct adapter_params { unsigned int max_ordird_qp; /* Max read depth per RDMA QP */ unsigned int max_ird_adapter; /* Max read depth per adapter */ bool fr_nsmr_tpte_wr_support; /* FW support for FR_NSMR_TPTE_WR */ + u8 fw_caps_support; /* 32-bit Port Capabilities */ /* MPS Buffer Group Map[per Port]. Bit i is set if buffer group i is * used by the Port @@ -439,18 +440,34 @@ struct trace_params { unsigned char port; }; +/* Firmware Port Capabilities types. */ + +typedef u16 fw_port_cap16_t; /* 16-bit Port Capabilities integral value */ +typedef u32 fw_port_cap32_t; /* 32-bit Port Capabilities integral value */ + +enum fw_caps { + FW_CAPS_UNKNOWN = 0, /* 0'ed out initial state */ + FW_CAPS16 = 1, /* old Firmware: 16-bit Port Capabilities */ + FW_CAPS32 = 2, /* new Firmware: 32-bit Port Capabilities */ +}; + struct link_config { - unsigned short supported; /* link capabilities */ - unsigned short advertising; /* advertised capabilities */ - unsigned short lp_advertising; /* peer advertised capabilities */ - unsigned int requested_speed; /* speed user has requested */ - unsigned int speed; /* actual link speed */ - unsigned char requested_fc; /* flow control user has requested */ - unsigned char fc; /* actual link flow control */ - unsigned char auto_fec; /* Forward Error Correction: */ - unsigned char requested_fec; /* "automatic" (IEEE 802.3), */ - unsigned char fec; /* requested, and actual in use */ + fw_port_cap32_t pcaps; /* link capabilities */ + fw_port_cap32_t def_acaps; /* default advertised capabilities */ + fw_port_cap32_t acaps; /* advertised capabilities */ + fw_port_cap32_t lpacaps; /* peer advertised capabilities */ + + fw_port_cap32_t speed_caps; /* speed(s) user has requested */ + unsigned int speed; /* actual link speed (Mb/s) */ + + enum cc_pause requested_fc; /* flow control user has requested */ + enum cc_pause fc; /* actual link flow control */ + + enum cc_fec requested_fec; /* Forward Error Correction: */ + enum cc_fec fec; /* requested and actual in use */ + unsigned char autoneg; /* autonegotiating? */ + unsigned char link_ok; /* link up? */ unsigned char link_down_rc; /* link down reason */ }; @@ -1580,6 +1597,8 @@ int t4_ofld_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox); void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl); int t4_update_port_info(struct port_info *pi); +int t4_get_link_params(struct port_info *pi, unsigned int *link_okp, + unsigned int *speedp, unsigned int *mtup); int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl); void t4_db_full(struct adapter *adapter); void t4_db_dropped(struct adapter *adapter); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 03f593e84c24..a71af1e587e2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -533,17 +533,23 @@ static int from_fw_port_mod_type(enum fw_port_type port_type, static unsigned int speed_to_fw_caps(int speed) { if (speed == 100) - return FW_PORT_CAP_SPEED_100M; + return FW_PORT_CAP32_SPEED_100M; if (speed == 1000) - return FW_PORT_CAP_SPEED_1G; + return FW_PORT_CAP32_SPEED_1G; if (speed == 10000) - return FW_PORT_CAP_SPEED_10G; + return FW_PORT_CAP32_SPEED_10G; if (speed == 25000) - return FW_PORT_CAP_SPEED_25G; + return FW_PORT_CAP32_SPEED_25G; if (speed == 40000) - return FW_PORT_CAP_SPEED_40G; + return FW_PORT_CAP32_SPEED_40G; + if (speed == 50000) + return FW_PORT_CAP32_SPEED_50G; if (speed == 100000) - return FW_PORT_CAP_SPEED_100G; + return FW_PORT_CAP32_SPEED_100G; + if (speed == 200000) + return FW_PORT_CAP32_SPEED_200G; + if (speed == 400000) + return FW_PORT_CAP32_SPEED_400G; return 0; } @@ -560,12 +566,13 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, unsigned int fw_caps, unsigned long *link_mode_mask) { - #define SET_LMM(__lmm_name) __set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name \ - ## _BIT, link_mode_mask) + #define SET_LMM(__lmm_name) \ + __set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name ## _BIT, \ + link_mode_mask) #define FW_CAPS_TO_LMM(__fw_name, __lmm_name) \ do { \ - if (fw_caps & FW_PORT_CAP_ ## __fw_name) \ + if (fw_caps & FW_PORT_CAP32_ ## __fw_name) \ SET_LMM(__lmm_name); \ } while (0) @@ -645,7 +652,10 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, case FW_PORT_TYPE_KR4_100G: case FW_PORT_TYPE_CR4_QSFP: SET_LMM(FIBRE); - SET_LMM(100000baseCR4_Full); + FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full); + FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full); + FW_CAPS_TO_LMM(SPEED_50G, 50000baseCR2_Full); + FW_CAPS_TO_LMM(SPEED_100G, 100000baseCR4_Full); break; default: @@ -663,8 +673,7 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, /** * lmm_to_fw_caps - translate ethtool Link Mode Mask to Firmware * capabilities - * - * @link_mode_mask: ethtool Link Mode Mask + * @et_lmm: ethtool Link Mode Mask * * Translate ethtool Link Mode Mask into a Firmware Port capabilities * value. @@ -677,7 +686,7 @@ static unsigned int lmm_to_fw_caps(const unsigned long *link_mode_mask) do { \ if (test_bit(ETHTOOL_LINK_MODE_ ## __lmm_name ## _BIT, \ link_mode_mask)) \ - fw_caps |= FW_PORT_CAP_ ## __fw_name; \ + fw_caps |= FW_PORT_CAP32_ ## __fw_name; \ } while (0) LMM_TO_FW_CAPS(100baseT_Full, SPEED_100M); @@ -685,6 +694,7 @@ static unsigned int lmm_to_fw_caps(const unsigned long *link_mode_mask) LMM_TO_FW_CAPS(10000baseT_Full, SPEED_10G); LMM_TO_FW_CAPS(40000baseSR4_Full, SPEED_40G); LMM_TO_FW_CAPS(25000baseCR_Full, SPEED_25G); + LMM_TO_FW_CAPS(50000baseCR2_Full, SPEED_50G); LMM_TO_FW_CAPS(100000baseCR4_Full, SPEED_100G); #undef LMM_TO_FW_CAPS @@ -698,10 +708,6 @@ static int get_link_ksettings(struct net_device *dev, struct port_info *pi = netdev_priv(dev); struct ethtool_link_settings *base = &link_ksettings->base; - ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); - ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); - ethtool_link_ksettings_zero_link_mode(link_ksettings, lp_advertising); - /* For the nonce, the Firmware doesn't send up Port State changes * when the Virtual Interface attached to the Port is down. So * if it's down, let's grab any changes. @@ -709,6 +715,10 @@ static int get_link_ksettings(struct net_device *dev, if (!netif_running(dev)) (void)t4_update_port_info(pi); + ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); + ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); + ethtool_link_ksettings_zero_link_mode(link_ksettings, lp_advertising); + base->port = from_fw_port_mod_type(pi->port_type, pi->mod_type); if (pi->mdio_addr >= 0) { @@ -721,11 +731,11 @@ static int get_link_ksettings(struct net_device *dev, base->mdio_support = 0; } - fw_caps_to_lmm(pi->port_type, pi->link_cfg.supported, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.pcaps, link_ksettings->link_modes.supported); - fw_caps_to_lmm(pi->port_type, pi->link_cfg.advertising, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.acaps, link_ksettings->link_modes.advertising); - fw_caps_to_lmm(pi->port_type, pi->link_cfg.lp_advertising, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.lpacaps, link_ksettings->link_modes.lp_advertising); if (netif_carrier_ok(dev)) { @@ -736,8 +746,24 @@ static int get_link_ksettings(struct net_device *dev, base->duplex = DUPLEX_UNKNOWN; } + if (pi->link_cfg.fc & PAUSE_RX) { + if (pi->link_cfg.fc & PAUSE_TX) { + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + Pause); + } else { + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + Asym_Pause); + } + } else if (pi->link_cfg.fc & PAUSE_TX) { + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + Asym_Pause); + } + base->autoneg = pi->link_cfg.autoneg; - if (pi->link_cfg.supported & FW_PORT_CAP_ANEG) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_ANEG) ethtool_link_ksettings_add_link_mode(link_ksettings, supported, Autoneg); if (pi->link_cfg.autoneg) @@ -748,8 +774,7 @@ static int get_link_ksettings(struct net_device *dev, } static int set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings - *link_ksettings) + const struct ethtool_link_ksettings *link_ksettings) { struct port_info *pi = netdev_priv(dev); struct link_config *lc = &pi->link_cfg; @@ -762,12 +787,12 @@ static int set_link_ksettings(struct net_device *dev, if (base->duplex != DUPLEX_FULL) return -EINVAL; - if (!(lc->supported & FW_PORT_CAP_ANEG)) { + if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) { /* PHY offers a single speed. See if that's what's * being requested. */ if (base->autoneg == AUTONEG_DISABLE && - (lc->supported & speed_to_fw_caps(base->speed))) + (lc->pcaps & speed_to_fw_caps(base->speed))) return 0; return -EINVAL; } @@ -776,18 +801,17 @@ static int set_link_ksettings(struct net_device *dev, if (base->autoneg == AUTONEG_DISABLE) { fw_caps = speed_to_fw_caps(base->speed); - if (!(lc->supported & fw_caps)) + if (!(lc->pcaps & fw_caps)) return -EINVAL; - lc->requested_speed = fw_caps; - lc->advertising = 0; + lc->speed_caps = fw_caps; + lc->acaps = 0; } else { fw_caps = - lmm_to_fw_caps(link_ksettings->link_modes.advertising); - - if (!(lc->supported & fw_caps)) + lmm_to_fw_caps(link_ksettings->link_modes.advertising); + if (!(lc->pcaps & fw_caps)) return -EINVAL; - lc->requested_speed = 0; - lc->advertising = fw_caps | FW_PORT_CAP_ANEG; + lc->speed_caps = 0; + lc->acaps = fw_caps | FW_PORT_CAP32_ANEG; } lc->autoneg = base->autoneg; @@ -806,9 +830,9 @@ static inline unsigned int fwcap_to_eth_fec(unsigned int fw_fec) { unsigned int eth_fec = 0; - if (fw_fec & FW_PORT_CAP_FEC_RS) + if (fw_fec & FW_PORT_CAP32_FEC_RS) eth_fec |= ETHTOOL_FEC_RS; - if (fw_fec & FW_PORT_CAP_FEC_BASER_RS) + if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS) eth_fec |= ETHTOOL_FEC_BASER; /* if nothing is set, then FEC is off */ @@ -864,7 +888,7 @@ static int get_fecparam(struct net_device *dev, struct ethtool_fecparam *fec) * always support IEEE 802.3 "automatic" selection of Link FEC type if * any FEC is supported. */ - fec->fec = fwcap_to_eth_fec(lc->supported); + fec->fec = fwcap_to_eth_fec(lc->pcaps); if (fec->fec != ETHTOOL_FEC_OFF) fec->fec |= ETHTOOL_FEC_AUTO; @@ -917,7 +941,7 @@ static int set_pauseparam(struct net_device *dev, if (epause->autoneg == AUTONEG_DISABLE) lc->requested_fc = 0; - else if (lc->supported & FW_PORT_CAP_ANEG) + else if (lc->pcaps & FW_PORT_CAP32_ANEG) lc->requested_fc = PAUSE_AUTONEG; else return -EINVAL; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index e55a9299547a..92d9d795d874 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -530,15 +530,22 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, FW_PORT_CMD_ACTION_G(ntohl(pcmd->action_to_len16)); if (cmd == FW_PORT_CMD && - action == FW_PORT_ACTION_GET_PORT_INFO) { + (action == FW_PORT_ACTION_GET_PORT_INFO || + action == FW_PORT_ACTION_GET_PORT_INFO32)) { int port = FW_PORT_CMD_PORTID_G( be32_to_cpu(pcmd->op_to_portid)); - struct net_device *dev = - q->adap->port[q->adap->chan_map[port]]; - int state_input = ((pcmd->u.info.dcbxdis_pkd & - FW_PORT_CMD_DCBXDIS_F) - ? CXGB4_DCB_INPUT_FW_DISABLED - : CXGB4_DCB_INPUT_FW_ENABLED); + struct net_device *dev; + int dcbxdis, state_input; + + dev = q->adap->port[q->adap->chan_map[port]]; + dcbxdis = (action == FW_PORT_ACTION_GET_PORT_INFO + ? !!(pcmd->u.info.dcbxdis_pkd & + FW_PORT_CMD_DCBXDIS_F) + : !!(pcmd->u.info32.lstatus32_to_cbllen32 & + FW_PORT_CMD_DCBXDIS32_F)); + state_input = (dcbxdis + ? CXGB4_DCB_INPUT_FW_DISABLED + : CXGB4_DCB_INPUT_FW_ENABLED); cxgb4_dcb_state_fsm(dev, state_input); } @@ -2672,11 +2679,10 @@ static int cxgb_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, { struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; - struct fw_port_cmd port_cmd, port_rpl; - u32 link_status, speed = 0; + unsigned int link_ok, speed, mtu; u32 fw_pfvf, fw_class; int class_id = vf; - int link_ok, ret; + int ret; u16 pktsize; if (vf >= adap->num_vfs) @@ -2688,41 +2694,18 @@ static int cxgb_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, min_tx_rate, vf); return -EINVAL; } - /* Retrieve link details for VF port */ - memset(&port_cmd, 0, sizeof(port_cmd)); - port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | - FW_CMD_REQUEST_F | - FW_CMD_READ_F | - FW_PORT_CMD_PORTID_V(pi->port_id)); - port_cmd.action_to_len16 = - cpu_to_be32(FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_GET_PORT_INFO) | - FW_LEN16(port_cmd)); - ret = t4_wr_mbox(adap, adap->mbox, &port_cmd, sizeof(port_cmd), - &port_rpl); + + ret = t4_get_link_params(pi, &link_ok, &speed, &mtu); if (ret != FW_SUCCESS) { dev_err(adap->pdev_dev, - "Failed to get link status for VF %d\n", vf); + "Failed to get link information for VF %d\n", vf); return -EINVAL; } - link_status = be32_to_cpu(port_rpl.u.info.lstatus_to_modtype); - link_ok = (link_status & FW_PORT_CMD_LSTATUS_F) != 0; + if (!link_ok) { dev_err(adap->pdev_dev, "Link down for VF %d\n", vf); return -EINVAL; } - /* Determine link speed */ - if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) - speed = 100; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) - speed = 1000; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) - speed = 10000; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) - speed = 25000; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) - speed = 40000; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) - speed = 100000; if (max_tx_rate > speed) { dev_err(adap->pdev_dev, @@ -2730,7 +2713,8 @@ static int cxgb_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, max_tx_rate, vf, speed); return -EINVAL; } - pktsize = be16_to_cpu(port_rpl.u.info.mtu); + + pktsize = mtu; /* subtract ethhdr size and 4 bytes crc since, f/w appends it */ pktsize = pktsize - sizeof(struct ethhdr) - 4; /* subtract ipv4 hdr size, tcp hdr size to get typical IPv4 MSS size */ @@ -2741,7 +2725,7 @@ static int cxgb_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, SCHED_CLASS_MODE_CLASS, SCHED_CLASS_RATEUNIT_BITS, SCHED_CLASS_RATEMODE_ABS, - pi->port_id, class_id, 0, + pi->tx_chan, class_id, 0, max_tx_rate * 1000, 0, pktsize); if (ret) { dev_err(adap->pdev_dev, "Err %d for Traffic Class config\n", @@ -4208,8 +4192,9 @@ static inline bool is_x_10g_port(const struct link_config *lc) { unsigned int speeds, high_speeds; - speeds = FW_PORT_CAP_SPEED_V(FW_PORT_CAP_SPEED_G(lc->supported)); - high_speeds = speeds & ~(FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G); + speeds = FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_G(lc->pcaps)); + high_speeds = speeds & + ~(FW_PORT_CAP32_SPEED_100M | FW_PORT_CAP32_SPEED_1G); return high_speeds != 0; } @@ -4590,18 +4575,24 @@ static void print_port_info(const struct net_device *dev) else if (adap->params.pci.speed == PCI_EXP_LNKSTA_CLS_8_0GB) spd = " 8 GT/s"; - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_100M) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_100M) bufp += sprintf(bufp, "100M/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_1G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_1G) bufp += sprintf(bufp, "1G/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_10G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_10G) bufp += sprintf(bufp, "10G/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_25G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_25G) bufp += sprintf(bufp, "25G/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_40G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_40G) bufp += sprintf(bufp, "40G/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_100G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_50G) + bufp += sprintf(bufp, "50G/"); + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_100G) bufp += sprintf(bufp, "100G/"); + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_200G) + bufp += sprintf(bufp, "200G/"); + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_400G) + bufp += sprintf(bufp, "400G/"); if (bufp != buf) --bufp; sprintf(bufp, "BASE-%s", t4_get_port_type_description(pi->port_type)); @@ -4707,10 +4698,11 @@ static int config_mgmt_dev(struct pci_dev *pdev) pi = netdev_priv(netdev); pi->adapter = adap; - pi->port_id = adap->pf % adap->params.nports; + pi->tx_chan = adap->pf % adap->params.nports; SET_NETDEV_DEV(netdev, &pdev->dev); adap->port[0] = netdev; + pi->port_id = 0; err = register_netdev(adap->port[0]); if (err) { diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index fff8fba86f97..a4a33ebd0b98 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -3835,59 +3835,133 @@ void t4_ulprx_read_la(struct adapter *adap, u32 *la_buf) } } -#define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\ - FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_25G | \ - FW_PORT_CAP_SPEED_40G | FW_PORT_CAP_SPEED_100G | \ - FW_PORT_CAP_ANEG) +#define ADVERT_MASK (FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_M) | \ + FW_PORT_CAP32_ANEG) + +/** + * fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits + * @caps16: a 16-bit Port Capabilities value + * + * Returns the equivalent 32-bit Port Capabilities value. + */ +static fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16) +{ + fw_port_cap32_t caps32 = 0; + + #define CAP16_TO_CAP32(__cap) \ + do { \ + if (caps16 & FW_PORT_CAP_##__cap) \ + caps32 |= FW_PORT_CAP32_##__cap; \ + } while (0) + + CAP16_TO_CAP32(SPEED_100M); + CAP16_TO_CAP32(SPEED_1G); + CAP16_TO_CAP32(SPEED_25G); + CAP16_TO_CAP32(SPEED_10G); + CAP16_TO_CAP32(SPEED_40G); + CAP16_TO_CAP32(SPEED_100G); + CAP16_TO_CAP32(FC_RX); + CAP16_TO_CAP32(FC_TX); + CAP16_TO_CAP32(ANEG); + CAP16_TO_CAP32(MDIX); + CAP16_TO_CAP32(MDIAUTO); + CAP16_TO_CAP32(FEC_RS); + CAP16_TO_CAP32(FEC_BASER_RS); + CAP16_TO_CAP32(802_3_PAUSE); + CAP16_TO_CAP32(802_3_ASM_DIR); + + #undef CAP16_TO_CAP32 + + return caps32; +} + +/** + * fwcaps32_to_caps16 - convert 32-bit Port Capabilities to 16-bits + * @caps32: a 32-bit Port Capabilities value + * + * Returns the equivalent 16-bit Port Capabilities value. Note that + * not all 32-bit Port Capabilities can be represented in the 16-bit + * Port Capabilities and some fields/values may not make it. + */ +static fw_port_cap16_t fwcaps32_to_caps16(fw_port_cap32_t caps32) +{ + fw_port_cap16_t caps16 = 0; + + #define CAP32_TO_CAP16(__cap) \ + do { \ + if (caps32 & FW_PORT_CAP32_##__cap) \ + caps16 |= FW_PORT_CAP_##__cap; \ + } while (0) + + CAP32_TO_CAP16(SPEED_100M); + CAP32_TO_CAP16(SPEED_1G); + CAP32_TO_CAP16(SPEED_10G); + CAP32_TO_CAP16(SPEED_25G); + CAP32_TO_CAP16(SPEED_40G); + CAP32_TO_CAP16(SPEED_100G); + CAP32_TO_CAP16(FC_RX); + CAP32_TO_CAP16(FC_TX); + CAP32_TO_CAP16(802_3_PAUSE); + CAP32_TO_CAP16(802_3_ASM_DIR); + CAP32_TO_CAP16(ANEG); + CAP32_TO_CAP16(MDIX); + CAP32_TO_CAP16(MDIAUTO); + CAP32_TO_CAP16(FEC_RS); + CAP32_TO_CAP16(FEC_BASER_RS); + + #undef CAP32_TO_CAP16 + + return caps16; +} /* Translate Firmware Port Capabilities Pause specification to Common Code */ -static inline unsigned int fwcap_to_cc_pause(unsigned int fw_pause) +static inline enum cc_pause fwcap_to_cc_pause(fw_port_cap32_t fw_pause) { - unsigned int cc_pause = 0; + enum cc_pause cc_pause = 0; - if (fw_pause & FW_PORT_CAP_FC_RX) + if (fw_pause & FW_PORT_CAP32_FC_RX) cc_pause |= PAUSE_RX; - if (fw_pause & FW_PORT_CAP_FC_TX) + if (fw_pause & FW_PORT_CAP32_FC_TX) cc_pause |= PAUSE_TX; return cc_pause; } /* Translate Common Code Pause specification into Firmware Port Capabilities */ -static inline unsigned int cc_to_fwcap_pause(unsigned int cc_pause) +static inline fw_port_cap32_t cc_to_fwcap_pause(enum cc_pause cc_pause) { - unsigned int fw_pause = 0; + fw_port_cap32_t fw_pause = 0; if (cc_pause & PAUSE_RX) - fw_pause |= FW_PORT_CAP_FC_RX; + fw_pause |= FW_PORT_CAP32_FC_RX; if (cc_pause & PAUSE_TX) - fw_pause |= FW_PORT_CAP_FC_TX; + fw_pause |= FW_PORT_CAP32_FC_TX; return fw_pause; } /* Translate Firmware Forward Error Correction specification to Common Code */ -static inline unsigned int fwcap_to_cc_fec(unsigned int fw_fec) +static inline enum cc_fec fwcap_to_cc_fec(fw_port_cap32_t fw_fec) { - unsigned int cc_fec = 0; + enum cc_fec cc_fec = 0; - if (fw_fec & FW_PORT_CAP_FEC_RS) + if (fw_fec & FW_PORT_CAP32_FEC_RS) cc_fec |= FEC_RS; - if (fw_fec & FW_PORT_CAP_FEC_BASER_RS) + if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS) cc_fec |= FEC_BASER_RS; return cc_fec; } /* Translate Common Code Forward Error Correction specification to Firmware */ -static inline unsigned int cc_to_fwcap_fec(unsigned int cc_fec) +static inline fw_port_cap32_t cc_to_fwcap_fec(enum cc_fec cc_fec) { - unsigned int fw_fec = 0; + fw_port_cap32_t fw_fec = 0; if (cc_fec & FEC_RS) - fw_fec |= FW_PORT_CAP_FEC_RS; + fw_fec |= FW_PORT_CAP32_FEC_RS; if (cc_fec & FEC_BASER_RS) - fw_fec |= FW_PORT_CAP_FEC_BASER_RS; + fw_fec |= FW_PORT_CAP32_FEC_BASER_RS; return fw_fec; } @@ -3906,13 +3980,13 @@ static inline unsigned int cc_to_fwcap_fec(unsigned int cc_fec) * - If auto-negotiation is off set the MAC to the proper speed/duplex/FC, * otherwise do it later based on the outcome of auto-negotiation. */ -int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port, - struct link_config *lc) +int t4_link_l1cfg(struct adapter *adapter, unsigned int mbox, + unsigned int port, struct link_config *lc) { - struct fw_port_cmd c; - unsigned int fw_mdi = FW_PORT_CAP_MDI_V(FW_PORT_CAP_MDI_AUTO); - unsigned int fw_fc, cc_fec, fw_fec; - unsigned int rcap; + unsigned int fw_caps = adapter->params.fw_caps_support; + struct fw_port_cmd cmd; + unsigned int fw_mdi = FW_PORT_CAP32_MDI_V(FW_PORT_CAP32_MDI_AUTO); + fw_port_cap32_t fw_fc, cc_fec, fw_fec, rcap; lc->link_ok = 0; @@ -3929,36 +4003,41 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port, * use whatever is in the current Requested FEC settings. */ if (lc->requested_fec & FEC_AUTO) - cc_fec = lc->auto_fec; + cc_fec = fwcap_to_cc_fec(lc->def_acaps); else cc_fec = lc->requested_fec; fw_fec = cc_to_fwcap_fec(cc_fec); /* Figure out what our Requested Port Capabilities are going to be. */ - if (!(lc->supported & FW_PORT_CAP_ANEG)) { - rcap = (lc->supported & ADVERT_MASK) | fw_fc | fw_fec; - lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); + if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) { + rcap = (lc->pcaps & ADVERT_MASK) | fw_fc | fw_fec; + lc->fc = lc->requested_fc & ~PAUSE_AUTONEG; lc->fec = cc_fec; } else if (lc->autoneg == AUTONEG_DISABLE) { - rcap = lc->requested_speed | fw_fc | fw_fec | fw_mdi; - lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); + rcap = lc->speed_caps | fw_fc | fw_fec | fw_mdi; + lc->fc = lc->requested_fc & ~PAUSE_AUTONEG; lc->fec = cc_fec; } else { - rcap = lc->advertising | fw_fc | fw_fec | fw_mdi; + rcap = lc->acaps | fw_fc | fw_fec | fw_mdi; } /* And send that on to the Firmware ... */ - memset(&c, 0, sizeof(c)); - c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | - FW_CMD_REQUEST_F | FW_CMD_EXEC_F | - FW_PORT_CMD_PORTID_V(port)); - c.action_to_len16 = - cpu_to_be32(FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_L1_CFG) | - FW_LEN16(c)); - c.u.l1cfg.rcap = cpu_to_be32(rcap); - return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); + memset(&cmd, 0, sizeof(cmd)); + cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | + FW_CMD_REQUEST_F | FW_CMD_EXEC_F | + FW_PORT_CMD_PORTID_V(port)); + cmd.action_to_len16 = + cpu_to_be32(FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_L1_CFG + : FW_PORT_ACTION_L1_CFG32) | + FW_LEN16(cmd)); + if (fw_caps == FW_CAPS16) + cmd.u.l1cfg.rcap = cpu_to_be32(fwcaps32_to_caps16(rcap)); + else + cmd.u.l1cfg32.rcap32 = cpu_to_be32(rcap); + return t4_wr_mbox(adapter, mbox, &cmd, sizeof(cmd), NULL); } /** @@ -3980,7 +4059,7 @@ int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port) c.action_to_len16 = cpu_to_be32(FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_L1_CFG) | FW_LEN16(c)); - c.u.l1cfg.rcap = cpu_to_be32(FW_PORT_CAP_ANEG); + c.u.l1cfg.rcap = cpu_to_be32(FW_PORT_CAP32_ANEG); return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); } @@ -7696,6 +7775,98 @@ static const char *t4_link_down_rc_str(unsigned char link_down_rc) return reason[link_down_rc]; } +/** + * Return the highest speed set in the port capabilities, in Mb/s. + */ +static unsigned int fwcap_to_speed(fw_port_cap32_t caps) +{ + #define TEST_SPEED_RETURN(__caps_speed, __speed) \ + do { \ + if (caps & FW_PORT_CAP32_SPEED_##__caps_speed) \ + return __speed; \ + } while (0) + + TEST_SPEED_RETURN(400G, 400000); + TEST_SPEED_RETURN(200G, 200000); + TEST_SPEED_RETURN(100G, 100000); + TEST_SPEED_RETURN(50G, 50000); + TEST_SPEED_RETURN(40G, 40000); + TEST_SPEED_RETURN(25G, 25000); + TEST_SPEED_RETURN(10G, 10000); + TEST_SPEED_RETURN(1G, 1000); + TEST_SPEED_RETURN(100M, 100); + + #undef TEST_SPEED_RETURN + + return 0; +} + +/** + * fwcap_to_fwspeed - return highest speed in Port Capabilities + * @acaps: advertised Port Capabilities + * + * Get the highest speed for the port from the advertised Port + * Capabilities. It will be either the highest speed from the list of + * speeds or whatever user has set using ethtool. + */ +static fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps) +{ + #define TEST_SPEED_RETURN(__caps_speed) \ + do { \ + if (acaps & FW_PORT_CAP32_SPEED_##__caps_speed) \ + return FW_PORT_CAP32_SPEED_##__caps_speed; \ + } while (0) + + TEST_SPEED_RETURN(400G); + TEST_SPEED_RETURN(200G); + TEST_SPEED_RETURN(100G); + TEST_SPEED_RETURN(50G); + TEST_SPEED_RETURN(40G); + TEST_SPEED_RETURN(25G); + TEST_SPEED_RETURN(10G); + TEST_SPEED_RETURN(1G); + TEST_SPEED_RETURN(100M); + + #undef TEST_SPEED_RETURN + + return 0; +} + +/** + * lstatus_to_fwcap - translate old lstatus to 32-bit Port Capabilities + * @lstatus: old FW_PORT_ACTION_GET_PORT_INFO lstatus value + * + * Translates old FW_PORT_ACTION_GET_PORT_INFO lstatus field into new + * 32-bit Port Capabilities value. + */ +static fw_port_cap32_t lstatus_to_fwcap(u32 lstatus) +{ + fw_port_cap32_t linkattr = 0; + + /* Unfortunately the format of the Link Status in the old + * 16-bit Port Information message isn't the same as the + * 16-bit Port Capabilities bitfield used everywhere else ... + */ + if (lstatus & FW_PORT_CMD_RXPAUSE_F) + linkattr |= FW_PORT_CAP32_FC_RX; + if (lstatus & FW_PORT_CMD_TXPAUSE_F) + linkattr |= FW_PORT_CAP32_FC_TX; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) + linkattr |= FW_PORT_CAP32_SPEED_100M; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) + linkattr |= FW_PORT_CAP32_SPEED_1G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) + linkattr |= FW_PORT_CAP32_SPEED_10G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) + linkattr |= FW_PORT_CAP32_SPEED_25G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) + linkattr |= FW_PORT_CAP32_SPEED_40G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) + linkattr |= FW_PORT_CAP32_SPEED_100G; + + return linkattr; +} + /** * t4_handle_get_port_info - process a FW reply message * @pi: the port info @@ -7705,76 +7876,123 @@ static const char *t4_link_down_rc_str(unsigned char link_down_rc) */ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) { - const struct fw_port_cmd *p = (const void *)rpl; - unsigned int acaps = be16_to_cpu(p->u.info.acap); - struct adapter *adap = pi->adapter; - - /* link/module state change message */ - int speed = 0, fc, fec; - struct link_config *lc; - u32 stat = be32_to_cpu(p->u.info.lstatus_to_modtype); - int link_ok = (stat & FW_PORT_CMD_LSTATUS_F) != 0; - u32 mod = FW_PORT_CMD_MODTYPE_G(stat); - - /* Unfortunately the format of the Link Status returned by the - * Firmware isn't the same as the Firmware Port Capabilities bitfield - * used everywhere else ... + const struct fw_port_cmd *cmd = (const void *)rpl; + int action = FW_PORT_CMD_ACTION_G(be32_to_cpu(cmd->action_to_len16)); + struct adapter *adapter = pi->adapter; + struct link_config *lc = &pi->link_cfg; + int link_ok, linkdnrc; + enum fw_port_type port_type; + enum fw_port_module_type mod_type; + unsigned int speed, fc, fec; + fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; + + /* Extract the various fields from the Port Information message. */ - fc = 0; - if (stat & FW_PORT_CMD_RXPAUSE_F) - fc |= PAUSE_RX; - if (stat & FW_PORT_CMD_TXPAUSE_F) - fc |= PAUSE_TX; + switch (action) { + case FW_PORT_ACTION_GET_PORT_INFO: { + u32 lstatus = be32_to_cpu(cmd->u.info.lstatus_to_modtype); + + link_ok = (lstatus & FW_PORT_CMD_LSTATUS_F) != 0; + linkdnrc = FW_PORT_CMD_LINKDNRC_G(lstatus); + port_type = FW_PORT_CMD_PTYPE_G(lstatus); + mod_type = FW_PORT_CMD_MODTYPE_G(lstatus); + pcaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.pcap)); + acaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.acap)); + lpacaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.lpacap)); + linkattr = lstatus_to_fwcap(lstatus); + break; + } + + case FW_PORT_ACTION_GET_PORT_INFO32: { + u32 lstatus32; + + lstatus32 = be32_to_cpu(cmd->u.info32.lstatus32_to_cbllen32); + link_ok = (lstatus32 & FW_PORT_CMD_LSTATUS32_F) != 0; + linkdnrc = FW_PORT_CMD_LINKDNRC32_G(lstatus32); + port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); + mod_type = FW_PORT_CMD_MODTYPE32_G(lstatus32); + pcaps = be32_to_cpu(cmd->u.info32.pcaps32); + acaps = be32_to_cpu(cmd->u.info32.acaps32); + lpacaps = be32_to_cpu(cmd->u.info32.lpacaps32); + linkattr = be32_to_cpu(cmd->u.info32.linkattr32); + break; + } + + default: + dev_err(adapter->pdev_dev, "Handle Port Information: Bad Command/Action %#x\n", + be32_to_cpu(cmd->action_to_len16)); + return; + } fec = fwcap_to_cc_fec(acaps); + fc = fwcap_to_cc_pause(linkattr); + speed = fwcap_to_speed(linkattr); + + if (mod_type != pi->mod_type) { + /* With the newer SFP28 and QSFP28 Transceiver Module Types, + * various fundamental Port Capabilities which used to be + * immutable can now change radically. We can now have + * Speeds, Auto-Negotiation, Forward Error Correction, etc. + * all change based on what Transceiver Module is inserted. + * So we need to record the Physical "Port" Capabilities on + * every Transceiver Module change. + */ + lc->pcaps = pcaps; - if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) - speed = 100; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) - speed = 1000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) - speed = 10000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) - speed = 25000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) - speed = 40000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) - speed = 100000; - - lc = &pi->link_cfg; - - if (mod != pi->mod_type) { /* When a new Transceiver Module is inserted, the Firmware - * will examine any Forward Error Correction parameters - * present in the Transceiver Module i2c EPROM and determine - * the supported and recommended FEC settings from those - * based on IEEE 802.3 standards. We always record the - * IEEE 802.3 recommended "automatic" settings. + * will examine its i2c EPROM to determine its type and + * general operating parameters including things like Forward + * Error Control, etc. Various IEEE 802.3 standards dictate + * how to interpret these i2c values to determine default + * "sutomatic" settings. We record these for future use when + * the user explicitly requests these standards-based values. */ - lc->auto_fec = fec; + lc->def_acaps = acaps; + + /* Some versions of the early T6 Firmware "cheated" when + * handling different Transceiver Modules by changing the + * underlaying Port Type reported to the Host Drivers. As + * such we need to capture whatever Port Type the Firmware + * sends us and record it in case it's different from what we + * were told earlier. Unfortunately, since Firmware is + * forever, we'll need to keep this code here forever, but in + * later T6 Firmware it should just be an assignment of the + * same value already recorded. + */ + pi->port_type = port_type; - pi->mod_type = mod; - t4_os_portmod_changed(adap, pi->port_id); + pi->mod_type = mod_type; + t4_os_portmod_changed(adapter, pi->port_id); } + if (link_ok != lc->link_ok || speed != lc->speed || fc != lc->fc || fec != lc->fec) { /* something changed */ if (!link_ok && lc->link_ok) { - unsigned char rc = FW_PORT_CMD_LINKDNRC_G(stat); - - lc->link_down_rc = rc; - dev_warn(adap->pdev_dev, - "Port %d link down, reason: %s\n", - pi->port_id, t4_link_down_rc_str(rc)); + lc->link_down_rc = linkdnrc; + dev_warn(adapter->pdev_dev, "Port %d link down, reason: %s\n", + pi->tx_chan, t4_link_down_rc_str(linkdnrc)); } lc->link_ok = link_ok; lc->speed = speed; lc->fc = fc; lc->fec = fec; - lc->supported = be16_to_cpu(p->u.info.pcap); - lc->lp_advertising = be16_to_cpu(p->u.info.lpacap); + lc->lpacaps = lpacaps; + lc->acaps = acaps & ADVERT_MASK; + + if (lc->acaps & FW_PORT_CAP32_ANEG) { + lc->autoneg = AUTONEG_ENABLE; + } else { + /* When Autoneg is disabled, user needs to set + * single speed. + * Similar to cxgb4_ethtool.c: set_link_ksettings + */ + lc->acaps = 0; + lc->speed_caps = fwcap_to_fwspeed(acaps); + lc->autoneg = AUTONEG_DISABLE; + } - t4_os_link_changed(adap, pi->port_id, link_ok); + t4_os_link_changed(adapter, pi->port_id, link_ok); } } @@ -7788,15 +8006,18 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) */ int t4_update_port_info(struct port_info *pi) { + unsigned int fw_caps = pi->adapter->params.fw_caps_support; struct fw_port_cmd port_cmd; int ret; memset(&port_cmd, 0, sizeof(port_cmd)); port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | FW_CMD_REQUEST_F | FW_CMD_READ_F | - FW_PORT_CMD_PORTID_V(pi->port_id)); + FW_PORT_CMD_PORTID_V(pi->tx_chan)); port_cmd.action_to_len16 = cpu_to_be32( - FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_GET_PORT_INFO) | + FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32) | FW_LEN16(port_cmd)); ret = t4_wr_mbox(pi->adapter, pi->adapter->mbox, &port_cmd, sizeof(port_cmd), &port_cmd); @@ -7807,6 +8028,65 @@ int t4_update_port_info(struct port_info *pi) return 0; } +/** + * t4_get_link_params - retrieve basic link parameters for given port + * @pi: the port + * @link_okp: value return pointer for link up/down + * @speedp: value return pointer for speed (Mb/s) + * @mtup: value return pointer for mtu + * + * Retrieves basic link parameters for a port: link up/down, speed (Mb/s), + * and MTU for a specified port. A negative error is returned on + * failure; 0 on success. + */ +int t4_get_link_params(struct port_info *pi, unsigned int *link_okp, + unsigned int *speedp, unsigned int *mtup) +{ + unsigned int fw_caps = pi->adapter->params.fw_caps_support; + struct fw_port_cmd port_cmd; + unsigned int action, link_ok, speed, mtu; + fw_port_cap32_t linkattr; + int ret; + + memset(&port_cmd, 0, sizeof(port_cmd)); + port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F | + FW_PORT_CMD_PORTID_V(pi->tx_chan)); + action = (fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32); + port_cmd.action_to_len16 = cpu_to_be32( + FW_PORT_CMD_ACTION_V(action) | + FW_LEN16(port_cmd)); + ret = t4_wr_mbox(pi->adapter, pi->adapter->mbox, + &port_cmd, sizeof(port_cmd), &port_cmd); + if (ret) + return ret; + + if (action == FW_PORT_ACTION_GET_PORT_INFO) { + u32 lstatus = be32_to_cpu(port_cmd.u.info.lstatus_to_modtype); + + link_ok = !!(lstatus & FW_PORT_CMD_LSTATUS_F); + linkattr = lstatus_to_fwcap(lstatus); + mtu = be16_to_cpu(port_cmd.u.info.mtu); + } else { + u32 lstatus32 = + be32_to_cpu(port_cmd.u.info32.lstatus32_to_cbllen32); + + link_ok = !!(lstatus32 & FW_PORT_CMD_LSTATUS32_F); + linkattr = be32_to_cpu(port_cmd.u.info32.linkattr32); + mtu = FW_PORT_CMD_MTU32_G( + be32_to_cpu(port_cmd.u.info32.auxlinfo32_mtu32)); + } + speed = fwcap_to_speed(linkattr); + + *link_okp = link_ok; + *speedp = fwcap_to_speed(linkattr); + *mtup = mtu; + + return 0; +} + /** * t4_handle_fw_rpl - process a FW reply message * @adap: the adapter @@ -7827,7 +8107,9 @@ int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl) unsigned int action = FW_PORT_CMD_ACTION_G(be32_to_cpu(p->action_to_len16)); - if (opcode == FW_PORT_CMD && action == FW_PORT_ACTION_GET_PORT_INFO) { + if (opcode == FW_PORT_CMD && + (action == FW_PORT_ACTION_GET_PORT_INFO || + action == FW_PORT_ACTION_GET_PORT_INFO32)) { int i; int chan = FW_PORT_CMD_PORTID_G(be32_to_cpu(p->op_to_portid)); struct port_info *pi = NULL; @@ -7840,7 +8122,8 @@ int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl) t4_handle_get_port_info(pi, rpl); } else { - dev_warn(adap->pdev_dev, "Unknown firmware reply %d\n", opcode); + dev_warn(adap->pdev_dev, "Unknown firmware reply %d\n", + opcode); return -EINVAL; } return 0; @@ -7859,35 +8142,35 @@ static void get_pci_mode(struct adapter *adapter, struct pci_params *p) /** * init_link_config - initialize a link's SW state - * @lc: structure holding the link state + * @lc: pointer to structure holding the link state * @pcaps: link Port Capabilities * @acaps: link current Advertised Port Capabilities * * Initializes the SW state maintained for each link, including the link's * capabilities and default speed/flow-control/autonegotiation settings. */ -static void init_link_config(struct link_config *lc, unsigned int pcaps, - unsigned int acaps) +static void init_link_config(struct link_config *lc, fw_port_cap32_t pcaps, + fw_port_cap32_t acaps) { - lc->supported = pcaps; - lc->lp_advertising = 0; - lc->requested_speed = 0; + lc->pcaps = pcaps; + lc->def_acaps = acaps; + lc->lpacaps = 0; + lc->speed_caps = 0; lc->speed = 0; lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; /* For Forward Error Control, we default to whatever the Firmware * tells us the Link is currently advertising. */ - lc->auto_fec = fwcap_to_cc_fec(acaps); lc->requested_fec = FEC_AUTO; - lc->fec = lc->auto_fec; + lc->fec = fwcap_to_cc_fec(lc->def_acaps); - if (lc->supported & FW_PORT_CAP_ANEG) { - lc->advertising = lc->supported & ADVERT_MASK; + if (lc->pcaps & FW_PORT_CAP32_ANEG) { + lc->acaps = lc->pcaps & ADVERT_MASK; lc->autoneg = AUTONEG_ENABLE; lc->requested_fc |= PAUSE_AUTONEG; } else { - lc->advertising = 0; + lc->acaps = 0; lc->autoneg = AUTONEG_DISABLE; } } @@ -8412,7 +8695,7 @@ int t4_init_rss_mode(struct adapter *adap, int mbox) } /** - * t4_init_portinfo - allocate a virtual interface amd initialize port_info + * t4_init_portinfo - allocate a virtual interface and initialize port_info * @pi: the port_info * @mbox: mailbox to use for the FW command * @port: physical port associated with the VI @@ -8428,21 +8711,67 @@ int t4_init_rss_mode(struct adapter *adap, int mbox) int t4_init_portinfo(struct port_info *pi, int mbox, int port, int pf, int vf, u8 mac[]) { - int ret; - struct fw_port_cmd c; + struct adapter *adapter = pi->adapter; + unsigned int fw_caps = adapter->params.fw_caps_support; + struct fw_port_cmd cmd; unsigned int rss_size; + enum fw_port_type port_type; + int mdio_addr; + fw_port_cap32_t pcaps, acaps; + int ret; - memset(&c, 0, sizeof(c)); - c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | - FW_CMD_REQUEST_F | FW_CMD_READ_F | - FW_PORT_CMD_PORTID_V(port)); - c.action_to_len16 = cpu_to_be32( - FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_GET_PORT_INFO) | - FW_LEN16(c)); - ret = t4_wr_mbox(pi->adapter, mbox, &c, sizeof(c), &c); + /* If we haven't yet determined whether we're talking to Firmware + * which knows the new 32-bit Port Capabilities, it's time to find + * out now. This will also tell new Firmware to send us Port Status + * Updates using the new 32-bit Port Capabilities version of the + * Port Information message. + */ + if (fw_caps == FW_CAPS_UNKNOWN) { + u32 param, val; + + param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_PFVF) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_PFVF_PORT_CAPS32)); + val = 1; + ret = t4_set_params(adapter, mbox, pf, vf, 1, ¶m, &val); + fw_caps = (ret == 0 ? FW_CAPS32 : FW_CAPS16); + adapter->params.fw_caps_support = fw_caps; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F | + FW_PORT_CMD_PORTID_V(port)); + cmd.action_to_len16 = cpu_to_be32( + FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32) | + FW_LEN16(cmd)); + ret = t4_wr_mbox(pi->adapter, mbox, &cmd, sizeof(cmd), &cmd); if (ret) return ret; + /* Extract the various fields from the Port Information message. + */ + if (fw_caps == FW_CAPS16) { + u32 lstatus = be32_to_cpu(cmd.u.info.lstatus_to_modtype); + + port_type = FW_PORT_CMD_PTYPE_G(lstatus); + mdio_addr = ((lstatus & FW_PORT_CMD_MDIOCAP_F) + ? FW_PORT_CMD_MDIOADDR_G(lstatus) + : -1); + pcaps = fwcaps16_to_caps32(be16_to_cpu(cmd.u.info.pcap)); + acaps = fwcaps16_to_caps32(be16_to_cpu(cmd.u.info.acap)); + } else { + u32 lstatus32 = be32_to_cpu(cmd.u.info32.lstatus32_to_cbllen32); + + port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); + mdio_addr = ((lstatus32 & FW_PORT_CMD_MDIOCAP32_F) + ? FW_PORT_CMD_MDIOADDR32_G(lstatus32) + : -1); + pcaps = be32_to_cpu(cmd.u.info32.pcaps32); + acaps = be32_to_cpu(cmd.u.info32.acaps32); + } + ret = t4_alloc_vi(pi->adapter, mbox, port, pf, vf, 1, mac, &rss_size); if (ret < 0) return ret; @@ -8452,14 +8781,11 @@ int t4_init_portinfo(struct port_info *pi, int mbox, pi->lport = port; pi->rss_size = rss_size; - ret = be32_to_cpu(c.u.info.lstatus_to_modtype); - pi->mdio_addr = (ret & FW_PORT_CMD_MDIOCAP_F) ? - FW_PORT_CMD_MDIOADDR_G(ret) : -1; - pi->port_type = FW_PORT_CMD_PTYPE_G(ret); + pi->port_type = port_type; + pi->mdio_addr = mdio_addr; pi->mod_type = FW_PORT_MOD_TYPE_NA; - init_link_config(&pi->link_cfg, be16_to_cpu(c.u.info.pcap), - be16_to_cpu(c.u.info.acap)); + init_link_config(&pi->link_cfg, pcaps, acaps); return 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index ad825fbc21a5..ca2756dcefc5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -1173,7 +1173,8 @@ enum fw_params_param_pfvf { FW_PARAMS_PARAM_PFVF_ACTIVE_FILTER_END = 0x2E, FW_PARAMS_PARAM_PFVF_ETHOFLD_END = 0x30, FW_PARAMS_PARAM_PFVF_CPLFW4MSG_ENCAP = 0x31, - FW_PARAMS_PARAM_PFVF_NCRYPTO_LOOKASIDE = 0x32 + FW_PARAMS_PARAM_PFVF_NCRYPTO_LOOKASIDE = 0x32, + FW_PARAMS_PARAM_PFVF_PORT_CAPS32 = 0x3A, }; /* @@ -2256,6 +2257,7 @@ struct fw_acl_vlan_cmd { #define FW_ACL_VLAN_CMD_FM_S 6 #define FW_ACL_VLAN_CMD_FM_V(x) ((x) << FW_ACL_VLAN_CMD_FM_S) +/* old 16-bit port capabilities bitmap (fw_port_cap16_t) */ enum fw_port_cap { FW_PORT_CAP_SPEED_100M = 0x0001, FW_PORT_CAP_SPEED_1G = 0x0002, @@ -2291,6 +2293,84 @@ enum fw_port_mdi { #define FW_PORT_CAP_MDI_S 9 #define FW_PORT_CAP_MDI_V(x) ((x) << FW_PORT_CAP_MDI_S) +/* new 32-bit port capabilities bitmap (fw_port_cap32_t) */ +#define FW_PORT_CAP32_SPEED_100M 0x00000001UL +#define FW_PORT_CAP32_SPEED_1G 0x00000002UL +#define FW_PORT_CAP32_SPEED_10G 0x00000004UL +#define FW_PORT_CAP32_SPEED_25G 0x00000008UL +#define FW_PORT_CAP32_SPEED_40G 0x00000010UL +#define FW_PORT_CAP32_SPEED_50G 0x00000020UL +#define FW_PORT_CAP32_SPEED_100G 0x00000040UL +#define FW_PORT_CAP32_SPEED_200G 0x00000080UL +#define FW_PORT_CAP32_SPEED_400G 0x00000100UL +#define FW_PORT_CAP32_SPEED_RESERVED1 0x00000200UL +#define FW_PORT_CAP32_SPEED_RESERVED2 0x00000400UL +#define FW_PORT_CAP32_SPEED_RESERVED3 0x00000800UL +#define FW_PORT_CAP32_RESERVED1 0x0000f000UL +#define FW_PORT_CAP32_FC_RX 0x00010000UL +#define FW_PORT_CAP32_FC_TX 0x00020000UL +#define FW_PORT_CAP32_802_3_PAUSE 0x00040000UL +#define FW_PORT_CAP32_802_3_ASM_DIR 0x00080000UL +#define FW_PORT_CAP32_ANEG 0x00100000UL +#define FW_PORT_CAP32_MDIX 0x00200000UL +#define FW_PORT_CAP32_MDIAUTO 0x00400000UL +#define FW_PORT_CAP32_FEC_RS 0x00800000UL +#define FW_PORT_CAP32_FEC_BASER_RS 0x01000000UL +#define FW_PORT_CAP32_FEC_RESERVED1 0x02000000UL +#define FW_PORT_CAP32_FEC_RESERVED2 0x04000000UL +#define FW_PORT_CAP32_FEC_RESERVED3 0x08000000UL +#define FW_PORT_CAP32_RESERVED2 0xf0000000UL + +#define FW_PORT_CAP32_SPEED_S 0 +#define FW_PORT_CAP32_SPEED_M 0xfff +#define FW_PORT_CAP32_SPEED_V(x) ((x) << FW_PORT_CAP32_SPEED_S) +#define FW_PORT_CAP32_SPEED_G(x) \ + (((x) >> FW_PORT_CAP32_SPEED_S) & FW_PORT_CAP32_SPEED_M) + +#define FW_PORT_CAP32_FC_S 16 +#define FW_PORT_CAP32_FC_M 0x3 +#define FW_PORT_CAP32_FC_V(x) ((x) << FW_PORT_CAP32_FC_S) +#define FW_PORT_CAP32_FC_G(x) \ + (((x) >> FW_PORT_CAP32_FC_S) & FW_PORT_CAP32_FC_M) + +#define FW_PORT_CAP32_802_3_S 18 +#define FW_PORT_CAP32_802_3_M 0x3 +#define FW_PORT_CAP32_802_3_V(x) ((x) << FW_PORT_CAP32_802_3_S) +#define FW_PORT_CAP32_802_3_G(x) \ + (((x) >> FW_PORT_CAP32_802_3_S) & FW_PORT_CAP32_802_3_M) + +#define FW_PORT_CAP32_ANEG_S 20 +#define FW_PORT_CAP32_ANEG_M 0x1 +#define FW_PORT_CAP32_ANEG_V(x) ((x) << FW_PORT_CAP32_ANEG_S) +#define FW_PORT_CAP32_ANEG_G(x) \ + (((x) >> FW_PORT_CAP32_ANEG_S) & FW_PORT_CAP32_ANEG_M) + +enum fw_port_mdi32 { + FW_PORT_CAP32_MDI_UNCHANGED, + FW_PORT_CAP32_MDI_AUTO, + FW_PORT_CAP32_MDI_F_STRAIGHT, + FW_PORT_CAP32_MDI_F_CROSSOVER +}; + +#define FW_PORT_CAP32_MDI_S 21 +#define FW_PORT_CAP32_MDI_M 3 +#define FW_PORT_CAP32_MDI_V(x) ((x) << FW_PORT_CAP32_MDI_S) +#define FW_PORT_CAP32_MDI_G(x) \ + (((x) >> FW_PORT_CAP32_MDI_S) & FW_PORT_CAP32_MDI_M) + +#define FW_PORT_CAP32_FEC_S 23 +#define FW_PORT_CAP32_FEC_M 0x1f +#define FW_PORT_CAP32_FEC_V(x) ((x) << FW_PORT_CAP32_FEC_S) +#define FW_PORT_CAP32_FEC_G(x) \ + (((x) >> FW_PORT_CAP32_FEC_S) & FW_PORT_CAP32_FEC_M) + +/* macros to isolate various 32-bit Port Capabilities sub-fields */ +#define CAP32_SPEED(__cap32) \ + (FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_M) & __cap32) + +#define CAP32_FEC(__cap32) \ + (FW_PORT_CAP32_FEC_V(FW_PORT_CAP32_FEC_M) & __cap32) + enum fw_port_action { FW_PORT_ACTION_L1_CFG = 0x0001, FW_PORT_ACTION_L2_CFG = 0x0002, @@ -2300,6 +2380,8 @@ enum fw_port_action { FW_PORT_ACTION_DCB_READ_TRANS = 0x0006, FW_PORT_ACTION_DCB_READ_RECV = 0x0007, FW_PORT_ACTION_DCB_READ_DET = 0x0008, + FW_PORT_ACTION_L1_CFG32 = 0x0009, + FW_PORT_ACTION_GET_PORT_INFO32 = 0x000a, FW_PORT_ACTION_LOW_PWR_TO_NORMAL = 0x0010, FW_PORT_ACTION_L1_LOW_PWR_EN = 0x0011, FW_PORT_ACTION_L2_WOL_MODE_EN = 0x0012, @@ -2447,6 +2529,18 @@ struct fw_port_cmd { __be64 r12; } control; } dcb; + struct fw_port_l1cfg32 { + __be32 rcap32; + __be32 r; + } l1cfg32; + struct fw_port_info32 { + __be32 lstatus32_to_cbllen32; + __be32 auxlinfo32_mtu32; + __be32 linkattr32; + __be32 pcaps32; + __be32 acaps32; + __be32 lpacaps32; + } info32; } u; }; @@ -2555,6 +2649,85 @@ struct fw_port_cmd { #define FW_PORT_CMD_DCB_VERSION_G(x) \ (((x) >> FW_PORT_CMD_DCB_VERSION_S) & FW_PORT_CMD_DCB_VERSION_M) +#define FW_PORT_CMD_LSTATUS32_S 31 +#define FW_PORT_CMD_LSTATUS32_M 0x1 +#define FW_PORT_CMD_LSTATUS32_V(x) ((x) << FW_PORT_CMD_LSTATUS32_S) +#define FW_PORT_CMD_LSTATUS32_G(x) \ + (((x) >> FW_PORT_CMD_LSTATUS32_S) & FW_PORT_CMD_LSTATUS32_M) +#define FW_PORT_CMD_LSTATUS32_F FW_PORT_CMD_LSTATUS32_V(1U) + +#define FW_PORT_CMD_LINKDNRC32_S 28 +#define FW_PORT_CMD_LINKDNRC32_M 0x7 +#define FW_PORT_CMD_LINKDNRC32_V(x) ((x) << FW_PORT_CMD_LINKDNRC32_S) +#define FW_PORT_CMD_LINKDNRC32_G(x) \ + (((x) >> FW_PORT_CMD_LINKDNRC32_S) & FW_PORT_CMD_LINKDNRC32_M) + +#define FW_PORT_CMD_DCBXDIS32_S 27 +#define FW_PORT_CMD_DCBXDIS32_M 0x1 +#define FW_PORT_CMD_DCBXDIS32_V(x) ((x) << FW_PORT_CMD_DCBXDIS32_S) +#define FW_PORT_CMD_DCBXDIS32_G(x) \ + (((x) >> FW_PORT_CMD_DCBXDIS32_S) & FW_PORT_CMD_DCBXDIS32_M) +#define FW_PORT_CMD_DCBXDIS32_F FW_PORT_CMD_DCBXDIS32_V(1U) + +#define FW_PORT_CMD_MDIOCAP32_S 26 +#define FW_PORT_CMD_MDIOCAP32_M 0x1 +#define FW_PORT_CMD_MDIOCAP32_V(x) ((x) << FW_PORT_CMD_MDIOCAP32_S) +#define FW_PORT_CMD_MDIOCAP32_G(x) \ + (((x) >> FW_PORT_CMD_MDIOCAP32_S) & FW_PORT_CMD_MDIOCAP32_M) +#define FW_PORT_CMD_MDIOCAP32_F FW_PORT_CMD_MDIOCAP32_V(1U) + +#define FW_PORT_CMD_MDIOADDR32_S 21 +#define FW_PORT_CMD_MDIOADDR32_M 0x1f +#define FW_PORT_CMD_MDIOADDR32_V(x) ((x) << FW_PORT_CMD_MDIOADDR32_S) +#define FW_PORT_CMD_MDIOADDR32_G(x) \ + (((x) >> FW_PORT_CMD_MDIOADDR32_S) & FW_PORT_CMD_MDIOADDR32_M) + +#define FW_PORT_CMD_PORTTYPE32_S 13 +#define FW_PORT_CMD_PORTTYPE32_M 0xff +#define FW_PORT_CMD_PORTTYPE32_V(x) ((x) << FW_PORT_CMD_PORTTYPE32_S) +#define FW_PORT_CMD_PORTTYPE32_G(x) \ + (((x) >> FW_PORT_CMD_PORTTYPE32_S) & FW_PORT_CMD_PORTTYPE32_M) + +#define FW_PORT_CMD_MODTYPE32_S 8 +#define FW_PORT_CMD_MODTYPE32_M 0x1f +#define FW_PORT_CMD_MODTYPE32_V(x) ((x) << FW_PORT_CMD_MODTYPE32_S) +#define FW_PORT_CMD_MODTYPE32_G(x) \ + (((x) >> FW_PORT_CMD_MODTYPE32_S) & FW_PORT_CMD_MODTYPE32_M) + +#define FW_PORT_CMD_CBLLEN32_S 0 +#define FW_PORT_CMD_CBLLEN32_M 0xff +#define FW_PORT_CMD_CBLLEN32_V(x) ((x) << FW_PORT_CMD_CBLLEN32_S) +#define FW_PORT_CMD_CBLLEN32_G(x) \ + (((x) >> FW_PORT_CMD_CBLLEN32_S) & FW_PORT_CMD_CBLLEN32_M) + +#define FW_PORT_CMD_AUXLINFO32_S 24 +#define FW_PORT_CMD_AUXLINFO32_M 0xff +#define FW_PORT_CMD_AUXLINFO32_V(x) ((x) << FW_PORT_CMD_AUXLINFO32_S) +#define FW_PORT_CMD_AUXLINFO32_G(x) \ + (((x) >> FW_PORT_CMD_AUXLINFO32_S) & FW_PORT_CMD_AUXLINFO32_M) + +#define FW_PORT_AUXLINFO32_KX4_S 2 +#define FW_PORT_AUXLINFO32_KX4_M 0x1 +#define FW_PORT_AUXLINFO32_KX4_V(x) \ + ((x) << FW_PORT_AUXLINFO32_KX4_S) +#define FW_PORT_AUXLINFO32_KX4_G(x) \ + (((x) >> FW_PORT_AUXLINFO32_KX4_S) & FW_PORT_AUXLINFO32_KX4_M) +#define FW_PORT_AUXLINFO32_KX4_F FW_PORT_AUXLINFO32_KX4_V(1U) + +#define FW_PORT_AUXLINFO32_KR_S 1 +#define FW_PORT_AUXLINFO32_KR_M 0x1 +#define FW_PORT_AUXLINFO32_KR_V(x) \ + ((x) << FW_PORT_AUXLINFO32_KR_S) +#define FW_PORT_AUXLINFO32_KR_G(x) \ + (((x) >> FW_PORT_AUXLINFO32_KR_S) & FW_PORT_AUXLINFO32_KR_M) +#define FW_PORT_AUXLINFO32_KR_F FW_PORT_AUXLINFO32_KR_V(1U) + +#define FW_PORT_CMD_MTU32_S 0 +#define FW_PORT_CMD_MTU32_M 0xffff +#define FW_PORT_CMD_MTU32_V(x) ((x) << FW_PORT_CMD_MTU32_S) +#define FW_PORT_CMD_MTU32_G(x) \ + (((x) >> FW_PORT_CMD_MTU32_S) & FW_PORT_CMD_MTU32_M) + enum fw_port_type { FW_PORT_TYPE_FIBER_XFI, FW_PORT_TYPE_FIBER_XAUI, diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 2b85b874fd0d..8996ebbd222e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -182,7 +182,7 @@ void t4vf_os_link_changed(struct adapter *adapter, int pidx, int link_ok) break; } - switch (pi->link_cfg.fc) { + switch ((int)pi->link_cfg.fc) { case PAUSE_RX: fc = "RX"; break; @@ -191,7 +191,7 @@ void t4vf_os_link_changed(struct adapter *adapter, int pidx, int link_ok) fc = "TX"; break; - case PAUSE_RX|PAUSE_TX: + case PAUSE_RX | PAUSE_TX: fc = "RX/TX"; break; @@ -1213,7 +1213,11 @@ static int from_fw_port_mod_type(enum fw_port_type port_type, } else if (port_type == FW_PORT_TYPE_SFP || port_type == FW_PORT_TYPE_QSFP_10G || port_type == FW_PORT_TYPE_QSA || - port_type == FW_PORT_TYPE_QSFP) { + port_type == FW_PORT_TYPE_QSFP || + port_type == FW_PORT_TYPE_CR4_QSFP || + port_type == FW_PORT_TYPE_CR_QSFP || + port_type == FW_PORT_TYPE_CR2_QSFP || + port_type == FW_PORT_TYPE_SFP28) { if (mod_type == FW_PORT_MOD_TYPE_LR || mod_type == FW_PORT_MOD_TYPE_SR || mod_type == FW_PORT_MOD_TYPE_ER || @@ -1224,6 +1228,9 @@ static int from_fw_port_mod_type(enum fw_port_type port_type, return PORT_DA; else return PORT_OTHER; + } else if (port_type == FW_PORT_TYPE_KR4_100G || + port_type == FW_PORT_TYPE_KR_SFP28) { + return PORT_NONE; } return PORT_OTHER; @@ -1242,12 +1249,13 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, unsigned int fw_caps, unsigned long *link_mode_mask) { - #define SET_LMM(__lmm_name) __set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name\ - ## _BIT, link_mode_mask) + #define SET_LMM(__lmm_name) \ + __set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name ## _BIT, \ + link_mode_mask) #define FW_CAPS_TO_LMM(__fw_name, __lmm_name) \ do { \ - if (fw_caps & FW_PORT_CAP_ ## __fw_name) \ + if (fw_caps & FW_PORT_CAP32_ ## __fw_name) \ SET_LMM(__lmm_name); \ } while (0) @@ -1310,6 +1318,16 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, SET_LMM(25000baseCR_Full); break; + case FW_PORT_TYPE_KR_SFP28: + SET_LMM(Backplane); + SET_LMM(25000baseKR_Full); + break; + + case FW_PORT_TYPE_CR2_QSFP: + SET_LMM(FIBRE); + SET_LMM(50000baseSR2_Full); + break; + case FW_PORT_TYPE_KR4_100G: case FW_PORT_TYPE_CR4_QSFP: SET_LMM(FIBRE); @@ -1329,12 +1347,18 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, } static int cxgb4vf_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings - *link_ksettings) + struct ethtool_link_ksettings *link_ksettings) { - const struct port_info *pi = netdev_priv(dev); + struct port_info *pi = netdev_priv(dev); struct ethtool_link_settings *base = &link_ksettings->base; + /* For the nonce, the Firmware doesn't send up Port State changes + * when the Virtual Interface attached to the Port is down. So + * if it's down, let's grab any changes. + */ + if (!netif_running(dev)) + (void)t4vf_update_port_info(pi); + ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); ethtool_link_ksettings_zero_link_mode(link_ksettings, lp_advertising); @@ -1351,11 +1375,11 @@ static int cxgb4vf_get_link_ksettings(struct net_device *dev, base->mdio_support = 0; } - fw_caps_to_lmm(pi->port_type, pi->link_cfg.supported, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.pcaps, link_ksettings->link_modes.supported); - fw_caps_to_lmm(pi->port_type, pi->link_cfg.advertising, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.acaps, link_ksettings->link_modes.advertising); - fw_caps_to_lmm(pi->port_type, pi->link_cfg.lp_advertising, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.lpacaps, link_ksettings->link_modes.lp_advertising); if (netif_carrier_ok(dev)) { @@ -1367,7 +1391,7 @@ static int cxgb4vf_get_link_ksettings(struct net_device *dev, } base->autoneg = pi->link_cfg.autoneg; - if (pi->link_cfg.supported & FW_PORT_CAP_ANEG) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_ANEG) ethtool_link_ksettings_add_link_mode(link_ksettings, supported, Autoneg); if (pi->link_cfg.autoneg) diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h index b3903fe411aa..9cf9c56b0f73 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h @@ -104,24 +104,62 @@ struct t4vf_port_stats { /* * Per-"port" (Virtual Interface) link configuration ... */ -struct link_config { - unsigned int supported; /* link capabilities */ - unsigned int advertising; /* advertised capabilities */ - unsigned short lp_advertising; /* peer advertised capabilities */ - unsigned int requested_speed; /* speed user has requested */ - unsigned int speed; /* actual link speed */ - unsigned char requested_fc; /* flow control user has requested */ - unsigned char fc; /* actual link flow control */ - unsigned char autoneg; /* autonegotiating? */ - unsigned char link_ok; /* link up? */ +typedef u16 fw_port_cap16_t; /* 16-bit Port Capabilities integral value */ +typedef u32 fw_port_cap32_t; /* 32-bit Port Capabilities integral value */ + +enum fw_caps { + FW_CAPS_UNKNOWN = 0, /* 0'ed out initial state */ + FW_CAPS16 = 1, /* old Firmware: 16-bit Port Capabilities */ + FW_CAPS32 = 2, /* new Firmware: 32-bit Port Capabilities */ }; -enum { - PAUSE_RX = 1 << 0, - PAUSE_TX = 1 << 1, - PAUSE_AUTONEG = 1 << 2 +enum cc_pause { + PAUSE_RX = 1 << 0, + PAUSE_TX = 1 << 1, + PAUSE_AUTONEG = 1 << 2 +}; + +enum cc_fec { + FEC_AUTO = 1 << 0, /* IEEE 802.3 "automatic" */ + FEC_RS = 1 << 1, /* Reed-Solomon */ + FEC_BASER_RS = 1 << 2, /* BaseR/Reed-Solomon */ +}; + +struct link_config { + fw_port_cap32_t pcaps; /* link capabilities */ + fw_port_cap32_t acaps; /* advertised capabilities */ + fw_port_cap32_t lpacaps; /* peer advertised capabilities */ + + fw_port_cap32_t speed_caps; /* speed(s) user has requested */ + u32 speed; /* actual link speed */ + + enum cc_pause requested_fc; /* flow control user has requested */ + enum cc_pause fc; /* actual link flow control */ + + enum cc_fec auto_fec; /* Forward Error Correction: */ + enum cc_fec requested_fec; /* "automatic" (IEEE 802.3), */ + enum cc_fec fec; /* requested, and actual in use */ + + unsigned char autoneg; /* autonegotiating? */ + + unsigned char link_ok; /* link up? */ + unsigned char link_down_rc; /* link down reason */ }; +/* Return true if the Link Configuration supports "High Speeds" (those greater + * than 1Gb/s). + */ +static inline bool is_x_10g_port(const struct link_config *lc) +{ + fw_port_cap32_t speeds, high_speeds; + + speeds = FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_G(lc->pcaps)); + high_speeds = + speeds & ~(FW_PORT_CAP32_SPEED_100M | FW_PORT_CAP32_SPEED_1G); + + return high_speeds != 0; +} + /* * General device parameters ... */ @@ -227,6 +265,7 @@ struct adapter_params { struct arch_specific_params arch; /* chip specific params */ enum chip_type chip; /* chip code */ u8 nports; /* # of Ethernet "ports" */ + u8 fw_caps_support; /* 32-bit Port Capabilities */ }; /* Firmware Mailbox Command/Reply log. All values are in Host-Endian format. @@ -266,24 +305,6 @@ static inline struct mbox_cmd *mbox_cmd_log_entry(struct mbox_cmd_log *log, #define for_each_port(adapter, iter) \ for (iter = 0; iter < (adapter)->params.nports; iter++) -static inline bool is_10g_port(const struct link_config *lc) -{ - return (lc->supported & FW_PORT_CAP_SPEED_10G) != 0; -} - -/* Return true if the Link Configuration supports "High Speeds" (those greater - * than 1Gb/s). - */ -static inline bool is_x_10g_port(const struct link_config *lc) -{ - unsigned int speeds, high_speeds; - - speeds = FW_PORT_CAP_SPEED_V(FW_PORT_CAP_SPEED_G(lc->supported)); - high_speeds = speeds & ~(FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G); - - return high_speeds != 0; -} - static inline unsigned int core_ticks_per_usec(const struct adapter *adapter) { return adapter->params.vpd.cclk / 1000; @@ -387,6 +408,7 @@ int t4vf_iq_free(struct adapter *, unsigned int, unsigned int, unsigned int, unsigned int); int t4vf_eth_eq_free(struct adapter *, unsigned int); +int t4vf_update_port_info(struct port_info *pi); int t4vf_handle_fw_rpl(struct adapter *, const __be64 *); int t4vf_prep_adapter(struct adapter *); int t4vf_get_vf_mac_acl(struct adapter *adapter, unsigned int pf, diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index e98248f00fef..a8d94963b4d0 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -313,32 +313,130 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, return ret; } -#define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\ - FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_25G | \ - FW_PORT_CAP_SPEED_40G | FW_PORT_CAP_SPEED_100G | \ - FW_PORT_CAP_ANEG) +#define ADVERT_MASK (FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_M) | \ + FW_PORT_CAP32_ANEG) /** + * fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits + * @caps16: a 16-bit Port Capabilities value + * + * Returns the equivalent 32-bit Port Capabilities value. + */ +static fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16) +{ + fw_port_cap32_t caps32 = 0; + + #define CAP16_TO_CAP32(__cap) \ + do { \ + if (caps16 & FW_PORT_CAP_##__cap) \ + caps32 |= FW_PORT_CAP32_##__cap; \ + } while (0) + + CAP16_TO_CAP32(SPEED_100M); + CAP16_TO_CAP32(SPEED_1G); + CAP16_TO_CAP32(SPEED_25G); + CAP16_TO_CAP32(SPEED_10G); + CAP16_TO_CAP32(SPEED_40G); + CAP16_TO_CAP32(SPEED_100G); + CAP16_TO_CAP32(FC_RX); + CAP16_TO_CAP32(FC_TX); + CAP16_TO_CAP32(ANEG); + CAP16_TO_CAP32(MDIX); + CAP16_TO_CAP32(MDIAUTO); + CAP16_TO_CAP32(FEC_RS); + CAP16_TO_CAP32(FEC_BASER_RS); + CAP16_TO_CAP32(802_3_PAUSE); + CAP16_TO_CAP32(802_3_ASM_DIR); + + #undef CAP16_TO_CAP32 + + return caps32; +} + +/* Translate Firmware Pause specification to Common Code */ +static inline enum cc_pause fwcap_to_cc_pause(fw_port_cap32_t fw_pause) +{ + enum cc_pause cc_pause = 0; + + if (fw_pause & FW_PORT_CAP32_FC_RX) + cc_pause |= PAUSE_RX; + if (fw_pause & FW_PORT_CAP32_FC_TX) + cc_pause |= PAUSE_TX; + + return cc_pause; +} + +/* Translate Firmware Forward Error Correction specification to Common Code */ +static inline enum cc_fec fwcap_to_cc_fec(fw_port_cap32_t fw_fec) +{ + enum cc_fec cc_fec = 0; + + if (fw_fec & FW_PORT_CAP32_FEC_RS) + cc_fec |= FEC_RS; + if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS) + cc_fec |= FEC_BASER_RS; + + return cc_fec; +} + +/** + * Return the highest speed set in the port capabilities, in Mb/s. + */ +static unsigned int fwcap_to_speed(fw_port_cap32_t caps) +{ + #define TEST_SPEED_RETURN(__caps_speed, __speed) \ + do { \ + if (caps & FW_PORT_CAP32_SPEED_##__caps_speed) \ + return __speed; \ + } while (0) + + TEST_SPEED_RETURN(400G, 400000); + TEST_SPEED_RETURN(200G, 200000); + TEST_SPEED_RETURN(100G, 100000); + TEST_SPEED_RETURN(50G, 50000); + TEST_SPEED_RETURN(40G, 40000); + TEST_SPEED_RETURN(25G, 25000); + TEST_SPEED_RETURN(10G, 10000); + TEST_SPEED_RETURN(1G, 1000); + TEST_SPEED_RETURN(100M, 100); + + #undef TEST_SPEED_RETURN + + return 0; +} + +/* * init_link_config - initialize a link's SW state * @lc: structure holding the link state - * @caps: link capabilities + * @pcaps: link Port Capabilities + * @acaps: link current Advertised Port Capabilities * * Initializes the SW state maintained for each link, including the link's * capabilities and default speed/flow-control/autonegotiation settings. */ -static void init_link_config(struct link_config *lc, unsigned int caps) +static void init_link_config(struct link_config *lc, + fw_port_cap32_t pcaps, + fw_port_cap32_t acaps) { - lc->supported = caps; - lc->lp_advertising = 0; - lc->requested_speed = 0; + lc->pcaps = pcaps; + lc->lpacaps = 0; + lc->speed_caps = 0; lc->speed = 0; lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; - if (lc->supported & FW_PORT_CAP_ANEG) { - lc->advertising = lc->supported & ADVERT_MASK; + + /* For Forward Error Control, we default to whatever the Firmware + * tells us the Link is currently advertising. + */ + lc->auto_fec = fwcap_to_cc_fec(acaps); + lc->requested_fec = FEC_AUTO; + lc->fec = lc->auto_fec; + + if (lc->pcaps & FW_PORT_CAP32_ANEG) { + lc->acaps = acaps & ADVERT_MASK; lc->autoneg = AUTONEG_ENABLE; lc->requested_fc |= PAUSE_AUTONEG; } else { - lc->advertising = 0; + lc->acaps = 0; lc->autoneg = AUTONEG_DISABLE; } } @@ -351,9 +449,30 @@ static void init_link_config(struct link_config *lc, unsigned int caps) int t4vf_port_init(struct adapter *adapter, int pidx) { struct port_info *pi = adap2pinfo(adapter, pidx); + unsigned int fw_caps = adapter->params.fw_caps_support; struct fw_vi_cmd vi_cmd, vi_rpl; struct fw_port_cmd port_cmd, port_rpl; - int v; + enum fw_port_type port_type; + int mdio_addr; + fw_port_cap32_t pcaps, acaps; + int ret; + + /* If we haven't yet determined whether we're talking to Firmware + * which knows the new 32-bit Port Capabilities, it's time to find + * out now. This will also tell new Firmware to send us Port Status + * Updates using the new 32-bit Port Capabilities version of the + * Port Information message. + */ + if (fw_caps == FW_CAPS_UNKNOWN) { + u32 param, val; + + param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_PFVF) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_PFVF_PORT_CAPS32)); + val = 1; + ret = t4vf_set_params(adapter, 1, ¶m, &val); + fw_caps = (ret == 0 ? FW_CAPS32 : FW_CAPS16); + adapter->params.fw_caps_support = fw_caps; + } /* * Execute a VI Read command to get our Virtual Interface information @@ -365,9 +484,9 @@ int t4vf_port_init(struct adapter *adapter, int pidx) FW_CMD_READ_F); vi_cmd.alloc_to_len16 = cpu_to_be32(FW_LEN16(vi_cmd)); vi_cmd.type_viid = cpu_to_be16(FW_VI_CMD_VIID_V(pi->viid)); - v = t4vf_wr_mbox(adapter, &vi_cmd, sizeof(vi_cmd), &vi_rpl); - if (v) - return v; + ret = t4vf_wr_mbox(adapter, &vi_cmd, sizeof(vi_cmd), &vi_rpl); + if (ret != FW_SUCCESS) + return ret; BUG_ON(pi->port_id != FW_VI_CMD_PORTID_G(vi_rpl.portid_pkd)); pi->rss_size = FW_VI_CMD_RSSSIZE_G(be16_to_cpu(vi_rpl.rsssize_pkd)); @@ -385,21 +504,42 @@ int t4vf_port_init(struct adapter *adapter, int pidx) FW_CMD_REQUEST_F | FW_CMD_READ_F | FW_PORT_CMD_PORTID_V(pi->port_id)); - port_cmd.action_to_len16 = - cpu_to_be32(FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_GET_PORT_INFO) | - FW_LEN16(port_cmd)); - v = t4vf_wr_mbox(adapter, &port_cmd, sizeof(port_cmd), &port_rpl); - if (v) - return v; + port_cmd.action_to_len16 = cpu_to_be32( + FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32) | + FW_LEN16(port_cmd)); + ret = t4vf_wr_mbox(adapter, &port_cmd, sizeof(port_cmd), &port_rpl); + if (ret != FW_SUCCESS) + return ret; - v = be32_to_cpu(port_rpl.u.info.lstatus_to_modtype); - pi->mdio_addr = (v & FW_PORT_CMD_MDIOCAP_F) ? - FW_PORT_CMD_MDIOADDR_G(v) : -1; - pi->port_type = FW_PORT_CMD_PTYPE_G(v); - pi->mod_type = FW_PORT_MOD_TYPE_NA; + /* Extract the various fields from the Port Information message. */ + if (fw_caps == FW_CAPS16) { + u32 lstatus = be32_to_cpu(port_rpl.u.info.lstatus_to_modtype); - init_link_config(&pi->link_cfg, be16_to_cpu(port_rpl.u.info.pcap)); + port_type = FW_PORT_CMD_PTYPE_G(lstatus); + mdio_addr = ((lstatus & FW_PORT_CMD_MDIOCAP_F) + ? FW_PORT_CMD_MDIOADDR_G(lstatus) + : -1); + pcaps = fwcaps16_to_caps32(be16_to_cpu(port_rpl.u.info.pcap)); + acaps = fwcaps16_to_caps32(be16_to_cpu(port_rpl.u.info.acap)); + } else { + u32 lstatus32 = + be32_to_cpu(port_rpl.u.info32.lstatus32_to_cbllen32); + + port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); + mdio_addr = ((lstatus32 & FW_PORT_CMD_MDIOCAP32_F) + ? FW_PORT_CMD_MDIOADDR32_G(lstatus32) + : -1); + pcaps = be32_to_cpu(port_rpl.u.info32.pcaps32); + acaps = be32_to_cpu(port_rpl.u.info32.acaps32); + } + pi->port_type = port_type; + pi->mdio_addr = mdio_addr; + pi->mod_type = FW_PORT_MOD_TYPE_NA; + + init_link_config(&pi->link_cfg, pcaps, acaps); return 0; } @@ -1666,6 +1806,202 @@ int t4vf_eth_eq_free(struct adapter *adapter, unsigned int eqid) return t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), NULL); } +/** + * t4vf_link_down_rc_str - return a string for a Link Down Reason Code + * @link_down_rc: Link Down Reason Code + * + * Returns a string representation of the Link Down Reason Code. + */ +const char *t4vf_link_down_rc_str(unsigned char link_down_rc) +{ + static const char * const reason[] = { + "Link Down", + "Remote Fault", + "Auto-negotiation Failure", + "Reserved", + "Insufficient Airflow", + "Unable To Determine Reason", + "No RX Signal Detected", + "Reserved", + }; + + if (link_down_rc >= ARRAY_SIZE(reason)) + return "Bad Reason Code"; + + return reason[link_down_rc]; +} + +/** + * t4vf_handle_get_port_info - process a FW reply message + * @pi: the port info + * @rpl: start of the FW message + * + * Processes a GET_PORT_INFO FW reply message. + */ +void t4vf_handle_get_port_info(struct port_info *pi, + const struct fw_port_cmd *cmd) +{ + int action = FW_PORT_CMD_ACTION_G(be32_to_cpu(cmd->action_to_len16)); + struct adapter *adapter = pi->adapter; + struct link_config *lc = &pi->link_cfg; + int link_ok, linkdnrc; + enum fw_port_type port_type; + enum fw_port_module_type mod_type; + unsigned int speed, fc, fec; + fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; + + /* Extract the various fields from the Port Information message. */ + switch (action) { + case FW_PORT_ACTION_GET_PORT_INFO: { + u32 lstatus = be32_to_cpu(cmd->u.info.lstatus_to_modtype); + + link_ok = (lstatus & FW_PORT_CMD_LSTATUS_F) != 0; + linkdnrc = FW_PORT_CMD_LINKDNRC_G(lstatus); + port_type = FW_PORT_CMD_PTYPE_G(lstatus); + mod_type = FW_PORT_CMD_MODTYPE_G(lstatus); + pcaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.pcap)); + acaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.acap)); + lpacaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.lpacap)); + + /* Unfortunately the format of the Link Status in the old + * 16-bit Port Information message isn't the same as the + * 16-bit Port Capabilities bitfield used everywhere else ... + */ + linkattr = 0; + if (lstatus & FW_PORT_CMD_RXPAUSE_F) + linkattr |= FW_PORT_CAP32_FC_RX; + if (lstatus & FW_PORT_CMD_TXPAUSE_F) + linkattr |= FW_PORT_CAP32_FC_TX; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) + linkattr |= FW_PORT_CAP32_SPEED_100M; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) + linkattr |= FW_PORT_CAP32_SPEED_1G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) + linkattr |= FW_PORT_CAP32_SPEED_10G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) + linkattr |= FW_PORT_CAP32_SPEED_25G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) + linkattr |= FW_PORT_CAP32_SPEED_40G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) + linkattr |= FW_PORT_CAP32_SPEED_100G; + + break; + } + + case FW_PORT_ACTION_GET_PORT_INFO32: { + u32 lstatus32; + + lstatus32 = be32_to_cpu(cmd->u.info32.lstatus32_to_cbllen32); + link_ok = (lstatus32 & FW_PORT_CMD_LSTATUS32_F) != 0; + linkdnrc = FW_PORT_CMD_LINKDNRC32_G(lstatus32); + port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); + mod_type = FW_PORT_CMD_MODTYPE32_G(lstatus32); + pcaps = be32_to_cpu(cmd->u.info32.pcaps32); + acaps = be32_to_cpu(cmd->u.info32.acaps32); + lpacaps = be32_to_cpu(cmd->u.info32.lpacaps32); + linkattr = be32_to_cpu(cmd->u.info32.linkattr32); + break; + } + + default: + dev_err(adapter->pdev_dev, "Handle Port Information: Bad Command/Action %#x\n", + be32_to_cpu(cmd->action_to_len16)); + return; + } + + fec = fwcap_to_cc_fec(acaps); + fc = fwcap_to_cc_pause(linkattr); + speed = fwcap_to_speed(linkattr); + + if (mod_type != pi->mod_type) { + /* When a new Transceiver Module is inserted, the Firmware + * will examine any Forward Error Correction parameters + * present in the Transceiver Module i2c EPROM and determine + * the supported and recommended FEC settings from those + * based on IEEE 802.3 standards. We always record the + * IEEE 802.3 recommended "automatic" settings. + */ + lc->auto_fec = fec; + + /* Some versions of the early T6 Firmware "cheated" when + * handling different Transceiver Modules by changing the + * underlaying Port Type reported to the Host Drivers. As + * such we need to capture whatever Port Type the Firmware + * sends us and record it in case it's different from what we + * were told earlier. Unfortunately, since Firmware is + * forever, we'll need to keep this code here forever, but in + * later T6 Firmware it should just be an assignment of the + * same value already recorded. + */ + pi->port_type = port_type; + + pi->mod_type = mod_type; + t4vf_os_portmod_changed(adapter, pi->pidx); + } + + if (link_ok != lc->link_ok || speed != lc->speed || + fc != lc->fc || fec != lc->fec) { /* something changed */ + if (!link_ok && lc->link_ok) { + lc->link_down_rc = linkdnrc; + dev_warn(adapter->pdev_dev, "Port %d link down, reason: %s\n", + pi->port_id, t4vf_link_down_rc_str(linkdnrc)); + } + lc->link_ok = link_ok; + lc->speed = speed; + lc->fc = fc; + lc->fec = fec; + + lc->pcaps = pcaps; + lc->lpacaps = lpacaps; + lc->acaps = acaps & ADVERT_MASK; + + if (lc->acaps & FW_PORT_CAP32_ANEG) { + lc->autoneg = AUTONEG_ENABLE; + } else { + /* When Autoneg is disabled, user needs to set + * single speed. + * Similar to cxgb4_ethtool.c: set_link_ksettings + */ + lc->acaps = 0; + lc->speed_caps = fwcap_to_speed(acaps); + lc->autoneg = AUTONEG_DISABLE; + } + + t4vf_os_link_changed(adapter, pi->pidx, link_ok); + } +} + +/** + * t4vf_update_port_info - retrieve and update port information if changed + * @pi: the port_info + * + * We issue a Get Port Information Command to the Firmware and, if + * successful, we check to see if anything is different from what we + * last recorded and update things accordingly. + */ +int t4vf_update_port_info(struct port_info *pi) +{ + unsigned int fw_caps = pi->adapter->params.fw_caps_support; + struct fw_port_cmd port_cmd; + int ret; + + memset(&port_cmd, 0, sizeof(port_cmd)); + port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F | + FW_PORT_CMD_PORTID_V(pi->port_id)); + port_cmd.action_to_len16 = cpu_to_be32( + FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32) | + FW_LEN16(port_cmd)); + ret = t4vf_wr_mbox(pi->adapter, &port_cmd, sizeof(port_cmd), + &port_cmd); + if (ret) + return ret; + t4vf_handle_get_port_info(pi, &port_cmd); + return 0; +} + /** * t4vf_handle_fw_rpl - process a firmware reply message * @adapter: the adapter @@ -1685,15 +2021,12 @@ int t4vf_handle_fw_rpl(struct adapter *adapter, const __be64 *rpl) */ const struct fw_port_cmd *port_cmd = (const struct fw_port_cmd *)rpl; - u32 stat, mod; - int action, port_id, link_ok, speed, fc, pidx; - - /* - * Extract various fields from port status change message. - */ - action = FW_PORT_CMD_ACTION_G( + int action = FW_PORT_CMD_ACTION_G( be32_to_cpu(port_cmd->action_to_len16)); - if (action != FW_PORT_ACTION_GET_PORT_INFO) { + int port_id, pidx; + + if (action != FW_PORT_ACTION_GET_PORT_INFO && + action != FW_PORT_ACTION_GET_PORT_INFO32) { dev_err(adapter->pdev_dev, "Unknown firmware PORT reply action %x\n", action); @@ -1702,61 +2035,12 @@ int t4vf_handle_fw_rpl(struct adapter *adapter, const __be64 *rpl) port_id = FW_PORT_CMD_PORTID_G( be32_to_cpu(port_cmd->op_to_portid)); - - stat = be32_to_cpu(port_cmd->u.info.lstatus_to_modtype); - link_ok = (stat & FW_PORT_CMD_LSTATUS_F) != 0; - speed = 0; - fc = 0; - if (stat & FW_PORT_CMD_RXPAUSE_F) - fc |= PAUSE_RX; - if (stat & FW_PORT_CMD_TXPAUSE_F) - fc |= PAUSE_TX; - if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) - speed = 100; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) - speed = 1000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) - speed = 10000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) - speed = 25000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) - speed = 40000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) - speed = 100000; - - /* - * Scan all of our "ports" (Virtual Interfaces) looking for - * those bound to the physical port which has changed. If - * our recorded state doesn't match the current state, - * signal that change to the OS code. - */ for_each_port(adapter, pidx) { struct port_info *pi = adap2pinfo(adapter, pidx); - struct link_config *lc; if (pi->port_id != port_id) continue; - - lc = &pi->link_cfg; - - mod = FW_PORT_CMD_MODTYPE_G(stat); - if (mod != pi->mod_type) { - pi->mod_type = mod; - t4vf_os_portmod_changed(adapter, pidx); - } - - if (link_ok != lc->link_ok || speed != lc->speed || - fc != lc->fc) { - /* something changed */ - lc->link_ok = link_ok; - lc->speed = speed; - lc->fc = fc; - lc->supported = - be16_to_cpu(port_cmd->u.info.pcap); - lc->lp_advertising = - be16_to_cpu(port_cmd->u.info.lpacap); - t4vf_os_link_changed(adapter, pidx, link_ok); - } + t4vf_handle_get_port_info(pi, port_cmd); } break; } -- cgit v1.2.3 From 138b57f0f893873badd86e33c2b1f7bbc0bab831 Mon Sep 17 00:00:00 2001 From: Christophe Jaillet Date: Sun, 20 Aug 2017 06:35:00 +0200 Subject: net: ibm: emac: Fix some error handling path in 'emac_probe()' If 'irq_of_parse_and_map()' or 'of_address_to_resource()' fail, 'err' is known to be 0 at this point. So return -ENODEV instead in the first case and use 'of_iomap()' instead of the equivalent 'of_address_to_resource()/ioremap()' combinaison in the 2nd case. Doing so, the 'rsrc_regs' field of the 'emac_instance struct' becomes redundant and is removed. While at it, turn a 'err != 0' test into an equivalent 'err' to be more consistent. Signed-off-by: Christophe JAILLET Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/emac/core.c | 12 ++++-------- drivers/net/ethernet/ibm/emac/core.h | 1 - 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 95135d20458f..7feff2450ed6 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -3032,7 +3032,7 @@ static int emac_probe(struct platform_device *ofdev) /* Init various config data based on device-tree */ err = emac_init_config(dev); - if (err != 0) + if (err) goto err_free; /* Get interrupts. EMAC irq is mandatory, WOL irq is optional */ @@ -3040,18 +3040,14 @@ static int emac_probe(struct platform_device *ofdev) dev->wol_irq = irq_of_parse_and_map(np, 1); if (!dev->emac_irq) { printk(KERN_ERR "%pOF: Can't map main interrupt\n", np); + err = -ENODEV; goto err_free; } ndev->irq = dev->emac_irq; /* Map EMAC regs */ - if (of_address_to_resource(np, 0, &dev->rsrc_regs)) { - printk(KERN_ERR "%pOF: Can't get registers address\n", np); - goto err_irq_unmap; - } - // TODO : request_mem_region - dev->emacp = ioremap(dev->rsrc_regs.start, - resource_size(&dev->rsrc_regs)); + // TODO : platform_get_resource() and devm_ioremap_resource() + dev->emacp = of_iomap(np, 0); if (dev->emacp == NULL) { printk(KERN_ERR "%pOF: Can't map device registers!\n", np); err = -ENOMEM; diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h index f10e156641d5..369de2cfb15b 100644 --- a/drivers/net/ethernet/ibm/emac/core.h +++ b/drivers/net/ethernet/ibm/emac/core.h @@ -167,7 +167,6 @@ struct emac_error_stats { struct emac_instance { struct net_device *ndev; - struct resource rsrc_regs; struct emac_regs __iomem *emacp; struct platform_device *ofdev; struct device_node **blist; /* bootlist entry */ -- cgit v1.2.3 From 6eb15e2130c939aec2edb08d84110c20ab3a16bf Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Sat, 19 Aug 2017 16:25:52 +0530 Subject: net: dsa: mv88e6xxx: make irq_chip const Make this const as it is only used in a copy operation. Done using Coccinelle. Signed-off-by: Bhumika Goyal Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- drivers/net/dsa/mv88e6xxx/global2.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 918d8f0fe091..c6678aa9b4ef 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -307,7 +307,7 @@ out: mutex_unlock(&chip->reg_lock); } -static struct irq_chip mv88e6xxx_g1_irq_chip = { +static const struct irq_chip mv88e6xxx_g1_irq_chip = { .name = "mv88e6xxx-g1", .irq_mask = mv88e6xxx_g1_irq_mask, .irq_unmask = mv88e6xxx_g1_irq_unmask, diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 16f556261022..af0727877825 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -1019,7 +1019,7 @@ static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d) mutex_unlock(&chip->reg_lock); } -static struct irq_chip mv88e6xxx_g2_irq_chip = { +static const struct irq_chip mv88e6xxx_g2_irq_chip = { .name = "mv88e6xxx-g2", .irq_mask = mv88e6xxx_g2_irq_mask, .irq_unmask = mv88e6xxx_g2_irq_unmask, -- cgit v1.2.3 From 0c45d7fe12c7e1510bae9dfac189c8b927e4636b Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Fri, 18 Aug 2017 18:21:49 -0700 Subject: liquidio: fix use of pf in pass-through mode in a virtual machine Fix problem when PF is used in pass-through mode in a VM (w/embedded f/w). If host error reading PF num from CN23XX_PCIE_SRIOV_FDL reg, try to retrieve PF num from SLI_PKT(0)_INPUT_CONTROL (initialized by f/w). Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_pf_device.c | 47 +++++++++++++++++++--- drivers/net/ethernet/cavium/liquidio/lio_main.c | 2 + 2 files changed, 44 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index 4b0ca9fb2cb4..fbc0d4e008f3 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -1150,14 +1150,50 @@ static void cn23xx_get_pcie_qlmport(struct octeon_device *oct) oct->pcie_port); } -static void cn23xx_get_pf_num(struct octeon_device *oct) +static int cn23xx_get_pf_num(struct octeon_device *oct) { u32 fdl_bit = 0; + u64 pkt0_in_ctl, d64; + int pfnum, mac, trs, ret; + + ret = 0; /** Read Function Dependency Link reg to get the function number */ - pci_read_config_dword(oct->pci_dev, CN23XX_PCIE_SRIOV_FDL, &fdl_bit); - oct->pf_num = ((fdl_bit >> CN23XX_PCIE_SRIOV_FDL_BIT_POS) & - CN23XX_PCIE_SRIOV_FDL_MASK); + if (pci_read_config_dword(oct->pci_dev, CN23XX_PCIE_SRIOV_FDL, + &fdl_bit) == 0) { + oct->pf_num = ((fdl_bit >> CN23XX_PCIE_SRIOV_FDL_BIT_POS) & + CN23XX_PCIE_SRIOV_FDL_MASK); + } else { + ret = EINVAL; + + /* Under some virtual environments, extended PCI regs are + * inaccessible, in which case the above read will have failed. + * In this case, read the PF number from the + * SLI_PKT0_INPUT_CONTROL reg (written by f/w) + */ + pkt0_in_ctl = octeon_read_csr64(oct, + CN23XX_SLI_IQ_PKT_CONTROL64(0)); + pfnum = (pkt0_in_ctl >> CN23XX_PKT_INPUT_CTL_PF_NUM_POS) & + CN23XX_PKT_INPUT_CTL_PF_NUM_MASK; + mac = (octeon_read_csr(oct, CN23XX_SLI_MAC_NUMBER)) & 0xff; + + /* validate PF num by reading RINFO; f/w writes RINFO.trs == 1*/ + d64 = octeon_read_csr64(oct, + CN23XX_SLI_PKT_MAC_RINFO64(mac, pfnum)); + trs = (int)(d64 >> CN23XX_PKT_MAC_CTL_RINFO_TRS_BIT_POS) & 0xff; + if (trs == 1) { + dev_err(&oct->pci_dev->dev, + "OCTEON: error reading PCI cfg space pfnum, re-read %u\n", + pfnum); + oct->pf_num = pfnum; + ret = 0; + } else { + dev_err(&oct->pci_dev->dev, + "OCTEON: error reading PCI cfg space pfnum; could not ascertain PF number\n"); + } + } + + return ret; } static void cn23xx_setup_reg_address(struct octeon_device *oct) @@ -1279,7 +1315,8 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) return 1; } - cn23xx_get_pf_num(oct); + if (cn23xx_get_pf_num(oct) != 0) + return 1; if (cn23xx_sriov_config(oct)) { octeon_unmap_pci_barx(oct, 0); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 89d4bbc81707..c2360fe8cef2 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1560,6 +1560,8 @@ static int octeon_chip_specific_setup(struct octeon_device *oct) case OCTEON_CN23XX_PCIID_PF: oct->chip_id = OCTEON_CN23XX_PF_VID; ret = setup_cn23xx_octeon_pf_device(oct); + if (ret) + break; #ifdef CONFIG_PCI_IOV if (!ret) pci_sriov_set_totalvfs(oct->pci_dev, -- cgit v1.2.3 From da6817ebc353c54c5e74676418bfa77d13e64159 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Mon, 21 Aug 2017 17:13:10 +0530 Subject: qlogic: make device_attribute const Make these const as they are only passed as an argument to the function device_create_file and device_remove_file and the corresponding arguments are of type const. Done using Coccinelle Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c | 4 ++-- drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 827de838389f..f2e8de607119 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -2828,7 +2828,7 @@ netxen_show_bridged_mode(struct device *dev, return sprintf(buf, "%d\n", bridged_mode); } -static struct device_attribute dev_attr_bridged_mode = { +static const struct device_attribute dev_attr_bridged_mode = { .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)}, .show = netxen_show_bridged_mode, .store = netxen_store_bridged_mode, @@ -2860,7 +2860,7 @@ netxen_show_diag_mode(struct device *dev, !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED)); } -static struct device_attribute dev_attr_diag_mode = { +static const struct device_attribute dev_attr_diag_mode = { .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)}, .show = netxen_show_diag_mode, .store = netxen_store_diag_mode, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 82fcb83ea3c8..287d89dd086f 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -1174,19 +1174,19 @@ static ssize_t qlcnic_83xx_sysfs_flash_write_handler(struct file *filp, return size; } -static struct device_attribute dev_attr_bridged_mode = { +static const struct device_attribute dev_attr_bridged_mode = { .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)}, .show = qlcnic_show_bridged_mode, .store = qlcnic_store_bridged_mode, }; -static struct device_attribute dev_attr_diag_mode = { +static const struct device_attribute dev_attr_diag_mode = { .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)}, .show = qlcnic_show_diag_mode, .store = qlcnic_store_diag_mode, }; -static struct device_attribute dev_attr_beacon = { +static const struct device_attribute dev_attr_beacon = { .attr = {.name = "beacon", .mode = (S_IRUGO | S_IWUSR)}, .show = qlcnic_show_beacon, .store = qlcnic_store_beacon, -- cgit v1.2.3 From d3213fbf695490acbffc7b1fff793c7e26989296 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 21 Aug 2017 12:47:30 +0300 Subject: ethernet: xircom: small clean up in setup_xirc2ps_cs() The get_options() function takes the whole ARRAY_SIZE(). It doesn't matter here because we don't use more than 7 elements. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/xircom/xirc2ps_cs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index f71883264cc0..fd5288ff53b5 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -1781,7 +1781,7 @@ static int __init setup_xirc2ps_cs(char *str) */ int ints[10] = { -1 }; - str = get_options(str, 9, ints); + str = get_options(str, ARRAY_SIZE(ints), ints); #define MAYBE_SET(X,Y) if (ints[0] >= Y && ints[Y] != -1) { X = ints[Y]; } MAYBE_SET(if_port, 3); -- cgit v1.2.3 From 89c9c1636f5aeef7b74cdcc141e0abc9bd764afe Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 21 Aug 2017 18:12:55 +0800 Subject: net: ethernet: stmmac: dwmac-rk: Add rv1108 gmac support It only supports rmii interface. Add constants and callback functions for the dwmac on rv1108 socs. As can be seen, the base structure is the same, only registers and the bits in them moved slightly. Signed-off-by: David Wu Signed-off-by: David S. Miller --- .../devicetree/bindings/net/rockchip-dwmac.txt | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 53 ++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'drivers/net') diff --git a/Documentation/devicetree/bindings/net/rockchip-dwmac.txt b/Documentation/devicetree/bindings/net/rockchip-dwmac.txt index 8f427550720a..c1325387632c 100644 --- a/Documentation/devicetree/bindings/net/rockchip-dwmac.txt +++ b/Documentation/devicetree/bindings/net/rockchip-dwmac.txt @@ -10,6 +10,7 @@ Required properties: "rockchip,rk3366-gmac": found on RK3366 SoCs "rockchip,rk3368-gmac": found on RK3368 SoCs "rockchip,rk3399-gmac": found on RK3399 SoCs + "rockchip,rv1108-gmac": found on RV1108 SoCs - reg: addresses and length of the register sets for the device. - interrupts: Should contain the GMAC interrupts. - interrupt-names: Should contain the interrupt names "macirq". diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 2176403c72d8..99823f54696a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -787,6 +787,58 @@ static const struct rk_gmac_ops rk3399_ops = { .set_rmii_speed = rk3399_set_rmii_speed, }; +#define RV1108_GRF_GMAC_CON0 0X0900 + +/* RV1108_GRF_GMAC_CON0 */ +#define RV1108_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | \ + GRF_BIT(6)) +#define RV1108_GMAC_FLOW_CTRL GRF_BIT(3) +#define RV1108_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3) +#define RV1108_GMAC_SPEED_10M GRF_CLR_BIT(2) +#define RV1108_GMAC_SPEED_100M GRF_BIT(2) +#define RV1108_GMAC_RMII_CLK_25M GRF_BIT(7) +#define RV1108_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(7) + +static void rv1108_set_to_rmii(struct rk_priv_data *bsp_priv) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + regmap_write(bsp_priv->grf, RV1108_GRF_GMAC_CON0, + RV1108_GMAC_PHY_INTF_SEL_RMII); +} + +static void rv1108_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + if (speed == 10) { + regmap_write(bsp_priv->grf, RV1108_GRF_GMAC_CON0, + RV1108_GMAC_RMII_CLK_2_5M | + RV1108_GMAC_SPEED_10M); + } else if (speed == 100) { + regmap_write(bsp_priv->grf, RV1108_GRF_GMAC_CON0, + RV1108_GMAC_RMII_CLK_25M | + RV1108_GMAC_SPEED_100M); + } else { + dev_err(dev, "unknown speed value for RMII! speed=%d", speed); + } +} + +static const struct rk_gmac_ops rv1108_ops = { + .set_to_rmii = rv1108_set_to_rmii, + .set_rmii_speed = rv1108_set_rmii_speed, +}; + #define RK_GRF_MACPHY_CON0 0xb00 #define RK_GRF_MACPHY_CON1 0xb04 #define RK_GRF_MACPHY_CON2 0xb08 @@ -1267,6 +1319,7 @@ static const struct of_device_id rk_gmac_dwmac_match[] = { { .compatible = "rockchip,rk3366-gmac", .data = &rk3366_ops }, { .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops }, { .compatible = "rockchip,rk3399-gmac", .data = &rk3399_ops }, + { .compatible = "rockchip,rv1108-gmac", .data = &rv1108_ops }, { } }; MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match); -- cgit v1.2.3 From 51ba902a16e68b786028db8b0482f3a5f22e7d4f Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:47 +0800 Subject: net-next/hinic: Initialize hw interface Initialize hw interface as part of the nic initialization for accessing hw. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- Documentation/networking/hinic.txt | 125 ++++++++++++++ drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/huawei/Kconfig | 19 +++ drivers/net/ethernet/huawei/Makefile | 5 + drivers/net/ethernet/huawei/hinic/Kconfig | 13 ++ drivers/net/ethernet/huawei/hinic/Makefile | 3 + drivers/net/ethernet/huawei/hinic/hinic_dev.h | 33 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 36 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 201 ++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 42 +++++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.c | 208 +++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 160 +++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_main.c | 195 +++++++++++++++++++++ 14 files changed, 1042 insertions(+) create mode 100644 Documentation/networking/hinic.txt create mode 100644 drivers/net/ethernet/huawei/Kconfig create mode 100644 drivers/net/ethernet/huawei/Makefile create mode 100644 drivers/net/ethernet/huawei/hinic/Kconfig create mode 100644 drivers/net/ethernet/huawei/hinic/Makefile create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_dev.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_if.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_if.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_main.c (limited to 'drivers/net') diff --git a/Documentation/networking/hinic.txt b/Documentation/networking/hinic.txt new file mode 100644 index 000000000000..989366a4039c --- /dev/null +++ b/Documentation/networking/hinic.txt @@ -0,0 +1,125 @@ +Linux Kernel Driver for Huawei Intelligent NIC(HiNIC) family +============================================================ + +Overview: +========= +HiNIC is a network interface card for the Data Center Area. + +The driver supports a range of link-speed devices (10GbE, 25GbE, 40GbE, etc.). +The driver supports also a negotiated and extendable feature set. + +Some HiNIC devices support SR-IOV. This driver is used for Physical Function +(PF). + +HiNIC devices support MSI-X interrupt vector for each Tx/Rx queue and +adaptive interrupt moderation. + +HiNIC devices support also various offload features such as checksum offload, +TCP Transmit Segmentation Offload(TSO), Receive-Side Scaling(RSS) and +LRO(Large Receive Offload). + + +Supported PCI vendor ID/device IDs: +=================================== + +19e5:1822 - HiNIC PF + + +Driver Architecture and Source Code: +==================================== + +hinic_dev - Implement a Logical Network device that is independent from +specific HW details about HW data structure formats. + +hinic_hwdev - Implement the HW details of the device and include the components +for accessing the PCI NIC. + +hinic_hwdev contains the following components: +=============================================== + +HW Interface: +============= + +The interface for accessing the pci device (DMA memory and PCI BARs). +(hinic_hw_if.c, hinic_hw_if.h) + +Configuration Status Registers Area that describes the HW Registers on the +configuration and status BAR0. (hinic_hw_csr.h) + +MGMT components: +================ + +Asynchronous Event Queues(AEQs) - The event queues for receiving messages from +the MGMT modules on the cards. (hinic_hw_eqs.c, hinic_hw_eqs.h) + +Application Programmable Interface commands(API CMD) - Interface for sending +MGMT commands to the card. (hinic_hw_api_cmd.c, hinic_hw_api_cmd.h) + +Management (MGMT) - the PF to MGMT channel that uses API CMD for sending MGMT +commands to the card and receives notifications from the MGMT modules on the +card by AEQs. Also set the addresses of the IO CMDQs in HW. +(hinic_hw_mgmt.c, hinic_hw_mgmt.h) + +IO components: +============== + +Completion Event Queues(CEQs) - The completion Event Queues that describe IO +tasks that are finished. (hinic_hw_eqs.c, hinic_hw_eqs.h) + +Work Queues(WQ) - Contain the memory and operations for use by CMD queues and +the Queue Pairs. The WQ is a Memory Block in a Page. The Block contains +pointers to Memory Areas that are the Memory for the Work Queue Elements(WQEs). +(hinic_hw_wq.c, hinic_hw_wq.h) + +Command Queues(CMDQ) - The queues for sending commands for IO management and is +used to set the QPs addresses in HW. The commands completion events are +accumulated on the CEQ that is configured to receive the CMDQ completion events. +(hinic_hw_cmdq.c, hinic_hw_cmdq.h) + +Queue Pairs(QPs) - The HW Receive and Send queues for Receiving and Transmitting +Data. (hinic_hw_qp.c, hinic_hw_qp.h, hinic_hw_qp_ctxt.h) + +IO - de/constructs all the IO components. (hinic_hw_io.c, hinic_hw_io.h) + +HW device: +========== + +HW device - de/constructs the HW Interface, the MGMT components on the +initialization of the driver and the IO components on the case of Interface +UP/DOWN Events. (hinic_hw_dev.c, hinic_hw_dev.h) + + +hinic_dev contains the following components: +=============================================== + +PCI ID table - Contains the supported PCI Vendor/Device IDs. +(hinic_pci_tbl.h) + +Port Commands - Send commands to the HW device for port management +(MAC, Vlan, MTU, ...). (hinic_port.c, hinic_port.h) + +Tx Queues - Logical Tx Queues that use the HW Send Queues for transmit. +The Logical Tx queue is not dependent on the format of the HW Send Queue. +(hinic_tx.c, hinic_tx.h) + +Rx Queues - Logical Rx Queues that use the HW Receive Queues for receive. +The Logical Rx queue is not dependent on the format of the HW Receive Queue. +(hinic_rx.c, hinic_rx.h) + +hinic_dev - de/constructs the Logical Tx and Rx Queues. +(hinic_main.c, hinic_dev.h) + + +Miscellaneous: +============= + +Common functions that are used by HW and Logical Device. +(hinic_common.c, hinic_common.h) + + +Support +======= + +If an issue is identified with the released source code on the supported kernel +with a supported adapter, email the specific information related to the issue to +aviad.krawczyk@huawei.com. diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index edae15ac0e98..c60421339a98 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -78,6 +78,7 @@ source "drivers/net/ethernet/freescale/Kconfig" source "drivers/net/ethernet/fujitsu/Kconfig" source "drivers/net/ethernet/hisilicon/Kconfig" source "drivers/net/ethernet/hp/Kconfig" +source "drivers/net/ethernet/huawei/Kconfig" source "drivers/net/ethernet/ibm/Kconfig" source "drivers/net/ethernet/intel/Kconfig" source "drivers/net/ethernet/i825xx/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index bf7f4502cabc..a0a03d4d939a 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/ obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/ obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/ obj-$(CONFIG_NET_VENDOR_HP) += hp/ +obj-$(CONFIG_NET_VENDOR_HUAWEI) += huawei/ obj-$(CONFIG_NET_VENDOR_IBM) += ibm/ obj-$(CONFIG_NET_VENDOR_INTEL) += intel/ obj-$(CONFIG_NET_VENDOR_I825XX) += i825xx/ diff --git a/drivers/net/ethernet/huawei/Kconfig b/drivers/net/ethernet/huawei/Kconfig new file mode 100644 index 000000000000..c1a95ae4058b --- /dev/null +++ b/drivers/net/ethernet/huawei/Kconfig @@ -0,0 +1,19 @@ +# +# Huawei driver configuration +# + +config NET_VENDOR_HUAWEI + bool "Huawei devices" + default y + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y. + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Huawei cards. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_HUAWEI + +source "drivers/net/ethernet/huawei/hinic/Kconfig" + +endif # NET_VENDOR_HUAWEI diff --git a/drivers/net/ethernet/huawei/Makefile b/drivers/net/ethernet/huawei/Makefile new file mode 100644 index 000000000000..5c37cc8fc1bc --- /dev/null +++ b/drivers/net/ethernet/huawei/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Huawei device drivers. +# + +obj-$(CONFIG_HINIC) += hinic/ diff --git a/drivers/net/ethernet/huawei/hinic/Kconfig b/drivers/net/ethernet/huawei/hinic/Kconfig new file mode 100644 index 000000000000..69f2b1fba48d --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/Kconfig @@ -0,0 +1,13 @@ +# +# Huawei driver configuration +# + +config HINIC + tristate "Huawei Intelligent PCIE Network Interface Card" + depends on (PCI_MSI && X86) + default m + ---help--- + This driver supports HiNIC PCIE Ethernet cards. + To compile this driver as part of the kernel, choose Y here. + If unsure, choose N. + The default is compiled as module. diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile new file mode 100644 index 000000000000..353cee03cf2d --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_HINIC) += hinic.o + +hinic-y := hinic_main.o hinic_hw_dev.o hinic_hw_if.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h new file mode 100644 index 000000000000..6c2c896015a5 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -0,0 +1,33 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_DEV_H +#define HINIC_DEV_H + +#include +#include + +#include "hinic_hw_dev.h" + +#define HINIC_DRV_NAME "hinic" + +struct hinic_dev { + struct net_device *netdev; + struct hinic_hwdev *hwdev; + + u32 msg_enable; +}; + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h new file mode 100644 index 000000000000..c3440a9f5a1e --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -0,0 +1,36 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_CSR_H +#define HINIC_HW_CSR_H + +/* HW interface registers */ +#define HINIC_CSR_FUNC_ATTR0_ADDR 0x0 +#define HINIC_CSR_FUNC_ATTR1_ADDR 0x4 + +#define HINIC_DMA_ATTR_BASE 0xC80 +#define HINIC_ELECTION_BASE 0x4200 + +#define HINIC_DMA_ATTR_STRIDE 0x4 +#define HINIC_CSR_DMA_ATTR_ADDR(idx) \ + (HINIC_DMA_ATTR_BASE + (idx) * HINIC_DMA_ATTR_STRIDE) + +#define HINIC_PPF_ELECTION_STRIDE 0x4 +#define HINIC_CSR_MAX_PORTS 4 + +#define HINIC_CSR_PPF_ELECTION_ADDR(idx) \ + (HINIC_ELECTION_BASE + (idx) * HINIC_PPF_ELECTION_STRIDE) + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c new file mode 100644 index 000000000000..f681846e51d5 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -0,0 +1,201 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_dev.h" + +#define MAX_IRQS(max_qps, num_aeqs, num_ceqs) \ + (2 * (max_qps) + (num_aeqs) + (num_ceqs)) + +/** + * init_msix - enable the msix and save the entries + * @hwdev: the NIC HW device + * + * Return 0 - Success, negative - Failure + **/ +static int init_msix(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + int nr_irqs, num_aeqs, num_ceqs; + size_t msix_entries_size; + int i, err; + + num_aeqs = HINIC_HWIF_NUM_AEQS(hwif); + num_ceqs = HINIC_HWIF_NUM_CEQS(hwif); + nr_irqs = MAX_IRQS(HINIC_MAX_QPS, num_aeqs, num_ceqs); + if (nr_irqs > HINIC_HWIF_NUM_IRQS(hwif)) + nr_irqs = HINIC_HWIF_NUM_IRQS(hwif); + + msix_entries_size = nr_irqs * sizeof(*hwdev->msix_entries); + hwdev->msix_entries = devm_kzalloc(&pdev->dev, msix_entries_size, + GFP_KERNEL); + if (!hwdev->msix_entries) + return -ENOMEM; + + for (i = 0; i < nr_irqs; i++) + hwdev->msix_entries[i].entry = i; + + err = pci_enable_msix_exact(pdev, hwdev->msix_entries, nr_irqs); + if (err) { + dev_err(&pdev->dev, "Failed to enable pci msix\n"); + return err; + } + + return 0; +} + +/** + * disable_msix - disable the msix + * @hwdev: the NIC HW device + **/ +static void disable_msix(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + + pci_disable_msix(pdev); +} + +/** + * init_pfhwdev - Initialize the extended components of PF + * @pfhwdev: the HW device for PF + * + * Return 0 - success, negative - failure + **/ +static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev) +{ + /* Initialize PF HW device extended components */ + return 0; +} + +/** + * free_pfhwdev - Free the extended components of PF + * @pfhwdev: the HW device for PF + **/ +static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev) +{ +} + +/** + * hinic_init_hwdev - Initialize the NIC HW + * @pdev: the NIC pci device + * + * Return initialized NIC HW device + * + * Initialize the NIC HW device and return a pointer to it + **/ +struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) +{ + struct hinic_pfhwdev *pfhwdev; + struct hinic_hwdev *hwdev; + struct hinic_hwif *hwif; + int err; + + hwif = devm_kzalloc(&pdev->dev, sizeof(*hwif), GFP_KERNEL); + if (!hwif) + return ERR_PTR(-ENOMEM); + + err = hinic_init_hwif(hwif, pdev); + if (err) { + dev_err(&pdev->dev, "Failed to init HW interface\n"); + return ERR_PTR(err); + } + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + err = -EFAULT; + goto err_func_type; + } + + pfhwdev = devm_kzalloc(&pdev->dev, sizeof(*pfhwdev), GFP_KERNEL); + if (!pfhwdev) { + err = -ENOMEM; + goto err_pfhwdev_alloc; + } + + hwdev = &pfhwdev->hwdev; + hwdev->hwif = hwif; + + err = init_msix(hwdev); + if (err) { + dev_err(&pdev->dev, "Failed to init msix\n"); + goto err_init_msix; + } + + err = init_pfhwdev(pfhwdev); + if (err) { + dev_err(&pdev->dev, "Failed to init PF HW device\n"); + goto err_init_pfhwdev; + } + + return hwdev; + +err_init_pfhwdev: + disable_msix(hwdev); + +err_init_msix: +err_pfhwdev_alloc: +err_func_type: + hinic_free_hwif(hwif); + return ERR_PTR(err); +} + +/** + * hinic_free_hwdev - Free the NIC HW device + * @hwdev: the NIC HW device + **/ +void hinic_free_hwdev(struct hinic_hwdev *hwdev) +{ + struct hinic_pfhwdev *pfhwdev = container_of(hwdev, + struct hinic_pfhwdev, + hwdev); + + free_pfhwdev(pfhwdev); + + disable_msix(hwdev); + + hinic_free_hwif(hwdev->hwif); +} + +/** + * hinic_hwdev_num_qps - return the number QPs available for use + * @hwdev: the NIC HW device + * + * Return number QPs available for use + **/ +int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev) +{ + int num_aeqs, num_ceqs, nr_irqs, num_qps; + + num_aeqs = HINIC_HWIF_NUM_AEQS(hwdev->hwif); + num_ceqs = HINIC_HWIF_NUM_CEQS(hwdev->hwif); + nr_irqs = HINIC_HWIF_NUM_IRQS(hwdev->hwif); + + /* Each QP has its own (SQ + RQ) interrupt */ + num_qps = (nr_irqs - (num_aeqs + num_ceqs)) / 2; + + /* num_qps must be power of 2 */ + return BIT(fls(num_qps) - 1); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h new file mode 100644 index 000000000000..b42e0ebdd97b --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -0,0 +1,42 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_DEV_H +#define HINIC_HW_DEV_H + +#include + +#include "hinic_hw_if.h" + +#define HINIC_MAX_QPS 32 + +struct hinic_hwdev { + struct hinic_hwif *hwif; + struct msix_entry *msix_entries; +}; + +struct hinic_pfhwdev { + struct hinic_hwdev hwdev; + + /* PF Extended components should be here */ +}; + +struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev); + +void hinic_free_hwdev(struct hinic_hwdev *hwdev); + +int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c new file mode 100644 index 000000000000..edf184242172 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c @@ -0,0 +1,208 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "hinic_hw_csr.h" +#include "hinic_hw_if.h" + +#define PCIE_ATTR_ENTRY 0 + +/** + * hwif_ready - test if the HW is ready for use + * @hwif: the HW interface of a pci function device + * + * Return 0 - Success, negative - Failure + **/ +static int hwif_ready(struct hinic_hwif *hwif) +{ + struct pci_dev *pdev = hwif->pdev; + u32 addr, attr1; + + addr = HINIC_CSR_FUNC_ATTR1_ADDR; + attr1 = hinic_hwif_read_reg(hwif, addr); + + if (!HINIC_FA1_GET(attr1, INIT_STATUS)) { + dev_err(&pdev->dev, "hwif status is not ready\n"); + return -EFAULT; + } + + return 0; +} + +/** + * set_hwif_attr - set the attributes in the relevant members in hwif + * @hwif: the HW interface of a pci function device + * @attr0: the first attribute that was read from the hw + * @attr1: the second attribute that was read from the hw + **/ +static void set_hwif_attr(struct hinic_hwif *hwif, u32 attr0, u32 attr1) +{ + hwif->attr.func_idx = HINIC_FA0_GET(attr0, FUNC_IDX); + hwif->attr.pf_idx = HINIC_FA0_GET(attr0, PF_IDX); + hwif->attr.pci_intf_idx = HINIC_FA0_GET(attr0, PCI_INTF_IDX); + hwif->attr.func_type = HINIC_FA0_GET(attr0, FUNC_TYPE); + + hwif->attr.num_aeqs = BIT(HINIC_FA1_GET(attr1, AEQS_PER_FUNC)); + hwif->attr.num_ceqs = BIT(HINIC_FA1_GET(attr1, CEQS_PER_FUNC)); + hwif->attr.num_irqs = BIT(HINIC_FA1_GET(attr1, IRQS_PER_FUNC)); + hwif->attr.num_dma_attr = BIT(HINIC_FA1_GET(attr1, DMA_ATTR_PER_FUNC)); +} + +/** + * read_hwif_attr - read the attributes and set members in hwif + * @hwif: the HW interface of a pci function device + **/ +static void read_hwif_attr(struct hinic_hwif *hwif) +{ + u32 addr, attr0, attr1; + + addr = HINIC_CSR_FUNC_ATTR0_ADDR; + attr0 = hinic_hwif_read_reg(hwif, addr); + + addr = HINIC_CSR_FUNC_ATTR1_ADDR; + attr1 = hinic_hwif_read_reg(hwif, addr); + + set_hwif_attr(hwif, attr0, attr1); +} + +/** + * set_ppf - try to set hwif as ppf and set the type of hwif in this case + * @hwif: the HW interface of a pci function device + **/ +static void set_ppf(struct hinic_hwif *hwif) +{ + struct hinic_func_attr *attr = &hwif->attr; + u32 addr, val, ppf_election; + + /* Read Modify Write */ + addr = HINIC_CSR_PPF_ELECTION_ADDR(HINIC_HWIF_PCI_INTF(hwif)); + + val = hinic_hwif_read_reg(hwif, addr); + val = HINIC_PPF_ELECTION_CLEAR(val, IDX); + + ppf_election = HINIC_PPF_ELECTION_SET(HINIC_HWIF_FUNC_IDX(hwif), IDX); + + val |= ppf_election; + hinic_hwif_write_reg(hwif, addr, val); + + /* check PPF */ + val = hinic_hwif_read_reg(hwif, addr); + + attr->ppf_idx = HINIC_PPF_ELECTION_GET(val, IDX); + if (attr->ppf_idx == HINIC_HWIF_FUNC_IDX(hwif)) + attr->func_type = HINIC_PPF; +} + +/** + * set_dma_attr - set the dma attributes in the HW + * @hwif: the HW interface of a pci function device + * @entry_idx: the entry index in the dma table + * @st: PCIE TLP steering tag + * @at: PCIE TLP AT field + * @ph: PCIE TLP Processing Hint field + * @no_snooping: PCIE TLP No snooping + * @tph_en: PCIE TLP Processing Hint Enable + **/ +static void set_dma_attr(struct hinic_hwif *hwif, u32 entry_idx, + u8 st, u8 at, u8 ph, + enum hinic_pcie_nosnoop no_snooping, + enum hinic_pcie_tph tph_en) +{ + u32 addr, val, dma_attr_entry; + + /* Read Modify Write */ + addr = HINIC_CSR_DMA_ATTR_ADDR(entry_idx); + + val = hinic_hwif_read_reg(hwif, addr); + val = HINIC_DMA_ATTR_CLEAR(val, ST) & + HINIC_DMA_ATTR_CLEAR(val, AT) & + HINIC_DMA_ATTR_CLEAR(val, PH) & + HINIC_DMA_ATTR_CLEAR(val, NO_SNOOPING) & + HINIC_DMA_ATTR_CLEAR(val, TPH_EN); + + dma_attr_entry = HINIC_DMA_ATTR_SET(st, ST) | + HINIC_DMA_ATTR_SET(at, AT) | + HINIC_DMA_ATTR_SET(ph, PH) | + HINIC_DMA_ATTR_SET(no_snooping, NO_SNOOPING) | + HINIC_DMA_ATTR_SET(tph_en, TPH_EN); + + val |= dma_attr_entry; + hinic_hwif_write_reg(hwif, addr, val); +} + +/** + * dma_attr_table_init - initialize the the default dma attributes + * @hwif: the HW interface of a pci function device + **/ +static void dma_attr_init(struct hinic_hwif *hwif) +{ + set_dma_attr(hwif, PCIE_ATTR_ENTRY, HINIC_PCIE_ST_DISABLE, + HINIC_PCIE_AT_DISABLE, HINIC_PCIE_PH_DISABLE, + HINIC_PCIE_SNOOP, HINIC_PCIE_TPH_DISABLE); +} + +/** + * hinic_init_hwif - initialize the hw interface + * @hwif: the HW interface of a pci function device + * @pdev: the pci device for acessing PCI resources + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev) +{ + int err; + + hwif->pdev = pdev; + + hwif->cfg_regs_bar = pci_ioremap_bar(pdev, HINIC_PCI_CFG_REGS_BAR); + if (!hwif->cfg_regs_bar) { + dev_err(&pdev->dev, "Failed to map configuration regs\n"); + return -ENOMEM; + } + + err = hwif_ready(hwif); + if (err) { + dev_err(&pdev->dev, "HW interface is not ready\n"); + goto err_hwif_ready; + } + + read_hwif_attr(hwif); + + if (HINIC_IS_PF(hwif)) + set_ppf(hwif); + + /* No transactionss before DMA is initialized */ + dma_attr_init(hwif); + return 0; + +err_hwif_ready: + iounmap(hwif->cfg_regs_bar); + return err; +} + +/** + * hinic_free_hwif - free the HW interface + * @hwif: the HW interface of a pci function device + **/ +void hinic_free_hwif(struct hinic_hwif *hwif) +{ + iounmap(hwif->cfg_regs_bar); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h new file mode 100644 index 000000000000..d1a8fa2bc3ee --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -0,0 +1,160 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_IF_H +#define HINIC_HW_IF_H + +#include +#include +#include +#include + +#define HINIC_DMA_ATTR_ST_SHIFT 0 +#define HINIC_DMA_ATTR_AT_SHIFT 8 +#define HINIC_DMA_ATTR_PH_SHIFT 10 +#define HINIC_DMA_ATTR_NO_SNOOPING_SHIFT 12 +#define HINIC_DMA_ATTR_TPH_EN_SHIFT 13 + +#define HINIC_DMA_ATTR_ST_MASK 0xFF +#define HINIC_DMA_ATTR_AT_MASK 0x3 +#define HINIC_DMA_ATTR_PH_MASK 0x3 +#define HINIC_DMA_ATTR_NO_SNOOPING_MASK 0x1 +#define HINIC_DMA_ATTR_TPH_EN_MASK 0x1 + +#define HINIC_DMA_ATTR_SET(val, member) \ + (((u32)(val) & HINIC_DMA_ATTR_##member##_MASK) << \ + HINIC_DMA_ATTR_##member##_SHIFT) + +#define HINIC_DMA_ATTR_CLEAR(val, member) \ + ((val) & (~(HINIC_DMA_ATTR_##member##_MASK \ + << HINIC_DMA_ATTR_##member##_SHIFT))) + +#define HINIC_FA0_FUNC_IDX_SHIFT 0 +#define HINIC_FA0_PF_IDX_SHIFT 10 +#define HINIC_FA0_PCI_INTF_IDX_SHIFT 14 +/* reserved members - off 16 */ +#define HINIC_FA0_FUNC_TYPE_SHIFT 24 + +#define HINIC_FA0_FUNC_IDX_MASK 0x3FF +#define HINIC_FA0_PF_IDX_MASK 0xF +#define HINIC_FA0_PCI_INTF_IDX_MASK 0x3 +#define HINIC_FA0_FUNC_TYPE_MASK 0x1 + +#define HINIC_FA0_GET(val, member) \ + (((val) >> HINIC_FA0_##member##_SHIFT) & HINIC_FA0_##member##_MASK) + +#define HINIC_FA1_AEQS_PER_FUNC_SHIFT 8 +/* reserved members - off 10 */ +#define HINIC_FA1_CEQS_PER_FUNC_SHIFT 12 +/* reserved members - off 15 */ +#define HINIC_FA1_IRQS_PER_FUNC_SHIFT 20 +#define HINIC_FA1_DMA_ATTR_PER_FUNC_SHIFT 24 +/* reserved members - off 27 */ +#define HINIC_FA1_INIT_STATUS_SHIFT 30 + +#define HINIC_FA1_AEQS_PER_FUNC_MASK 0x3 +#define HINIC_FA1_CEQS_PER_FUNC_MASK 0x7 +#define HINIC_FA1_IRQS_PER_FUNC_MASK 0xF +#define HINIC_FA1_DMA_ATTR_PER_FUNC_MASK 0x7 +#define HINIC_FA1_INIT_STATUS_MASK 0x1 + +#define HINIC_FA1_GET(val, member) \ + (((val) >> HINIC_FA1_##member##_SHIFT) & HINIC_FA1_##member##_MASK) + +#define HINIC_PPF_ELECTION_IDX_SHIFT 0 +#define HINIC_PPF_ELECTION_IDX_MASK 0x1F + +#define HINIC_PPF_ELECTION_SET(val, member) \ + (((u32)(val) & HINIC_PPF_ELECTION_##member##_MASK) << \ + HINIC_PPF_ELECTION_##member##_SHIFT) + +#define HINIC_PPF_ELECTION_GET(val, member) \ + (((val) >> HINIC_PPF_ELECTION_##member##_SHIFT) & \ + HINIC_PPF_ELECTION_##member##_MASK) + +#define HINIC_PPF_ELECTION_CLEAR(val, member) \ + ((val) & (~(HINIC_PPF_ELECTION_##member##_MASK \ + << HINIC_PPF_ELECTION_##member##_SHIFT))) + +#define HINIC_HWIF_NUM_AEQS(hwif) ((hwif)->attr.num_aeqs) +#define HINIC_HWIF_NUM_CEQS(hwif) ((hwif)->attr.num_ceqs) +#define HINIC_HWIF_NUM_IRQS(hwif) ((hwif)->attr.num_irqs) +#define HINIC_HWIF_FUNC_IDX(hwif) ((hwif)->attr.func_idx) +#define HINIC_HWIF_PCI_INTF(hwif) ((hwif)->attr.pci_intf_idx) + +#define HINIC_FUNC_TYPE(hwif) ((hwif)->attr.func_type) +#define HINIC_IS_PF(hwif) (HINIC_FUNC_TYPE(hwif) == HINIC_PF) +#define HINIC_IS_PPF(hwif) (HINIC_FUNC_TYPE(hwif) == HINIC_PPF) + +#define HINIC_PCI_CFG_REGS_BAR 0 + +#define HINIC_PCIE_ST_DISABLE 0 +#define HINIC_PCIE_AT_DISABLE 0 +#define HINIC_PCIE_PH_DISABLE 0 + +enum hinic_pcie_nosnoop { + HINIC_PCIE_SNOOP = 0, + HINIC_PCIE_NO_SNOOP = 1, +}; + +enum hinic_pcie_tph { + HINIC_PCIE_TPH_DISABLE = 0, + HINIC_PCIE_TPH_ENABLE = 1, +}; + +enum hinic_func_type { + HINIC_PF = 0, + HINIC_PPF = 2, +}; + +struct hinic_func_attr { + u16 func_idx; + u8 pf_idx; + u8 pci_intf_idx; + + enum hinic_func_type func_type; + + u8 ppf_idx; + + u16 num_irqs; + u8 num_aeqs; + u8 num_ceqs; + + u8 num_dma_attr; +}; + +struct hinic_hwif { + struct pci_dev *pdev; + void __iomem *cfg_regs_bar; + + struct hinic_func_attr attr; +}; + +static inline u32 hinic_hwif_read_reg(struct hinic_hwif *hwif, u32 reg) +{ + return be32_to_cpu(readl(hwif->cfg_regs_bar + reg)); +} + +static inline void hinic_hwif_write_reg(struct hinic_hwif *hwif, u32 reg, + u32 val) +{ + writel(cpu_to_be32(val), hwif->cfg_regs_bar + reg); +} + +int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev); + +void hinic_free_hwif(struct hinic_hwif *hwif); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c new file mode 100644 index 000000000000..1d7aed07b25f --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -0,0 +1,195 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hinic_hw_dev.h" +#include "hinic_dev.h" + +MODULE_AUTHOR("Huawei Technologies CO., Ltd"); +MODULE_DESCRIPTION("Huawei Intelligent NIC driver"); +MODULE_LICENSE("GPL"); + +#define PCI_DEVICE_ID_HI1822_PF 0x1822 + +#define MSG_ENABLE_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR) + +static const struct net_device_ops hinic_netdev_ops = { + /* Operations are empty, should be filled */ +}; + +/** + * nic_dev_init - Initialize the NIC device + * @pdev: the NIC pci device + * + * Return 0 - Success, negative - Failure + **/ +static int nic_dev_init(struct pci_dev *pdev) +{ + struct hinic_dev *nic_dev; + struct net_device *netdev; + struct hinic_hwdev *hwdev; + int err, num_qps; + + hwdev = hinic_init_hwdev(pdev); + if (IS_ERR(hwdev)) { + dev_err(&pdev->dev, "Failed to initialize HW device\n"); + return PTR_ERR(hwdev); + } + + num_qps = hinic_hwdev_num_qps(hwdev); + if (num_qps <= 0) { + dev_err(&pdev->dev, "Invalid number of QPS\n"); + err = -EINVAL; + goto err_num_qps; + } + + netdev = alloc_etherdev_mq(sizeof(*nic_dev), num_qps); + if (!netdev) { + dev_err(&pdev->dev, "Failed to allocate Ethernet device\n"); + err = -ENOMEM; + goto err_alloc_etherdev; + } + + netdev->netdev_ops = &hinic_netdev_ops; + + nic_dev = netdev_priv(netdev); + nic_dev->netdev = netdev; + nic_dev->hwdev = hwdev; + nic_dev->msg_enable = MSG_ENABLE_DEFAULT; + + pci_set_drvdata(pdev, netdev); + + netif_carrier_off(netdev); + + err = register_netdev(netdev); + if (err) { + dev_err(&pdev->dev, "Failed to register netdev\n"); + goto err_reg_netdev; + } + + return 0; + +err_reg_netdev: + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); + +err_alloc_etherdev: +err_num_qps: + hinic_free_hwdev(hwdev); + return err; +} + +static int hinic_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int err = pci_enable_device(pdev); + + if (err) { + dev_err(&pdev->dev, "Failed to enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, HINIC_DRV_NAME); + if (err) { + dev_err(&pdev->dev, "Failed to request PCI regions\n"); + goto err_pci_regions; + } + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (err) { + dev_warn(&pdev->dev, "Couldn't set 64-bit DMA mask\n"); + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, "Failed to set DMA mask\n"); + goto err_dma_mask; + } + } + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); + if (err) { + dev_warn(&pdev->dev, + "Couldn't set 64-bit consistent DMA mask\n"); + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, + "Failed to set consistent DMA mask\n"); + goto err_dma_consistent_mask; + } + } + + err = nic_dev_init(pdev); + if (err) { + dev_err(&pdev->dev, "Failed to initialize NIC device\n"); + goto err_nic_dev_init; + } + + dev_info(&pdev->dev, "HiNIC driver - probed\n"); + return 0; + +err_nic_dev_init: +err_dma_consistent_mask: +err_dma_mask: + pci_release_regions(pdev); + +err_pci_regions: + pci_disable_device(pdev); + return err; +} + +static void hinic_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct hinic_dev *nic_dev = netdev_priv(netdev); + + unregister_netdev(netdev); + + pci_set_drvdata(pdev, NULL); + + hinic_free_hwdev(nic_dev->hwdev); + + free_netdev(netdev); + + pci_release_regions(pdev); + pci_disable_device(pdev); + + dev_info(&pdev->dev, "HiNIC driver - removed\n"); +} + +static const struct pci_device_id hinic_pci_table[] = { + { PCI_VDEVICE(HUAWEI, PCI_DEVICE_ID_HI1822_PF), 0}, + { 0, 0} +}; +MODULE_DEVICE_TABLE(pci, hinic_pci_table); + +static struct pci_driver hinic_driver = { + .name = HINIC_DRV_NAME, + .id_table = hinic_pci_table, + .probe = hinic_probe, + .remove = hinic_remove, +}; + +module_pci_driver(hinic_driver); -- cgit v1.2.3 From a5564e7e44268bcac402697eff85286722f753a9 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:48 +0800 Subject: net-next/hinic: Initialize hw device components Initialize hw device by calling the initialization functions of aeqs and management channel. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/Makefile | 3 +- drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 172 ++++++++++++++++++++-- drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 14 +- drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c | 149 +++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h | 107 ++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 8 + drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c | 92 ++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 45 ++++++ 8 files changed, 576 insertions(+), 14 deletions(-) create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index 353cee03cf2d..717ad71213fb 100644 --- a/drivers/net/ethernet/huawei/hinic/Makefile +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_HINIC) += hinic.o -hinic-y := hinic_main.o hinic_hw_dev.o hinic_hw_if.o +hinic-y := hinic_main.o hinic_hw_dev.o hinic_hw_mgmt.o hinic_hw_eqs.o \ + hinic_hw_if.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index f681846e51d5..d430e60192af 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -23,11 +23,132 @@ #include #include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" +#include "hinic_hw_mgmt.h" #include "hinic_hw_dev.h" #define MAX_IRQS(max_qps, num_aeqs, num_ceqs) \ (2 * (max_qps) + (num_aeqs) + (num_ceqs)) +enum intr_type { + INTR_MSIX_TYPE, +}; + +/* HW struct */ +struct hinic_dev_cap { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 rsvd1[5]; + u8 intr_type; + u8 rsvd2[66]; + u16 max_sqs; + u16 max_rqs; + u8 rsvd3[208]; +}; + +/** + * get_capability - convert device capabilities to NIC capabilities + * @hwdev: the HW device to set and convert device capabilities for + * @dev_cap: device capabilities from FW + * + * Return 0 - Success, negative - Failure + **/ +static int get_capability(struct hinic_hwdev *hwdev, + struct hinic_dev_cap *dev_cap) +{ + struct hinic_cap *nic_cap = &hwdev->nic_cap; + int num_aeqs, num_ceqs, num_irqs; + + if (!HINIC_IS_PF(hwdev->hwif) && !HINIC_IS_PPF(hwdev->hwif)) + return -EINVAL; + + if (dev_cap->intr_type != INTR_MSIX_TYPE) + return -EFAULT; + + num_aeqs = HINIC_HWIF_NUM_AEQS(hwdev->hwif); + num_ceqs = HINIC_HWIF_NUM_CEQS(hwdev->hwif); + num_irqs = HINIC_HWIF_NUM_IRQS(hwdev->hwif); + + /* Each QP has its own (SQ + RQ) interrupts */ + nic_cap->num_qps = (num_irqs - (num_aeqs + num_ceqs)) / 2; + + /* num_qps must be power of 2 */ + nic_cap->num_qps = BIT(fls(nic_cap->num_qps) - 1); + + nic_cap->max_qps = dev_cap->max_sqs + 1; + if (nic_cap->max_qps != (dev_cap->max_rqs + 1)) + return -EFAULT; + + if (nic_cap->num_qps > nic_cap->max_qps) + nic_cap->num_qps = nic_cap->max_qps; + + return 0; +} + +/** + * get_cap_from_fw - get device capabilities from FW + * @pfhwdev: the PF HW device to get capabilities for + * + * Return 0 - Success, negative - Failure + **/ +static int get_cap_from_fw(struct hinic_pfhwdev *pfhwdev) +{ + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_dev_cap dev_cap; + u16 in_len, out_len; + int err; + + in_len = 0; + out_len = sizeof(dev_cap); + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_CFGM, + HINIC_CFG_NIC_CAP, &dev_cap, in_len, &dev_cap, + &out_len, HINIC_MGMT_MSG_SYNC); + if (err) { + dev_err(&pdev->dev, "Failed to get capability from FW\n"); + return err; + } + + return get_capability(hwdev, &dev_cap); +} + +/** + * get_dev_cap - get device capabilities + * @hwdev: the NIC HW device to get capabilities for + * + * Return 0 - Success, negative - Failure + **/ +static int get_dev_cap(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + int err; + + switch (HINIC_FUNC_TYPE(hwif)) { + case HINIC_PPF: + case HINIC_PF: + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + err = get_cap_from_fw(pfhwdev); + if (err) { + dev_err(&pdev->dev, "Failed to get capability from FW\n"); + return err; + } + break; + + default: + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + return 0; +} + /** * init_msix - enable the msix and save the entries * @hwdev: the NIC HW device @@ -86,7 +207,17 @@ static void disable_msix(struct hinic_hwdev *hwdev) **/ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev) { - /* Initialize PF HW device extended components */ + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + err = hinic_pf_to_mgmt_init(&pfhwdev->pf_to_mgmt, hwif); + if (err) { + dev_err(&pdev->dev, "Failed to initialize PF to MGMT channel\n"); + return err; + } + return 0; } @@ -96,6 +227,7 @@ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev) **/ static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev) { + hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); } /** @@ -111,7 +243,7 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) struct hinic_pfhwdev *pfhwdev; struct hinic_hwdev *hwdev; struct hinic_hwif *hwif; - int err; + int err, num_aeqs; hwif = devm_kzalloc(&pdev->dev, sizeof(*hwif), GFP_KERNEL); if (!hwif) @@ -144,15 +276,37 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) goto err_init_msix; } + num_aeqs = HINIC_HWIF_NUM_AEQS(hwif); + + err = hinic_aeqs_init(&hwdev->aeqs, hwif, num_aeqs, + HINIC_DEFAULT_AEQ_LEN, HINIC_EQ_PAGE_SIZE, + hwdev->msix_entries); + if (err) { + dev_err(&pdev->dev, "Failed to init async event queues\n"); + goto err_aeqs_init; + } + err = init_pfhwdev(pfhwdev); if (err) { dev_err(&pdev->dev, "Failed to init PF HW device\n"); goto err_init_pfhwdev; } + err = get_dev_cap(hwdev); + if (err) { + dev_err(&pdev->dev, "Failed to get device capabilities\n"); + goto err_dev_cap; + } + return hwdev; +err_dev_cap: + free_pfhwdev(pfhwdev); + err_init_pfhwdev: + hinic_aeqs_free(&hwdev->aeqs); + +err_aeqs_init: disable_msix(hwdev); err_init_msix: @@ -174,6 +328,8 @@ void hinic_free_hwdev(struct hinic_hwdev *hwdev) free_pfhwdev(pfhwdev); + hinic_aeqs_free(&hwdev->aeqs); + disable_msix(hwdev); hinic_free_hwif(hwdev->hwif); @@ -187,15 +343,7 @@ void hinic_free_hwdev(struct hinic_hwdev *hwdev) **/ int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev) { - int num_aeqs, num_ceqs, nr_irqs, num_qps; + struct hinic_cap *nic_cap = &hwdev->nic_cap; - num_aeqs = HINIC_HWIF_NUM_AEQS(hwdev->hwif); - num_ceqs = HINIC_HWIF_NUM_CEQS(hwdev->hwif); - nr_irqs = HINIC_HWIF_NUM_IRQS(hwdev->hwif); - - /* Each QP has its own (SQ + RQ) interrupt */ - num_qps = (nr_irqs - (num_aeqs + num_ceqs)) / 2; - - /* num_qps must be power of 2 */ - return BIT(fls(num_qps) - 1); + return nic_cap->num_qps; } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index b42e0ebdd97b..feb601388e59 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -17,20 +17,32 @@ #define HINIC_HW_DEV_H #include +#include #include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" +#include "hinic_hw_mgmt.h" #define HINIC_MAX_QPS 32 +struct hinic_cap { + u16 max_qps; + u16 num_qps; +}; + struct hinic_hwdev { struct hinic_hwif *hwif; struct msix_entry *msix_entries; + + struct hinic_aeqs aeqs; + + struct hinic_cap nic_cap; }; struct hinic_pfhwdev { struct hinic_hwdev hwdev; - /* PF Extended components should be here */ + struct hinic_pf_to_mgmt pf_to_mgmt; }; struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c new file mode 100644 index 000000000000..a099d20bac8c --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c @@ -0,0 +1,149 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" + +#define HINIC_EQS_WQ_NAME "hinic_eqs" + +/** + * hinic_aeq_register_hw_cb - register AEQ callback for specific event + * @aeqs: pointer to Async eqs of the chip + * @event: aeq event to register callback for it + * @handle: private data will be used by the callback + * @hw_handler: callback function + **/ +void hinic_aeq_register_hw_cb(struct hinic_aeqs *aeqs, + enum hinic_aeq_type event, void *handle, + void (*hwe_handler)(void *handle, void *data, + u8 size)) +{ + struct hinic_hw_event_cb *hwe_cb = &aeqs->hwe_cb[event]; + + hwe_cb->hwe_handler = hwe_handler; + hwe_cb->handle = handle; + hwe_cb->hwe_state = HINIC_EQE_ENABLED; +} + +/** + * hinic_aeq_unregister_hw_cb - unregister the AEQ callback for specific event + * @aeqs: pointer to Async eqs of the chip + * @event: aeq event to unregister callback for it + **/ +void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs, + enum hinic_aeq_type event) +{ + struct hinic_hw_event_cb *hwe_cb = &aeqs->hwe_cb[event]; + + hwe_cb->hwe_state &= ~HINIC_EQE_ENABLED; + + while (hwe_cb->hwe_state & HINIC_EQE_RUNNING) + schedule(); + + hwe_cb->hwe_handler = NULL; +} + +/** + * init_eq - initialize Event Queue + * @eq: the event queue + * @hwif: the HW interface of a PCI function device + * @type: the type of the event queue, aeq or ceq + * @q_id: Queue id number + * @q_len: the number of EQ elements + * @page_size: the page size of the pages in the event queue + * @entry: msix entry associated with the event queue + * + * Return 0 - Success, Negative - Failure + **/ +static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif, + enum hinic_eq_type type, int q_id, u32 q_len, u32 page_size, + struct msix_entry entry) +{ + /* should be implemented */ + return 0; +} + +/** + * remove_eq - remove Event Queue + * @eq: the event queue + **/ +static void remove_eq(struct hinic_eq *eq) +{ + /* should be implemented */ +} + +/** + * hinic_aeqs_init - initialize all the aeqs + * @aeqs: pointer to Async eqs of the chip + * @hwif: the HW interface of a PCI function device + * @num_aeqs: number of AEQs + * @q_len: number of EQ elements + * @page_size: the page size of the pages in the event queue + * @msix_entries: msix entries associated with the event queues + * + * Return 0 - Success, negative - Failure + **/ +int hinic_aeqs_init(struct hinic_aeqs *aeqs, struct hinic_hwif *hwif, + int num_aeqs, u32 q_len, u32 page_size, + struct msix_entry *msix_entries) +{ + struct pci_dev *pdev = hwif->pdev; + int err, i, q_id; + + aeqs->workq = create_singlethread_workqueue(HINIC_EQS_WQ_NAME); + if (!aeqs->workq) + return -ENOMEM; + + aeqs->hwif = hwif; + aeqs->num_aeqs = num_aeqs; + + for (q_id = 0; q_id < num_aeqs; q_id++) { + err = init_eq(&aeqs->aeq[q_id], hwif, HINIC_AEQ, q_id, q_len, + page_size, msix_entries[q_id]); + if (err) { + dev_err(&pdev->dev, "Failed to init aeq %d\n", q_id); + goto err_init_aeq; + } + } + + return 0; + +err_init_aeq: + for (i = 0; i < q_id; i++) + remove_eq(&aeqs->aeq[i]); + + destroy_workqueue(aeqs->workq); + return err; +} + +/** + * hinic_aeqs_free - free all the aeqs + * @aeqs: pointer to Async eqs of the chip + **/ +void hinic_aeqs_free(struct hinic_aeqs *aeqs) +{ + int q_id; + + for (q_id = 0; q_id < aeqs->num_aeqs ; q_id++) + remove_eq(&aeqs->aeq[q_id]); + + destroy_workqueue(aeqs->workq); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h new file mode 100644 index 000000000000..1580127b26c4 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h @@ -0,0 +1,107 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_EQS_H +#define HINIC_HW_EQS_H + +#include +#include +#include +#include +#include + +#include "hinic_hw_if.h" + +#define HINIC_MAX_AEQS 4 + +#define HINIC_DEFAULT_AEQ_LEN 64 + +#define HINIC_EQ_PAGE_SIZE SZ_4K + +enum hinic_eq_type { + HINIC_AEQ, +}; + +enum hinic_aeq_type { + HINIC_MSG_FROM_MGMT_CPU = 2, + + HINIC_MAX_AEQ_EVENTS, +}; + +enum hinic_eqe_state { + HINIC_EQE_ENABLED = BIT(0), + HINIC_EQE_RUNNING = BIT(1), +}; + +struct hinic_eq_work { + struct work_struct work; + void *data; +}; + +struct hinic_eq { + struct hinic_hwif *hwif; + + enum hinic_eq_type type; + int q_id; + u32 q_len; + u32 page_size; + + u32 cons_idx; + int wrapped; + + size_t elem_size; + int num_pages; + int num_elem_in_pg; + + struct msix_entry msix_entry; + + dma_addr_t *dma_addr; + void **virt_addr; + + struct hinic_eq_work aeq_work; +}; + +struct hinic_hw_event_cb { + void (*hwe_handler)(void *handle, void *data, u8 size); + void *handle; + unsigned long hwe_state; +}; + +struct hinic_aeqs { + struct hinic_hwif *hwif; + + struct hinic_eq aeq[HINIC_MAX_AEQS]; + int num_aeqs; + + struct hinic_hw_event_cb hwe_cb[HINIC_MAX_AEQ_EVENTS]; + + struct workqueue_struct *workq; +}; + +void hinic_aeq_register_hw_cb(struct hinic_aeqs *aeqs, + enum hinic_aeq_type event, void *handle, + void (*hwe_handler)(void *handle, void *data, + u8 size)); + +void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs, + enum hinic_aeq_type event); + +int hinic_aeqs_init(struct hinic_aeqs *aeqs, struct hinic_hwif *hwif, + int num_aeqs, u32 q_len, u32 page_size, + struct msix_entry *msix_entries); + +void hinic_aeqs_free(struct hinic_aeqs *aeqs); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index d1a8fa2bc3ee..b6d985042907 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -119,6 +119,14 @@ enum hinic_func_type { HINIC_PPF = 2, }; +enum hinic_mod_type { + HINIC_MOD_COMM = 0, /* HW communication module */ + HINIC_MOD_L2NIC = 1, /* L2NIC module */ + HINIC_MOD_CFGM = 7, /* Configuration module */ + + HINIC_MOD_MAX = 15 +}; + struct hinic_func_attr { u16 func_idx; u8 pf_idx; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c new file mode 100644 index 000000000000..8ae8ed90dbec --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c @@ -0,0 +1,92 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" +#include "hinic_hw_mgmt.h" +#include "hinic_hw_dev.h" + +#define mgmt_to_pfhwdev(pf_mgmt) \ + container_of(pf_mgmt, struct hinic_pfhwdev, pf_to_mgmt) + +/** + * hinic_msg_to_mgmt - send message to mgmt + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that will get the message + * @cmd: command of the message + * @buf_in: the msg data + * @in_size: the msg data length + * @buf_out: response + * @out_size: returned response length + * @sync: sync msg or async msg + * + * Return 0 - Success, negative - Failure + **/ +int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + void *buf_in, u16 in_size, void *buf_out, u16 *out_size, + enum hinic_mgmt_msg_type sync) +{ + /* should be implemented */ + return -EINVAL; +} + +/** + * mgmt_msg_aeqe_handler - handler for a mgmt message event + * @handle: PF to MGMT channel + * @data: the header of the message + * @size: unused + **/ +static void mgmt_msg_aeqe_handler(void *handle, void *data, u8 size) +{ + /* should be implemented */ +} + +/** + * hinic_pf_to_mgmt_init - initialize PF to MGMT channel + * @pf_to_mgmt: PF to MGMT channel + * @hwif: HW interface the PF to MGMT will use for accessing HW + * + * Return 0 - Success, negative - Failure + **/ +int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_hwif *hwif) +{ + struct hinic_pfhwdev *pfhwdev = mgmt_to_pfhwdev(pf_to_mgmt); + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + + pf_to_mgmt->hwif = hwif; + + hinic_aeq_register_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU, + pf_to_mgmt, + mgmt_msg_aeqe_handler); + return 0; +} + +/** + * hinic_pf_to_mgmt_free - free PF to MGMT channel + * @pf_to_mgmt: PF to MGMT channel + **/ +void hinic_pf_to_mgmt_free(struct hinic_pf_to_mgmt *pf_to_mgmt) +{ + struct hinic_pfhwdev *pfhwdev = mgmt_to_pfhwdev(pf_to_mgmt); + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + + hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h new file mode 100644 index 000000000000..b4b34b703447 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -0,0 +1,45 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_MGMT_H +#define HINIC_HW_MGMT_H + +#include + +#include "hinic_hw_if.h" + +enum hinic_mgmt_msg_type { + HINIC_MGMT_MSG_SYNC = 1, +}; + +enum hinic_cfg_cmd { + HINIC_CFG_NIC_CAP = 0, +}; + +struct hinic_pf_to_mgmt { + struct hinic_hwif *hwif; +}; + +int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + void *buf_in, u16 in_size, void *buf_out, u16 *out_size, + enum hinic_mgmt_msg_type sync); + +int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_hwif *hwif); + +void hinic_pf_to_mgmt_free(struct hinic_pf_to_mgmt *pf_to_mgmt); + +#endif -- cgit v1.2.3 From eabf0fad81d52b8e23f3a6eb5d2c8cac452f50ee Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:49 +0800 Subject: net-next/hinic: Initialize api cmd resources Initialize api cmd resources as part of management initialization. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/Makefile | 4 +- .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.c | 446 +++++++++++++++++++++ .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.h | 102 +++++ drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c | 10 + drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 3 + 5 files changed, 563 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index 717ad71213fb..beba90a6dc96 100644 --- a/drivers/net/ethernet/huawei/hinic/Makefile +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_HINIC) += hinic.o -hinic-y := hinic_main.o hinic_hw_dev.o hinic_hw_mgmt.o hinic_hw_eqs.o \ - hinic_hw_if.o +hinic-y := hinic_main.o hinic_hw_dev.o hinic_hw_mgmt.o hinic_hw_api_cmd.o \ + hinic_hw_eqs.o hinic_hw_if.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c new file mode 100644 index 000000000000..4291f8eb45cc --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c @@ -0,0 +1,446 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_api_cmd.h" + +#define API_CHAIN_NUM_CELLS 32 + +#define API_CMD_CELL_SIZE_SHIFT 6 +#define API_CMD_CELL_SIZE_MIN (BIT(API_CMD_CELL_SIZE_SHIFT)) + +#define API_CMD_CELL_SIZE(cell_size) \ + (((cell_size) >= API_CMD_CELL_SIZE_MIN) ? \ + (1 << (fls(cell_size - 1))) : API_CMD_CELL_SIZE_MIN) + +#define API_CMD_BUF_SIZE 2048 + +/** + * api_cmd_chain_hw_init - initialize the chain in the HW + * @chain: the API CMD specific chain to initialize in HW + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd_chain_hw_init(struct hinic_api_cmd_chain *chain) +{ + /* should be implemented */ + return 0; +} + +/** + * free_cmd_buf - free the dma buffer of API CMD command + * @chain: the API CMD specific chain of the cmd + * @cell_idx: the cell index of the cmd + **/ +static void free_cmd_buf(struct hinic_api_cmd_chain *chain, int cell_idx) +{ + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + + cell_ctxt = &chain->cell_ctxt[cell_idx]; + + dma_free_coherent(&pdev->dev, API_CMD_BUF_SIZE, + cell_ctxt->api_cmd_vaddr, + cell_ctxt->api_cmd_paddr); +} + +/** + * alloc_cmd_buf - allocate a dma buffer for API CMD command + * @chain: the API CMD specific chain for the cmd + * @cell: the cell in the HW for the cmd + * @cell_idx: the index of the cell + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_cmd_buf(struct hinic_api_cmd_chain *chain, + struct hinic_api_cmd_cell *cell, int cell_idx) +{ + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + dma_addr_t cmd_paddr; + u8 *cmd_vaddr; + int err = 0; + + cmd_vaddr = dma_zalloc_coherent(&pdev->dev, API_CMD_BUF_SIZE, + &cmd_paddr, GFP_KERNEL); + if (!cmd_vaddr) { + dev_err(&pdev->dev, "Failed to allocate API CMD DMA memory\n"); + return -ENOMEM; + } + + cell_ctxt = &chain->cell_ctxt[cell_idx]; + + cell_ctxt->api_cmd_vaddr = cmd_vaddr; + cell_ctxt->api_cmd_paddr = cmd_paddr; + + /* set the cmd DMA address in the cell */ + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + /* The data in the HW should be in Big Endian Format */ + cell->write.hw_cmd_paddr = cpu_to_be64(cmd_paddr); + break; + + default: + dev_err(&pdev->dev, "Unsupported API CMD chain type\n"); + free_cmd_buf(chain, cell_idx); + err = -EINVAL; + break; + } + + return err; +} + +/** + * api_cmd_create_cell - create API CMD cell for specific chain + * @chain: the API CMD specific chain to create its cell + * @cell_idx: the index of the cell to create + * @pre_node: previous cell + * @node_vaddr: the returned virt addr of the cell + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd_create_cell(struct hinic_api_cmd_chain *chain, + int cell_idx, + struct hinic_api_cmd_cell *pre_node, + struct hinic_api_cmd_cell **node_vaddr) +{ + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_api_cmd_cell *node; + dma_addr_t node_paddr; + int err; + + node = dma_zalloc_coherent(&pdev->dev, chain->cell_size, + &node_paddr, GFP_KERNEL); + if (!node) { + dev_err(&pdev->dev, "Failed to allocate dma API CMD cell\n"); + return -ENOMEM; + } + + node->read.hw_wb_resp_paddr = 0; + + cell_ctxt = &chain->cell_ctxt[cell_idx]; + cell_ctxt->cell_vaddr = node; + cell_ctxt->cell_paddr = node_paddr; + + if (!pre_node) { + chain->head_cell_paddr = node_paddr; + chain->head_node = node; + } else { + /* The data in the HW should be in Big Endian Format */ + pre_node->next_cell_paddr = cpu_to_be64(node_paddr); + } + + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + err = alloc_cmd_buf(chain, node, cell_idx); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmd buffer\n"); + goto err_alloc_cmd_buf; + } + break; + + default: + dev_err(&pdev->dev, "Unsupported API CMD chain type\n"); + err = -EINVAL; + goto err_alloc_cmd_buf; + } + + *node_vaddr = node; + return 0; + +err_alloc_cmd_buf: + dma_free_coherent(&pdev->dev, chain->cell_size, node, node_paddr); + return err; +} + +/** + * api_cmd_destroy_cell - destroy API CMD cell of specific chain + * @chain: the API CMD specific chain to destroy its cell + * @cell_idx: the cell to destroy + **/ +static void api_cmd_destroy_cell(struct hinic_api_cmd_chain *chain, + int cell_idx) +{ + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_api_cmd_cell *node; + dma_addr_t node_paddr; + size_t node_size; + + cell_ctxt = &chain->cell_ctxt[cell_idx]; + + node = cell_ctxt->cell_vaddr; + node_paddr = cell_ctxt->cell_paddr; + node_size = chain->cell_size; + + if (cell_ctxt->api_cmd_vaddr) { + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + free_cmd_buf(chain, cell_idx); + break; + default: + dev_err(&pdev->dev, "Unsupported API CMD chain type\n"); + break; + } + + dma_free_coherent(&pdev->dev, node_size, node, + node_paddr); + } +} + +/** + * api_cmd_destroy_cells - destroy API CMD cells of specific chain + * @chain: the API CMD specific chain to destroy its cells + * @num_cells: number of cells to destroy + **/ +static void api_cmd_destroy_cells(struct hinic_api_cmd_chain *chain, + int num_cells) +{ + int cell_idx; + + for (cell_idx = 0; cell_idx < num_cells; cell_idx++) + api_cmd_destroy_cell(chain, cell_idx); +} + +/** + * api_cmd_create_cells - create API CMD cells for specific chain + * @chain: the API CMD specific chain + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd_create_cells(struct hinic_api_cmd_chain *chain) +{ + struct hinic_api_cmd_cell *node = NULL, *pre_node = NULL; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + int err, cell_idx; + + for (cell_idx = 0; cell_idx < chain->num_cells; cell_idx++) { + err = api_cmd_create_cell(chain, cell_idx, pre_node, &node); + if (err) { + dev_err(&pdev->dev, "Failed to create API CMD cell\n"); + goto err_create_cell; + } + + pre_node = node; + } + + /* set the Final node to point on the start */ + node->next_cell_paddr = cpu_to_be64(chain->head_cell_paddr); + + /* set the current node to be the head */ + chain->curr_node = chain->head_node; + return 0; + +err_create_cell: + api_cmd_destroy_cells(chain, cell_idx); + return err; +} + +/** + * api_chain_init - initialize API CMD specific chain + * @chain: the API CMD specific chain to initialize + * @attr: attributes to set in the chain + * + * Return 0 - Success, negative - Failure + **/ +static int api_chain_init(struct hinic_api_cmd_chain *chain, + struct hinic_api_cmd_chain_attr *attr) +{ + struct hinic_hwif *hwif = attr->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t cell_ctxt_size; + + chain->hwif = hwif; + chain->chain_type = attr->chain_type; + chain->num_cells = attr->num_cells; + chain->cell_size = attr->cell_size; + + chain->prod_idx = 0; + chain->cons_idx = 0; + + cell_ctxt_size = chain->num_cells * sizeof(*chain->cell_ctxt); + chain->cell_ctxt = devm_kzalloc(&pdev->dev, cell_ctxt_size, GFP_KERNEL); + if (!chain->cell_ctxt) + return -ENOMEM; + + chain->wb_status = dma_zalloc_coherent(&pdev->dev, + sizeof(*chain->wb_status), + &chain->wb_status_paddr, + GFP_KERNEL); + if (!chain->wb_status) { + dev_err(&pdev->dev, "Failed to allocate DMA wb status\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * api_chain_free - free API CMD specific chain + * @chain: the API CMD specific chain to free + **/ +static void api_chain_free(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + + dma_free_coherent(&pdev->dev, sizeof(*chain->wb_status), + chain->wb_status, chain->wb_status_paddr); +} + +/** + * api_cmd_create_chain - create API CMD specific chain + * @attr: attributes to set the chain + * + * Return the created chain + **/ +static struct hinic_api_cmd_chain * + api_cmd_create_chain(struct hinic_api_cmd_chain_attr *attr) +{ + struct hinic_hwif *hwif = attr->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_api_cmd_chain *chain; + int err; + + if (attr->num_cells & (attr->num_cells - 1)) { + dev_err(&pdev->dev, "Invalid number of cells, must be power of 2\n"); + return ERR_PTR(-EINVAL); + } + + chain = devm_kzalloc(&pdev->dev, sizeof(*chain), GFP_KERNEL); + if (!chain) + return ERR_PTR(-ENOMEM); + + err = api_chain_init(chain, attr); + if (err) { + dev_err(&pdev->dev, "Failed to initialize chain\n"); + return ERR_PTR(err); + } + + err = api_cmd_create_cells(chain); + if (err) { + dev_err(&pdev->dev, "Failed to create cells for API CMD chain\n"); + goto err_create_cells; + } + + err = api_cmd_chain_hw_init(chain); + if (err) { + dev_err(&pdev->dev, "Failed to initialize chain HW\n"); + goto err_chain_hw_init; + } + + return chain; + +err_chain_hw_init: + api_cmd_destroy_cells(chain, chain->num_cells); + +err_create_cells: + api_chain_free(chain); + return ERR_PTR(err); +} + +/** + * api_cmd_destroy_chain - destroy API CMD specific chain + * @chain: the API CMD specific chain to destroy + **/ +static void api_cmd_destroy_chain(struct hinic_api_cmd_chain *chain) +{ + api_cmd_destroy_cells(chain, chain->num_cells); + api_chain_free(chain); +} + +/** + * hinic_api_cmd_init - Initialize all the API CMD chains + * @chain: the API CMD chains that are initialized + * @hwif: the hardware interface of a pci function device + * + * Return 0 - Success, negative - Failure + **/ +int hinic_api_cmd_init(struct hinic_api_cmd_chain **chain, + struct hinic_hwif *hwif) +{ + enum hinic_api_cmd_chain_type type, chain_type; + struct hinic_api_cmd_chain_attr attr; + struct pci_dev *pdev = hwif->pdev; + size_t hw_cell_sz; + int err; + + hw_cell_sz = sizeof(struct hinic_api_cmd_cell); + + attr.hwif = hwif; + attr.num_cells = API_CHAIN_NUM_CELLS; + attr.cell_size = API_CMD_CELL_SIZE(hw_cell_sz); + + chain_type = HINIC_API_CMD_WRITE_TO_MGMT_CPU; + for ( ; chain_type < HINIC_API_CMD_MAX; chain_type++) { + attr.chain_type = chain_type; + + if (chain_type != HINIC_API_CMD_WRITE_TO_MGMT_CPU) + continue; + + chain[chain_type] = api_cmd_create_chain(&attr); + if (IS_ERR(chain[chain_type])) { + dev_err(&pdev->dev, "Failed to create chain %d\n", + chain_type); + goto err_create_chain; + } + } + + return 0; + +err_create_chain: + type = HINIC_API_CMD_WRITE_TO_MGMT_CPU; + for ( ; type < chain_type; type++) { + if (type != HINIC_API_CMD_WRITE_TO_MGMT_CPU) + continue; + + api_cmd_destroy_chain(chain[type]); + } + + return err; +} + +/** + * hinic_api_cmd_free - free the API CMD chains + * @chain: the API CMD chains that are freed + **/ +void hinic_api_cmd_free(struct hinic_api_cmd_chain **chain) +{ + enum hinic_api_cmd_chain_type chain_type; + + chain_type = HINIC_API_CMD_WRITE_TO_MGMT_CPU; + for ( ; chain_type < HINIC_API_CMD_MAX; chain_type++) { + if (chain_type != HINIC_API_CMD_WRITE_TO_MGMT_CPU) + continue; + + api_cmd_destroy_chain(chain[chain_type]); + } +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h new file mode 100644 index 000000000000..9c7d1e5ee00c --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h @@ -0,0 +1,102 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_API_CMD_H +#define HINIC_HW_API_CMD_H + +#include + +#include "hinic_hw_if.h" + +enum hinic_api_cmd_chain_type { + HINIC_API_CMD_WRITE_TO_MGMT_CPU = 2, + + HINIC_API_CMD_MAX, +}; + +struct hinic_api_cmd_chain_attr { + struct hinic_hwif *hwif; + enum hinic_api_cmd_chain_type chain_type; + + u32 num_cells; + u16 cell_size; +}; + +struct hinic_api_cmd_status { + u64 header; + u32 status; + u32 rsvd0; + u32 rsvd1; + u32 rsvd2; + u64 rsvd3; +}; + +/* HW struct */ +struct hinic_api_cmd_cell { + u64 ctrl; + + /* address is 64 bit in HW struct */ + u64 next_cell_paddr; + + u64 desc; + + /* HW struct */ + union { + struct { + u64 hw_cmd_paddr; + } write; + + struct { + u64 hw_wb_resp_paddr; + u64 hw_cmd_paddr; + } read; + }; +}; + +struct hinic_api_cmd_cell_ctxt { + dma_addr_t cell_paddr; + struct hinic_api_cmd_cell *cell_vaddr; + + dma_addr_t api_cmd_paddr; + u8 *api_cmd_vaddr; +}; + +struct hinic_api_cmd_chain { + struct hinic_hwif *hwif; + enum hinic_api_cmd_chain_type chain_type; + + u32 num_cells; + u16 cell_size; + + /* HW members in 24 bit format */ + u32 prod_idx; + u32 cons_idx; + + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + + dma_addr_t wb_status_paddr; + struct hinic_api_cmd_status *wb_status; + + dma_addr_t head_cell_paddr; + struct hinic_api_cmd_cell *head_node; + struct hinic_api_cmd_cell *curr_node; +}; + +int hinic_api_cmd_init(struct hinic_api_cmd_chain **chain, + struct hinic_hwif *hwif); + +void hinic_api_cmd_free(struct hinic_api_cmd_chain **chain); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c index 8ae8ed90dbec..f914bc77dc62 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c @@ -19,6 +19,7 @@ #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" +#include "hinic_hw_api_cmd.h" #include "hinic_hw_mgmt.h" #include "hinic_hw_dev.h" @@ -70,9 +71,17 @@ int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, { struct hinic_pfhwdev *pfhwdev = mgmt_to_pfhwdev(pf_to_mgmt); struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + struct pci_dev *pdev = hwif->pdev; + int err; pf_to_mgmt->hwif = hwif; + err = hinic_api_cmd_init(pf_to_mgmt->cmd_chain, hwif); + if (err) { + dev_err(&pdev->dev, "Failed to initialize cmd chains\n"); + return err; + } + hinic_aeq_register_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU, pf_to_mgmt, mgmt_msg_aeqe_handler); @@ -89,4 +98,5 @@ void hinic_pf_to_mgmt_free(struct hinic_pf_to_mgmt *pf_to_mgmt) struct hinic_hwdev *hwdev = &pfhwdev->hwdev; hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU); + hinic_api_cmd_free(pf_to_mgmt->cmd_chain); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h index b4b34b703447..dff321c1b793 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -19,6 +19,7 @@ #include #include "hinic_hw_if.h" +#include "hinic_hw_api_cmd.h" enum hinic_mgmt_msg_type { HINIC_MGMT_MSG_SYNC = 1, @@ -30,6 +31,8 @@ enum hinic_cfg_cmd { struct hinic_pf_to_mgmt { struct hinic_hwif *hwif; + + struct hinic_api_cmd_chain *cmd_chain[HINIC_API_CMD_MAX]; }; int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, -- cgit v1.2.3 From 3dcea32193d35d9a4cb9ae56f0e831156bd0b479 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:50 +0800 Subject: net-next/hinic: Initialize api cmd hw Update the hardware about api cmd resources and initialize it. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.c | 173 ++++++++++++++++++++- .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.h | 38 +++++ drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 26 ++++ 3 files changed, 236 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c index 4291f8eb45cc..4bcdf358cda2 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c @@ -13,6 +13,7 @@ * */ +#include #include #include #include @@ -21,8 +22,12 @@ #include #include #include +#include +#include +#include #include +#include "hinic_hw_csr.h" #include "hinic_hw_if.h" #include "hinic_hw_api_cmd.h" @@ -35,8 +40,157 @@ (((cell_size) >= API_CMD_CELL_SIZE_MIN) ? \ (1 << (fls(cell_size - 1))) : API_CMD_CELL_SIZE_MIN) +#define API_CMD_CELL_SIZE_VAL(size) \ + ilog2((size) >> API_CMD_CELL_SIZE_SHIFT) + #define API_CMD_BUF_SIZE 2048 +#define API_CMD_TIMEOUT 1000 + +enum api_cmd_xor_chk_level { + XOR_CHK_DIS = 0, + + XOR_CHK_ALL = 3, +}; + +/** + * api_cmd_hw_restart - restart the chain in the HW + * @chain: the API CMD specific chain to restart + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd_hw_restart(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + int err = -ETIMEDOUT; + unsigned long end; + u32 reg_addr, val; + + /* Read Modify Write */ + reg_addr = HINIC_CSR_API_CMD_CHAIN_REQ_ADDR(chain->chain_type); + val = hinic_hwif_read_reg(hwif, reg_addr); + + val = HINIC_API_CMD_CHAIN_REQ_CLEAR(val, RESTART); + val |= HINIC_API_CMD_CHAIN_REQ_SET(1, RESTART); + + hinic_hwif_write_reg(hwif, reg_addr, val); + + end = jiffies + msecs_to_jiffies(API_CMD_TIMEOUT); + do { + val = hinic_hwif_read_reg(hwif, reg_addr); + + if (!HINIC_API_CMD_CHAIN_REQ_GET(val, RESTART)) { + err = 0; + break; + } + + msleep(20); + } while (time_before(jiffies, end)); + + return err; +} + +/** + * api_cmd_ctrl_init - set the control register of a chain + * @chain: the API CMD specific chain to set control register for + **/ +static void api_cmd_ctrl_init(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, ctrl; + u16 cell_size; + + /* Read Modify Write */ + addr = HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(chain->chain_type); + + cell_size = API_CMD_CELL_SIZE_VAL(chain->cell_size); + + ctrl = hinic_hwif_read_reg(hwif, addr); + + ctrl = HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, RESTART_WB_STAT) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_ERR) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, AEQE_EN) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_CHK_EN) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, CELL_SIZE); + + ctrl |= HINIC_API_CMD_CHAIN_CTRL_SET(1, XOR_ERR) | + HINIC_API_CMD_CHAIN_CTRL_SET(XOR_CHK_ALL, XOR_CHK_EN) | + HINIC_API_CMD_CHAIN_CTRL_SET(cell_size, CELL_SIZE); + + hinic_hwif_write_reg(hwif, addr, ctrl); +} + +/** + * api_cmd_set_status_addr - set the status address of a chain in the HW + * @chain: the API CMD specific chain to set in HW status address for + **/ +static void api_cmd_set_status_addr(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, val; + + addr = HINIC_CSR_API_CMD_STATUS_HI_ADDR(chain->chain_type); + val = upper_32_bits(chain->wb_status_paddr); + hinic_hwif_write_reg(hwif, addr, val); + + addr = HINIC_CSR_API_CMD_STATUS_LO_ADDR(chain->chain_type); + val = lower_32_bits(chain->wb_status_paddr); + hinic_hwif_write_reg(hwif, addr, val); +} + +/** + * api_cmd_set_num_cells - set the number cells of a chain in the HW + * @chain: the API CMD specific chain to set in HW the number of cells for + **/ +static void api_cmd_set_num_cells(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, val; + + addr = HINIC_CSR_API_CMD_CHAIN_NUM_CELLS_ADDR(chain->chain_type); + val = chain->num_cells; + hinic_hwif_write_reg(hwif, addr, val); +} + +/** + * api_cmd_head_init - set the head of a chain in the HW + * @chain: the API CMD specific chain to set in HW the head for + **/ +static void api_cmd_head_init(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, val; + + addr = HINIC_CSR_API_CMD_CHAIN_HEAD_HI_ADDR(chain->chain_type); + val = upper_32_bits(chain->head_cell_paddr); + hinic_hwif_write_reg(hwif, addr, val); + + addr = HINIC_CSR_API_CMD_CHAIN_HEAD_LO_ADDR(chain->chain_type); + val = lower_32_bits(chain->head_cell_paddr); + hinic_hwif_write_reg(hwif, addr, val); +} + +/** + * api_cmd_chain_hw_clean - clean the HW + * @chain: the API CMD specific chain + **/ +static void api_cmd_chain_hw_clean(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, ctrl; + + addr = HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(chain->chain_type); + + ctrl = hinic_hwif_read_reg(hwif, addr); + ctrl = HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, RESTART_WB_STAT) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_ERR) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, AEQE_EN) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_CHK_EN) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, CELL_SIZE); + + hinic_hwif_write_reg(hwif, addr, ctrl); +} + /** * api_cmd_chain_hw_init - initialize the chain in the HW * @chain: the API CMD specific chain to initialize in HW @@ -45,7 +199,23 @@ **/ static int api_cmd_chain_hw_init(struct hinic_api_cmd_chain *chain) { - /* should be implemented */ + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + api_cmd_chain_hw_clean(chain); + + api_cmd_set_status_addr(chain); + + err = api_cmd_hw_restart(chain); + if (err) { + dev_err(&pdev->dev, "Failed to restart API CMD HW\n"); + return err; + } + + api_cmd_ctrl_init(chain); + api_cmd_set_num_cells(chain); + api_cmd_head_init(chain); return 0; } @@ -373,6 +543,7 @@ err_create_cells: **/ static void api_cmd_destroy_chain(struct hinic_api_cmd_chain *chain) { + api_cmd_chain_hw_clean(chain); api_cmd_destroy_cells(chain, chain->num_cells); api_chain_free(chain); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h index 9c7d1e5ee00c..0c83b807111e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h @@ -20,6 +20,44 @@ #include "hinic_hw_if.h" +#define HINIC_API_CMD_CHAIN_REQ_RESTART_SHIFT 1 + +#define HINIC_API_CMD_CHAIN_REQ_RESTART_MASK 0x1 + +#define HINIC_API_CMD_CHAIN_REQ_SET(val, member) \ + (((u32)(val) & HINIC_API_CMD_CHAIN_REQ_##member##_MASK) << \ + HINIC_API_CMD_CHAIN_REQ_##member##_SHIFT) + +#define HINIC_API_CMD_CHAIN_REQ_GET(val, member) \ + (((val) >> HINIC_API_CMD_CHAIN_REQ_##member##_SHIFT) & \ + HINIC_API_CMD_CHAIN_REQ_##member##_MASK) + +#define HINIC_API_CMD_CHAIN_REQ_CLEAR(val, member) \ + ((val) & (~(HINIC_API_CMD_CHAIN_REQ_##member##_MASK \ + << HINIC_API_CMD_CHAIN_REQ_##member##_SHIFT))) + +#define HINIC_API_CMD_CHAIN_CTRL_RESTART_WB_STAT_SHIFT 1 +#define HINIC_API_CMD_CHAIN_CTRL_XOR_ERR_SHIFT 2 +#define HINIC_API_CMD_CHAIN_CTRL_AEQE_EN_SHIFT 4 +#define HINIC_API_CMD_CHAIN_CTRL_AEQ_ID_SHIFT 8 +#define HINIC_API_CMD_CHAIN_CTRL_XOR_CHK_EN_SHIFT 28 +#define HINIC_API_CMD_CHAIN_CTRL_CELL_SIZE_SHIFT 30 + +#define HINIC_API_CMD_CHAIN_CTRL_RESTART_WB_STAT_MASK 0x1 +#define HINIC_API_CMD_CHAIN_CTRL_XOR_ERR_MASK 0x1 +#define HINIC_API_CMD_CHAIN_CTRL_AEQE_EN_MASK 0x1 +#define HINIC_API_CMD_CHAIN_CTRL_AEQ_ID_MASK 0x3 +#define HINIC_API_CMD_CHAIN_CTRL_XOR_CHK_EN_MASK 0x3 +#define HINIC_API_CMD_CHAIN_CTRL_CELL_SIZE_MASK 0x3 + +#define HINIC_API_CMD_CHAIN_CTRL_SET(val, member) \ + (((u32)(val) & HINIC_API_CMD_CHAIN_CTRL_##member##_MASK) << \ + HINIC_API_CMD_CHAIN_CTRL_##member##_SHIFT) + +#define HINIC_API_CMD_CHAIN_CTRL_CLEAR(val, member) \ + ((val) & (~(HINIC_API_CMD_CHAIN_CTRL_##member##_MASK \ + << HINIC_API_CMD_CHAIN_CTRL_##member##_SHIFT))) + enum hinic_api_cmd_chain_type { HINIC_API_CMD_WRITE_TO_MGMT_CPU = 2, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h index c3440a9f5a1e..a9ece6db3414 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -33,4 +33,30 @@ #define HINIC_CSR_PPF_ELECTION_ADDR(idx) \ (HINIC_ELECTION_BASE + (idx) * HINIC_PPF_ELECTION_STRIDE) +/* API CMD registers */ +#define HINIC_CSR_API_CMD_BASE 0xF000 + +#define HINIC_CSR_API_CMD_STRIDE 0x100 + +#define HINIC_CSR_API_CMD_CHAIN_HEAD_HI_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x0 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_CHAIN_HEAD_LO_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x4 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_STATUS_HI_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x8 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_STATUS_LO_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0xC + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_CHAIN_NUM_CELLS_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x10 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x14 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_CHAIN_REQ_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x20 + (idx) * HINIC_CSR_API_CMD_STRIDE) + #endif -- cgit v1.2.3 From 6dd8b68214f4af663faec507169a123222dfa7e7 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:51 +0800 Subject: net-next/hinic: Add management messages Add the management messages for sending to api cmd and the asynchronous event handler for the completion of the messages. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.c | 35 ++ .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.h | 3 + drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 5 + drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c | 439 ++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 59 +++ 5 files changed, 538 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c index 4bcdf358cda2..7e7a76e7d049 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c @@ -53,6 +53,41 @@ enum api_cmd_xor_chk_level { XOR_CHK_ALL = 3, }; +/** + * api_cmd - API CMD command + * @chain: chain for the command + * @dest: destination node on the card that will receive the command + * @cmd: command data + * @size: the command size + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, u8 *cmd, u16 cmd_size) +{ + /* should be implemented */ + return -EINVAL; +} + +/** + * hinic_api_cmd_write - Write API CMD command + * @chain: chain for write command + * @dest: destination node on the card that will receive the command + * @cmd: command data + * @size: the command size + * + * Return 0 - Success, negative - Failure + **/ +int hinic_api_cmd_write(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, u8 *cmd, u16 size) +{ + /* Verify the chain type */ + if (chain->chain_type == HINIC_API_CMD_WRITE_TO_MGMT_CPU) + return api_cmd(chain, dest, cmd, size); + + return -EINVAL; +} + /** * api_cmd_hw_restart - restart the chain in the HW * @chain: the API CMD specific chain to restart diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h index 0c83b807111e..e8865d627b58 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h @@ -132,6 +132,9 @@ struct hinic_api_cmd_chain { struct hinic_api_cmd_cell *curr_node; }; +int hinic_api_cmd_write(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, u8 *cmd, u16 size); + int hinic_api_cmd_init(struct hinic_api_cmd_chain **chain, struct hinic_hwif *hwif); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index b6d985042907..98623d69f2ba 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -93,6 +93,7 @@ #define HINIC_HWIF_NUM_IRQS(hwif) ((hwif)->attr.num_irqs) #define HINIC_HWIF_FUNC_IDX(hwif) ((hwif)->attr.func_idx) #define HINIC_HWIF_PCI_INTF(hwif) ((hwif)->attr.pci_intf_idx) +#define HINIC_HWIF_PF_IDX(hwif) ((hwif)->attr.pf_idx) #define HINIC_FUNC_TYPE(hwif) ((hwif)->attr.func_type) #define HINIC_IS_PF(hwif) (HINIC_FUNC_TYPE(hwif) == HINIC_PF) @@ -127,6 +128,10 @@ enum hinic_mod_type { HINIC_MOD_MAX = 15 }; +enum hinic_node_id { + HINIC_NODE_ID_MGMT = 21, +}; + struct hinic_func_attr { u16 func_idx; u8 pf_idx; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c index f914bc77dc62..0150b71a5aba 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c @@ -16,6 +16,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" @@ -23,9 +29,267 @@ #include "hinic_hw_mgmt.h" #include "hinic_hw_dev.h" +#define SYNC_MSG_ID_MASK 0x1FF + +#define SYNC_MSG_ID(pf_to_mgmt) ((pf_to_mgmt)->sync_msg_id) + +#define SYNC_MSG_ID_INC(pf_to_mgmt) (SYNC_MSG_ID(pf_to_mgmt) = \ + ((SYNC_MSG_ID(pf_to_mgmt) + 1) & \ + SYNC_MSG_ID_MASK)) + +#define MSG_SZ_IS_VALID(in_size) ((in_size) <= MAX_MSG_LEN) + +#define MGMT_MSG_LEN_MIN 20 +#define MGMT_MSG_LEN_STEP 16 +#define MGMT_MSG_RSVD_FOR_DEV 8 + +#define SEGMENT_LEN 48 + +#define MAX_PF_MGMT_BUF_SIZE 2048 + +/* Data should be SEG LEN size aligned */ +#define MAX_MSG_LEN 2016 + +#define MSG_NOT_RESP 0xFFFF + +#define MGMT_MSG_TIMEOUT 1000 + #define mgmt_to_pfhwdev(pf_mgmt) \ container_of(pf_mgmt, struct hinic_pfhwdev, pf_to_mgmt) +enum msg_segment_type { + NOT_LAST_SEGMENT = 0, + LAST_SEGMENT = 1, +}; + +enum mgmt_direction_type { + MGMT_DIRECT_SEND = 0, + MGMT_RESP = 1, +}; + +enum msg_ack_type { + MSG_ACK = 0, + MSG_NO_ACK = 1, +}; + +/** + * prepare_header - prepare the header of the message + * @pf_to_mgmt: PF to MGMT channel + * @msg_len: the length of the message + * @mod: module in the chip that will get the message + * @ack_type: ask for response + * @direction: the direction of the message + * @cmd: command of the message + * @msg_id: message id + * + * Return the prepared header value + **/ +static u64 prepare_header(struct hinic_pf_to_mgmt *pf_to_mgmt, + u16 msg_len, enum hinic_mod_type mod, + enum msg_ack_type ack_type, + enum mgmt_direction_type direction, + u16 cmd, u16 msg_id) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + + return HINIC_MSG_HEADER_SET(msg_len, MSG_LEN) | + HINIC_MSG_HEADER_SET(mod, MODULE) | + HINIC_MSG_HEADER_SET(SEGMENT_LEN, SEG_LEN) | + HINIC_MSG_HEADER_SET(ack_type, NO_ACK) | + HINIC_MSG_HEADER_SET(0, ASYNC_MGMT_TO_PF) | + HINIC_MSG_HEADER_SET(0, SEQID) | + HINIC_MSG_HEADER_SET(LAST_SEGMENT, LAST) | + HINIC_MSG_HEADER_SET(direction, DIRECTION) | + HINIC_MSG_HEADER_SET(cmd, CMD) | + HINIC_MSG_HEADER_SET(HINIC_HWIF_PCI_INTF(hwif), PCI_INTF) | + HINIC_MSG_HEADER_SET(HINIC_HWIF_PF_IDX(hwif), PF_IDX) | + HINIC_MSG_HEADER_SET(msg_id, MSG_ID); +} + +/** + * prepare_mgmt_cmd - prepare the mgmt command + * @mgmt_cmd: pointer to the command to prepare + * @header: pointer of the header for the message + * @msg: the data of the message + * @msg_len: the length of the message + **/ +static void prepare_mgmt_cmd(u8 *mgmt_cmd, u64 *header, u8 *msg, u16 msg_len) +{ + memset(mgmt_cmd, 0, MGMT_MSG_RSVD_FOR_DEV); + + mgmt_cmd += MGMT_MSG_RSVD_FOR_DEV; + memcpy(mgmt_cmd, header, sizeof(*header)); + + mgmt_cmd += sizeof(*header); + memcpy(mgmt_cmd, msg, msg_len); +} + +/** + * mgmt_msg_len - calculate the total message length + * @msg_data_len: the length of the message data + * + * Return the total message length + **/ +static u16 mgmt_msg_len(u16 msg_data_len) +{ + /* RSVD + HEADER_SIZE + DATA_LEN */ + u16 msg_len = MGMT_MSG_RSVD_FOR_DEV + sizeof(u64) + msg_data_len; + + if (msg_len > MGMT_MSG_LEN_MIN) + msg_len = MGMT_MSG_LEN_MIN + + ALIGN((msg_len - MGMT_MSG_LEN_MIN), + MGMT_MSG_LEN_STEP); + else + msg_len = MGMT_MSG_LEN_MIN; + + return msg_len; +} + +/** + * send_msg_to_mgmt - send message to mgmt by API CMD + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that will get the message + * @cmd: command of the message + * @data: the msg data + * @data_len: the msg data length + * @ack_type: ask for response + * @direction: the direction of the original message + * @resp_msg_id: msg id to response for + * + * Return 0 - Success, negative - Failure + **/ +static int send_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + u8 *data, u16 data_len, + enum msg_ack_type ack_type, + enum mgmt_direction_type direction, + u16 resp_msg_id) +{ + struct hinic_api_cmd_chain *chain; + u64 header; + u16 msg_id; + + msg_id = SYNC_MSG_ID(pf_to_mgmt); + + if (direction == MGMT_RESP) { + header = prepare_header(pf_to_mgmt, data_len, mod, ack_type, + direction, cmd, resp_msg_id); + } else { + SYNC_MSG_ID_INC(pf_to_mgmt); + header = prepare_header(pf_to_mgmt, data_len, mod, ack_type, + direction, cmd, msg_id); + } + + prepare_mgmt_cmd(pf_to_mgmt->sync_msg_buf, &header, data, data_len); + + chain = pf_to_mgmt->cmd_chain[HINIC_API_CMD_WRITE_TO_MGMT_CPU]; + return hinic_api_cmd_write(chain, HINIC_NODE_ID_MGMT, + pf_to_mgmt->sync_msg_buf, + mgmt_msg_len(data_len)); +} + +/** + * msg_to_mgmt_sync - send sync message to mgmt + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that will get the message + * @cmd: command of the message + * @buf_in: the msg data + * @in_size: the msg data length + * @buf_out: response + * @out_size: response length + * @direction: the direction of the original message + * @resp_msg_id: msg id to response for + * + * Return 0 - Success, negative - Failure + **/ +static int msg_to_mgmt_sync(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + u8 *buf_in, u16 in_size, + u8 *buf_out, u16 *out_size, + enum mgmt_direction_type direction, + u16 resp_msg_id) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_recv_msg *recv_msg; + struct completion *recv_done; + u16 msg_id; + int err; + + /* Lock the sync_msg_buf */ + down(&pf_to_mgmt->sync_msg_lock); + + recv_msg = &pf_to_mgmt->recv_resp_msg_from_mgmt; + recv_done = &recv_msg->recv_done; + + if (resp_msg_id == MSG_NOT_RESP) + msg_id = SYNC_MSG_ID(pf_to_mgmt); + else + msg_id = resp_msg_id; + + init_completion(recv_done); + + err = send_msg_to_mgmt(pf_to_mgmt, mod, cmd, buf_in, in_size, + MSG_ACK, direction, resp_msg_id); + if (err) { + dev_err(&pdev->dev, "Failed to send sync msg to mgmt\n"); + goto unlock_sync_msg; + } + + if (!wait_for_completion_timeout(recv_done, MGMT_MSG_TIMEOUT)) { + dev_err(&pdev->dev, "MGMT timeout, MSG id = %d\n", msg_id); + err = -ETIMEDOUT; + goto unlock_sync_msg; + } + + smp_rmb(); /* verify reading after completion */ + + if (recv_msg->msg_id != msg_id) { + dev_err(&pdev->dev, "incorrect MSG for id = %d\n", msg_id); + err = -EFAULT; + goto unlock_sync_msg; + } + + if ((buf_out) && (recv_msg->msg_len <= MAX_PF_MGMT_BUF_SIZE)) { + memcpy(buf_out, recv_msg->msg, recv_msg->msg_len); + *out_size = recv_msg->msg_len; + } + +unlock_sync_msg: + up(&pf_to_mgmt->sync_msg_lock); + return err; +} + +/** + * msg_to_mgmt_async - send message to mgmt without response + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that will get the message + * @cmd: command of the message + * @buf_in: the msg data + * @in_size: the msg data length + * @direction: the direction of the original message + * @resp_msg_id: msg id to response for + * + * Return 0 - Success, negative - Failure + **/ +static int msg_to_mgmt_async(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + u8 *buf_in, u16 in_size, + enum mgmt_direction_type direction, + u16 resp_msg_id) +{ + int err; + + /* Lock the sync_msg_buf */ + down(&pf_to_mgmt->sync_msg_lock); + + err = send_msg_to_mgmt(pf_to_mgmt, mod, cmd, buf_in, in_size, + MSG_NO_ACK, direction, resp_msg_id); + + up(&pf_to_mgmt->sync_msg_lock); + return err; +} + /** * hinic_msg_to_mgmt - send message to mgmt * @pf_to_mgmt: PF to MGMT channel @@ -44,8 +308,98 @@ int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, enum hinic_mgmt_msg_type sync) { - /* should be implemented */ - return -EINVAL; + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + + if (sync != HINIC_MGMT_MSG_SYNC) { + dev_err(&pdev->dev, "Invalid MGMT msg type\n"); + return -EINVAL; + } + + if (!MSG_SZ_IS_VALID(in_size)) { + dev_err(&pdev->dev, "Invalid MGMT msg buffer size\n"); + return -EINVAL; + } + + return msg_to_mgmt_sync(pf_to_mgmt, mod, cmd, buf_in, in_size, + buf_out, out_size, MGMT_DIRECT_SEND, + MSG_NOT_RESP); +} + +/** + * mgmt_recv_msg_handler - handler for message from mgmt cpu + * @pf_to_mgmt: PF to MGMT channel + * @recv_msg: received message details + **/ +static void mgmt_recv_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_recv_msg *recv_msg) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + u8 *buf_out = recv_msg->buf_out; + u16 out_size = 0; + + dev_err(&pdev->dev, "No MGMT msg handler, mod = %d\n", recv_msg->mod); + + if (!recv_msg->async_mgmt_to_pf) + /* MGMT sent sync msg, send the response */ + msg_to_mgmt_async(pf_to_mgmt, recv_msg->mod, recv_msg->cmd, + buf_out, out_size, MGMT_RESP, + recv_msg->msg_id); +} + +/** + * mgmt_resp_msg_handler - handler for a response message from mgmt cpu + * @pf_to_mgmt: PF to MGMT channel + * @recv_msg: received message details + **/ +static void mgmt_resp_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_recv_msg *recv_msg) +{ + wmb(); /* verify writing all, before reading */ + + complete(&recv_msg->recv_done); +} + +/** + * recv_mgmt_msg_handler - handler for a message from mgmt cpu + * @pf_to_mgmt: PF to MGMT channel + * @header: the header of the message + * @recv_msg: received message details + **/ +static void recv_mgmt_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, + u64 *header, struct hinic_recv_msg *recv_msg) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + int seq_id, seg_len; + u8 *msg_body; + + seq_id = HINIC_MSG_HEADER_GET(*header, SEQID); + seg_len = HINIC_MSG_HEADER_GET(*header, SEG_LEN); + + if (seq_id >= (MAX_MSG_LEN / SEGMENT_LEN)) { + dev_err(&pdev->dev, "recv big mgmt msg\n"); + return; + } + + msg_body = (u8 *)header + sizeof(*header); + memcpy(recv_msg->msg + seq_id * SEGMENT_LEN, msg_body, seg_len); + + if (!HINIC_MSG_HEADER_GET(*header, LAST)) + return; + + recv_msg->cmd = HINIC_MSG_HEADER_GET(*header, CMD); + recv_msg->mod = HINIC_MSG_HEADER_GET(*header, MODULE); + recv_msg->async_mgmt_to_pf = HINIC_MSG_HEADER_GET(*header, + ASYNC_MGMT_TO_PF); + recv_msg->msg_len = HINIC_MSG_HEADER_GET(*header, MSG_LEN); + recv_msg->msg_id = HINIC_MSG_HEADER_GET(*header, MSG_ID); + + if (HINIC_MSG_HEADER_GET(*header, DIRECTION) == MGMT_RESP) + mgmt_resp_msg_handler(pf_to_mgmt, recv_msg); + else + mgmt_recv_msg_handler(pf_to_mgmt, recv_msg); } /** @@ -56,7 +410,77 @@ int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, **/ static void mgmt_msg_aeqe_handler(void *handle, void *data, u8 size) { - /* should be implemented */ + struct hinic_pf_to_mgmt *pf_to_mgmt = handle; + struct hinic_recv_msg *recv_msg; + u64 *header = (u64 *)data; + + recv_msg = HINIC_MSG_HEADER_GET(*header, DIRECTION) == + MGMT_DIRECT_SEND ? + &pf_to_mgmt->recv_msg_from_mgmt : + &pf_to_mgmt->recv_resp_msg_from_mgmt; + + recv_mgmt_msg_handler(pf_to_mgmt, header, recv_msg); +} + +/** + * alloc_recv_msg - allocate receive message memory + * @pf_to_mgmt: PF to MGMT channel + * @recv_msg: pointer that will hold the allocated data + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_recv_msg(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_recv_msg *recv_msg) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + + recv_msg->msg = devm_kzalloc(&pdev->dev, MAX_PF_MGMT_BUF_SIZE, + GFP_KERNEL); + if (!recv_msg->msg) + return -ENOMEM; + + recv_msg->buf_out = devm_kzalloc(&pdev->dev, MAX_PF_MGMT_BUF_SIZE, + GFP_KERNEL); + if (!recv_msg->buf_out) + return -ENOMEM; + + return 0; +} + +/** + * alloc_msg_buf - allocate all the message buffers of PF to MGMT channel + * @pf_to_mgmt: PF to MGMT channel + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_msg_buf(struct hinic_pf_to_mgmt *pf_to_mgmt) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + err = alloc_recv_msg(pf_to_mgmt, + &pf_to_mgmt->recv_msg_from_mgmt); + if (err) { + dev_err(&pdev->dev, "Failed to allocate recv msg\n"); + return err; + } + + err = alloc_recv_msg(pf_to_mgmt, + &pf_to_mgmt->recv_resp_msg_from_mgmt); + if (err) { + dev_err(&pdev->dev, "Failed to allocate resp recv msg\n"); + return err; + } + + pf_to_mgmt->sync_msg_buf = devm_kzalloc(&pdev->dev, + MAX_PF_MGMT_BUF_SIZE, + GFP_KERNEL); + if (!pf_to_mgmt->sync_msg_buf) + return -ENOMEM; + + return 0; } /** @@ -76,6 +500,15 @@ int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, pf_to_mgmt->hwif = hwif; + sema_init(&pf_to_mgmt->sync_msg_lock, 1); + pf_to_mgmt->sync_msg_id = 0; + + err = alloc_msg_buf(pf_to_mgmt); + if (err) { + dev_err(&pdev->dev, "Failed to allocate msg buffers\n"); + return err; + } + err = hinic_api_cmd_init(pf_to_mgmt->cmd_chain, hwif); if (err) { dev_err(&pdev->dev, "Failed to initialize cmd chains\n"); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h index dff321c1b793..eca7ad865bca 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -17,10 +17,48 @@ #define HINIC_HW_MGMT_H #include +#include +#include #include "hinic_hw_if.h" #include "hinic_hw_api_cmd.h" +#define HINIC_MSG_HEADER_MSG_LEN_SHIFT 0 +#define HINIC_MSG_HEADER_MODULE_SHIFT 11 +#define HINIC_MSG_HEADER_SEG_LEN_SHIFT 16 +#define HINIC_MSG_HEADER_NO_ACK_SHIFT 22 +#define HINIC_MSG_HEADER_ASYNC_MGMT_TO_PF_SHIFT 23 +#define HINIC_MSG_HEADER_SEQID_SHIFT 24 +#define HINIC_MSG_HEADER_LAST_SHIFT 30 +#define HINIC_MSG_HEADER_DIRECTION_SHIFT 31 +#define HINIC_MSG_HEADER_CMD_SHIFT 32 +#define HINIC_MSG_HEADER_ZEROS_SHIFT 40 +#define HINIC_MSG_HEADER_PCI_INTF_SHIFT 48 +#define HINIC_MSG_HEADER_PF_IDX_SHIFT 50 +#define HINIC_MSG_HEADER_MSG_ID_SHIFT 54 + +#define HINIC_MSG_HEADER_MSG_LEN_MASK 0x7FF +#define HINIC_MSG_HEADER_MODULE_MASK 0x1F +#define HINIC_MSG_HEADER_SEG_LEN_MASK 0x3F +#define HINIC_MSG_HEADER_NO_ACK_MASK 0x1 +#define HINIC_MSG_HEADER_ASYNC_MGMT_TO_PF_MASK 0x1 +#define HINIC_MSG_HEADER_SEQID_MASK 0x3F +#define HINIC_MSG_HEADER_LAST_MASK 0x1 +#define HINIC_MSG_HEADER_DIRECTION_MASK 0x1 +#define HINIC_MSG_HEADER_CMD_MASK 0xFF +#define HINIC_MSG_HEADER_ZEROS_MASK 0xFF +#define HINIC_MSG_HEADER_PCI_INTF_MASK 0x3 +#define HINIC_MSG_HEADER_PF_IDX_MASK 0xF +#define HINIC_MSG_HEADER_MSG_ID_MASK 0x3FF + +#define HINIC_MSG_HEADER_SET(val, member) \ + ((u64)((val) & HINIC_MSG_HEADER_##member##_MASK) << \ + HINIC_MSG_HEADER_##member##_SHIFT) + +#define HINIC_MSG_HEADER_GET(val, member) \ + (((val) >> HINIC_MSG_HEADER_##member##_SHIFT) & \ + HINIC_MSG_HEADER_##member##_MASK) + enum hinic_mgmt_msg_type { HINIC_MGMT_MSG_SYNC = 1, }; @@ -29,9 +67,30 @@ enum hinic_cfg_cmd { HINIC_CFG_NIC_CAP = 0, }; +struct hinic_recv_msg { + u8 *msg; + u8 *buf_out; + + struct completion recv_done; + + u16 cmd; + enum hinic_mod_type mod; + int async_mgmt_to_pf; + + u16 msg_len; + u16 msg_id; +}; + struct hinic_pf_to_mgmt { struct hinic_hwif *hwif; + struct semaphore sync_msg_lock; + u16 sync_msg_id; + u8 *sync_msg_buf; + + struct hinic_recv_msg recv_resp_msg_from_mgmt; + struct hinic_recv_msg recv_msg_from_mgmt; + struct hinic_api_cmd_chain *cmd_chain[HINIC_API_CMD_MAX]; }; -- cgit v1.2.3 From 0ac599c7903cb9e315081b731a4401e1726630d5 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:52 +0800 Subject: net-next/hinic: Add api cmd commands Add the api cmd commands for sending management messages to the nic. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.c | 329 ++++++++++++++++++++- .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.h | 65 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 6 + 3 files changed, 398 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c index 7e7a76e7d049..8901801fe426 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c @@ -25,7 +25,9 @@ #include #include #include +#include #include +#include #include "hinic_hw_csr.h" #include "hinic_hw_if.h" @@ -45,14 +47,313 @@ #define API_CMD_BUF_SIZE 2048 +/* Sizes of the members in hinic_api_cmd_cell */ +#define API_CMD_CELL_DESC_SIZE 8 +#define API_CMD_CELL_DATA_ADDR_SIZE 8 + +#define API_CMD_CELL_ALIGNMENT 8 + #define API_CMD_TIMEOUT 1000 +#define MASKED_IDX(chain, idx) ((idx) & ((chain)->num_cells - 1)) + +#define SIZE_8BYTES(size) (ALIGN((size), 8) >> 3) +#define SIZE_4BYTES(size) (ALIGN((size), 4) >> 2) + +#define RD_DMA_ATTR_DEFAULT 0 +#define WR_DMA_ATTR_DEFAULT 0 + +enum api_cmd_data_format { + SGE_DATA = 1, /* cell data is passed by hw address */ +}; + +enum api_cmd_type { + API_CMD_WRITE = 0, +}; + +enum api_cmd_bypass { + NO_BYPASS = 0, + BYPASS = 1, +}; + enum api_cmd_xor_chk_level { XOR_CHK_DIS = 0, XOR_CHK_ALL = 3, }; +static u8 xor_chksum_set(void *data) +{ + int idx; + u8 *val, checksum = 0; + + val = data; + + for (idx = 0; idx < 7; idx++) + checksum ^= val[idx]; + + return checksum; +} + +static void set_prod_idx(struct hinic_api_cmd_chain *chain) +{ + enum hinic_api_cmd_chain_type chain_type = chain->chain_type; + struct hinic_hwif *hwif = chain->hwif; + u32 addr, prod_idx; + + addr = HINIC_CSR_API_CMD_CHAIN_PI_ADDR(chain_type); + prod_idx = hinic_hwif_read_reg(hwif, addr); + + prod_idx = HINIC_API_CMD_PI_CLEAR(prod_idx, IDX); + + prod_idx |= HINIC_API_CMD_PI_SET(chain->prod_idx, IDX); + + hinic_hwif_write_reg(hwif, addr, prod_idx); +} + +static u32 get_hw_cons_idx(struct hinic_api_cmd_chain *chain) +{ + u32 addr, val; + + addr = HINIC_CSR_API_CMD_STATUS_ADDR(chain->chain_type); + val = hinic_hwif_read_reg(chain->hwif, addr); + + return HINIC_API_CMD_STATUS_GET(val, CONS_IDX); +} + +/** + * chain_busy - check if the chain is still processing last requests + * @chain: chain to check + * + * Return 0 - Success, negative - Failure + **/ +static int chain_busy(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + u32 prod_idx; + + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + chain->cons_idx = get_hw_cons_idx(chain); + prod_idx = chain->prod_idx; + + /* check for a space for a new command */ + if (chain->cons_idx == MASKED_IDX(chain, prod_idx + 1)) { + dev_err(&pdev->dev, "API CMD chain %d is busy\n", + chain->chain_type); + return -EBUSY; + } + break; + + default: + dev_err(&pdev->dev, "Unknown API CMD Chain type\n"); + break; + } + + return 0; +} + +/** + * get_cell_data_size - get the data size of a specific cell type + * @type: chain type + * + * Return the data(Desc + Address) size in the cell + **/ +static u8 get_cell_data_size(enum hinic_api_cmd_chain_type type) +{ + u8 cell_data_size = 0; + + switch (type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + cell_data_size = ALIGN(API_CMD_CELL_DESC_SIZE + + API_CMD_CELL_DATA_ADDR_SIZE, + API_CMD_CELL_ALIGNMENT); + break; + default: + break; + } + + return cell_data_size; +} + +/** + * prepare_cell_ctrl - prepare the ctrl of the cell for the command + * @cell_ctrl: the control of the cell to set the control value into it + * @data_size: the size of the data in the cell + **/ +static void prepare_cell_ctrl(u64 *cell_ctrl, u16 data_size) +{ + u8 chksum; + u64 ctrl; + + ctrl = HINIC_API_CMD_CELL_CTRL_SET(SIZE_8BYTES(data_size), DATA_SZ) | + HINIC_API_CMD_CELL_CTRL_SET(RD_DMA_ATTR_DEFAULT, RD_DMA_ATTR) | + HINIC_API_CMD_CELL_CTRL_SET(WR_DMA_ATTR_DEFAULT, WR_DMA_ATTR); + + chksum = xor_chksum_set(&ctrl); + + ctrl |= HINIC_API_CMD_CELL_CTRL_SET(chksum, XOR_CHKSUM); + + /* The data in the HW should be in Big Endian Format */ + *cell_ctrl = cpu_to_be64(ctrl); +} + +/** + * prepare_api_cmd - prepare API CMD command + * @chain: chain for the command + * @dest: destination node on the card that will receive the command + * @cmd: command data + * @cmd_size: the command size + **/ +static void prepare_api_cmd(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, + void *cmd, u16 cmd_size) +{ + struct hinic_api_cmd_cell *cell = chain->curr_node; + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + + cell_ctxt = &chain->cell_ctxt[chain->prod_idx]; + + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + cell->desc = HINIC_API_CMD_DESC_SET(SGE_DATA, API_TYPE) | + HINIC_API_CMD_DESC_SET(API_CMD_WRITE, RD_WR) | + HINIC_API_CMD_DESC_SET(NO_BYPASS, MGMT_BYPASS); + break; + + default: + dev_err(&pdev->dev, "unknown Chain type\n"); + return; + } + + cell->desc |= HINIC_API_CMD_DESC_SET(dest, DEST) | + HINIC_API_CMD_DESC_SET(SIZE_4BYTES(cmd_size), SIZE); + + cell->desc |= HINIC_API_CMD_DESC_SET(xor_chksum_set(&cell->desc), + XOR_CHKSUM); + + /* The data in the HW should be in Big Endian Format */ + cell->desc = cpu_to_be64(cell->desc); + + memcpy(cell_ctxt->api_cmd_vaddr, cmd, cmd_size); +} + +/** + * prepare_cell - prepare cell ctrl and cmd in the current cell + * @chain: chain for the command + * @dest: destination node on the card that will receive the command + * @cmd: command data + * @cmd_size: the command size + * + * Return 0 - Success, negative - Failure + **/ +static void prepare_cell(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, + void *cmd, u16 cmd_size) +{ + struct hinic_api_cmd_cell *curr_node = chain->curr_node; + u16 data_size = get_cell_data_size(chain->chain_type); + + prepare_cell_ctrl(&curr_node->ctrl, data_size); + prepare_api_cmd(chain, dest, cmd, cmd_size); +} + +static inline void cmd_chain_prod_idx_inc(struct hinic_api_cmd_chain *chain) +{ + chain->prod_idx = MASKED_IDX(chain, chain->prod_idx + 1); +} + +/** + * api_cmd_status_update - update the status in the chain struct + * @chain: chain to update + **/ +static void api_cmd_status_update(struct hinic_api_cmd_chain *chain) +{ + enum hinic_api_cmd_chain_type chain_type; + struct hinic_api_cmd_status *wb_status; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + u64 status_header; + u32 status; + + wb_status = chain->wb_status; + status_header = be64_to_cpu(wb_status->header); + + status = be32_to_cpu(wb_status->status); + if (HINIC_API_CMD_STATUS_GET(status, CHKSUM_ERR)) { + dev_err(&pdev->dev, "API CMD status: Xor check error\n"); + return; + } + + chain_type = HINIC_API_CMD_STATUS_HEADER_GET(status_header, CHAIN_ID); + if (chain_type >= HINIC_API_CMD_MAX) { + dev_err(&pdev->dev, "unknown API CMD Chain %d\n", chain_type); + return; + } + + chain->cons_idx = HINIC_API_CMD_STATUS_GET(status, CONS_IDX); +} + +/** + * wait_for_status_poll - wait for write to api cmd command to complete + * @chain: the chain of the command + * + * Return 0 - Success, negative - Failure + **/ +static int wait_for_status_poll(struct hinic_api_cmd_chain *chain) +{ + int err = -ETIMEDOUT; + unsigned long end; + + end = jiffies + msecs_to_jiffies(API_CMD_TIMEOUT); + do { + api_cmd_status_update(chain); + + /* wait for CI to be updated - sign for completion */ + if (chain->cons_idx == chain->prod_idx) { + err = 0; + break; + } + + msleep(20); + } while (time_before(jiffies, end)); + + return err; +} + +/** + * wait_for_api_cmd_completion - wait for command to complete + * @chain: chain for the command + * + * Return 0 - Success, negative - Failure + **/ +static int wait_for_api_cmd_completion(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + err = wait_for_status_poll(chain); + if (err) { + dev_err(&pdev->dev, "API CMD Poll status timeout\n"); + break; + } + break; + + default: + dev_err(&pdev->dev, "unknown API CMD Chain type\n"); + err = -EINVAL; + break; + } + + return err; +} + /** * api_cmd - API CMD command * @chain: chain for the command @@ -65,8 +366,30 @@ enum api_cmd_xor_chk_level { static int api_cmd(struct hinic_api_cmd_chain *chain, enum hinic_node_id dest, u8 *cmd, u16 cmd_size) { - /* should be implemented */ - return -EINVAL; + struct hinic_api_cmd_cell_ctxt *ctxt; + int err; + + down(&chain->sem); + if (chain_busy(chain)) { + up(&chain->sem); + return -EBUSY; + } + + prepare_cell(chain, dest, cmd, cmd_size); + cmd_chain_prod_idx_inc(chain); + + wmb(); /* inc pi before issue the command */ + + set_prod_idx(chain); /* issue the command */ + + ctxt = &chain->cell_ctxt[chain->prod_idx]; + + chain->curr_node = ctxt->cell_vaddr; + + err = wait_for_api_cmd_completion(chain); + + up(&chain->sem); + return err; } /** @@ -491,6 +814,8 @@ static int api_chain_init(struct hinic_api_cmd_chain *chain, chain->prod_idx = 0; chain->cons_idx = 0; + sema_init(&chain->sem, 1); + cell_ctxt_size = chain->num_cells * sizeof(*chain->cell_ctxt); chain->cell_ctxt = devm_kzalloc(&pdev->dev, cell_ctxt_size, GFP_KERNEL); if (!chain->cell_ctxt) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h index e8865d627b58..31b94d5d47f7 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h @@ -17,9 +17,22 @@ #define HINIC_HW_API_CMD_H #include +#include #include "hinic_hw_if.h" +#define HINIC_API_CMD_PI_IDX_SHIFT 0 + +#define HINIC_API_CMD_PI_IDX_MASK 0xFFFFFF + +#define HINIC_API_CMD_PI_SET(val, member) \ + (((u32)(val) & HINIC_API_CMD_PI_##member##_MASK) << \ + HINIC_API_CMD_PI_##member##_SHIFT) + +#define HINIC_API_CMD_PI_CLEAR(val, member) \ + ((val) & (~(HINIC_API_CMD_PI_##member##_MASK \ + << HINIC_API_CMD_PI_##member##_SHIFT))) + #define HINIC_API_CMD_CHAIN_REQ_RESTART_SHIFT 1 #define HINIC_API_CMD_CHAIN_REQ_RESTART_MASK 0x1 @@ -58,6 +71,56 @@ ((val) & (~(HINIC_API_CMD_CHAIN_CTRL_##member##_MASK \ << HINIC_API_CMD_CHAIN_CTRL_##member##_SHIFT))) +#define HINIC_API_CMD_CELL_CTRL_DATA_SZ_SHIFT 0 +#define HINIC_API_CMD_CELL_CTRL_RD_DMA_ATTR_SHIFT 16 +#define HINIC_API_CMD_CELL_CTRL_WR_DMA_ATTR_SHIFT 24 +#define HINIC_API_CMD_CELL_CTRL_XOR_CHKSUM_SHIFT 56 + +#define HINIC_API_CMD_CELL_CTRL_DATA_SZ_MASK 0x3F +#define HINIC_API_CMD_CELL_CTRL_RD_DMA_ATTR_MASK 0x3F +#define HINIC_API_CMD_CELL_CTRL_WR_DMA_ATTR_MASK 0x3F +#define HINIC_API_CMD_CELL_CTRL_XOR_CHKSUM_MASK 0xFF + +#define HINIC_API_CMD_CELL_CTRL_SET(val, member) \ + ((((u64)val) & HINIC_API_CMD_CELL_CTRL_##member##_MASK) << \ + HINIC_API_CMD_CELL_CTRL_##member##_SHIFT) + +#define HINIC_API_CMD_DESC_API_TYPE_SHIFT 0 +#define HINIC_API_CMD_DESC_RD_WR_SHIFT 1 +#define HINIC_API_CMD_DESC_MGMT_BYPASS_SHIFT 2 +#define HINIC_API_CMD_DESC_DEST_SHIFT 32 +#define HINIC_API_CMD_DESC_SIZE_SHIFT 40 +#define HINIC_API_CMD_DESC_XOR_CHKSUM_SHIFT 56 + +#define HINIC_API_CMD_DESC_API_TYPE_MASK 0x1 +#define HINIC_API_CMD_DESC_RD_WR_MASK 0x1 +#define HINIC_API_CMD_DESC_MGMT_BYPASS_MASK 0x1 +#define HINIC_API_CMD_DESC_DEST_MASK 0x1F +#define HINIC_API_CMD_DESC_SIZE_MASK 0x7FF +#define HINIC_API_CMD_DESC_XOR_CHKSUM_MASK 0xFF + +#define HINIC_API_CMD_DESC_SET(val, member) \ + ((((u64)val) & HINIC_API_CMD_DESC_##member##_MASK) << \ + HINIC_API_CMD_DESC_##member##_SHIFT) + +#define HINIC_API_CMD_STATUS_HEADER_CHAIN_ID_SHIFT 16 + +#define HINIC_API_CMD_STATUS_HEADER_CHAIN_ID_MASK 0xFF + +#define HINIC_API_CMD_STATUS_HEADER_GET(val, member) \ + (((val) >> HINIC_API_CMD_STATUS_HEADER_##member##_SHIFT) & \ + HINIC_API_CMD_STATUS_HEADER_##member##_MASK) + +#define HINIC_API_CMD_STATUS_CONS_IDX_SHIFT 0 +#define HINIC_API_CMD_STATUS_CHKSUM_ERR_SHIFT 28 + +#define HINIC_API_CMD_STATUS_CONS_IDX_MASK 0xFFFFFF +#define HINIC_API_CMD_STATUS_CHKSUM_ERR_MASK 0x3 + +#define HINIC_API_CMD_STATUS_GET(val, member) \ + (((val) >> HINIC_API_CMD_STATUS_##member##_SHIFT) & \ + HINIC_API_CMD_STATUS_##member##_MASK) + enum hinic_api_cmd_chain_type { HINIC_API_CMD_WRITE_TO_MGMT_CPU = 2, @@ -122,6 +185,8 @@ struct hinic_api_cmd_chain { u32 prod_idx; u32 cons_idx; + struct semaphore sem; + struct hinic_api_cmd_cell_ctxt *cell_ctxt; dma_addr_t wb_status_paddr; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h index a9ece6db3414..ebbf0549404c 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -56,7 +56,13 @@ #define HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x14 + (idx) * HINIC_CSR_API_CMD_STRIDE) +#define HINIC_CSR_API_CMD_CHAIN_PI_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x1C + (idx) * HINIC_CSR_API_CMD_STRIDE) + #define HINIC_CSR_API_CMD_CHAIN_REQ_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x20 + (idx) * HINIC_CSR_API_CMD_STRIDE) +#define HINIC_CSR_API_CMD_STATUS_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x30 + (idx) * HINIC_CSR_API_CMD_STRIDE) + #endif -- cgit v1.2.3 From f00fe738b5d863d170907de908870b762f1b6387 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:53 +0800 Subject: net-next/hinic: Add aeqs Handle aeq elements that are accumulated on the aeq by calling the registered handler for the specific event. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 49 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c | 463 ++++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h | 81 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.c | 90 +++++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 46 +++ 5 files changed, 727 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h index ebbf0549404c..52eb89c7df9a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -65,4 +65,53 @@ #define HINIC_CSR_API_CMD_STATUS_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x30 + (idx) * HINIC_CSR_API_CMD_STRIDE) +/* MSI-X registers */ +#define HINIC_CSR_MSIX_CTRL_BASE 0x2000 +#define HINIC_CSR_MSIX_CNT_BASE 0x2004 + +#define HINIC_CSR_MSIX_STRIDE 0x8 + +#define HINIC_CSR_MSIX_CTRL_ADDR(idx) \ + (HINIC_CSR_MSIX_CTRL_BASE + (idx) * HINIC_CSR_MSIX_STRIDE) + +#define HINIC_CSR_MSIX_CNT_ADDR(idx) \ + (HINIC_CSR_MSIX_CNT_BASE + (idx) * HINIC_CSR_MSIX_STRIDE) + +/* EQ registers */ +#define HINIC_AEQ_MTT_OFF_BASE_ADDR 0x200 + +#define HINIC_EQ_MTT_OFF_STRIDE 0x40 + +#define HINIC_CSR_AEQ_MTT_OFF(id) \ + (HINIC_AEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE) + +#define HINIC_CSR_EQ_PAGE_OFF_STRIDE 8 + +#define HINIC_CSR_AEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \ + (HINIC_CSR_AEQ_MTT_OFF(q_id) + \ + (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE) + +#define HINIC_CSR_AEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \ + (HINIC_CSR_AEQ_MTT_OFF(q_id) + \ + (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4) + +#define HINIC_AEQ_CTRL_0_ADDR_BASE 0xE00 +#define HINIC_AEQ_CTRL_1_ADDR_BASE 0xE04 +#define HINIC_AEQ_CONS_IDX_ADDR_BASE 0xE08 +#define HINIC_AEQ_PROD_IDX_ADDR_BASE 0xE0C + +#define HINIC_EQ_OFF_STRIDE 0x80 + +#define HINIC_CSR_AEQ_CTRL_0_ADDR(idx) \ + (HINIC_AEQ_CTRL_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_AEQ_CTRL_1_ADDR(idx) \ + (HINIC_AEQ_CTRL_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_AEQ_CONS_IDX_ADDR(idx) \ + (HINIC_AEQ_CONS_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_AEQ_PROD_IDX_ADDR(idx) \ + (HINIC_AEQ_PROD_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c index a099d20bac8c..6b03bc6ae940 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c @@ -13,17 +13,74 @@ * */ +#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include "hinic_hw_csr.h" #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" #define HINIC_EQS_WQ_NAME "hinic_eqs" +#define GET_EQ_NUM_PAGES(eq, pg_size) \ + (ALIGN((eq)->q_len * (eq)->elem_size, pg_size) / (pg_size)) + +#define GET_EQ_NUM_ELEMS_IN_PG(eq, pg_size) ((pg_size) / (eq)->elem_size) + +#define EQ_CONS_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_CONS_IDX_ADDR((eq)->q_id) +#define EQ_PROD_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_PROD_IDX_ADDR((eq)->q_id) + +#define EQ_HI_PHYS_ADDR_REG(eq, pg_num) \ + HINIC_CSR_AEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num) + +#define EQ_LO_PHYS_ADDR_REG(eq, pg_num) \ + HINIC_CSR_AEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num) + +#define GET_EQ_ELEMENT(eq, idx) \ + ((eq)->virt_addr[(idx) / (eq)->num_elem_in_pg] + \ + (((idx) & ((eq)->num_elem_in_pg - 1)) * (eq)->elem_size)) + +#define GET_AEQ_ELEM(eq, idx) ((struct hinic_aeq_elem *) \ + GET_EQ_ELEMENT(eq, idx)) + +#define GET_CURR_AEQ_ELEM(eq) GET_AEQ_ELEM(eq, (eq)->cons_idx) + +#define PAGE_IN_4K(page_size) ((page_size) >> 12) +#define EQ_SET_HW_PAGE_SIZE_VAL(eq) (ilog2(PAGE_IN_4K((eq)->page_size))) + +#define ELEMENT_SIZE_IN_32B(eq) (((eq)->elem_size) >> 5) +#define EQ_SET_HW_ELEM_SIZE_VAL(eq) (ilog2(ELEMENT_SIZE_IN_32B(eq))) + +#define EQ_MAX_PAGES 8 + +#define aeq_to_aeqs(eq) \ + container_of((eq) - (eq)->q_id, struct hinic_aeqs, aeq[0]) + +#define work_to_aeq_work(work) \ + container_of(work, struct hinic_eq_work, work) + +#define DMA_ATTR_AEQ_DEFAULT 0 + +enum eq_int_mode { + EQ_INT_MODE_ARMED, + EQ_INT_MODE_ALWAYS +}; + +enum eq_arm_state { + EQ_NOT_ARMED, + EQ_ARMED +}; + /** * hinic_aeq_register_hw_cb - register AEQ callback for specific event * @aeqs: pointer to Async eqs of the chip @@ -61,6 +118,325 @@ void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs, hwe_cb->hwe_handler = NULL; } +static u8 eq_cons_idx_checksum_set(u32 val) +{ + u8 checksum = 0; + int idx; + + for (idx = 0; idx < 32; idx += 4) + checksum ^= ((val >> idx) & 0xF); + + return (checksum & 0xF); +} + +/** + * eq_update_ci - update the HW cons idx of event queue + * @eq: the event queue to update the cons idx for + **/ +static void eq_update_ci(struct hinic_eq *eq) +{ + u32 val, addr = EQ_CONS_IDX_REG_ADDR(eq); + + /* Read Modify Write */ + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_EQ_CI_CLEAR(val, IDX) & + HINIC_EQ_CI_CLEAR(val, WRAPPED) & + HINIC_EQ_CI_CLEAR(val, INT_ARMED) & + HINIC_EQ_CI_CLEAR(val, XOR_CHKSUM); + + val |= HINIC_EQ_CI_SET(eq->cons_idx, IDX) | + HINIC_EQ_CI_SET(eq->wrapped, WRAPPED) | + HINIC_EQ_CI_SET(EQ_ARMED, INT_ARMED); + + val |= HINIC_EQ_CI_SET(eq_cons_idx_checksum_set(val), XOR_CHKSUM); + + hinic_hwif_write_reg(eq->hwif, addr, val); +} + +/** + * aeq_irq_handler - handler for the AEQ event + * @eq: the Async Event Queue that received the event + **/ +static void aeq_irq_handler(struct hinic_eq *eq) +{ + struct hinic_aeqs *aeqs = aeq_to_aeqs(eq); + struct hinic_hwif *hwif = aeqs->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_aeq_elem *aeqe_curr; + struct hinic_hw_event_cb *hwe_cb; + enum hinic_aeq_type event; + unsigned long eqe_state; + u32 aeqe_desc; + int i, size; + + for (i = 0; i < eq->q_len; i++) { + aeqe_curr = GET_CURR_AEQ_ELEM(eq); + + /* Data in HW is in Big endian Format */ + aeqe_desc = be32_to_cpu(aeqe_curr->desc); + + /* HW toggles the wrapped bit, when it adds eq element */ + if (HINIC_EQ_ELEM_DESC_GET(aeqe_desc, WRAPPED) == eq->wrapped) + break; + + event = HINIC_EQ_ELEM_DESC_GET(aeqe_desc, TYPE); + if (event >= HINIC_MAX_AEQ_EVENTS) { + dev_err(&pdev->dev, "Unknown AEQ Event %d\n", event); + return; + } + + if (!HINIC_EQ_ELEM_DESC_GET(aeqe_desc, SRC)) { + hwe_cb = &aeqs->hwe_cb[event]; + + size = HINIC_EQ_ELEM_DESC_GET(aeqe_desc, SIZE); + + eqe_state = cmpxchg(&hwe_cb->hwe_state, + HINIC_EQE_ENABLED, + HINIC_EQE_ENABLED | + HINIC_EQE_RUNNING); + if ((eqe_state == HINIC_EQE_ENABLED) && + (hwe_cb->hwe_handler)) + hwe_cb->hwe_handler(hwe_cb->handle, + aeqe_curr->data, size); + else + dev_err(&pdev->dev, "Unhandled AEQ Event %d\n", + event); + + hwe_cb->hwe_state &= ~HINIC_EQE_RUNNING; + } + + eq->cons_idx++; + + if (eq->cons_idx == eq->q_len) { + eq->cons_idx = 0; + eq->wrapped = !eq->wrapped; + } + } +} + +/** + * eq_irq_handler - handler for the EQ event + * @data: the Event Queue that received the event + **/ +static void eq_irq_handler(void *data) +{ + struct hinic_eq *eq = data; + + if (eq->type == HINIC_AEQ) + aeq_irq_handler(eq); + + eq_update_ci(eq); +} + +/** + * eq_irq_work - the work of the EQ that received the event + * @work: the work struct that is associated with the EQ + **/ +static void eq_irq_work(struct work_struct *work) +{ + struct hinic_eq_work *aeq_work = work_to_aeq_work(work); + struct hinic_eq *aeq; + + aeq = aeq_work->data; + eq_irq_handler(aeq); +} + +/** + * aeq_interrupt - aeq interrupt handler + * @irq: irq number + * @data: the Async Event Queue that collected the event + **/ +static irqreturn_t aeq_interrupt(int irq, void *data) +{ + struct hinic_eq_work *aeq_work; + struct hinic_eq *aeq = data; + struct hinic_aeqs *aeqs; + + /* clear resend timer cnt register */ + hinic_msix_attr_cnt_clear(aeq->hwif, aeq->msix_entry.entry); + + aeq_work = &aeq->aeq_work; + aeq_work->data = aeq; + + aeqs = aeq_to_aeqs(aeq); + queue_work(aeqs->workq, &aeq_work->work); + + return IRQ_HANDLED; +} + +void set_ctrl0(struct hinic_eq *eq) +{ + struct msix_entry *msix_entry = &eq->msix_entry; + enum hinic_eq_type type = eq->type; + u32 addr, val, ctrl0; + + if (type == HINIC_AEQ) { + /* RMW Ctrl0 */ + addr = HINIC_CSR_AEQ_CTRL_0_ADDR(eq->q_id); + + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_AEQ_CTRL_0_CLEAR(val, INT_IDX) & + HINIC_AEQ_CTRL_0_CLEAR(val, DMA_ATTR) & + HINIC_AEQ_CTRL_0_CLEAR(val, PCI_INTF_IDX) & + HINIC_AEQ_CTRL_0_CLEAR(val, INT_MODE); + + ctrl0 = HINIC_AEQ_CTRL_0_SET(msix_entry->entry, INT_IDX) | + HINIC_AEQ_CTRL_0_SET(DMA_ATTR_AEQ_DEFAULT, DMA_ATTR) | + HINIC_AEQ_CTRL_0_SET(HINIC_HWIF_PCI_INTF(eq->hwif), + PCI_INTF_IDX) | + HINIC_AEQ_CTRL_0_SET(EQ_INT_MODE_ARMED, INT_MODE); + + val |= ctrl0; + + hinic_hwif_write_reg(eq->hwif, addr, val); + } +} + +void set_ctrl1(struct hinic_eq *eq) +{ + enum hinic_eq_type type = eq->type; + u32 page_size_val, elem_size; + u32 addr, val, ctrl1; + + if (type == HINIC_AEQ) { + /* RMW Ctrl1 */ + addr = HINIC_CSR_AEQ_CTRL_1_ADDR(eq->q_id); + + page_size_val = EQ_SET_HW_PAGE_SIZE_VAL(eq); + elem_size = EQ_SET_HW_ELEM_SIZE_VAL(eq); + + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_AEQ_CTRL_1_CLEAR(val, LEN) & + HINIC_AEQ_CTRL_1_CLEAR(val, ELEM_SIZE) & + HINIC_AEQ_CTRL_1_CLEAR(val, PAGE_SIZE); + + ctrl1 = HINIC_AEQ_CTRL_1_SET(eq->q_len, LEN) | + HINIC_AEQ_CTRL_1_SET(elem_size, ELEM_SIZE) | + HINIC_AEQ_CTRL_1_SET(page_size_val, PAGE_SIZE); + + val |= ctrl1; + + hinic_hwif_write_reg(eq->hwif, addr, val); + } +} + +/** + * set_eq_ctrls - setting eq's ctrl registers + * @eq: the Event Queue for setting + **/ +static void set_eq_ctrls(struct hinic_eq *eq) +{ + set_ctrl0(eq); + set_ctrl1(eq); +} + +/** + * aeq_elements_init - initialize all the elements in the aeq + * @eq: the Async Event Queue + * @init_val: value to initialize the elements with it + **/ +static void aeq_elements_init(struct hinic_eq *eq, u32 init_val) +{ + struct hinic_aeq_elem *aeqe; + int i; + + for (i = 0; i < eq->q_len; i++) { + aeqe = GET_AEQ_ELEM(eq, i); + aeqe->desc = cpu_to_be32(init_val); + } + + wmb(); /* Write the initilzation values */ +} + +/** + * alloc_eq_pages - allocate the pages for the queue + * @eq: the event queue + * + * Return 0 - Success, Negative - Failure + **/ +static int alloc_eq_pages(struct hinic_eq *eq) +{ + struct hinic_hwif *hwif = eq->hwif; + struct pci_dev *pdev = hwif->pdev; + u32 init_val, addr, val; + size_t addr_size; + int err, pg; + + addr_size = eq->num_pages * sizeof(*eq->dma_addr); + eq->dma_addr = devm_kzalloc(&pdev->dev, addr_size, GFP_KERNEL); + if (!eq->dma_addr) + return -ENOMEM; + + addr_size = eq->num_pages * sizeof(*eq->virt_addr); + eq->virt_addr = devm_kzalloc(&pdev->dev, addr_size, GFP_KERNEL); + if (!eq->virt_addr) { + err = -ENOMEM; + goto err_virt_addr_alloc; + } + + for (pg = 0; pg < eq->num_pages; pg++) { + eq->virt_addr[pg] = dma_zalloc_coherent(&pdev->dev, + eq->page_size, + &eq->dma_addr[pg], + GFP_KERNEL); + if (!eq->virt_addr[pg]) { + err = -ENOMEM; + goto err_dma_alloc; + } + + addr = EQ_HI_PHYS_ADDR_REG(eq, pg); + val = upper_32_bits(eq->dma_addr[pg]); + + hinic_hwif_write_reg(hwif, addr, val); + + addr = EQ_LO_PHYS_ADDR_REG(eq, pg); + val = lower_32_bits(eq->dma_addr[pg]); + + hinic_hwif_write_reg(hwif, addr, val); + } + + init_val = HINIC_EQ_ELEM_DESC_SET(eq->wrapped, WRAPPED); + + if (eq->type == HINIC_AEQ) + aeq_elements_init(eq, init_val); + + return 0; + +err_dma_alloc: + while (--pg >= 0) + dma_free_coherent(&pdev->dev, eq->page_size, + eq->virt_addr[pg], + eq->dma_addr[pg]); + + devm_kfree(&pdev->dev, eq->virt_addr); + +err_virt_addr_alloc: + devm_kfree(&pdev->dev, eq->dma_addr); + return err; +} + +/** + * free_eq_pages - free the pages of the queue + * @eq: the Event Queue + **/ +static void free_eq_pages(struct hinic_eq *eq) +{ + struct hinic_hwif *hwif = eq->hwif; + struct pci_dev *pdev = hwif->pdev; + int pg; + + for (pg = 0; pg < eq->num_pages; pg++) + dma_free_coherent(&pdev->dev, eq->page_size, + eq->virt_addr[pg], + eq->dma_addr[pg]); + + devm_kfree(&pdev->dev, eq->virt_addr); + devm_kfree(&pdev->dev, eq->dma_addr); +} + /** * init_eq - initialize Event Queue * @eq: the event queue @@ -77,8 +453,81 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif, enum hinic_eq_type type, int q_id, u32 q_len, u32 page_size, struct msix_entry entry) { - /* should be implemented */ + struct pci_dev *pdev = hwif->pdev; + int err; + + eq->hwif = hwif; + eq->type = type; + eq->q_id = q_id; + eq->q_len = q_len; + eq->page_size = page_size; + + /* Clear PI and CI, also clear the ARM bit */ + hinic_hwif_write_reg(eq->hwif, EQ_CONS_IDX_REG_ADDR(eq), 0); + hinic_hwif_write_reg(eq->hwif, EQ_PROD_IDX_REG_ADDR(eq), 0); + + eq->cons_idx = 0; + eq->wrapped = 0; + + if (type == HINIC_AEQ) { + eq->elem_size = HINIC_AEQE_SIZE; + } else { + dev_err(&pdev->dev, "Invalid EQ type\n"); + return -EINVAL; + } + + eq->num_pages = GET_EQ_NUM_PAGES(eq, page_size); + eq->num_elem_in_pg = GET_EQ_NUM_ELEMS_IN_PG(eq, page_size); + + eq->msix_entry = entry; + + if (eq->num_elem_in_pg & (eq->num_elem_in_pg - 1)) { + dev_err(&pdev->dev, "num elements in eq page != power of 2\n"); + return -EINVAL; + } + + if (eq->num_pages > EQ_MAX_PAGES) { + dev_err(&pdev->dev, "too many pages for eq\n"); + return -EINVAL; + } + + set_eq_ctrls(eq); + eq_update_ci(eq); + + err = alloc_eq_pages(eq); + if (err) { + dev_err(&pdev->dev, "Failed to allocate pages for eq\n"); + return err; + } + + if (type == HINIC_AEQ) { + struct hinic_eq_work *aeq_work = &eq->aeq_work; + + INIT_WORK(&aeq_work->work, eq_irq_work); + } + + /* set the attributes of the msix entry */ + hinic_msix_attr_set(eq->hwif, eq->msix_entry.entry, + HINIC_EQ_MSIX_PENDING_LIMIT_DEFAULT, + HINIC_EQ_MSIX_COALESC_TIMER_DEFAULT, + HINIC_EQ_MSIX_LLI_TIMER_DEFAULT, + HINIC_EQ_MSIX_LLI_CREDIT_LIMIT_DEFAULT, + HINIC_EQ_MSIX_RESEND_TIMER_DEFAULT); + + if (type == HINIC_AEQ) + err = request_irq(entry.vector, aeq_interrupt, 0, + "hinic_aeq", eq); + + if (err) { + dev_err(&pdev->dev, "Failed to request irq for the EQ\n"); + goto err_req_irq; + } + return 0; + +err_req_irq: + free_eq_pages(eq); + return err; } /** @@ -87,7 +536,17 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif, **/ static void remove_eq(struct hinic_eq *eq) { - /* should be implemented */ + struct msix_entry *entry = &eq->msix_entry; + + free_irq(entry->vector, eq); + + if (eq->type == HINIC_AEQ) { + struct hinic_eq_work *aeq_work = &eq->aeq_work; + + cancel_work_sync(&aeq_work->work); + } + + free_eq_pages(eq); } /** diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h index 1580127b26c4..7f50b2f6cc22 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h @@ -24,8 +24,84 @@ #include "hinic_hw_if.h" +#define HINIC_AEQ_CTRL_0_INT_IDX_SHIFT 0 +#define HINIC_AEQ_CTRL_0_DMA_ATTR_SHIFT 12 +#define HINIC_AEQ_CTRL_0_PCI_INTF_IDX_SHIFT 20 +#define HINIC_AEQ_CTRL_0_INT_MODE_SHIFT 31 + +#define HINIC_AEQ_CTRL_0_INT_IDX_MASK 0x3FF +#define HINIC_AEQ_CTRL_0_DMA_ATTR_MASK 0x3F +#define HINIC_AEQ_CTRL_0_PCI_INTF_IDX_MASK 0x3 +#define HINIC_AEQ_CTRL_0_INT_MODE_MASK 0x1 + +#define HINIC_AEQ_CTRL_0_SET(val, member) \ + (((u32)(val) & HINIC_AEQ_CTRL_0_##member##_MASK) << \ + HINIC_AEQ_CTRL_0_##member##_SHIFT) + +#define HINIC_AEQ_CTRL_0_CLEAR(val, member) \ + ((val) & (~(HINIC_AEQ_CTRL_0_##member##_MASK \ + << HINIC_AEQ_CTRL_0_##member##_SHIFT))) + +#define HINIC_AEQ_CTRL_1_LEN_SHIFT 0 +#define HINIC_AEQ_CTRL_1_ELEM_SIZE_SHIFT 24 +#define HINIC_AEQ_CTRL_1_PAGE_SIZE_SHIFT 28 + +#define HINIC_AEQ_CTRL_1_LEN_MASK 0x1FFFFF +#define HINIC_AEQ_CTRL_1_ELEM_SIZE_MASK 0x3 +#define HINIC_AEQ_CTRL_1_PAGE_SIZE_MASK 0xF + +#define HINIC_AEQ_CTRL_1_SET(val, member) \ + (((u32)(val) & HINIC_AEQ_CTRL_1_##member##_MASK) << \ + HINIC_AEQ_CTRL_1_##member##_SHIFT) + +#define HINIC_AEQ_CTRL_1_CLEAR(val, member) \ + ((val) & (~(HINIC_AEQ_CTRL_1_##member##_MASK \ + << HINIC_AEQ_CTRL_1_##member##_SHIFT))) + +#define HINIC_EQ_ELEM_DESC_TYPE_SHIFT 0 +#define HINIC_EQ_ELEM_DESC_SRC_SHIFT 7 +#define HINIC_EQ_ELEM_DESC_SIZE_SHIFT 8 +#define HINIC_EQ_ELEM_DESC_WRAPPED_SHIFT 31 + +#define HINIC_EQ_ELEM_DESC_TYPE_MASK 0x7F +#define HINIC_EQ_ELEM_DESC_SRC_MASK 0x1 +#define HINIC_EQ_ELEM_DESC_SIZE_MASK 0xFF +#define HINIC_EQ_ELEM_DESC_WRAPPED_MASK 0x1 + +#define HINIC_EQ_ELEM_DESC_SET(val, member) \ + (((u32)(val) & HINIC_EQ_ELEM_DESC_##member##_MASK) << \ + HINIC_EQ_ELEM_DESC_##member##_SHIFT) + +#define HINIC_EQ_ELEM_DESC_GET(val, member) \ + (((val) >> HINIC_EQ_ELEM_DESC_##member##_SHIFT) & \ + HINIC_EQ_ELEM_DESC_##member##_MASK) + +#define HINIC_EQ_CI_IDX_SHIFT 0 +#define HINIC_EQ_CI_WRAPPED_SHIFT 20 +#define HINIC_EQ_CI_XOR_CHKSUM_SHIFT 24 +#define HINIC_EQ_CI_INT_ARMED_SHIFT 31 + +#define HINIC_EQ_CI_IDX_MASK 0xFFFFF +#define HINIC_EQ_CI_WRAPPED_MASK 0x1 +#define HINIC_EQ_CI_XOR_CHKSUM_MASK 0xF +#define HINIC_EQ_CI_INT_ARMED_MASK 0x1 + +#define HINIC_EQ_CI_SET(val, member) \ + (((u32)(val) & HINIC_EQ_CI_##member##_MASK) << \ + HINIC_EQ_CI_##member##_SHIFT) + +#define HINIC_EQ_CI_CLEAR(val, member) \ + ((val) & (~(HINIC_EQ_CI_##member##_MASK \ + << HINIC_EQ_CI_##member##_SHIFT))) + #define HINIC_MAX_AEQS 4 +#define HINIC_AEQE_SIZE 64 + +#define HINIC_AEQE_DESC_SIZE 4 +#define HINIC_AEQE_DATA_SIZE \ + (HINIC_AEQE_SIZE - HINIC_AEQE_DESC_SIZE) + #define HINIC_DEFAULT_AEQ_LEN 64 #define HINIC_EQ_PAGE_SIZE SZ_4K @@ -45,6 +121,11 @@ enum hinic_eqe_state { HINIC_EQE_RUNNING = BIT(1), }; +struct hinic_aeq_elem { + u8 data[HINIC_AEQE_DATA_SIZE]; + u32 desc; +}; + struct hinic_eq_work { struct work_struct work; void *data; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c index edf184242172..0cfada793a7a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c @@ -25,6 +25,96 @@ #define PCIE_ATTR_ENTRY 0 +#define VALID_MSIX_IDX(attr, msix_index) ((msix_index) < (attr)->num_irqs) + +/** + * hinic_msix_attr_set - set message attribute for msix entry + * @hwif: the HW interface of a pci function device + * @msix_index: msix_index + * @pending_limit: the maximum pending interrupt events (unit 8) + * @coalesc_timer: coalesc period for interrupt (unit 8 us) + * @lli_timer: replenishing period for low latency credit (unit 8 us) + * @lli_credit_limit: maximum credits for low latency msix messages (unit 8) + * @resend_timer: maximum wait for resending msix (unit coalesc period) + * + * Return 0 - Success, negative - Failure + **/ +int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, + u8 pending_limit, u8 coalesc_timer, + u8 lli_timer, u8 lli_credit_limit, + u8 resend_timer) +{ + u32 msix_ctrl, addr; + + if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) + return -EINVAL; + + msix_ctrl = HINIC_MSIX_ATTR_SET(pending_limit, PENDING_LIMIT) | + HINIC_MSIX_ATTR_SET(coalesc_timer, COALESC_TIMER) | + HINIC_MSIX_ATTR_SET(lli_timer, LLI_TIMER) | + HINIC_MSIX_ATTR_SET(lli_credit_limit, LLI_CREDIT) | + HINIC_MSIX_ATTR_SET(resend_timer, RESEND_TIMER); + + addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index); + + hinic_hwif_write_reg(hwif, addr, msix_ctrl); + return 0; +} + +/** + * hinic_msix_attr_get - get message attribute of msix entry + * @hwif: the HW interface of a pci function device + * @msix_index: msix_index + * @pending_limit: the maximum pending interrupt events (unit 8) + * @coalesc_timer: coalesc period for interrupt (unit 8 us) + * @lli_timer: replenishing period for low latency credit (unit 8 us) + * @lli_credit_limit: maximum credits for low latency msix messages (unit 8) + * @resend_timer: maximum wait for resending msix (unit coalesc period) + * + * Return 0 - Success, negative - Failure + **/ +int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index, + u8 *pending_limit, u8 *coalesc_timer, + u8 *lli_timer, u8 *lli_credit_limit, + u8 *resend_timer) +{ + u32 addr, val; + + if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) + return -EINVAL; + + addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index); + val = hinic_hwif_read_reg(hwif, addr); + + *pending_limit = HINIC_MSIX_ATTR_GET(val, PENDING_LIMIT); + *coalesc_timer = HINIC_MSIX_ATTR_GET(val, COALESC_TIMER); + *lli_timer = HINIC_MSIX_ATTR_GET(val, LLI_TIMER); + *lli_credit_limit = HINIC_MSIX_ATTR_GET(val, LLI_CREDIT); + *resend_timer = HINIC_MSIX_ATTR_GET(val, RESEND_TIMER); + return 0; +} + +/** + * hinic_msix_attr_cnt_clear - clear message attribute counters for msix entry + * @hwif: the HW interface of a pci function device + * @msix_index: msix_index + * + * Return 0 - Success, negative - Failure + **/ +int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index) +{ + u32 msix_ctrl, addr; + + if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) + return -EINVAL; + + msix_ctrl = HINIC_MSIX_CNT_SET(1, RESEND_TIMER); + addr = HINIC_CSR_MSIX_CNT_ADDR(msix_index); + + hinic_hwif_write_reg(hwif, addr, msix_ctrl); + return 0; +} + /** * hwif_ready - test if the HW is ready for use * @hwif: the HW interface of a pci function device diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index 98623d69f2ba..707a04614d5b 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -88,6 +88,34 @@ ((val) & (~(HINIC_PPF_ELECTION_##member##_MASK \ << HINIC_PPF_ELECTION_##member##_SHIFT))) +#define HINIC_MSIX_PENDING_LIMIT_SHIFT 0 +#define HINIC_MSIX_COALESC_TIMER_SHIFT 8 +#define HINIC_MSIX_LLI_TIMER_SHIFT 16 +#define HINIC_MSIX_LLI_CREDIT_SHIFT 24 +#define HINIC_MSIX_RESEND_TIMER_SHIFT 29 + +#define HINIC_MSIX_PENDING_LIMIT_MASK 0xFF +#define HINIC_MSIX_COALESC_TIMER_MASK 0xFF +#define HINIC_MSIX_LLI_TIMER_MASK 0xFF +#define HINIC_MSIX_LLI_CREDIT_MASK 0x1F +#define HINIC_MSIX_RESEND_TIMER_MASK 0x7 + +#define HINIC_MSIX_ATTR_SET(val, member) \ + (((u32)(val) & HINIC_MSIX_##member##_MASK) << \ + HINIC_MSIX_##member##_SHIFT) + +#define HINIC_MSIX_ATTR_GET(val, member) \ + (((val) >> HINIC_MSIX_##member##_SHIFT) & \ + HINIC_MSIX_##member##_MASK) + +#define HINIC_MSIX_CNT_RESEND_TIMER_SHIFT 29 + +#define HINIC_MSIX_CNT_RESEND_TIMER_MASK 0x1 + +#define HINIC_MSIX_CNT_SET(val, member) \ + (((u32)(val) & HINIC_MSIX_CNT_##member##_MASK) << \ + HINIC_MSIX_CNT_##member##_SHIFT) + #define HINIC_HWIF_NUM_AEQS(hwif) ((hwif)->attr.num_aeqs) #define HINIC_HWIF_NUM_CEQS(hwif) ((hwif)->attr.num_ceqs) #define HINIC_HWIF_NUM_IRQS(hwif) ((hwif)->attr.num_irqs) @@ -105,6 +133,12 @@ #define HINIC_PCIE_AT_DISABLE 0 #define HINIC_PCIE_PH_DISABLE 0 +#define HINIC_EQ_MSIX_PENDING_LIMIT_DEFAULT 0 /* Disabled */ +#define HINIC_EQ_MSIX_COALESC_TIMER_DEFAULT 0xFF /* max */ +#define HINIC_EQ_MSIX_LLI_TIMER_DEFAULT 0 /* Disabled */ +#define HINIC_EQ_MSIX_LLI_CREDIT_LIMIT_DEFAULT 0 /* Disabled */ +#define HINIC_EQ_MSIX_RESEND_TIMER_DEFAULT 7 /* max */ + enum hinic_pcie_nosnoop { HINIC_PCIE_SNOOP = 0, HINIC_PCIE_NO_SNOOP = 1, @@ -166,6 +200,18 @@ static inline void hinic_hwif_write_reg(struct hinic_hwif *hwif, u32 reg, writel(cpu_to_be32(val), hwif->cfg_regs_bar + reg); } +int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, + u8 pending_limit, u8 coalesc_timer, + u8 lli_timer_cfg, u8 lli_credit_limit, + u8 resend_timer); + +int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index, + u8 *pending_limit, u8 *coalesc_timer_cfg, + u8 *lli_timer, u8 *lli_credit_limit, + u8 *resend_timer); + +int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index); + int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev); void hinic_free_hwif(struct hinic_hwif *hwif); -- cgit v1.2.3 From 25a3ba610609375751aa991f4a4054aca1b7fc5e Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:54 +0800 Subject: net-next/hinic: Add port management commands Add the port management commands that are sent as management messages. The port management commands are used for netdev operations. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/Makefile | 4 +- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 4 + drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 30 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 29 +++ drivers/net/ethernet/huawei/hinic/hinic_main.c | 195 +++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_port.c | 224 +++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_port.h | 68 +++++++ 7 files changed, 551 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_port.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_port.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index beba90a6dc96..dbb1b9dbaa59 100644 --- a/drivers/net/ethernet/huawei/hinic/Makefile +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_HINIC) += hinic.o -hinic-y := hinic_main.o hinic_hw_dev.o hinic_hw_mgmt.o hinic_hw_api_cmd.o \ - hinic_hw_eqs.o hinic_hw_if.o +hinic-y := hinic_main.o hinic_port.o hinic_hw_dev.o hinic_hw_mgmt.o \ + hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 6c2c896015a5..e54a45cd5bbe 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -18,6 +18,7 @@ #include #include +#include #include "hinic_hw_dev.h" @@ -28,6 +29,9 @@ struct hinic_dev { struct hinic_hwdev *hwdev; u32 msg_enable; + + struct semaphore mgmt_lock; + unsigned long *vlan_bitmap; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index d430e60192af..6bb6c3313fc3 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -199,6 +199,36 @@ static void disable_msix(struct hinic_hwdev *hwdev) pci_disable_msix(pdev); } +/** + * hinic_port_msg_cmd - send port msg to mgmt + * @hwdev: the NIC HW device + * @cmd: the port command + * @buf_in: input buffer + * @in_size: input size + * @buf_out: output buffer + * @out_size: returned output size + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, + void *buf_in, u16 in_size, void *buf_out, u16 *out_size) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return -EINVAL; + } + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC, cmd, + buf_in, in_size, buf_out, out_size, + HINIC_MGMT_MSG_SYNC); +} + /** * init_pfhwdev - Initialize the extended components of PF * @pfhwdev: the HW device for PF diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index feb601388e59..ee9e76ada303 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -30,6 +30,31 @@ struct hinic_cap { u16 num_qps; }; +enum hinic_port_cmd { + HINIC_PORT_CMD_CHANGE_MTU = 2, + + HINIC_PORT_CMD_ADD_VLAN = 3, + HINIC_PORT_CMD_DEL_VLAN = 4, + + HINIC_PORT_CMD_SET_MAC = 9, + HINIC_PORT_CMD_GET_MAC = 10, + HINIC_PORT_CMD_DEL_MAC = 11, + + HINIC_PORT_CMD_SET_RX_MODE = 12, + + HINIC_PORT_CMD_GET_LINK_STATE = 24, + + HINIC_PORT_CMD_SET_PORT_STATE = 41, + + HINIC_PORT_CMD_FWCTXT_INIT = 69, + + HINIC_PORT_CMD_SET_FUNC_STATE = 93, + + HINIC_PORT_CMD_GET_GLOBAL_QPN = 102, + + HINIC_PORT_CMD_GET_CAP = 170, +}; + struct hinic_hwdev { struct hinic_hwif *hwif; struct msix_entry *msix_entries; @@ -45,6 +70,10 @@ struct hinic_pfhwdev { struct hinic_pf_to_mgmt pf_to_mgmt; }; +int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, + void *buf_in, u16 in_size, void *buf_out, + u16 *out_size); + struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev); void hinic_free_hwdev(struct hinic_hwdev *hwdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 1d7aed07b25f..772e4e6e68af 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -13,6 +13,7 @@ * */ +#include #include #include #include @@ -20,9 +21,16 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include "hinic_hw_dev.h" +#include "hinic_port.h" #include "hinic_dev.h" MODULE_AUTHOR("Huawei Technologies CO., Ltd"); @@ -35,10 +43,163 @@ MODULE_LICENSE("GPL"); NETIF_MSG_IFUP | \ NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR) +#define VLAN_BITMAP_SIZE(nic_dev) (ALIGN(VLAN_N_VID, 8) / 8) + +static int hinic_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int err; + + netif_info(nic_dev, drv, netdev, "set_mtu = %d\n", new_mtu); + + err = hinic_port_set_mtu(nic_dev, new_mtu); + if (err) + netif_err(nic_dev, drv, netdev, "Failed to set port mtu\n"); + else + netdev->mtu = new_mtu; + + return err; +} + +/** + * change_mac_addr - change the main mac address of network device + * @netdev: network device + * @addr: mac address to set + * + * Return 0 - Success, negative - Failure + **/ +static int change_mac_addr(struct net_device *netdev, const u8 *addr) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u16 vid = 0; + int err; + + if (!is_valid_ether_addr(addr)) + return -EADDRNOTAVAIL; + + netif_info(nic_dev, drv, netdev, "change mac addr = %02x %02x %02x %02x %02x %02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + down(&nic_dev->mgmt_lock); + + do { + err = hinic_port_del_mac(nic_dev, netdev->dev_addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to delete mac\n"); + break; + } + + err = hinic_port_add_mac(nic_dev, addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to add mac\n"); + break; + } + + vid = find_next_bit(nic_dev->vlan_bitmap, VLAN_N_VID, vid + 1); + } while (vid != VLAN_N_VID); + + up(&nic_dev->mgmt_lock); + return err; +} + +static int hinic_set_mac_addr(struct net_device *netdev, void *addr) +{ + unsigned char new_mac[ETH_ALEN]; + struct sockaddr *saddr = addr; + int err; + + memcpy(new_mac, saddr->sa_data, ETH_ALEN); + + err = change_mac_addr(netdev, new_mac); + if (!err) + memcpy(netdev->dev_addr, new_mac, ETH_ALEN); + + return err; +} + +static int hinic_vlan_rx_add_vid(struct net_device *netdev, + __always_unused __be16 proto, u16 vid) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int ret, err; + + netif_info(nic_dev, drv, netdev, "add vid = %d\n", vid); + + down(&nic_dev->mgmt_lock); + + err = hinic_port_add_vlan(nic_dev, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to add vlan\n"); + goto err_vlan_add; + } + + err = hinic_port_add_mac(nic_dev, netdev->dev_addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to set mac\n"); + goto err_add_mac; + } + + bitmap_set(nic_dev->vlan_bitmap, vid, 1); + + up(&nic_dev->mgmt_lock); + return 0; + +err_add_mac: + ret = hinic_port_del_vlan(nic_dev, vid); + if (ret) + netif_err(nic_dev, drv, netdev, + "Failed to revert by removing vlan\n"); + +err_vlan_add: + up(&nic_dev->mgmt_lock); + return err; +} + +static int hinic_vlan_rx_kill_vid(struct net_device *netdev, + __always_unused __be16 proto, u16 vid) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int err; + + netif_info(nic_dev, drv, netdev, "remove vid = %d\n", vid); + + down(&nic_dev->mgmt_lock); + + err = hinic_port_del_vlan(nic_dev, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to delete vlan\n"); + goto err_del_vlan; + } + + bitmap_clear(nic_dev->vlan_bitmap, vid, 1); + + up(&nic_dev->mgmt_lock); + return 0; + +err_del_vlan: + up(&nic_dev->mgmt_lock); + return err; +} + static const struct net_device_ops hinic_netdev_ops = { - /* Operations are empty, should be filled */ + .ndo_change_mtu = hinic_change_mtu, + .ndo_set_mac_address = hinic_set_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_vlan_rx_add_vid = hinic_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = hinic_vlan_rx_kill_vid, + /* more operations should be filled */ }; +static void netdev_features_init(struct net_device *netdev) +{ + netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA; + + netdev->vlan_features = netdev->hw_features; + + netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; +} + /** * nic_dev_init - Initialize the NIC device * @pdev: the NIC pci device @@ -79,8 +240,36 @@ static int nic_dev_init(struct pci_dev *pdev) nic_dev->hwdev = hwdev; nic_dev->msg_enable = MSG_ENABLE_DEFAULT; + sema_init(&nic_dev->mgmt_lock, 1); + + nic_dev->vlan_bitmap = devm_kzalloc(&pdev->dev, + VLAN_BITMAP_SIZE(nic_dev), + GFP_KERNEL); + if (!nic_dev->vlan_bitmap) { + err = -ENOMEM; + goto err_vlan_bitmap; + } + pci_set_drvdata(pdev, netdev); + err = hinic_port_get_mac(nic_dev, netdev->dev_addr); + if (err) + dev_warn(&pdev->dev, "Failed to get mac address\n"); + + err = hinic_port_add_mac(nic_dev, netdev->dev_addr, 0); + if (err) { + dev_err(&pdev->dev, "Failed to add mac\n"); + goto err_add_mac; + } + + err = hinic_port_set_mtu(nic_dev, netdev->mtu); + if (err) { + dev_err(&pdev->dev, "Failed to set mtu\n"); + goto err_set_mtu; + } + + netdev_features_init(netdev); + netif_carrier_off(netdev); err = register_netdev(netdev); @@ -92,7 +281,11 @@ static int nic_dev_init(struct pci_dev *pdev) return 0; err_reg_netdev: +err_set_mtu: +err_add_mac: pci_set_drvdata(pdev, NULL); + +err_vlan_bitmap: free_netdev(netdev); err_alloc_etherdev: diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c new file mode 100644 index 000000000000..5b249e8121bd --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -0,0 +1,224 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_dev.h" +#include "hinic_port.h" +#include "hinic_dev.h" + +#define HINIC_MIN_MTU_SIZE 256 +#define HINIC_MAX_JUMBO_FRAME_SIZE 15872 + +enum mac_op { + MAC_DEL, + MAC_SET, +}; + +/** + * change_mac - change(add or delete) mac address + * @nic_dev: nic device + * @addr: mac address + * @vlan_id: vlan number to set with the mac + * @op: add or delete the mac + * + * Return 0 - Success, negative - Failure + **/ +static int change_mac(struct hinic_dev *nic_dev, const u8 *addr, + u16 vlan_id, enum mac_op op) +{ + struct net_device *netdev = nic_dev->netdev; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_mac_cmd port_mac_cmd; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + enum hinic_port_cmd cmd; + u16 out_size; + int err; + + if (vlan_id >= VLAN_N_VID) { + netif_err(nic_dev, drv, netdev, "Invalid VLAN number\n"); + return -EINVAL; + } + + if (op == MAC_SET) + cmd = HINIC_PORT_CMD_SET_MAC; + else + cmd = HINIC_PORT_CMD_DEL_MAC; + + port_mac_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + port_mac_cmd.vlan_id = vlan_id; + memcpy(port_mac_cmd.mac, addr, ETH_ALEN); + + err = hinic_port_msg_cmd(hwdev, cmd, &port_mac_cmd, + sizeof(port_mac_cmd), + &port_mac_cmd, &out_size); + if (err || (out_size != sizeof(port_mac_cmd)) || port_mac_cmd.status) { + dev_err(&pdev->dev, "Failed to change MAC, ret = %d\n", + port_mac_cmd.status); + return -EFAULT; + } + + return 0; +} + +/** + * hinic_port_add_mac - add mac address + * @nic_dev: nic device + * @addr: mac address + * @vlan_id: vlan number to set with the mac + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_add_mac(struct hinic_dev *nic_dev, + const u8 *addr, u16 vlan_id) +{ + return change_mac(nic_dev, addr, vlan_id, MAC_SET); +} + +/** + * hinic_port_del_mac - remove mac address + * @nic_dev: nic device + * @addr: mac address + * @vlan_id: vlan number that is connected to the mac + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_del_mac(struct hinic_dev *nic_dev, const u8 *addr, + u16 vlan_id) +{ + return change_mac(nic_dev, addr, vlan_id, MAC_DEL); +} + +/** + * hinic_port_get_mac - get the mac address of the nic device + * @nic_dev: nic device + * @addr: returned mac address + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_mac_cmd port_mac_cmd; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + port_mac_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_MAC, + &port_mac_cmd, sizeof(port_mac_cmd), + &port_mac_cmd, &out_size); + if (err || (out_size != sizeof(port_mac_cmd)) || port_mac_cmd.status) { + dev_err(&pdev->dev, "Failed to get mac, ret = %d\n", + port_mac_cmd.status); + return -EFAULT; + } + + memcpy(addr, port_mac_cmd.mac, ETH_ALEN); + return 0; +} + +/** + * hinic_port_set_mtu - set mtu + * @nic_dev: nic device + * @new_mtu: new mtu + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu) +{ + struct net_device *netdev = nic_dev->netdev; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_mtu_cmd port_mtu_cmd; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + int err, max_frame; + u16 out_size; + + if (new_mtu < HINIC_MIN_MTU_SIZE) { + netif_err(nic_dev, drv, netdev, "mtu < MIN MTU size"); + return -EINVAL; + } + + max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; + if (max_frame > HINIC_MAX_JUMBO_FRAME_SIZE) { + netif_err(nic_dev, drv, netdev, "mtu > MAX MTU size"); + return -EINVAL; + } + + port_mtu_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + port_mtu_cmd.mtu = new_mtu; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_CHANGE_MTU, + &port_mtu_cmd, sizeof(port_mtu_cmd), + &port_mtu_cmd, &out_size); + if (err || (out_size != sizeof(port_mtu_cmd)) || port_mtu_cmd.status) { + dev_err(&pdev->dev, "Failed to set mtu, ret = %d\n", + port_mtu_cmd.status); + return -EFAULT; + } + + return 0; +} + +/** + * hinic_port_add_vlan - add vlan to the nic device + * @nic_dev: nic device + * @vlan_id: the vlan number to add + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_add_vlan(struct hinic_dev *nic_dev, u16 vlan_id) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_vlan_cmd port_vlan_cmd; + + port_vlan_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + port_vlan_cmd.vlan_id = vlan_id; + + return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_ADD_VLAN, + &port_vlan_cmd, sizeof(port_vlan_cmd), + NULL, NULL); +} + +/** + * hinic_port_del_vlan - delete vlan from the nic device + * @nic_dev: nic device + * @vlan_id: the vlan number to delete + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_vlan_cmd port_vlan_cmd; + + port_vlan_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + port_vlan_cmd.vlan_id = vlan_id; + + return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_DEL_VLAN, + &port_vlan_cmd, sizeof(port_vlan_cmd), + NULL, NULL); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h new file mode 100644 index 000000000000..4cafb9448ea8 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -0,0 +1,68 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_PORT_H +#define HINIC_PORT_H + +#include +#include + +#include "hinic_dev.h" + +struct hinic_port_mac_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 vlan_id; + u16 rsvd1; + unsigned char mac[ETH_ALEN]; +}; + +struct hinic_port_mtu_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rsvd1; + u32 mtu; +}; + +struct hinic_port_vlan_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 vlan_id; +}; + +int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, + u16 vlan_id); + +int hinic_port_del_mac(struct hinic_dev *nic_dev, const u8 *addr, + u16 vlan_id); + +int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr); + +int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu); + +int hinic_port_add_vlan(struct hinic_dev *nic_dev, u16 vlan_id); + +int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id); + +#endif -- cgit v1.2.3 From c4d06d2d208a6cd86bc76e52473cc853f83cad03 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:55 +0800 Subject: net-next/hinic: Add Rx mode and link event handler Add port management message for setting Rx mode in the card, used for rx_mode netdev operation. The link event handler is used for getting a notification about the link state. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 17 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 2 + drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 122 ++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 37 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.c | 17 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 17 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c | 64 ++++- drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 28 +++ drivers/net/ethernet/huawei/hinic/hinic_main.c | 284 ++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_port.c | 92 +++++++ drivers/net/ethernet/huawei/hinic/hinic_port.h | 66 +++++ 11 files changed, 745 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index e54a45cd5bbe..5c5b4e974c56 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -19,19 +19,36 @@ #include #include #include +#include +#include #include "hinic_hw_dev.h" #define HINIC_DRV_NAME "hinic" +enum hinic_flags { + HINIC_LINK_UP = BIT(0), + HINIC_INTF_UP = BIT(1), +}; + +struct hinic_rx_mode_work { + struct work_struct work; + u32 rx_mode; +}; + struct hinic_dev { struct net_device *netdev; struct hinic_hwdev *hwdev; u32 msg_enable; + unsigned int flags; + struct semaphore mgmt_lock; unsigned long *vlan_bitmap; + + struct hinic_rx_mode_work rx_mode_work; + struct workqueue_struct *workq; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h index 52eb89c7df9a..1f57301ce527 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -20,6 +20,8 @@ #define HINIC_CSR_FUNC_ATTR0_ADDR 0x0 #define HINIC_CSR_FUNC_ATTR1_ADDR 0x4 +#define HINIC_CSR_FUNC_ATTR5_ADDR 0x14 + #define HINIC_DMA_ATTR_BASE 0xC80 #define HINIC_ELECTION_BASE 0x4200 diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index 6bb6c3313fc3..c3122b030740 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -229,6 +229,118 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, HINIC_MGMT_MSG_SYNC); } +/** + * hinic_hwdev_cb_register - register callback handler for MGMT events + * @hwdev: the NIC HW device + * @cmd: the mgmt event + * @handle: private data for the handler + * @handler: event handler + **/ +void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev, + enum hinic_mgmt_msg_cmd cmd, void *handle, + void (*handler)(void *handle, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size)) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + struct hinic_nic_cb *nic_cb; + u8 cmd_cb; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return; + } + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE; + nic_cb = &pfhwdev->nic_cb[cmd_cb]; + + nic_cb->handler = handler; + nic_cb->handle = handle; + nic_cb->cb_state = HINIC_CB_ENABLED; +} + +/** + * hinic_hwdev_cb_unregister - unregister callback handler for MGMT events + * @hwdev: the NIC HW device + * @cmd: the mgmt event + **/ +void hinic_hwdev_cb_unregister(struct hinic_hwdev *hwdev, + enum hinic_mgmt_msg_cmd cmd) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + struct hinic_nic_cb *nic_cb; + u8 cmd_cb; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return; + } + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE; + nic_cb = &pfhwdev->nic_cb[cmd_cb]; + + nic_cb->cb_state &= ~HINIC_CB_ENABLED; + + while (nic_cb->cb_state & HINIC_CB_RUNNING) + schedule(); + + nic_cb->handler = NULL; +} + +/** + * nic_mgmt_msg_handler - nic mgmt event handler + * @handle: private data for the handler + * @buf_in: input buffer + * @in_size: input size + * @buf_out: output buffer + * @out_size: returned output size + **/ +static void nic_mgmt_msg_handler(void *handle, u8 cmd, void *buf_in, + u16 in_size, void *buf_out, u16 *out_size) +{ + struct hinic_pfhwdev *pfhwdev = handle; + enum hinic_cb_state cb_state; + struct hinic_nic_cb *nic_cb; + struct hinic_hwdev *hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u8 cmd_cb; + + hwdev = &pfhwdev->hwdev; + hwif = hwdev->hwif; + pdev = hwif->pdev; + + if ((cmd < HINIC_MGMT_MSG_CMD_BASE) || + (cmd >= HINIC_MGMT_MSG_CMD_MAX)) { + dev_err(&pdev->dev, "unknown L2NIC event, cmd = %d\n", cmd); + return; + } + + cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE; + + nic_cb = &pfhwdev->nic_cb[cmd_cb]; + + cb_state = cmpxchg(&nic_cb->cb_state, + HINIC_CB_ENABLED, + HINIC_CB_ENABLED | HINIC_CB_RUNNING); + + if ((cb_state == HINIC_CB_ENABLED) && (nic_cb->handler)) + nic_cb->handler(nic_cb->handle, buf_in, + in_size, buf_out, out_size); + else + dev_err(&pdev->dev, "Unhandled NIC Event %d\n", cmd); + + nic_cb->cb_state &= ~HINIC_CB_RUNNING; +} + /** * init_pfhwdev - Initialize the extended components of PF * @pfhwdev: the HW device for PF @@ -248,6 +360,10 @@ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev) return err; } + hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC, + pfhwdev, nic_mgmt_msg_handler); + + hinic_set_pf_action(hwif, HINIC_PF_MGMT_ACTIVE); return 0; } @@ -257,6 +373,12 @@ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev) **/ static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev) { + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + + hinic_set_pf_action(hwdev->hwif, HINIC_PF_MGMT_INIT); + + hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC); + hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index ee9e76ada303..1cd8159766d4 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -18,6 +18,7 @@ #include #include +#include #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" @@ -25,6 +26,9 @@ #define HINIC_MAX_QPS 32 +#define HINIC_MGMT_NUM_MSG_CMD (HINIC_MGMT_MSG_CMD_MAX - \ + HINIC_MGMT_MSG_CMD_BASE) + struct hinic_cap { u16 max_qps; u16 num_qps; @@ -55,6 +59,19 @@ enum hinic_port_cmd { HINIC_PORT_CMD_GET_CAP = 170, }; +enum hinic_mgmt_msg_cmd { + HINIC_MGMT_MSG_CMD_BASE = 160, + + HINIC_MGMT_MSG_CMD_LINK_STATUS = 160, + + HINIC_MGMT_MSG_CMD_MAX, +}; + +enum hinic_cb_state { + HINIC_CB_ENABLED = BIT(0), + HINIC_CB_RUNNING = BIT(1), +}; + struct hinic_hwdev { struct hinic_hwif *hwif; struct msix_entry *msix_entries; @@ -64,12 +81,32 @@ struct hinic_hwdev { struct hinic_cap nic_cap; }; +struct hinic_nic_cb { + void (*handler)(void *handle, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size); + + void *handle; + unsigned long cb_state; +}; + struct hinic_pfhwdev { struct hinic_hwdev hwdev; struct hinic_pf_to_mgmt pf_to_mgmt; + + struct hinic_nic_cb nic_cb[HINIC_MGMT_NUM_MSG_CMD]; }; +void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev, + enum hinic_mgmt_msg_cmd cmd, void *handle, + void (*handler)(void *handle, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size)); + +void hinic_hwdev_cb_unregister(struct hinic_hwdev *hwdev, + enum hinic_mgmt_msg_cmd cmd); + int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c index 0cfada793a7a..b340695bff8b 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c @@ -115,6 +115,23 @@ int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index) return 0; } +/** + * hinic_set_pf_action - set action on pf channel + * @hwif: the HW interface of a pci function device + * @action: action on pf channel + * + * Return 0 - Success, negative - Failure + **/ +void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action) +{ + u32 attr5 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR); + + attr5 = HINIC_FA5_CLEAR(attr5, PF_ACTION); + attr5 |= HINIC_FA5_SET(action, PF_ACTION); + + hinic_hwif_write_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR, attr5); +} + /** * hwif_ready - test if the HW is ready for use * @hwif: the HW interface of a pci function device diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index 707a04614d5b..228069895f3b 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -73,6 +73,15 @@ #define HINIC_FA1_GET(val, member) \ (((val) >> HINIC_FA1_##member##_SHIFT) & HINIC_FA1_##member##_MASK) +#define HINIC_FA5_PF_ACTION_SHIFT 0 +#define HINIC_FA5_PF_ACTION_MASK 0xFFFF + +#define HINIC_FA5_SET(val, member) \ + (((u32)(val) & HINIC_FA5_##member##_MASK) << HINIC_FA5_##member##_SHIFT) + +#define HINIC_FA5_CLEAR(val, member) \ + ((val) & (~(HINIC_FA5_##member##_MASK << HINIC_FA5_##member##_SHIFT))) + #define HINIC_PPF_ELECTION_IDX_SHIFT 0 #define HINIC_PPF_ELECTION_IDX_MASK 0x1F @@ -166,6 +175,12 @@ enum hinic_node_id { HINIC_NODE_ID_MGMT = 21, }; +enum hinic_pf_action { + HINIC_PF_MGMT_INIT = 0x0, + + HINIC_PF_MGMT_ACTIVE = 0x11, +}; + struct hinic_func_attr { u16 func_idx; u8 pf_idx; @@ -212,6 +227,8 @@ int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index, int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index); +void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action); + int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev); void hinic_free_hwif(struct hinic_hwif *hwif); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c index 0150b71a5aba..278dc13f3dae 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c @@ -72,6 +72,46 @@ enum msg_ack_type { MSG_NO_ACK = 1, }; +/** + * hinic_register_mgmt_msg_cb - register msg handler for a msg from a module + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that this handler will handle its messages + * @handle: private data for the callback + * @callback: the handler that will handle messages + **/ +void hinic_register_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, + void *handle, + void (*callback)(void *handle, + u8 cmd, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size)) +{ + struct hinic_mgmt_cb *mgmt_cb = &pf_to_mgmt->mgmt_cb[mod]; + + mgmt_cb->cb = callback; + mgmt_cb->handle = handle; + mgmt_cb->state = HINIC_MGMT_CB_ENABLED; +} + +/** + * hinic_unregister_mgmt_msg_cb - unregister msg handler for a msg from a module + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that this handler handles its messages + **/ +void hinic_unregister_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod) +{ + struct hinic_mgmt_cb *mgmt_cb = &pf_to_mgmt->mgmt_cb[mod]; + + mgmt_cb->state &= ~HINIC_MGMT_CB_ENABLED; + + while (mgmt_cb->state & HINIC_MGMT_CB_RUNNING) + schedule(); + + mgmt_cb->cb = NULL; +} + /** * prepare_header - prepare the header of the message * @pf_to_mgmt: PF to MGMT channel @@ -337,9 +377,31 @@ static void mgmt_recv_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, struct hinic_hwif *hwif = pf_to_mgmt->hwif; struct pci_dev *pdev = hwif->pdev; u8 *buf_out = recv_msg->buf_out; + struct hinic_mgmt_cb *mgmt_cb; + unsigned long cb_state; u16 out_size = 0; - dev_err(&pdev->dev, "No MGMT msg handler, mod = %d\n", recv_msg->mod); + if (recv_msg->mod >= HINIC_MOD_MAX) { + dev_err(&pdev->dev, "Unknown MGMT MSG module = %d\n", + recv_msg->mod); + return; + } + + mgmt_cb = &pf_to_mgmt->mgmt_cb[recv_msg->mod]; + + cb_state = cmpxchg(&mgmt_cb->state, + HINIC_MGMT_CB_ENABLED, + HINIC_MGMT_CB_ENABLED | HINIC_MGMT_CB_RUNNING); + + if ((cb_state == HINIC_MGMT_CB_ENABLED) && (mgmt_cb->cb)) + mgmt_cb->cb(mgmt_cb->handle, recv_msg->cmd, + recv_msg->msg, recv_msg->msg_len, + buf_out, &out_size); + else + dev_err(&pdev->dev, "No MGMT msg handler, mod = %d\n", + recv_msg->mod); + + mgmt_cb->state &= ~HINIC_MGMT_CB_RUNNING; if (!recv_msg->async_mgmt_to_pf) /* MGMT sent sync msg, send the response */ diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h index eca7ad865bca..8021406cca7c 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "hinic_hw_if.h" #include "hinic_hw_api_cmd.h" @@ -67,6 +68,11 @@ enum hinic_cfg_cmd { HINIC_CFG_NIC_CAP = 0, }; +enum hinic_mgmt_cb_state { + HINIC_MGMT_CB_ENABLED = BIT(0), + HINIC_MGMT_CB_RUNNING = BIT(1), +}; + struct hinic_recv_msg { u8 *msg; u8 *buf_out; @@ -81,6 +87,15 @@ struct hinic_recv_msg { u16 msg_id; }; +struct hinic_mgmt_cb { + void (*cb)(void *handle, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size); + + void *handle; + unsigned long state; +}; + struct hinic_pf_to_mgmt { struct hinic_hwif *hwif; @@ -92,8 +107,21 @@ struct hinic_pf_to_mgmt { struct hinic_recv_msg recv_msg_from_mgmt; struct hinic_api_cmd_chain *cmd_chain[HINIC_API_CMD_MAX]; + + struct hinic_mgmt_cb mgmt_cb[HINIC_MOD_MAX]; }; +void hinic_register_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, + void *handle, + void (*callback)(void *handle, + u8 cmd, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size)); + +void hinic_unregister_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod); + int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 772e4e6e68af..7aebc6207931 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -24,9 +24,11 @@ #include #include #include +#include #include #include #include +#include #include #include "hinic_hw_dev.h" @@ -39,12 +41,99 @@ MODULE_LICENSE("GPL"); #define PCI_DEVICE_ID_HI1822_PF 0x1822 +#define HINIC_WQ_NAME "hinic_dev" + #define MSG_ENABLE_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \ NETIF_MSG_IFUP | \ NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR) #define VLAN_BITMAP_SIZE(nic_dev) (ALIGN(VLAN_N_VID, 8) / 8) +#define work_to_rx_mode_work(work) \ + container_of(work, struct hinic_rx_mode_work, work) + +#define rx_mode_work_to_nic_dev(rx_mode_work) \ + container_of(rx_mode_work, struct hinic_dev, rx_mode_work) + +static int change_mac_addr(struct net_device *netdev, const u8 *addr); + +static int hinic_open(struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + enum hinic_port_link_state link_state; + int err, ret; + + err = hinic_port_set_state(nic_dev, HINIC_PORT_ENABLE); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to set port state\n"); + return err; + } + + /* Wait up to 3 sec between port enable to link state */ + msleep(3000); + + down(&nic_dev->mgmt_lock); + + err = hinic_port_link_state(nic_dev, &link_state); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to get link state\n"); + goto err_port_link; + } + + if (link_state == HINIC_LINK_STATE_UP) + nic_dev->flags |= HINIC_LINK_UP; + + nic_dev->flags |= HINIC_INTF_UP; + + if ((nic_dev->flags & (HINIC_LINK_UP | HINIC_INTF_UP)) == + (HINIC_LINK_UP | HINIC_INTF_UP)) { + netif_info(nic_dev, drv, netdev, "link + intf UP\n"); + netif_carrier_on(netdev); + netif_tx_wake_all_queues(netdev); + } + + up(&nic_dev->mgmt_lock); + + netif_info(nic_dev, drv, netdev, "HINIC_INTF is UP\n"); + return 0; + +err_port_link: + up(&nic_dev->mgmt_lock); + ret = hinic_port_set_state(nic_dev, HINIC_PORT_DISABLE); + if (ret) + netif_warn(nic_dev, drv, netdev, + "Failed to revert port state\n"); + return err; +} + +static int hinic_close(struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + unsigned int flags; + int err; + + down(&nic_dev->mgmt_lock); + + flags = nic_dev->flags; + nic_dev->flags &= ~HINIC_INTF_UP; + + netif_carrier_off(netdev); + netif_tx_disable(netdev); + + up(&nic_dev->mgmt_lock); + + err = hinic_port_set_state(nic_dev, HINIC_PORT_DISABLE); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to set port state\n"); + nic_dev->flags |= (flags & HINIC_INTF_UP); + return err; + } + + netif_info(nic_dev, drv, netdev, "HINIC_INTF is DOWN\n"); + return 0; +} + static int hinic_change_mtu(struct net_device *netdev, int new_mtu) { struct hinic_dev *nic_dev = netdev_priv(netdev); @@ -118,6 +207,77 @@ static int hinic_set_mac_addr(struct net_device *netdev, void *addr) return err; } +/** + * add_mac_addr - add mac address to network device + * @netdev: network device + * @addr: mac address to add + * + * Return 0 - Success, negative - Failure + **/ +static int add_mac_addr(struct net_device *netdev, const u8 *addr) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u16 vid = 0; + int err; + + if (!is_valid_ether_addr(addr)) + return -EADDRNOTAVAIL; + + netif_info(nic_dev, drv, netdev, "set mac addr = %02x %02x %02x %02x %02x %02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + down(&nic_dev->mgmt_lock); + + do { + err = hinic_port_add_mac(nic_dev, addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to add mac\n"); + break; + } + + vid = find_next_bit(nic_dev->vlan_bitmap, VLAN_N_VID, vid + 1); + } while (vid != VLAN_N_VID); + + up(&nic_dev->mgmt_lock); + return err; +} + +/** + * remove_mac_addr - remove mac address from network device + * @netdev: network device + * @addr: mac address to remove + * + * Return 0 - Success, negative - Failure + **/ +static int remove_mac_addr(struct net_device *netdev, const u8 *addr) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u16 vid = 0; + int err; + + if (!is_valid_ether_addr(addr)) + return -EADDRNOTAVAIL; + + netif_info(nic_dev, drv, netdev, "remove mac addr = %02x %02x %02x %02x %02x %02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + down(&nic_dev->mgmt_lock); + + do { + err = hinic_port_del_mac(nic_dev, addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to delete mac\n"); + break; + } + + vid = find_next_bit(nic_dev->vlan_bitmap, VLAN_N_VID, vid + 1); + } while (vid != VLAN_N_VID); + + up(&nic_dev->mgmt_lock); + return err; +} + static int hinic_vlan_rx_add_vid(struct net_device *netdev, __always_unused __be16 proto, u16 vid) { @@ -182,12 +342,56 @@ err_del_vlan: return err; } +static void set_rx_mode(struct work_struct *work) +{ + struct hinic_rx_mode_work *rx_mode_work = work_to_rx_mode_work(work); + struct hinic_dev *nic_dev = rx_mode_work_to_nic_dev(rx_mode_work); + + netif_info(nic_dev, drv, nic_dev->netdev, "set rx mode work\n"); + + hinic_port_set_rx_mode(nic_dev, rx_mode_work->rx_mode); + + __dev_uc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr); + __dev_mc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr); +} + +static void hinic_set_rx_mode(struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_rx_mode_work *rx_mode_work; + u32 rx_mode; + + rx_mode_work = &nic_dev->rx_mode_work; + + rx_mode = HINIC_RX_MODE_UC | + HINIC_RX_MODE_MC | + HINIC_RX_MODE_BC; + + if (netdev->flags & IFF_PROMISC) + rx_mode |= HINIC_RX_MODE_PROMISC; + else if (netdev->flags & IFF_ALLMULTI) + rx_mode |= HINIC_RX_MODE_MC_ALL; + + rx_mode_work->rx_mode = rx_mode; + + queue_work(nic_dev->workq, &rx_mode_work->work); +} + +netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + return NETDEV_TX_BUSY; +} + static const struct net_device_ops hinic_netdev_ops = { + .ndo_open = hinic_open, + .ndo_stop = hinic_close, .ndo_change_mtu = hinic_change_mtu, .ndo_set_mac_address = hinic_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_vlan_rx_add_vid = hinic_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = hinic_vlan_rx_kill_vid, + .ndo_set_rx_mode = hinic_set_rx_mode, + .ndo_start_xmit = hinic_xmit_frame, /* more operations should be filled */ }; @@ -200,6 +404,57 @@ static void netdev_features_init(struct net_device *netdev) netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; } +/** + * link_status_event_handler - link event handler + * @handle: nic device for the handler + * @buf_in: input buffer + * @in_size: input size + * @buf_in: output buffer + * @out_size: returned output size + * + * Return 0 - Success, negative - Failure + **/ +static void link_status_event_handler(void *handle, void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_port_link_status *link_status, *ret_link_status; + struct hinic_dev *nic_dev = handle; + + link_status = buf_in; + + if (link_status->link == HINIC_LINK_STATE_UP) { + down(&nic_dev->mgmt_lock); + + nic_dev->flags |= HINIC_LINK_UP; + + if ((nic_dev->flags & (HINIC_LINK_UP | HINIC_INTF_UP)) == + (HINIC_LINK_UP | HINIC_INTF_UP)) { + netif_carrier_on(nic_dev->netdev); + netif_tx_wake_all_queues(nic_dev->netdev); + } + + up(&nic_dev->mgmt_lock); + + netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is UP\n"); + } else { + down(&nic_dev->mgmt_lock); + + nic_dev->flags &= ~HINIC_LINK_UP; + + netif_carrier_off(nic_dev->netdev); + netif_tx_disable(nic_dev->netdev); + + up(&nic_dev->mgmt_lock); + + netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is DOWN\n"); + } + + ret_link_status = buf_out; + ret_link_status->status = 0; + + *out_size = sizeof(*ret_link_status); +} + /** * nic_dev_init - Initialize the NIC device * @pdev: the NIC pci device @@ -208,6 +463,7 @@ static void netdev_features_init(struct net_device *netdev) **/ static int nic_dev_init(struct pci_dev *pdev) { + struct hinic_rx_mode_work *rx_mode_work; struct hinic_dev *nic_dev; struct net_device *netdev; struct hinic_hwdev *hwdev; @@ -239,6 +495,7 @@ static int nic_dev_init(struct pci_dev *pdev) nic_dev->netdev = netdev; nic_dev->hwdev = hwdev; nic_dev->msg_enable = MSG_ENABLE_DEFAULT; + nic_dev->flags = 0; sema_init(&nic_dev->mgmt_lock, 1); @@ -250,6 +507,12 @@ static int nic_dev_init(struct pci_dev *pdev) goto err_vlan_bitmap; } + nic_dev->workq = create_singlethread_workqueue(HINIC_WQ_NAME); + if (!nic_dev->workq) { + err = -ENOMEM; + goto err_workq; + } + pci_set_drvdata(pdev, netdev); err = hinic_port_get_mac(nic_dev, netdev->dev_addr); @@ -268,10 +531,16 @@ static int nic_dev_init(struct pci_dev *pdev) goto err_set_mtu; } + rx_mode_work = &nic_dev->rx_mode_work; + INIT_WORK(&rx_mode_work->work, set_rx_mode); + netdev_features_init(netdev); netif_carrier_off(netdev); + hinic_hwdev_cb_register(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS, + nic_dev, link_status_event_handler); + err = register_netdev(netdev); if (err) { dev_err(&pdev->dev, "Failed to register netdev\n"); @@ -281,10 +550,16 @@ static int nic_dev_init(struct pci_dev *pdev) return 0; err_reg_netdev: + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_STATUS); + cancel_work_sync(&rx_mode_work->work); + err_set_mtu: err_add_mac: pci_set_drvdata(pdev, NULL); + destroy_workqueue(nic_dev->workq); +err_workq: err_vlan_bitmap: free_netdev(netdev); @@ -357,11 +632,20 @@ static void hinic_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_rx_mode_work *rx_mode_work; unregister_netdev(netdev); + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_STATUS); + + rx_mode_work = &nic_dev->rx_mode_work; + cancel_work_sync(&rx_mode_work->work); + pci_set_drvdata(pdev, NULL); + destroy_workqueue(nic_dev->workq); + hinic_free_hwdev(nic_dev->hwdev); free_netdev(netdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 5b249e8121bd..0dafede7169e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -222,3 +222,95 @@ int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id) &port_vlan_cmd, sizeof(port_vlan_cmd), NULL, NULL); } + +/** + * hinic_port_set_rx_mode - set rx mode in the nic device + * @nic_dev: nic device + * @rx_mode: the rx mode to set + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_set_rx_mode(struct hinic_dev *nic_dev, u32 rx_mode) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_rx_mode_cmd rx_mode_cmd; + + rx_mode_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + rx_mode_cmd.rx_mode = rx_mode; + + return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_MODE, + &rx_mode_cmd, sizeof(rx_mode_cmd), + NULL, NULL); +} + +/** + * hinic_port_link_state - get the link state + * @nic_dev: nic device + * @link_state: the returned link state + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_link_state(struct hinic_dev *nic_dev, + enum hinic_port_link_state *link_state) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_port_link_cmd link_cmd; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return -EINVAL; + } + + link_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_STATE, + &link_cmd, sizeof(link_cmd), + &link_cmd, &out_size); + if (err || (out_size != sizeof(link_cmd)) || link_cmd.status) { + dev_err(&pdev->dev, "Failed to get link state, ret = %d\n", + link_cmd.status); + return -EINVAL; + } + + *link_state = link_cmd.state; + return 0; +} + +/** + * hinic_port_set_state - set port state + * @nic_dev: nic device + * @state: the state to set + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_set_state(struct hinic_dev *nic_dev, enum hinic_port_state state) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_state_cmd port_state; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return -EINVAL; + } + + port_state.state = state; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PORT_STATE, + &port_state, sizeof(port_state), + &port_state, &out_size); + if (err || (out_size != sizeof(port_state)) || port_state.status) { + dev_err(&pdev->dev, "Failed to set port state, ret = %d\n", + port_state.status); + return -EFAULT; + } + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 4cafb9448ea8..3a8da8eadb9b 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -18,9 +18,28 @@ #include #include +#include #include "hinic_dev.h" +enum hinic_rx_mode { + HINIC_RX_MODE_UC = BIT(0), + HINIC_RX_MODE_MC = BIT(1), + HINIC_RX_MODE_BC = BIT(2), + HINIC_RX_MODE_MC_ALL = BIT(3), + HINIC_RX_MODE_PROMISC = BIT(4), +}; + +enum hinic_port_link_state { + HINIC_LINK_STATE_DOWN, + HINIC_LINK_STATE_UP, +}; + +enum hinic_port_state { + HINIC_PORT_DISABLE = 0, + HINIC_PORT_ENABLE = 3, +}; + struct hinic_port_mac_cmd { u8 status; u8 version; @@ -51,6 +70,45 @@ struct hinic_port_vlan_cmd { u16 vlan_id; }; +struct hinic_port_rx_mode_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rsvd; + u32 rx_mode; +}; + +struct hinic_port_link_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 state; + u8 rsvd1; +}; + +struct hinic_port_state_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 state; + u8 rsvd1[3]; +}; + +struct hinic_port_link_status { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 rsvd1; + u8 link; + u8 rsvd2; +}; + int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, u16 vlan_id); @@ -65,4 +123,12 @@ int hinic_port_add_vlan(struct hinic_dev *nic_dev, u16 vlan_id); int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id); +int hinic_port_set_rx_mode(struct hinic_dev *nic_dev, u32 rx_mode); + +int hinic_port_link_state(struct hinic_dev *nic_dev, + enum hinic_port_link_state *link_state); + +int hinic_port_set_state(struct hinic_dev *nic_dev, + enum hinic_port_state state); + #endif -- cgit v1.2.3 From c3e79baf1b03b3ba53f60a8698f2fd9462a906b5 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:56 +0800 Subject: net-next/hinic: Add logical Txq and Rxq Create the logical queues of the nic. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/Makefile | 5 +- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 5 + drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 131 +++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 20 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_io.c | 144 +++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_io.h | 46 ++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h | 32 +++++ drivers/net/ethernet/huawei/hinic/hinic_main.c | 172 ++++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_rx.c | 72 ++++++++++ drivers/net/ethernet/huawei/hinic/hinic_rx.h | 46 ++++++ drivers/net/ethernet/huawei/hinic/hinic_tx.c | 75 ++++++++++ drivers/net/ethernet/huawei/hinic/hinic_tx.h | 49 +++++++ 12 files changed, 793 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_io.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_io.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_rx.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_rx.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_tx.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_tx.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index dbb1b9dbaa59..f60c449fbb40 100644 --- a/drivers/net/ethernet/huawei/hinic/Makefile +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_HINIC) += hinic.o -hinic-y := hinic_main.o hinic_port.o hinic_hw_dev.o hinic_hw_mgmt.o \ - hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o +hinic-y := hinic_main.o hinic_tx.o hinic_rx.o hinic_port.o hinic_hw_dev.o \ + hinic_hw_io.o hinic_hw_mgmt.o hinic_hw_api_cmd.o hinic_hw_eqs.o \ + hinic_hw_if.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 5c5b4e974c56..5b8231dc3ff1 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -23,6 +23,8 @@ #include #include "hinic_hw_dev.h" +#include "hinic_tx.h" +#include "hinic_rx.h" #define HINIC_DRV_NAME "hinic" @@ -49,6 +51,9 @@ struct hinic_dev { struct hinic_rx_mode_work rx_mode_work; struct workqueue_struct *workq; + + struct hinic_txq *txqs; + struct hinic_rxq *rxqs; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index c3122b030740..5ae1c3682be0 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -25,6 +25,8 @@ #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" #include "hinic_hw_mgmt.h" +#include "hinic_hw_qp.h" +#include "hinic_hw_io.h" #include "hinic_hw_dev.h" #define MAX_IRQS(max_qps, num_aeqs, num_ceqs) \ @@ -229,6 +231,99 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, HINIC_MGMT_MSG_SYNC); } +/** + * get_base_qpn - get the first qp number + * @hwdev: the NIC HW device + * @base_qpn: returned qp number + * + * Return 0 - Success, negative - Failure + **/ +static int get_base_qpn(struct hinic_hwdev *hwdev, u16 *base_qpn) +{ + struct hinic_cmd_base_qpn cmd_base_qpn; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + cmd_base_qpn.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_GLOBAL_QPN, + &cmd_base_qpn, sizeof(cmd_base_qpn), + &cmd_base_qpn, &out_size); + if (err || (out_size != sizeof(cmd_base_qpn)) || cmd_base_qpn.status) { + dev_err(&pdev->dev, "Failed to get base qpn, status = %d\n", + cmd_base_qpn.status); + return -EFAULT; + } + + *base_qpn = cmd_base_qpn.qpn; + return 0; +} + +/** + * hinic_hwdev_ifup - Preparing the HW for passing IO + * @hwdev: the NIC HW device + * + * Return 0 - Success, negative - Failure + **/ +int hinic_hwdev_ifup(struct hinic_hwdev *hwdev) +{ + struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; + struct hinic_cap *nic_cap = &hwdev->nic_cap; + struct hinic_hwif *hwif = hwdev->hwif; + int err, num_aeqs, num_ceqs, num_qps; + struct msix_entry *sq_msix_entries; + struct msix_entry *rq_msix_entries; + struct pci_dev *pdev = hwif->pdev; + u16 base_qpn; + + err = get_base_qpn(hwdev, &base_qpn); + if (err) { + dev_err(&pdev->dev, "Failed to get global base qp number\n"); + return err; + } + + num_aeqs = HINIC_HWIF_NUM_AEQS(hwif); + num_ceqs = HINIC_HWIF_NUM_CEQS(hwif); + err = hinic_io_init(func_to_io, hwif, nic_cap->max_qps, 0, NULL); + if (err) { + dev_err(&pdev->dev, "Failed to init IO channel\n"); + return err; + } + + num_qps = nic_cap->num_qps; + sq_msix_entries = &hwdev->msix_entries[num_aeqs + num_ceqs]; + rq_msix_entries = &hwdev->msix_entries[num_aeqs + num_ceqs + num_qps]; + + err = hinic_io_create_qps(func_to_io, base_qpn, num_qps, + sq_msix_entries, rq_msix_entries); + if (err) { + dev_err(&pdev->dev, "Failed to create QPs\n"); + goto err_create_qps; + } + + return 0; + +err_create_qps: + hinic_io_free(func_to_io); + return err; +} + +/** + * hinic_hwdev_ifdown - Closing the HW for passing IO + * @hwdev: the NIC HW device + * + **/ +void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev) +{ + struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; + struct hinic_cap *nic_cap = &hwdev->nic_cap; + + hinic_io_destroy_qps(func_to_io, nic_cap->num_qps); + hinic_io_free(func_to_io); +} + /** * hinic_hwdev_cb_register - register callback handler for MGMT events * @hwdev: the NIC HW device @@ -499,3 +594,39 @@ int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev) return nic_cap->num_qps; } + +/** + * hinic_hwdev_get_sq - get SQ + * @hwdev: the NIC HW device + * @i: the position of the SQ + * + * Return: the SQ in the i position + **/ +struct hinic_sq *hinic_hwdev_get_sq(struct hinic_hwdev *hwdev, int i) +{ + struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; + struct hinic_qp *qp = &func_to_io->qps[i]; + + if (i >= hinic_hwdev_num_qps(hwdev)) + return NULL; + + return &qp->sq; +} + +/** + * hinic_hwdev_get_sq - get RQ + * @hwdev: the NIC HW device + * @i: the position of the RQ + * + * Return: the RQ in the i position + **/ +struct hinic_rq *hinic_hwdev_get_rq(struct hinic_hwdev *hwdev, int i) +{ + struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; + struct hinic_qp *qp = &func_to_io->qps[i]; + + if (i >= hinic_hwdev_num_qps(hwdev)) + return NULL; + + return &qp->rq; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 1cd8159766d4..81c2c6e92898 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -23,6 +23,8 @@ #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" #include "hinic_hw_mgmt.h" +#include "hinic_hw_qp.h" +#include "hinic_hw_io.h" #define HINIC_MAX_QPS 32 @@ -72,11 +74,21 @@ enum hinic_cb_state { HINIC_CB_RUNNING = BIT(1), }; +struct hinic_cmd_base_qpn { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 qpn; +}; + struct hinic_hwdev { struct hinic_hwif *hwif; struct msix_entry *msix_entries; struct hinic_aeqs aeqs; + struct hinic_func_to_io func_to_io; struct hinic_cap nic_cap; }; @@ -111,10 +123,18 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); +int hinic_hwdev_ifup(struct hinic_hwdev *hwdev); + +void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev); + struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev); void hinic_free_hwdev(struct hinic_hwdev *hwdev); int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev); +struct hinic_sq *hinic_hwdev_get_sq(struct hinic_hwdev *hwdev, int i); + +struct hinic_rq *hinic_hwdev_get_rq(struct hinic_hwdev *hwdev, int i); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c new file mode 100644 index 000000000000..ebe28ee9af9c --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c @@ -0,0 +1,144 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_qp.h" +#include "hinic_hw_io.h" + +/** + * init_qp - Initialize a Queue Pair + * @func_to_io: func to io channel that holds the IO components + * @qp: pointer to the qp to initialize + * @q_id: the id of the qp + * @sq_msix_entry: msix entry for sq + * @rq_msix_entry: msix entry for rq + * + * Return 0 - Success, negative - Failure + **/ +static int init_qp(struct hinic_func_to_io *func_to_io, + struct hinic_qp *qp, int q_id, + struct msix_entry *sq_msix_entry, + struct msix_entry *rq_msix_entry) +{ + /* should be implemented */ + return 0; +} + +/** + * destroy_qp - Clean the resources of a Queue Pair + * @func_to_io: func to io channel that holds the IO components + * @qp: pointer to the qp to clean + **/ +static void destroy_qp(struct hinic_func_to_io *func_to_io, + struct hinic_qp *qp) +{ + /* should be implemented */ +} + +/** + * hinic_io_create_qps - Create Queue Pairs + * @func_to_io: func to io channel that holds the IO components + * @base_qpn: base qp number + * @num_qps: number queue pairs to create + * @sq_msix_entry: msix entries for sq + * @rq_msix_entry: msix entries for rq + * + * Return 0 - Success, negative - Failure + **/ +int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, + u16 base_qpn, int num_qps, + struct msix_entry *sq_msix_entries, + struct msix_entry *rq_msix_entries) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t qps_size; + int i, j, err; + + qps_size = num_qps * sizeof(*func_to_io->qps); + func_to_io->qps = devm_kzalloc(&pdev->dev, qps_size, GFP_KERNEL); + if (!func_to_io->qps) + return -ENOMEM; + + for (i = 0; i < num_qps; i++) { + err = init_qp(func_to_io, &func_to_io->qps[i], i, + &sq_msix_entries[i], &rq_msix_entries[i]); + if (err) { + dev_err(&pdev->dev, "Failed to create QP %d\n", i); + goto err_init_qp; + } + } + + return 0; + +err_init_qp: + for (j = 0; j < i; j++) + destroy_qp(func_to_io, &func_to_io->qps[j]); + + devm_kfree(&pdev->dev, func_to_io->qps); + return err; +} + +/** + * hinic_io_destroy_qps - Destroy the IO Queue Pairs + * @func_to_io: func to io channel that holds the IO components + * @num_qps: number queue pairs to destroy + **/ +void hinic_io_destroy_qps(struct hinic_func_to_io *func_to_io, int num_qps) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct pci_dev *pdev = hwif->pdev; + int i; + + for (i = 0; i < num_qps; i++) + destroy_qp(func_to_io, &func_to_io->qps[i]); + + devm_kfree(&pdev->dev, func_to_io->qps); +} + +/** + * hinic_io_init - Initialize the IO components + * @func_to_io: func to io channel that holds the IO components + * @hwif: HW interface for accessing IO + * @max_qps: maximum QPs in HW + * @num_ceqs: number completion event queues + * @ceq_msix_entries: msix entries for ceqs + * + * Return 0 - Success, negative - Failure + **/ +int hinic_io_init(struct hinic_func_to_io *func_to_io, + struct hinic_hwif *hwif, u16 max_qps, int num_ceqs, + struct msix_entry *ceq_msix_entries) +{ + func_to_io->hwif = hwif; + func_to_io->qps = NULL; + func_to_io->max_qps = max_qps; + + return 0; +} + +/** + * hinic_io_free - Free the IO components + * @func_to_io: func to io channel that holds the IO components + **/ +void hinic_io_free(struct hinic_func_to_io *func_to_io) +{ +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h new file mode 100644 index 000000000000..7cdcffd4cf77 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h @@ -0,0 +1,46 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_IO_H +#define HINIC_HW_IO_H + +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_qp.h" + +struct hinic_func_to_io { + struct hinic_hwif *hwif; + + struct hinic_qp *qps; + u16 max_qps; +}; + +int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, + u16 base_qpn, int num_qps, + struct msix_entry *sq_msix_entries, + struct msix_entry *rq_msix_entries); + +void hinic_io_destroy_qps(struct hinic_func_to_io *func_to_io, + int num_qps); + +int hinic_io_init(struct hinic_func_to_io *func_to_io, + struct hinic_hwif *hwif, u16 max_qps, int num_ceqs, + struct msix_entry *ceq_msix_entries); + +void hinic_io_free(struct hinic_func_to_io *func_to_io); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h new file mode 100644 index 000000000000..64330fb6f10f --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -0,0 +1,32 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_QP_H +#define HINIC_HW_QP_H + +struct hinic_sq { + /* should be implemented */ +}; + +struct hinic_rq { + /* should be implemented */ +}; + +struct hinic_qp { + struct hinic_sq sq; + struct hinic_rq rq; +}; + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 7aebc6207931..22d5b61b0426 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -31,8 +31,11 @@ #include #include +#include "hinic_hw_qp.h" #include "hinic_hw_dev.h" #include "hinic_port.h" +#include "hinic_tx.h" +#include "hinic_rx.h" #include "hinic_dev.h" MODULE_AUTHOR("Huawei Technologies CO., Ltd"); @@ -57,17 +60,164 @@ MODULE_LICENSE("GPL"); static int change_mac_addr(struct net_device *netdev, const u8 *addr); +/** + * create_txqs - Create the Logical Tx Queues of specific NIC device + * @nic_dev: the specific NIC device + * + * Return 0 - Success, negative - Failure + **/ +static int create_txqs(struct hinic_dev *nic_dev) +{ + int err, i, j, num_txqs = hinic_hwdev_num_qps(nic_dev->hwdev); + struct net_device *netdev = nic_dev->netdev; + size_t txq_size; + + if (nic_dev->txqs) + return -EINVAL; + + txq_size = num_txqs * sizeof(*nic_dev->txqs); + nic_dev->txqs = devm_kzalloc(&netdev->dev, txq_size, GFP_KERNEL); + if (!nic_dev->txqs) + return -ENOMEM; + + for (i = 0; i < num_txqs; i++) { + struct hinic_sq *sq = hinic_hwdev_get_sq(nic_dev->hwdev, i); + + err = hinic_init_txq(&nic_dev->txqs[i], sq, netdev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to init Txq\n"); + goto err_init_txq; + } + } + + return 0; + +err_init_txq: + for (j = 0; j < i; j++) + hinic_clean_txq(&nic_dev->txqs[j]); + + devm_kfree(&netdev->dev, nic_dev->txqs); + return err; +} + +/** + * free_txqs - Free the Logical Tx Queues of specific NIC device + * @nic_dev: the specific NIC device + **/ +static void free_txqs(struct hinic_dev *nic_dev) +{ + int i, num_txqs = hinic_hwdev_num_qps(nic_dev->hwdev); + struct net_device *netdev = nic_dev->netdev; + + if (!nic_dev->txqs) + return; + + for (i = 0; i < num_txqs; i++) + hinic_clean_txq(&nic_dev->txqs[i]); + + devm_kfree(&netdev->dev, nic_dev->txqs); + nic_dev->txqs = NULL; +} + +/** + * create_txqs - Create the Logical Rx Queues of specific NIC device + * @nic_dev: the specific NIC device + * + * Return 0 - Success, negative - Failure + **/ +static int create_rxqs(struct hinic_dev *nic_dev) +{ + int err, i, j, num_rxqs = hinic_hwdev_num_qps(nic_dev->hwdev); + struct net_device *netdev = nic_dev->netdev; + size_t rxq_size; + + if (nic_dev->rxqs) + return -EINVAL; + + rxq_size = num_rxqs * sizeof(*nic_dev->rxqs); + nic_dev->rxqs = devm_kzalloc(&netdev->dev, rxq_size, GFP_KERNEL); + if (!nic_dev->rxqs) + return -ENOMEM; + + for (i = 0; i < num_rxqs; i++) { + struct hinic_rq *rq = hinic_hwdev_get_rq(nic_dev->hwdev, i); + + err = hinic_init_rxq(&nic_dev->rxqs[i], rq, netdev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to init rxq\n"); + goto err_init_rxq; + } + } + + return 0; + +err_init_rxq: + for (j = 0; j < i; j++) + hinic_clean_rxq(&nic_dev->rxqs[j]); + + devm_kfree(&netdev->dev, nic_dev->rxqs); + return err; +} + +/** + * free_txqs - Free the Logical Rx Queues of specific NIC device + * @nic_dev: the specific NIC device + **/ +static void free_rxqs(struct hinic_dev *nic_dev) +{ + int i, num_rxqs = hinic_hwdev_num_qps(nic_dev->hwdev); + struct net_device *netdev = nic_dev->netdev; + + if (!nic_dev->rxqs) + return; + + for (i = 0; i < num_rxqs; i++) + hinic_clean_rxq(&nic_dev->rxqs[i]); + + devm_kfree(&netdev->dev, nic_dev->rxqs); + nic_dev->rxqs = NULL; +} + static int hinic_open(struct net_device *netdev) { struct hinic_dev *nic_dev = netdev_priv(netdev); enum hinic_port_link_state link_state; - int err, ret; + int err, ret, num_qps; + + if (!(nic_dev->flags & HINIC_INTF_UP)) { + err = hinic_hwdev_ifup(nic_dev->hwdev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed - HW interface up\n"); + return err; + } + } + + err = create_txqs(nic_dev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to create Tx queues\n"); + goto err_create_txqs; + } + + err = create_rxqs(nic_dev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to create Rx queues\n"); + goto err_create_rxqs; + } + + num_qps = hinic_hwdev_num_qps(nic_dev->hwdev); + netif_set_real_num_tx_queues(netdev, num_qps); + netif_set_real_num_rx_queues(netdev, num_qps); err = hinic_port_set_state(nic_dev, HINIC_PORT_ENABLE); if (err) { netif_err(nic_dev, drv, netdev, "Failed to set port state\n"); - return err; + goto err_port_state; } /* Wait up to 3 sec between port enable to link state */ @@ -104,6 +254,16 @@ err_port_link: if (ret) netif_warn(nic_dev, drv, netdev, "Failed to revert port state\n"); + +err_port_state: + free_rxqs(nic_dev); + +err_create_rxqs: + free_txqs(nic_dev); + +err_create_txqs: + if (!(nic_dev->flags & HINIC_INTF_UP)) + hinic_hwdev_ifdown(nic_dev->hwdev); return err; } @@ -130,6 +290,12 @@ static int hinic_close(struct net_device *netdev) return err; } + free_rxqs(nic_dev); + free_txqs(nic_dev); + + if (flags & HINIC_INTF_UP) + hinic_hwdev_ifdown(nic_dev->hwdev); + netif_info(nic_dev, drv, netdev, "HINIC_INTF is DOWN\n"); return 0; } @@ -496,6 +662,8 @@ static int nic_dev_init(struct pci_dev *pdev) nic_dev->hwdev = hwdev; nic_dev->msg_enable = MSG_ENABLE_DEFAULT; nic_dev->flags = 0; + nic_dev->txqs = NULL; + nic_dev->rxqs = NULL; sema_init(&nic_dev->mgmt_lock, 1); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c new file mode 100644 index 000000000000..3c79f65d44da --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c @@ -0,0 +1,72 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include + +#include "hinic_hw_qp.h" +#include "hinic_rx.h" + +/** + * hinic_rxq_clean_stats - Clean the statistics of specific queue + * @rxq: Logical Rx Queue + **/ +void hinic_rxq_clean_stats(struct hinic_rxq *rxq) +{ + struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; + + u64_stats_update_begin(&rxq_stats->syncp); + rxq_stats->pkts = 0; + rxq_stats->bytes = 0; + u64_stats_update_end(&rxq_stats->syncp); +} + +/** + * rxq_stats_init - Initialize the statistics of specific queue + * @rxq: Logical Rx Queue + **/ +static void rxq_stats_init(struct hinic_rxq *rxq) +{ + struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; + + u64_stats_init(&rxq_stats->syncp); + hinic_rxq_clean_stats(rxq); +} + +/** + * hinic_init_rxq - Initialize the Rx Queue + * @rxq: Logical Rx Queue + * @rq: Hardware Rx Queue to connect the Logical queue with + * @netdev: network device to connect the Logical queue with + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, + struct net_device *netdev) +{ + rxq->netdev = netdev; + rxq->rq = rq; + + rxq_stats_init(rxq); + return 0; +} + +/** + * hinic_clean_rxq - Clean the Rx Queue + * @rxq: Logical Rx Queue + **/ +void hinic_clean_rxq(struct hinic_rxq *rxq) +{ +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.h b/drivers/net/ethernet/huawei/hinic/hinic_rx.h new file mode 100644 index 000000000000..fbd0246165dc --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.h @@ -0,0 +1,46 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_RX_H +#define HINIC_RX_H + +#include +#include +#include + +#include "hinic_hw_qp.h" + +struct hinic_rxq_stats { + u64 pkts; + u64 bytes; + + struct u64_stats_sync syncp; +}; + +struct hinic_rxq { + struct net_device *netdev; + struct hinic_rq *rq; + + struct hinic_rxq_stats rxq_stats; +}; + +void hinic_rxq_clean_stats(struct hinic_rxq *rxq); + +int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, + struct net_device *netdev); + +void hinic_clean_rxq(struct hinic_rxq *rxq); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c new file mode 100644 index 000000000000..9835912039c9 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -0,0 +1,75 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include + +#include "hinic_hw_qp.h" +#include "hinic_tx.h" + +/** + * hinic_txq_clean_stats - Clean the statistics of specific queue + * @txq: Logical Tx Queue + **/ +void hinic_txq_clean_stats(struct hinic_txq *txq) +{ + struct hinic_txq_stats *txq_stats = &txq->txq_stats; + + u64_stats_update_begin(&txq_stats->syncp); + txq_stats->pkts = 0; + txq_stats->bytes = 0; + txq_stats->tx_busy = 0; + txq_stats->tx_wake = 0; + txq_stats->tx_dropped = 0; + u64_stats_update_end(&txq_stats->syncp); +} + +/** + * txq_stats_init - Initialize the statistics of specific queue + * @txq: Logical Tx Queue + **/ +static void txq_stats_init(struct hinic_txq *txq) +{ + struct hinic_txq_stats *txq_stats = &txq->txq_stats; + + u64_stats_init(&txq_stats->syncp); + hinic_txq_clean_stats(txq); +} + +/** + * hinic_init_txq - Initialize the Tx Queue + * @txq: Logical Tx Queue + * @sq: Hardware Tx Queue to connect the Logical queue with + * @netdev: network device to connect the Logical queue with + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, + struct net_device *netdev) +{ + txq->netdev = netdev; + txq->sq = sq; + + txq_stats_init(txq); + return 0; +} + +/** + * hinic_clean_txq - Clean the Tx Queue + * @txq: Logical Tx Queue + **/ +void hinic_clean_txq(struct hinic_txq *txq) +{ +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.h b/drivers/net/ethernet/huawei/hinic/hinic_tx.h new file mode 100644 index 000000000000..bbdb4b62d940 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.h @@ -0,0 +1,49 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_TX_H +#define HINIC_TX_H + +#include +#include +#include + +#include "hinic_hw_qp.h" + +struct hinic_txq_stats { + u64 pkts; + u64 bytes; + u64 tx_busy; + u64 tx_wake; + u64 tx_dropped; + + struct u64_stats_sync syncp; +}; + +struct hinic_txq { + struct net_device *netdev; + struct hinic_sq *sq; + + struct hinic_txq_stats txq_stats; +}; + +void hinic_txq_clean_stats(struct hinic_txq *txq); + +int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, + struct net_device *netdev); + +void hinic_clean_txq(struct hinic_txq *txq); + +#endif -- cgit v1.2.3 From b15a9f37be2bc90f46f7e6b0c615c80e72e96431 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:57 +0800 Subject: net-next/hinic: Add wq Create work queues for being used by the queue pairs. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/Makefile | 4 +- drivers/net/ethernet/huawei/hinic/hinic_common.h | 25 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_io.c | 69 ++- drivers/net/ethernet/huawei/hinic/hinic_hw_io.h | 6 + drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h | 14 + drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c | 516 +++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h | 86 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h | 253 +++++++++++ 8 files changed, 968 insertions(+), 5 deletions(-) create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_common.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index f60c449fbb40..0575a3452692 100644 --- a/drivers/net/ethernet/huawei/hinic/Makefile +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_HINIC) += hinic.o hinic-y := hinic_main.o hinic_tx.o hinic_rx.o hinic_port.o hinic_hw_dev.o \ - hinic_hw_io.o hinic_hw_mgmt.o hinic_hw_api_cmd.o hinic_hw_eqs.o \ - hinic_hw_if.o + hinic_hw_io.o hinic_hw_wq.o hinic_hw_mgmt.o hinic_hw_api_cmd.o \ + hinic_hw_eqs.o hinic_hw_if.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_common.h b/drivers/net/ethernet/huawei/hinic/hinic_common.h new file mode 100644 index 000000000000..6a83c157b978 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_common.h @@ -0,0 +1,25 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_COMMON_H +#define HINIC_COMMON_H + +struct hinic_sge { + u32 hi_addr; + u32 lo_addr; + u32 len; +}; + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c index ebe28ee9af9c..1bf944e5aa28 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c @@ -20,6 +20,8 @@ #include #include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" #include "hinic_hw_qp.h" #include "hinic_hw_io.h" @@ -38,8 +40,33 @@ static int init_qp(struct hinic_func_to_io *func_to_io, struct msix_entry *sq_msix_entry, struct msix_entry *rq_msix_entry) { - /* should be implemented */ + struct hinic_hwif *hwif = func_to_io->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + qp->q_id = q_id; + + err = hinic_wq_allocate(&func_to_io->wqs, &func_to_io->sq_wq[q_id], + HINIC_SQ_WQEBB_SIZE, HINIC_SQ_PAGE_SIZE, + HINIC_SQ_DEPTH, HINIC_SQ_WQE_MAX_SIZE); + if (err) { + dev_err(&pdev->dev, "Failed to allocate WQ for SQ\n"); + return err; + } + + err = hinic_wq_allocate(&func_to_io->wqs, &func_to_io->rq_wq[q_id], + HINIC_RQ_WQEBB_SIZE, HINIC_RQ_PAGE_SIZE, + HINIC_RQ_DEPTH, HINIC_RQ_WQE_SIZE); + if (err) { + dev_err(&pdev->dev, "Failed to allocate WQ for RQ\n"); + goto err_rq_alloc; + } + return 0; + +err_rq_alloc: + hinic_wq_free(&func_to_io->wqs, &func_to_io->sq_wq[q_id]); + return err; } /** @@ -50,7 +77,10 @@ static int init_qp(struct hinic_func_to_io *func_to_io, static void destroy_qp(struct hinic_func_to_io *func_to_io, struct hinic_qp *qp) { - /* should be implemented */ + int q_id = qp->q_id; + + hinic_wq_free(&func_to_io->wqs, &func_to_io->rq_wq[q_id]); + hinic_wq_free(&func_to_io->wqs, &func_to_io->sq_wq[q_id]); } /** @@ -70,7 +100,7 @@ int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, { struct hinic_hwif *hwif = func_to_io->hwif; struct pci_dev *pdev = hwif->pdev; - size_t qps_size; + size_t qps_size, wq_size; int i, j, err; qps_size = num_qps * sizeof(*func_to_io->qps); @@ -78,6 +108,20 @@ int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, if (!func_to_io->qps) return -ENOMEM; + wq_size = num_qps * sizeof(*func_to_io->sq_wq); + func_to_io->sq_wq = devm_kzalloc(&pdev->dev, wq_size, GFP_KERNEL); + if (!func_to_io->sq_wq) { + err = -ENOMEM; + goto err_sq_wq; + } + + wq_size = num_qps * sizeof(*func_to_io->rq_wq); + func_to_io->rq_wq = devm_kzalloc(&pdev->dev, wq_size, GFP_KERNEL); + if (!func_to_io->rq_wq) { + err = -ENOMEM; + goto err_rq_wq; + } + for (i = 0; i < num_qps; i++) { err = init_qp(func_to_io, &func_to_io->qps[i], i, &sq_msix_entries[i], &rq_msix_entries[i]); @@ -93,6 +137,12 @@ err_init_qp: for (j = 0; j < i; j++) destroy_qp(func_to_io, &func_to_io->qps[j]); + devm_kfree(&pdev->dev, func_to_io->rq_wq); + +err_rq_wq: + devm_kfree(&pdev->dev, func_to_io->sq_wq); + +err_sq_wq: devm_kfree(&pdev->dev, func_to_io->qps); return err; } @@ -111,6 +161,9 @@ void hinic_io_destroy_qps(struct hinic_func_to_io *func_to_io, int num_qps) for (i = 0; i < num_qps; i++) destroy_qp(func_to_io, &func_to_io->qps[i]); + devm_kfree(&pdev->dev, func_to_io->rq_wq); + devm_kfree(&pdev->dev, func_to_io->sq_wq); + devm_kfree(&pdev->dev, func_to_io->qps); } @@ -128,10 +181,19 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io, struct hinic_hwif *hwif, u16 max_qps, int num_ceqs, struct msix_entry *ceq_msix_entries) { + struct pci_dev *pdev = hwif->pdev; + int err; + func_to_io->hwif = hwif; func_to_io->qps = NULL; func_to_io->max_qps = max_qps; + err = hinic_wqs_alloc(&func_to_io->wqs, 2 * max_qps, hwif); + if (err) { + dev_err(&pdev->dev, "Failed to allocate WQS for IO\n"); + return err; + } + return 0; } @@ -141,4 +203,5 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io, **/ void hinic_io_free(struct hinic_func_to_io *func_to_io) { + hinic_wqs_free(&func_to_io->wqs); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h index 7cdcffd4cf77..6cacb8eddd2e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h @@ -20,11 +20,17 @@ #include #include "hinic_hw_if.h" +#include "hinic_hw_wq.h" #include "hinic_hw_qp.h" struct hinic_func_to_io { struct hinic_hwif *hwif; + struct hinic_wqs wqs; + + struct hinic_wq *sq_wq; + struct hinic_wq *rq_wq; + struct hinic_qp *qps; u16 max_qps; }; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h index 64330fb6f10f..4031728a2281 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -16,6 +16,18 @@ #ifndef HINIC_HW_QP_H #define HINIC_HW_QP_H +#include +#include + +#define HINIC_SQ_WQEBB_SIZE 64 +#define HINIC_RQ_WQEBB_SIZE 32 + +#define HINIC_SQ_PAGE_SIZE SZ_4K +#define HINIC_RQ_PAGE_SIZE SZ_4K + +#define HINIC_SQ_DEPTH SZ_4K +#define HINIC_RQ_DEPTH SZ_4K + struct hinic_sq { /* should be implemented */ }; @@ -27,6 +39,8 @@ struct hinic_rq { struct hinic_qp { struct hinic_sq sq; struct hinic_rq rq; + + u16 q_id; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c new file mode 100644 index 000000000000..fc72b76e744b --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c @@ -0,0 +1,516 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_wq.h" + +#define WQS_BLOCKS_PER_PAGE 4 + +#define WQ_BLOCK_SIZE 4096 +#define WQS_PAGE_SIZE (WQS_BLOCKS_PER_PAGE * WQ_BLOCK_SIZE) + +#define WQS_MAX_NUM_BLOCKS 128 +#define WQS_FREE_BLOCKS_SIZE(wqs) (WQS_MAX_NUM_BLOCKS * \ + sizeof((wqs)->free_blocks[0])) + +#define WQ_SIZE(wq) ((wq)->q_depth * (wq)->wqebb_size) + +#define WQ_PAGE_ADDR_SIZE sizeof(u64) +#define WQ_MAX_PAGES (WQ_BLOCK_SIZE / WQ_PAGE_ADDR_SIZE) + +#define WQ_BASE_VADDR(wqs, wq) \ + ((void *)((wqs)->page_vaddr[(wq)->page_idx]) \ + + (wq)->block_idx * WQ_BLOCK_SIZE) + +#define WQ_BASE_PADDR(wqs, wq) \ + ((wqs)->page_paddr[(wq)->page_idx] \ + + (wq)->block_idx * WQ_BLOCK_SIZE) + +#define WQ_BASE_ADDR(wqs, wq) \ + ((void *)((wqs)->shadow_page_vaddr[(wq)->page_idx]) \ + + (wq)->block_idx * WQ_BLOCK_SIZE) + +/** + * queue_alloc_page - allocate page for Queue + * @hwif: HW interface for allocating DMA + * @vaddr: virtual address will be returned in this address + * @paddr: physical address will be returned in this address + * @shadow_vaddr: VM area will be return here for holding WQ page addresses + * @page_sz: page size of each WQ page + * + * Return 0 - Success, negative - Failure + **/ +static int queue_alloc_page(struct hinic_hwif *hwif, u64 **vaddr, u64 *paddr, + void ***shadow_vaddr, size_t page_sz) +{ + struct pci_dev *pdev = hwif->pdev; + dma_addr_t dma_addr; + + *vaddr = dma_zalloc_coherent(&pdev->dev, page_sz, &dma_addr, + GFP_KERNEL); + if (!*vaddr) { + dev_err(&pdev->dev, "Failed to allocate dma for wqs page\n"); + return -ENOMEM; + } + + *paddr = (u64)dma_addr; + + /* use vzalloc for big mem */ + *shadow_vaddr = vzalloc(page_sz); + if (!*shadow_vaddr) + goto err_shadow_vaddr; + + return 0; + +err_shadow_vaddr: + dma_free_coherent(&pdev->dev, page_sz, *vaddr, dma_addr); + return -ENOMEM; +} + +/** + * wqs_allocate_page - allocate page for WQ set + * @wqs: Work Queue Set + * @page_idx: the page index of the page will be allocated + * + * Return 0 - Success, negative - Failure + **/ +static int wqs_allocate_page(struct hinic_wqs *wqs, int page_idx) +{ + return queue_alloc_page(wqs->hwif, &wqs->page_vaddr[page_idx], + &wqs->page_paddr[page_idx], + &wqs->shadow_page_vaddr[page_idx], + WQS_PAGE_SIZE); +} + +/** + * wqs_free_page - free page of WQ set + * @wqs: Work Queue Set + * @page_idx: the page index of the page will be freed + **/ +static void wqs_free_page(struct hinic_wqs *wqs, int page_idx) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + + dma_free_coherent(&pdev->dev, WQS_PAGE_SIZE, + wqs->page_vaddr[page_idx], + (dma_addr_t)wqs->page_paddr[page_idx]); + vfree(wqs->shadow_page_vaddr[page_idx]); +} + +static int alloc_page_arrays(struct hinic_wqs *wqs) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t size; + + size = wqs->num_pages * sizeof(*wqs->page_paddr); + wqs->page_paddr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wqs->page_paddr) + return -ENOMEM; + + size = wqs->num_pages * sizeof(*wqs->page_vaddr); + wqs->page_vaddr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wqs->page_vaddr) + goto err_page_vaddr; + + size = wqs->num_pages * sizeof(*wqs->shadow_page_vaddr); + wqs->shadow_page_vaddr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wqs->shadow_page_vaddr) + goto err_page_shadow_vaddr; + + return 0; + +err_page_shadow_vaddr: + devm_kfree(&pdev->dev, wqs->page_vaddr); + +err_page_vaddr: + devm_kfree(&pdev->dev, wqs->page_paddr); + return -ENOMEM; +} + +static void free_page_arrays(struct hinic_wqs *wqs) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + + devm_kfree(&pdev->dev, wqs->shadow_page_vaddr); + devm_kfree(&pdev->dev, wqs->page_vaddr); + devm_kfree(&pdev->dev, wqs->page_paddr); +} + +static int wqs_next_block(struct hinic_wqs *wqs, int *page_idx, + int *block_idx) +{ + int pos; + + down(&wqs->alloc_blocks_lock); + + wqs->num_free_blks--; + + if (wqs->num_free_blks < 0) { + wqs->num_free_blks++; + up(&wqs->alloc_blocks_lock); + return -ENOMEM; + } + + pos = wqs->alloc_blk_pos++; + pos &= WQS_MAX_NUM_BLOCKS - 1; + + *page_idx = wqs->free_blocks[pos].page_idx; + *block_idx = wqs->free_blocks[pos].block_idx; + + wqs->free_blocks[pos].page_idx = -1; + wqs->free_blocks[pos].block_idx = -1; + + up(&wqs->alloc_blocks_lock); + return 0; +} + +static void wqs_return_block(struct hinic_wqs *wqs, int page_idx, + int block_idx) +{ + int pos; + + down(&wqs->alloc_blocks_lock); + + pos = wqs->return_blk_pos++; + pos &= WQS_MAX_NUM_BLOCKS - 1; + + wqs->free_blocks[pos].page_idx = page_idx; + wqs->free_blocks[pos].block_idx = block_idx; + + wqs->num_free_blks++; + + up(&wqs->alloc_blocks_lock); +} + +static void init_wqs_blocks_arr(struct hinic_wqs *wqs) +{ + int page_idx, blk_idx, pos = 0; + + for (page_idx = 0; page_idx < wqs->num_pages; page_idx++) { + for (blk_idx = 0; blk_idx < WQS_BLOCKS_PER_PAGE; blk_idx++) { + wqs->free_blocks[pos].page_idx = page_idx; + wqs->free_blocks[pos].block_idx = blk_idx; + pos++; + } + } + + wqs->alloc_blk_pos = 0; + wqs->return_blk_pos = pos; + wqs->num_free_blks = pos; + + sema_init(&wqs->alloc_blocks_lock, 1); +} + +/** + * hinic_wqs_alloc - allocate Work Queues set + * @wqs: Work Queue Set + * @max_wqs: maximum wqs to allocate + * @hwif: HW interface for use for the allocation + * + * Return 0 - Success, negative - Failure + **/ +int hinic_wqs_alloc(struct hinic_wqs *wqs, int max_wqs, + struct hinic_hwif *hwif) +{ + struct pci_dev *pdev = hwif->pdev; + int err, i, page_idx; + + max_wqs = ALIGN(max_wqs, WQS_BLOCKS_PER_PAGE); + if (max_wqs > WQS_MAX_NUM_BLOCKS) { + dev_err(&pdev->dev, "Invalid max_wqs = %d\n", max_wqs); + return -EINVAL; + } + + wqs->hwif = hwif; + wqs->num_pages = max_wqs / WQS_BLOCKS_PER_PAGE; + + if (alloc_page_arrays(wqs)) { + dev_err(&pdev->dev, + "Failed to allocate mem for page addresses\n"); + return -ENOMEM; + } + + for (page_idx = 0; page_idx < wqs->num_pages; page_idx++) { + err = wqs_allocate_page(wqs, page_idx); + if (err) { + dev_err(&pdev->dev, "Failed wq page allocation\n"); + goto err_wq_allocate_page; + } + } + + wqs->free_blocks = devm_kzalloc(&pdev->dev, WQS_FREE_BLOCKS_SIZE(wqs), + GFP_KERNEL); + if (!wqs->free_blocks) { + err = -ENOMEM; + goto err_alloc_blocks; + } + + init_wqs_blocks_arr(wqs); + return 0; + +err_alloc_blocks: +err_wq_allocate_page: + for (i = 0; i < page_idx; i++) + wqs_free_page(wqs, i); + + free_page_arrays(wqs); + return err; +} + +/** + * hinic_wqs_free - free Work Queues set + * @wqs: Work Queue Set + **/ +void hinic_wqs_free(struct hinic_wqs *wqs) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + int page_idx; + + devm_kfree(&pdev->dev, wqs->free_blocks); + + for (page_idx = 0; page_idx < wqs->num_pages; page_idx++) + wqs_free_page(wqs, page_idx); + + free_page_arrays(wqs); +} + +/** + * alloc_wqes_shadow - allocate WQE shadows for WQ + * @wq: WQ to allocate shadows for + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_wqes_shadow(struct hinic_wq *wq) +{ + struct hinic_hwif *hwif = wq->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t size; + + size = wq->num_q_pages * wq->max_wqe_size; + wq->shadow_wqe = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wq->shadow_wqe) + return -ENOMEM; + + size = wq->num_q_pages * sizeof(wq->prod_idx); + wq->shadow_idx = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wq->shadow_idx) + goto err_shadow_idx; + + return 0; + +err_shadow_idx: + devm_kfree(&pdev->dev, wq->shadow_wqe); + return -ENOMEM; +} + +/** + * free_wqes_shadow - free WQE shadows of WQ + * @wq: WQ to free shadows from + **/ +static void free_wqes_shadow(struct hinic_wq *wq) +{ + struct hinic_hwif *hwif = wq->hwif; + struct pci_dev *pdev = hwif->pdev; + + devm_kfree(&pdev->dev, wq->shadow_idx); + devm_kfree(&pdev->dev, wq->shadow_wqe); +} + +/** + * free_wq_pages - free pages of WQ + * @hwif: HW interface for releasing dma addresses + * @wq: WQ to free pages from + * @num_q_pages: number pages to free + **/ +static void free_wq_pages(struct hinic_wq *wq, struct hinic_hwif *hwif, + int num_q_pages) +{ + struct pci_dev *pdev = hwif->pdev; + int i; + + for (i = 0; i < num_q_pages; i++) { + void **vaddr = &wq->shadow_block_vaddr[i]; + u64 *paddr = &wq->block_vaddr[i]; + dma_addr_t dma_addr; + + dma_addr = (dma_addr_t)be64_to_cpu(*paddr); + dma_free_coherent(&pdev->dev, wq->wq_page_size, *vaddr, + dma_addr); + } + + free_wqes_shadow(wq); +} + +/** + * alloc_wq_pages - alloc pages for WQ + * @hwif: HW interface for allocating dma addresses + * @wq: WQ to allocate pages for + * @max_pages: maximum pages allowed + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_wq_pages(struct hinic_wq *wq, struct hinic_hwif *hwif, + int max_pages) +{ + struct pci_dev *pdev = hwif->pdev; + int i, err, num_q_pages; + + num_q_pages = ALIGN(WQ_SIZE(wq), wq->wq_page_size) / wq->wq_page_size; + if (num_q_pages > max_pages) { + dev_err(&pdev->dev, "Number wq pages exceeds the limit\n"); + return -EINVAL; + } + + if (num_q_pages & (num_q_pages - 1)) { + dev_err(&pdev->dev, "Number wq pages must be power of 2\n"); + return -EINVAL; + } + + wq->num_q_pages = num_q_pages; + + err = alloc_wqes_shadow(wq); + if (err) { + dev_err(&pdev->dev, "Failed to allocate wqe shadow\n"); + return err; + } + + for (i = 0; i < num_q_pages; i++) { + void **vaddr = &wq->shadow_block_vaddr[i]; + u64 *paddr = &wq->block_vaddr[i]; + dma_addr_t dma_addr; + + *vaddr = dma_zalloc_coherent(&pdev->dev, wq->wq_page_size, + &dma_addr, GFP_KERNEL); + if (!*vaddr) { + dev_err(&pdev->dev, "Failed to allocate wq page\n"); + goto err_alloc_wq_pages; + } + + /* HW uses Big Endian Format */ + *paddr = cpu_to_be64(dma_addr); + } + + return 0; + +err_alloc_wq_pages: + free_wq_pages(wq, hwif, i); + return -ENOMEM; +} + +/** + * hinic_wq_allocate - Allocate the WQ resources from the WQS + * @wqs: WQ set from which to allocate the WQ resources + * @wq: WQ to allocate resources for it from the WQ set + * @wqebb_size: Work Queue Block Byte Size + * @wq_page_size: the page size in the Work Queue + * @q_depth: number of wqebbs in WQ + * @max_wqe_size: maximum WQE size that will be used in the WQ + * + * Return 0 - Success, negative - Failure + **/ +int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq, + u16 wqebb_size, u16 wq_page_size, u16 q_depth, + u16 max_wqe_size) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 num_wqebbs_per_page; + int err; + + if (wqebb_size == 0) { + dev_err(&pdev->dev, "wqebb_size must be > 0\n"); + return -EINVAL; + } + + if (wq_page_size == 0) { + dev_err(&pdev->dev, "wq_page_size must be > 0\n"); + return -EINVAL; + } + + if (q_depth & (q_depth - 1)) { + dev_err(&pdev->dev, "WQ q_depth must be power of 2\n"); + return -EINVAL; + } + + num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size; + + if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) { + dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n"); + return -EINVAL; + } + + wq->hwif = hwif; + + err = wqs_next_block(wqs, &wq->page_idx, &wq->block_idx); + if (err) { + dev_err(&pdev->dev, "Failed to get free wqs next block\n"); + return err; + } + + wq->wqebb_size = wqebb_size; + wq->wq_page_size = wq_page_size; + wq->q_depth = q_depth; + wq->max_wqe_size = max_wqe_size; + wq->num_wqebbs_per_page = num_wqebbs_per_page; + + wq->block_vaddr = WQ_BASE_VADDR(wqs, wq); + wq->shadow_block_vaddr = WQ_BASE_ADDR(wqs, wq); + wq->block_paddr = WQ_BASE_PADDR(wqs, wq); + + err = alloc_wq_pages(wq, wqs->hwif, WQ_MAX_PAGES); + if (err) { + dev_err(&pdev->dev, "Failed to allocate wq pages\n"); + goto err_alloc_wq_pages; + } + + atomic_set(&wq->cons_idx, 0); + atomic_set(&wq->prod_idx, 0); + atomic_set(&wq->delta, q_depth); + wq->mask = q_depth - 1; + + return 0; + +err_alloc_wq_pages: + wqs_return_block(wqs, wq->page_idx, wq->block_idx); + return err; +} + +/** + * hinic_wq_free - Free the WQ resources to the WQS + * @wqs: WQ set to free the WQ resources to it + * @wq: WQ to free its resources to the WQ set resources + **/ +void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq) +{ + free_wq_pages(wq, wqs->hwif, wq->num_q_pages); + + wqs_return_block(wqs, wq->page_idx, wq->block_idx); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h new file mode 100644 index 000000000000..7c114daf13ef --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h @@ -0,0 +1,86 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_WQ_H +#define HINIC_HW_WQ_H + +#include +#include +#include + +#include "hinic_hw_if.h" + +struct hinic_free_block { + int page_idx; + int block_idx; +}; + +struct hinic_wq { + struct hinic_hwif *hwif; + + int page_idx; + int block_idx; + + u16 wqebb_size; + u16 wq_page_size; + u16 q_depth; + u16 max_wqe_size; + u16 num_wqebbs_per_page; + + /* The addresses are 64 bit in the HW */ + u64 block_paddr; + void **shadow_block_vaddr; + u64 *block_vaddr; + + int num_q_pages; + u8 *shadow_wqe; + u16 *shadow_idx; + + atomic_t cons_idx; + atomic_t prod_idx; + atomic_t delta; + u16 mask; +}; + +struct hinic_wqs { + struct hinic_hwif *hwif; + int num_pages; + + /* The addresses are 64 bit in the HW */ + u64 *page_paddr; + u64 **page_vaddr; + void ***shadow_page_vaddr; + + struct hinic_free_block *free_blocks; + int alloc_blk_pos; + int return_blk_pos; + int num_free_blks; + + /* Lock for getting a free block from the WQ set */ + struct semaphore alloc_blocks_lock; +}; + +int hinic_wqs_alloc(struct hinic_wqs *wqs, int num_wqs, + struct hinic_hwif *hwif); + +void hinic_wqs_free(struct hinic_wqs *wqs); + +int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq, + u16 wqebb_size, u16 wq_page_size, u16 q_depth, + u16 max_wqe_size); + +void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h new file mode 100644 index 000000000000..d727c4dc9d6c --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h @@ -0,0 +1,253 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_WQE_H +#define HINIC_HW_WQE_H + +#include "hinic_common.h" + +#define HINIC_SQ_CTRL_BUFDESC_SECT_LEN_SHIFT 0 +#define HINIC_SQ_CTRL_TASKSECT_LEN_SHIFT 16 +#define HINIC_SQ_CTRL_DATA_FORMAT_SHIFT 22 +#define HINIC_SQ_CTRL_LEN_SHIFT 29 + +#define HINIC_SQ_CTRL_BUFDESC_SECT_LEN_MASK 0xFF +#define HINIC_SQ_CTRL_TASKSECT_LEN_MASK 0x1F +#define HINIC_SQ_CTRL_DATA_FORMAT_MASK 0x1 +#define HINIC_SQ_CTRL_LEN_MASK 0x3 + +#define HINIC_SQ_CTRL_QUEUE_INFO_MSS_SHIFT 13 + +#define HINIC_SQ_CTRL_QUEUE_INFO_MSS_MASK 0x3FFF + +#define HINIC_SQ_CTRL_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTRL_##member##_MASK) \ + << HINIC_SQ_CTRL_##member##_SHIFT) + +#define HINIC_SQ_CTRL_GET(val, member) \ + (((val) >> HINIC_SQ_CTRL_##member##_SHIFT) \ + & HINIC_SQ_CTRL_##member##_MASK) + +#define HINIC_SQ_TASK_INFO0_L2HDR_LEN_SHIFT 0 +#define HINIC_SQ_TASK_INFO0_L4_OFFLOAD_SHIFT 8 +#define HINIC_SQ_TASK_INFO0_INNER_L3TYPE_SHIFT 10 +#define HINIC_SQ_TASK_INFO0_VLAN_OFFLOAD_SHIFT 12 +#define HINIC_SQ_TASK_INFO0_PARSE_FLAG_SHIFT 13 +/* 1 bit reserved */ +#define HINIC_SQ_TASK_INFO0_TSO_FLAG_SHIFT 15 +#define HINIC_SQ_TASK_INFO0_VLAN_TAG_SHIFT 16 + +#define HINIC_SQ_TASK_INFO0_L2HDR_LEN_MASK 0xFF +#define HINIC_SQ_TASK_INFO0_L4_OFFLOAD_MASK 0x3 +#define HINIC_SQ_TASK_INFO0_INNER_L3TYPE_MASK 0x3 +#define HINIC_SQ_TASK_INFO0_VLAN_OFFLOAD_MASK 0x1 +#define HINIC_SQ_TASK_INFO0_PARSE_FLAG_MASK 0x1 +/* 1 bit reserved */ +#define HINIC_SQ_TASK_INFO0_TSO_FLAG_MASK 0x1 +#define HINIC_SQ_TASK_INFO0_VLAN_TAG_MASK 0xFFFF + +#define HINIC_SQ_TASK_INFO0_SET(val, member) \ + (((u32)(val) & HINIC_SQ_TASK_INFO0_##member##_MASK) << \ + HINIC_SQ_TASK_INFO0_##member##_SHIFT) + +/* 8 bits reserved */ +#define HINIC_SQ_TASK_INFO1_MEDIA_TYPE_SHIFT 8 +#define HINIC_SQ_TASK_INFO1_INNER_L4_LEN_SHIFT 16 +#define HINIC_SQ_TASK_INFO1_INNER_L3_LEN_SHIFT 24 + +/* 8 bits reserved */ +#define HINIC_SQ_TASK_INFO1_MEDIA_TYPE_MASK 0xFF +#define HINIC_SQ_TASK_INFO1_INNER_L4_LEN_MASK 0xFF +#define HINIC_SQ_TASK_INFO1_INNER_L3_LEN_MASK 0xFF + +#define HINIC_SQ_TASK_INFO1_SET(val, member) \ + (((u32)(val) & HINIC_SQ_TASK_INFO1_##member##_MASK) << \ + HINIC_SQ_TASK_INFO1_##member##_SHIFT) + +#define HINIC_SQ_TASK_INFO2_TUNNEL_L4_LEN_SHIFT 0 +#define HINIC_SQ_TASK_INFO2_OUTER_L3_LEN_SHIFT 12 +#define HINIC_SQ_TASK_INFO2_TUNNEL_L4TYPE_SHIFT 19 +/* 1 bit reserved */ +#define HINIC_SQ_TASK_INFO2_OUTER_L3TYPE_SHIFT 22 +/* 8 bits reserved */ + +#define HINIC_SQ_TASK_INFO2_TUNNEL_L4_LEN_MASK 0xFFF +#define HINIC_SQ_TASK_INFO2_OUTER_L3_LEN_MASK 0x7F +#define HINIC_SQ_TASK_INFO2_TUNNEL_L4TYPE_MASK 0x3 +/* 1 bit reserved */ +#define HINIC_SQ_TASK_INFO2_OUTER_L3TYPE_MASK 0x3 +/* 8 bits reserved */ + +#define HINIC_SQ_TASK_INFO2_SET(val, member) \ + (((u32)(val) & HINIC_SQ_TASK_INFO2_##member##_MASK) << \ + HINIC_SQ_TASK_INFO2_##member##_SHIFT) + +/* 31 bits reserved */ +#define HINIC_SQ_TASK_INFO4_L2TYPE_SHIFT 31 + +/* 31 bits reserved */ +#define HINIC_SQ_TASK_INFO4_L2TYPE_MASK 0x1 + +#define HINIC_SQ_TASK_INFO4_SET(val, member) \ + (((u32)(val) & HINIC_SQ_TASK_INFO4_##member##_MASK) << \ + HINIC_SQ_TASK_INFO4_##member##_SHIFT) + +#define HINIC_RQ_CQE_STATUS_RXDONE_SHIFT 31 + +#define HINIC_RQ_CQE_STATUS_RXDONE_MASK 0x1 + +#define HINIC_RQ_CQE_STATUS_GET(val, member) \ + (((val) >> HINIC_RQ_CQE_STATUS_##member##_SHIFT) & \ + HINIC_RQ_CQE_STATUS_##member##_MASK) + +#define HINIC_RQ_CQE_STATUS_CLEAR(val, member) \ + ((val) & (~(HINIC_RQ_CQE_STATUS_##member##_MASK << \ + HINIC_RQ_CQE_STATUS_##member##_SHIFT))) + +#define HINIC_RQ_CQE_SGE_LEN_SHIFT 16 + +#define HINIC_RQ_CQE_SGE_LEN_MASK 0xFFFF + +#define HINIC_RQ_CQE_SGE_GET(val, member) \ + (((val) >> HINIC_RQ_CQE_SGE_##member##_SHIFT) & \ + HINIC_RQ_CQE_SGE_##member##_MASK) + +#define HINIC_RQ_CTRL_BUFDESC_SECT_LEN_SHIFT 0 +#define HINIC_RQ_CTRL_COMPLETE_FORMAT_SHIFT 15 +#define HINIC_RQ_CTRL_COMPLETE_LEN_SHIFT 27 +#define HINIC_RQ_CTRL_LEN_SHIFT 29 + +#define HINIC_RQ_CTRL_BUFDESC_SECT_LEN_MASK 0xFF +#define HINIC_RQ_CTRL_COMPLETE_FORMAT_MASK 0x1 +#define HINIC_RQ_CTRL_COMPLETE_LEN_MASK 0x3 +#define HINIC_RQ_CTRL_LEN_MASK 0x3 + +#define HINIC_RQ_CTRL_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTRL_##member##_MASK) << \ + HINIC_RQ_CTRL_##member##_SHIFT) + +#define HINIC_SQ_WQE_SIZE(nr_sges) \ + (sizeof(struct hinic_sq_ctrl) + \ + sizeof(struct hinic_sq_task) + \ + (nr_sges) * sizeof(struct hinic_sq_bufdesc)) + +#define HINIC_MAX_SQ_BUFDESCS 17 + +#define HINIC_SQ_WQE_MAX_SIZE 320 +#define HINIC_RQ_WQE_SIZE 32 + +enum hinic_l4offload_type { + HINIC_L4_OFF_DISABLE = 0, + HINIC_TCP_OFFLOAD_ENABLE = 1, + HINIC_SCTP_OFFLOAD_ENABLE = 2, + HINIC_UDP_OFFLOAD_ENABLE = 3, +}; + +enum hinic_vlan_offload { + HINIC_VLAN_OFF_DISABLE = 0, + HINIC_VLAN_OFF_ENABLE = 1, +}; + +enum hinic_pkt_parsed { + HINIC_PKT_NOT_PARSED = 0, + HINIC_PKT_PARSED = 1, +}; + +enum hinic_outer_l3type { + HINIC_OUTER_L3TYPE_UNKNOWN = 0, + HINIC_OUTER_L3TYPE_IPV6 = 1, + HINIC_OUTER_L3TYPE_IPV4_NO_CHKSUM = 2, + HINIC_OUTER_L3TYPE_IPV4_CHKSUM = 3, +}; + +enum hinic_media_type { + HINIC_MEDIA_UNKNOWN = 0, +}; + +enum hinic_l2type { + HINIC_L2TYPE_ETH = 0, +}; + +enum hinc_tunnel_l4type { + HINIC_TUNNEL_L4TYPE_UNKNOWN = 0, +}; + +struct hinic_sq_ctrl { + u32 ctrl_info; + u32 queue_info; +}; + +struct hinic_sq_task { + u32 pkt_info0; + u32 pkt_info1; + u32 pkt_info2; + u32 ufo_v6_identify; + u32 pkt_info4; + u32 zero_pad; +}; + +struct hinic_sq_bufdesc { + struct hinic_sge sge; + u32 rsvd; +}; + +struct hinic_sq_wqe { + struct hinic_sq_ctrl ctrl; + struct hinic_sq_task task; + struct hinic_sq_bufdesc buf_descs[HINIC_MAX_SQ_BUFDESCS]; +}; + +struct hinic_rq_cqe { + u32 status; + u32 len; + + u32 rsvd2; + u32 rsvd3; + u32 rsvd4; + u32 rsvd5; + u32 rsvd6; + u32 rsvd7; +}; + +struct hinic_rq_ctrl { + u32 ctrl_info; +}; + +struct hinic_rq_cqe_sect { + struct hinic_sge sge; + u32 rsvd; +}; + +struct hinic_rq_bufdesc { + u32 hi_addr; + u32 lo_addr; +}; + +struct hinic_rq_wqe { + struct hinic_rq_ctrl ctrl; + u32 rsvd; + struct hinic_rq_cqe_sect cqe_sect; + struct hinic_rq_bufdesc buf_desc; +}; + +struct hinic_hw_wqe { + /* HW Format */ + union { + struct hinic_sq_wqe sq_wqe; + struct hinic_rq_wqe rq_wqe; + }; +}; + +#endif -- cgit v1.2.3 From f91090f7da3a215e3cf8f678ab71ad65d1d627a1 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:58 +0800 Subject: net-next/hinic: Add qp resources Create the resources for queue pair operations: doorbell area, consumer index address and producer index address. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/Makefile | 4 +- drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 1 + drivers/net/ethernet/huawei/hinic/hinic_hw_io.c | 164 ++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_io.h | 27 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c | 266 ++++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h | 50 ++++- 6 files changed, 507 insertions(+), 5 deletions(-) create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index 0575a3452692..84815f7c28dc 100644 --- a/drivers/net/ethernet/huawei/hinic/Makefile +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_HINIC) += hinic.o hinic-y := hinic_main.o hinic_tx.o hinic_rx.o hinic_port.o hinic_hw_dev.o \ - hinic_hw_io.o hinic_hw_wq.o hinic_hw_mgmt.o hinic_hw_api_cmd.o \ - hinic_hw_eqs.o hinic_hw_if.o + hinic_hw_io.o hinic_hw_qp.o hinic_hw_wq.o hinic_hw_mgmt.o \ + hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index 228069895f3b..8f5919540c19 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -137,6 +137,7 @@ #define HINIC_IS_PPF(hwif) (HINIC_FUNC_TYPE(hwif) == HINIC_PPF) #define HINIC_PCI_CFG_REGS_BAR 0 +#define HINIC_PCI_DB_BAR 4 #define HINIC_PCIE_ST_DISABLE 0 #define HINIC_PCIE_AT_DISABLE 0 diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c index 1bf944e5aa28..ad12cc77dc5c 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c @@ -13,11 +13,16 @@ * */ +#include #include #include #include #include #include +#include +#include +#include +#include #include "hinic_hw_if.h" #include "hinic_hw_wqe.h" @@ -25,6 +30,76 @@ #include "hinic_hw_qp.h" #include "hinic_hw_io.h" +#define CI_Q_ADDR_SIZE sizeof(u32) + +#define CI_ADDR(base_addr, q_id) ((base_addr) + \ + (q_id) * CI_Q_ADDR_SIZE) + +#define CI_TABLE_SIZE(num_qps) ((num_qps) * CI_Q_ADDR_SIZE) + +#define DB_IDX(db, db_base) \ + (((unsigned long)(db) - (unsigned long)(db_base)) / HINIC_DB_PAGE_SIZE) + +static void init_db_area_idx(struct hinic_free_db_area *free_db_area) +{ + int i; + + for (i = 0; i < HINIC_DB_MAX_AREAS; i++) + free_db_area->db_idx[i] = i; + + free_db_area->alloc_pos = 0; + free_db_area->return_pos = HINIC_DB_MAX_AREAS; + + free_db_area->num_free = HINIC_DB_MAX_AREAS; + + sema_init(&free_db_area->idx_lock, 1); +} + +static void __iomem *get_db_area(struct hinic_func_to_io *func_to_io) +{ + struct hinic_free_db_area *free_db_area = &func_to_io->free_db_area; + int pos, idx; + + down(&free_db_area->idx_lock); + + free_db_area->num_free--; + + if (free_db_area->num_free < 0) { + free_db_area->num_free++; + up(&free_db_area->idx_lock); + return ERR_PTR(-ENOMEM); + } + + pos = free_db_area->alloc_pos++; + pos &= HINIC_DB_MAX_AREAS - 1; + + idx = free_db_area->db_idx[pos]; + + free_db_area->db_idx[pos] = -1; + + up(&free_db_area->idx_lock); + + return func_to_io->db_base + idx * HINIC_DB_PAGE_SIZE; +} + +static void return_db_area(struct hinic_func_to_io *func_to_io, + void __iomem *db_base) +{ + struct hinic_free_db_area *free_db_area = &func_to_io->free_db_area; + int pos, idx = DB_IDX(db_base, func_to_io->db_base); + + down(&free_db_area->idx_lock); + + pos = free_db_area->return_pos++; + pos &= HINIC_DB_MAX_AREAS - 1; + + free_db_area->db_idx[pos] = idx; + + free_db_area->num_free++; + + up(&free_db_area->idx_lock); +} + /** * init_qp - Initialize a Queue Pair * @func_to_io: func to io channel that holds the IO components @@ -42,6 +117,7 @@ static int init_qp(struct hinic_func_to_io *func_to_io, { struct hinic_hwif *hwif = func_to_io->hwif; struct pci_dev *pdev = hwif->pdev; + void __iomem *db_base; int err; qp->q_id = q_id; @@ -62,8 +138,42 @@ static int init_qp(struct hinic_func_to_io *func_to_io, goto err_rq_alloc; } + db_base = get_db_area(func_to_io); + if (IS_ERR(db_base)) { + dev_err(&pdev->dev, "Failed to get DB area for SQ\n"); + err = PTR_ERR(db_base); + goto err_get_db; + } + + func_to_io->sq_db[q_id] = db_base; + + err = hinic_init_sq(&qp->sq, hwif, &func_to_io->sq_wq[q_id], + sq_msix_entry, + CI_ADDR(func_to_io->ci_addr_base, q_id), + CI_ADDR(func_to_io->ci_dma_base, q_id), db_base); + if (err) { + dev_err(&pdev->dev, "Failed to init SQ\n"); + goto err_sq_init; + } + + err = hinic_init_rq(&qp->rq, hwif, &func_to_io->rq_wq[q_id], + rq_msix_entry); + if (err) { + dev_err(&pdev->dev, "Failed to init RQ\n"); + goto err_rq_init; + } + return 0; +err_rq_init: + hinic_clean_sq(&qp->sq); + +err_sq_init: + return_db_area(func_to_io, db_base); + +err_get_db: + hinic_wq_free(&func_to_io->wqs, &func_to_io->rq_wq[q_id]); + err_rq_alloc: hinic_wq_free(&func_to_io->wqs, &func_to_io->sq_wq[q_id]); return err; @@ -79,6 +189,11 @@ static void destroy_qp(struct hinic_func_to_io *func_to_io, { int q_id = qp->q_id; + hinic_clean_rq(&qp->rq); + hinic_clean_sq(&qp->sq); + + return_db_area(func_to_io, func_to_io->sq_db[q_id]); + hinic_wq_free(&func_to_io->wqs, &func_to_io->rq_wq[q_id]); hinic_wq_free(&func_to_io->wqs, &func_to_io->sq_wq[q_id]); } @@ -100,7 +215,8 @@ int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, { struct hinic_hwif *hwif = func_to_io->hwif; struct pci_dev *pdev = hwif->pdev; - size_t qps_size, wq_size; + size_t qps_size, wq_size, db_size; + void *ci_addr_base; int i, j, err; qps_size = num_qps * sizeof(*func_to_io->qps); @@ -122,6 +238,24 @@ int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, goto err_rq_wq; } + db_size = num_qps * sizeof(*func_to_io->sq_db); + func_to_io->sq_db = devm_kzalloc(&pdev->dev, db_size, GFP_KERNEL); + if (!func_to_io->sq_db) { + err = -ENOMEM; + goto err_sq_db; + } + + ci_addr_base = dma_zalloc_coherent(&pdev->dev, CI_TABLE_SIZE(num_qps), + &func_to_io->ci_dma_base, + GFP_KERNEL); + if (!ci_addr_base) { + dev_err(&pdev->dev, "Failed to allocate CI area\n"); + err = -ENOMEM; + goto err_ci_base; + } + + func_to_io->ci_addr_base = ci_addr_base; + for (i = 0; i < num_qps; i++) { err = init_qp(func_to_io, &func_to_io->qps[i], i, &sq_msix_entries[i], &rq_msix_entries[i]); @@ -137,6 +271,13 @@ err_init_qp: for (j = 0; j < i; j++) destroy_qp(func_to_io, &func_to_io->qps[j]); + dma_free_coherent(&pdev->dev, CI_TABLE_SIZE(num_qps), + func_to_io->ci_addr_base, func_to_io->ci_dma_base); + +err_ci_base: + devm_kfree(&pdev->dev, func_to_io->sq_db); + +err_sq_db: devm_kfree(&pdev->dev, func_to_io->rq_wq); err_rq_wq: @@ -156,11 +297,19 @@ void hinic_io_destroy_qps(struct hinic_func_to_io *func_to_io, int num_qps) { struct hinic_hwif *hwif = func_to_io->hwif; struct pci_dev *pdev = hwif->pdev; + size_t ci_table_size; int i; + ci_table_size = CI_TABLE_SIZE(num_qps); + for (i = 0; i < num_qps; i++) destroy_qp(func_to_io, &func_to_io->qps[i]); + dma_free_coherent(&pdev->dev, ci_table_size, func_to_io->ci_addr_base, + func_to_io->ci_dma_base); + + devm_kfree(&pdev->dev, func_to_io->sq_db); + devm_kfree(&pdev->dev, func_to_io->rq_wq); devm_kfree(&pdev->dev, func_to_io->sq_wq); @@ -194,7 +343,19 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io, return err; } + func_to_io->db_base = pci_ioremap_bar(pdev, HINIC_PCI_DB_BAR); + if (!func_to_io->db_base) { + dev_err(&pdev->dev, "Failed to remap IO DB area\n"); + err = -ENOMEM; + goto err_db_ioremap; + } + + init_db_area_idx(&func_to_io->free_db_area); return 0; + +err_db_ioremap: + hinic_wqs_free(&func_to_io->wqs); + return err; } /** @@ -203,5 +364,6 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io, **/ void hinic_io_free(struct hinic_func_to_io *func_to_io) { + iounmap(func_to_io->db_base); hinic_wqs_free(&func_to_io->wqs); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h index 6cacb8eddd2e..2d85a38a1df6 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h @@ -18,11 +18,30 @@ #include #include +#include +#include #include "hinic_hw_if.h" #include "hinic_hw_wq.h" #include "hinic_hw_qp.h" +#define HINIC_DB_PAGE_SIZE SZ_4K +#define HINIC_DB_SIZE SZ_4M + +#define HINIC_DB_MAX_AREAS (HINIC_DB_SIZE / HINIC_DB_PAGE_SIZE) + +struct hinic_free_db_area { + int db_idx[HINIC_DB_MAX_AREAS]; + + int alloc_pos; + int return_pos; + + int num_free; + + /* Lock for getting db area */ + struct semaphore idx_lock; +}; + struct hinic_func_to_io { struct hinic_hwif *hwif; @@ -33,6 +52,14 @@ struct hinic_func_to_io { struct hinic_qp *qps; u16 max_qps; + + void __iomem **sq_db; + void __iomem *db_base; + + void *ci_addr_base; + dma_addr_t ci_dma_base; + + struct hinic_free_db_area free_db_area; }; int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c new file mode 100644 index 000000000000..2b77b592e90d --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c @@ -0,0 +1,266 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_wq.h" +#include "hinic_hw_qp.h" + +#define SQ_DB_OFF SZ_2K + +/** + * alloc_sq_skb_arr - allocate sq array for saved skb + * @sq: HW Send Queue + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_sq_skb_arr(struct hinic_sq *sq) +{ + struct hinic_wq *wq = sq->wq; + size_t skb_arr_size; + + skb_arr_size = wq->q_depth * sizeof(*sq->saved_skb); + sq->saved_skb = vzalloc(skb_arr_size); + if (!sq->saved_skb) + return -ENOMEM; + + return 0; +} + +/** + * free_sq_skb_arr - free sq array for saved skb + * @sq: HW Send Queue + **/ +static void free_sq_skb_arr(struct hinic_sq *sq) +{ + vfree(sq->saved_skb); +} + +/** + * alloc_rq_skb_arr - allocate rq array for saved skb + * @rq: HW Receive Queue + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_rq_skb_arr(struct hinic_rq *rq) +{ + struct hinic_wq *wq = rq->wq; + size_t skb_arr_size; + + skb_arr_size = wq->q_depth * sizeof(*rq->saved_skb); + rq->saved_skb = vzalloc(skb_arr_size); + if (!rq->saved_skb) + return -ENOMEM; + + return 0; +} + +/** + * free_rq_skb_arr - free rq array for saved skb + * @rq: HW Receive Queue + **/ +static void free_rq_skb_arr(struct hinic_rq *rq) +{ + vfree(rq->saved_skb); +} + +/** + * hinic_init_sq - Initialize HW Send Queue + * @sq: HW Send Queue + * @hwif: HW Interface for accessing HW + * @wq: Work Queue for the data of the SQ + * @entry: msix entry for sq + * @ci_addr: address for reading the current HW consumer index + * @ci_dma_addr: dma address for reading the current HW consumer index + * @db_base: doorbell base address + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_sq(struct hinic_sq *sq, struct hinic_hwif *hwif, + struct hinic_wq *wq, struct msix_entry *entry, + void *ci_addr, dma_addr_t ci_dma_addr, + void __iomem *db_base) +{ + sq->hwif = hwif; + + sq->wq = wq; + + sq->irq = entry->vector; + sq->msix_entry = entry->entry; + + sq->hw_ci_addr = ci_addr; + sq->hw_ci_dma_addr = ci_dma_addr; + + sq->db_base = db_base + SQ_DB_OFF; + + return alloc_sq_skb_arr(sq); +} + +/** + * hinic_clean_sq - Clean HW Send Queue's Resources + * @sq: Send Queue + **/ +void hinic_clean_sq(struct hinic_sq *sq) +{ + free_sq_skb_arr(sq); +} + +/** + * alloc_rq_cqe - allocate rq completion queue elements + * @rq: HW Receive Queue + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_rq_cqe(struct hinic_rq *rq) +{ + struct hinic_hwif *hwif = rq->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t cqe_dma_size, cqe_size; + struct hinic_wq *wq = rq->wq; + int j, i; + + cqe_size = wq->q_depth * sizeof(*rq->cqe); + rq->cqe = vzalloc(cqe_size); + if (!rq->cqe) + return -ENOMEM; + + cqe_dma_size = wq->q_depth * sizeof(*rq->cqe_dma); + rq->cqe_dma = vzalloc(cqe_dma_size); + if (!rq->cqe_dma) + goto err_cqe_dma_arr_alloc; + + for (i = 0; i < wq->q_depth; i++) { + rq->cqe[i] = dma_zalloc_coherent(&pdev->dev, + sizeof(*rq->cqe[i]), + &rq->cqe_dma[i], GFP_KERNEL); + if (!rq->cqe[i]) + goto err_cqe_alloc; + } + + return 0; + +err_cqe_alloc: + for (j = 0; j < i; j++) + dma_free_coherent(&pdev->dev, sizeof(*rq->cqe[j]), rq->cqe[j], + rq->cqe_dma[j]); + + vfree(rq->cqe_dma); + +err_cqe_dma_arr_alloc: + vfree(rq->cqe); + return -ENOMEM; +} + +/** + * free_rq_cqe - free rq completion queue elements + * @rq: HW Receive Queue + **/ +static void free_rq_cqe(struct hinic_rq *rq) +{ + struct hinic_hwif *hwif = rq->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_wq *wq = rq->wq; + int i; + + for (i = 0; i < wq->q_depth; i++) + dma_free_coherent(&pdev->dev, sizeof(*rq->cqe[i]), rq->cqe[i], + rq->cqe_dma[i]); + + vfree(rq->cqe_dma); + vfree(rq->cqe); +} + +/** + * hinic_init_rq - Initialize HW Receive Queue + * @rq: HW Receive Queue + * @hwif: HW Interface for accessing HW + * @wq: Work Queue for the data of the RQ + * @entry: msix entry for rq + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_rq(struct hinic_rq *rq, struct hinic_hwif *hwif, + struct hinic_wq *wq, struct msix_entry *entry) +{ + struct pci_dev *pdev = hwif->pdev; + size_t pi_size; + int err; + + rq->hwif = hwif; + + rq->wq = wq; + + rq->irq = entry->vector; + rq->msix_entry = entry->entry; + + rq->buf_sz = HINIC_RX_BUF_SZ; + + err = alloc_rq_skb_arr(rq); + if (err) { + dev_err(&pdev->dev, "Failed to allocate rq priv data\n"); + return err; + } + + err = alloc_rq_cqe(rq); + if (err) { + dev_err(&pdev->dev, "Failed to allocate rq cqe\n"); + goto err_alloc_rq_cqe; + } + + /* HW requirements: Must be at least 32 bit */ + pi_size = ALIGN(sizeof(*rq->pi_virt_addr), sizeof(u32)); + rq->pi_virt_addr = dma_zalloc_coherent(&pdev->dev, pi_size, + &rq->pi_dma_addr, GFP_KERNEL); + if (!rq->pi_virt_addr) { + dev_err(&pdev->dev, "Failed to allocate PI address\n"); + err = -ENOMEM; + goto err_pi_virt; + } + + return 0; + +err_pi_virt: + free_rq_cqe(rq); + +err_alloc_rq_cqe: + free_rq_skb_arr(rq); + return err; +} + +/** + * hinic_clean_rq - Clean HW Receive Queue's Resources + * @rq: HW Receive Queue + **/ +void hinic_clean_rq(struct hinic_rq *rq) +{ + struct hinic_hwif *hwif = rq->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t pi_size; + + pi_size = ALIGN(sizeof(*rq->pi_virt_addr), sizeof(u32)); + dma_free_coherent(&pdev->dev, pi_size, rq->pi_virt_addr, + rq->pi_dma_addr); + + free_rq_cqe(rq); + free_rq_skb_arr(rq); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h index 4031728a2281..c5ec30dcda96 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -18,6 +18,12 @@ #include #include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" #define HINIC_SQ_WQEBB_SIZE 64 #define HINIC_RQ_WQEBB_SIZE 32 @@ -28,12 +34,41 @@ #define HINIC_SQ_DEPTH SZ_4K #define HINIC_RQ_DEPTH SZ_4K +#define HINIC_RX_BUF_SZ 2048 + struct hinic_sq { - /* should be implemented */ + struct hinic_hwif *hwif; + + struct hinic_wq *wq; + + u32 irq; + u16 msix_entry; + + void *hw_ci_addr; + dma_addr_t hw_ci_dma_addr; + + void __iomem *db_base; + + struct sk_buff **saved_skb; }; struct hinic_rq { - /* should be implemented */ + struct hinic_hwif *hwif; + + struct hinic_wq *wq; + + u32 irq; + u16 msix_entry; + + size_t buf_sz; + + struct sk_buff **saved_skb; + + struct hinic_rq_cqe **cqe; + dma_addr_t *cqe_dma; + + u16 *pi_virt_addr; + dma_addr_t pi_dma_addr; }; struct hinic_qp { @@ -43,4 +78,15 @@ struct hinic_qp { u16 q_id; }; +int hinic_init_sq(struct hinic_sq *sq, struct hinic_hwif *hwif, + struct hinic_wq *wq, struct msix_entry *entry, void *ci_addr, + dma_addr_t ci_dma_addr, void __iomem *db_base); + +void hinic_clean_sq(struct hinic_sq *sq); + +int hinic_init_rq(struct hinic_rq *rq, struct hinic_hwif *hwif, + struct hinic_wq *wq, struct msix_entry *entry); + +void hinic_clean_rq(struct hinic_rq *rq); + #endif -- cgit v1.2.3 From 53e7d6feb949b4df542897ab13a33fe484a45c72 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:55:59 +0800 Subject: net-next/hinic: Set qp context Update the nic about the resources of the queue pairs. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/Makefile | 5 +- drivers/net/ethernet/huawei/hinic/hinic_common.c | 55 ++++++ drivers/net/ethernet/huawei/hinic/hinic_common.h | 4 + drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c | 87 +++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h | 84 ++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 4 + drivers/net/ethernet/huawei/hinic/hinic_hw_io.c | 151 +++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_io.h | 5 + drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c | 160 +++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h | 11 ++ .../net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h | 214 +++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h | 9 + 12 files changed, 787 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_common.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index 84815f7c28dc..289ce88bb2d0 100644 --- a/drivers/net/ethernet/huawei/hinic/Makefile +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_HINIC) += hinic.o hinic-y := hinic_main.o hinic_tx.o hinic_rx.o hinic_port.o hinic_hw_dev.o \ - hinic_hw_io.o hinic_hw_qp.o hinic_hw_wq.o hinic_hw_mgmt.o \ - hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o + hinic_hw_io.o hinic_hw_qp.o hinic_hw_cmdq.o hinic_hw_wq.o \ + hinic_hw_mgmt.o hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o \ + hinic_common.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_common.c b/drivers/net/ethernet/huawei/hinic/hinic_common.c new file mode 100644 index 000000000000..1915ad63deec --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_common.c @@ -0,0 +1,55 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include + +#include "hinic_common.h" + +/** + * hinic_cpu_to_be32 - convert data to big endian 32 bit format + * @data: the data to convert + * @len: length of data to convert + **/ +void hinic_cpu_to_be32(void *data, int len) +{ + u32 *mem = data; + int i; + + len = len / sizeof(u32); + + for (i = 0; i < len; i++) { + *mem = cpu_to_be32(*mem); + mem++; + } +} + +/** + * hinic_be32_to_cpu - convert data from big endian 32 bit format + * @data: the data to convert + * @len: length of data to convert + **/ +void hinic_be32_to_cpu(void *data, int len) +{ + u32 *mem = data; + int i; + + len = len / sizeof(u32); + + for (i = 0; i < len; i++) { + *mem = be32_to_cpu(*mem); + mem++; + } +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_common.h b/drivers/net/ethernet/huawei/hinic/hinic_common.h index 6a83c157b978..0f2f4ff70c97 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_common.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_common.h @@ -22,4 +22,8 @@ struct hinic_sge { u32 len; }; +void hinic_cpu_to_be32(void *data, int len); + +void hinic_be32_to_cpu(void *data, int len); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c new file mode 100644 index 000000000000..2fd3924f4eaf --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c @@ -0,0 +1,87 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_cmdq.h" + +/** + * hinic_alloc_cmdq_buf - alloc buffer for sending command + * @cmdqs: the cmdqs + * @cmdq_buf: the buffer returned in this struct + * + * Return 0 - Success, negative - Failure + **/ +int hinic_alloc_cmdq_buf(struct hinic_cmdqs *cmdqs, + struct hinic_cmdq_buf *cmdq_buf) +{ + /* should be implemented */ + return -ENOMEM; +} + +/** + * hinic_free_cmdq_buf - free buffer + * @cmdqs: the cmdqs + * @cmdq_buf: the buffer to free that is in this struct + **/ +void hinic_free_cmdq_buf(struct hinic_cmdqs *cmdqs, + struct hinic_cmdq_buf *cmdq_buf) +{ + /* should be implemented */ +} + +/** + * hinic_cmdq_direct_resp - send command with direct data as resp + * @cmdqs: the cmdqs + * @mod: module on the card that will handle the command + * @cmd: the command + * @buf_in: the buffer for the command + * @resp: the response to return + * + * Return 0 - Success, negative - Failure + **/ +int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, + enum hinic_mod_type mod, u8 cmd, + struct hinic_cmdq_buf *buf_in, u64 *resp) +{ + /* should be implemented */ + return -EINVAL; +} + +/** + * hinic_init_cmdqs - init all cmdqs + * @cmdqs: cmdqs to init + * @hwif: HW interface for accessing cmdqs + * @db_area: doorbell areas for all the cmdqs + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, + void __iomem **db_area) +{ + /* should be implemented */ + return -EINVAL; +} + +/** + * hinic_free_cmdqs - free all cmdqs + * @cmdqs: cmdqs to free + **/ +void hinic_free_cmdqs(struct hinic_cmdqs *cmdqs) +{ + /* should be implemented */ +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h new file mode 100644 index 000000000000..c9e97cad9b29 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h @@ -0,0 +1,84 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_CMDQ_H +#define HINIC_CMDQ_H + +#include +#include +#include +#include + +#include "hinic_hw_if.h" +#include "hinic_hw_wq.h" + +#define HINIC_CMDQ_BUF_SIZE 2048 + +enum hinic_cmdq_type { + HINIC_CMDQ_SYNC, + + HINIC_MAX_CMDQ_TYPES, +}; + +struct hinic_cmdq_buf { + void *buf; + dma_addr_t dma_addr; + size_t size; +}; + +struct hinic_cmdq { + struct hinic_wq *wq; + + enum hinic_cmdq_type cmdq_type; + int wrapped; + + /* Lock for keeping the doorbell order */ + spinlock_t cmdq_lock; + + struct completion **done; + int **errcode; + + /* doorbell area */ + void __iomem *db_base; +}; + +struct hinic_cmdqs { + struct hinic_hwif *hwif; + + struct pci_pool *cmdq_buf_pool; + + struct hinic_wq *saved_wqs; + + struct hinic_cmdq_pages cmdq_pages; + + struct hinic_cmdq cmdq[HINIC_MAX_CMDQ_TYPES]; +}; + +int hinic_alloc_cmdq_buf(struct hinic_cmdqs *cmdqs, + struct hinic_cmdq_buf *cmdq_buf); + +void hinic_free_cmdq_buf(struct hinic_cmdqs *cmdqs, + struct hinic_cmdq_buf *cmdq_buf); + +int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, + enum hinic_mod_type mod, u8 cmd, + struct hinic_cmdq_buf *buf_in, u64 *out_param); + +int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, + void __iomem **db_area); + +void hinic_free_cmdqs(struct hinic_cmdqs *cmdqs); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index 5ae1c3682be0..f29fea1dc9d2 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -25,6 +25,7 @@ #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" #include "hinic_hw_mgmt.h" +#include "hinic_hw_qp_ctxt.h" #include "hinic_hw_qp.h" #include "hinic_hw_io.h" #include "hinic_hw_dev.h" @@ -76,6 +77,9 @@ static int get_capability(struct hinic_hwdev *hwdev, /* Each QP has its own (SQ + RQ) interrupts */ nic_cap->num_qps = (num_irqs - (num_aeqs + num_ceqs)) / 2; + if (nic_cap->num_qps > HINIC_Q_CTXT_MAX) + nic_cap->num_qps = HINIC_Q_CTXT_MAX; + /* num_qps must be power of 2 */ nic_cap->num_qps = BIT(fls(nic_cap->num_qps) - 1); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c index ad12cc77dc5c..bb4b93fe622a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c @@ -27,6 +27,8 @@ #include "hinic_hw_if.h" #include "hinic_hw_wqe.h" #include "hinic_hw_wq.h" +#include "hinic_hw_cmdq.h" +#include "hinic_hw_qp_ctxt.h" #include "hinic_hw_qp.h" #include "hinic_hw_io.h" @@ -40,6 +42,10 @@ #define DB_IDX(db, db_base) \ (((unsigned long)(db) - (unsigned long)(db_base)) / HINIC_DB_PAGE_SIZE) +enum io_cmd { + IO_CMD_MODIFY_QUEUE_CTXT = 0, +}; + static void init_db_area_idx(struct hinic_free_db_area *free_db_area) { int i; @@ -100,6 +106,109 @@ static void return_db_area(struct hinic_func_to_io *func_to_io, up(&free_db_area->idx_lock); } +static int write_sq_ctxts(struct hinic_func_to_io *func_to_io, u16 base_qpn, + u16 num_sqs) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct hinic_sq_ctxt_block *sq_ctxt_block; + struct pci_dev *pdev = hwif->pdev; + struct hinic_cmdq_buf cmdq_buf; + struct hinic_sq_ctxt *sq_ctxt; + struct hinic_qp *qp; + u64 out_param; + int err, i; + + err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmdq buf\n"); + return err; + } + + sq_ctxt_block = cmdq_buf.buf; + sq_ctxt = sq_ctxt_block->sq_ctxt; + + hinic_qp_prepare_header(&sq_ctxt_block->hdr, HINIC_QP_CTXT_TYPE_SQ, + num_sqs, func_to_io->max_qps); + for (i = 0; i < num_sqs; i++) { + qp = &func_to_io->qps[i]; + + hinic_sq_prepare_ctxt(&sq_ctxt[i], &qp->sq, + base_qpn + qp->q_id); + } + + cmdq_buf.size = HINIC_SQ_CTXT_SIZE(num_sqs); + + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + IO_CMD_MODIFY_QUEUE_CTXT, &cmdq_buf, + &out_param); + if ((err) || (out_param != 0)) { + dev_err(&pdev->dev, "Failed to set SQ ctxts\n"); + err = -EFAULT; + } + + hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + return err; +} + +static int write_rq_ctxts(struct hinic_func_to_io *func_to_io, u16 base_qpn, + u16 num_rqs) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct hinic_rq_ctxt_block *rq_ctxt_block; + struct pci_dev *pdev = hwif->pdev; + struct hinic_cmdq_buf cmdq_buf; + struct hinic_rq_ctxt *rq_ctxt; + struct hinic_qp *qp; + u64 out_param; + int err, i; + + err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmdq buf\n"); + return err; + } + + rq_ctxt_block = cmdq_buf.buf; + rq_ctxt = rq_ctxt_block->rq_ctxt; + + hinic_qp_prepare_header(&rq_ctxt_block->hdr, HINIC_QP_CTXT_TYPE_RQ, + num_rqs, func_to_io->max_qps); + for (i = 0; i < num_rqs; i++) { + qp = &func_to_io->qps[i]; + + hinic_rq_prepare_ctxt(&rq_ctxt[i], &qp->rq, + base_qpn + qp->q_id); + } + + cmdq_buf.size = HINIC_RQ_CTXT_SIZE(num_rqs); + + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + IO_CMD_MODIFY_QUEUE_CTXT, &cmdq_buf, + &out_param); + if ((err) || (out_param != 0)) { + dev_err(&pdev->dev, "Failed to set RQ ctxts\n"); + err = -EFAULT; + } + + hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + return err; +} + +/** + * write_qp_ctxts - write the qp ctxt to HW + * @func_to_io: func to io channel that holds the IO components + * @base_qpn: first qp number + * @num_qps: number of qps to write + * + * Return 0 - Success, negative - Failure + **/ +static int write_qp_ctxts(struct hinic_func_to_io *func_to_io, u16 base_qpn, + u16 num_qps) +{ + return (write_sq_ctxts(func_to_io, base_qpn, num_qps) || + write_rq_ctxts(func_to_io, base_qpn, num_qps)); +} + /** * init_qp - Initialize a Queue Pair * @func_to_io: func to io channel that holds the IO components @@ -265,8 +374,15 @@ int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, } } + err = write_qp_ctxts(func_to_io, base_qpn, num_qps); + if (err) { + dev_err(&pdev->dev, "Failed to init QP ctxts\n"); + goto err_write_qp_ctxts; + } + return 0; +err_write_qp_ctxts: err_init_qp: for (j = 0; j < i; j++) destroy_qp(func_to_io, &func_to_io->qps[j]); @@ -331,6 +447,8 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io, struct msix_entry *ceq_msix_entries) { struct pci_dev *pdev = hwif->pdev; + enum hinic_cmdq_type cmdq, type; + void __iomem *db_area; int err; func_to_io->hwif = hwif; @@ -351,8 +469,34 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io, } init_db_area_idx(&func_to_io->free_db_area); + + for (cmdq = HINIC_CMDQ_SYNC; cmdq < HINIC_MAX_CMDQ_TYPES; cmdq++) { + db_area = get_db_area(func_to_io); + if (IS_ERR(db_area)) { + dev_err(&pdev->dev, "Failed to get cmdq db area\n"); + err = PTR_ERR(db_area); + goto err_db_area; + } + + func_to_io->cmdq_db_area[cmdq] = db_area; + } + + err = hinic_init_cmdqs(&func_to_io->cmdqs, hwif, + func_to_io->cmdq_db_area); + if (err) { + dev_err(&pdev->dev, "Failed to initialize cmdqs\n"); + goto err_init_cmdqs; + } + return 0; +err_init_cmdqs: +err_db_area: + for (type = HINIC_CMDQ_SYNC; type < cmdq; type++) + return_db_area(func_to_io, func_to_io->cmdq_db_area[type]); + + iounmap(func_to_io->db_base); + err_db_ioremap: hinic_wqs_free(&func_to_io->wqs); return err; @@ -364,6 +508,13 @@ err_db_ioremap: **/ void hinic_io_free(struct hinic_func_to_io *func_to_io) { + enum hinic_cmdq_type cmdq; + + hinic_free_cmdqs(&func_to_io->cmdqs); + + for (cmdq = HINIC_CMDQ_SYNC; cmdq < HINIC_MAX_CMDQ_TYPES; cmdq++) + return_db_area(func_to_io, func_to_io->cmdq_db_area[cmdq]); + iounmap(func_to_io->db_base); hinic_wqs_free(&func_to_io->wqs); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h index 2d85a38a1df6..60d77b343fa7 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h @@ -23,6 +23,7 @@ #include "hinic_hw_if.h" #include "hinic_hw_wq.h" +#include "hinic_hw_cmdq.h" #include "hinic_hw_qp.h" #define HINIC_DB_PAGE_SIZE SZ_4K @@ -60,6 +61,10 @@ struct hinic_func_to_io { dma_addr_t ci_dma_base; struct hinic_free_db_area free_db_area; + + void __iomem *cmdq_db_area[HINIC_MAX_CMDQ_TYPES]; + + struct hinic_cmdqs cmdqs; }; int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c index 2b77b592e90d..13e0ff3533a4 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c @@ -21,13 +21,173 @@ #include #include #include +#include +#include +#include "hinic_common.h" #include "hinic_hw_if.h" #include "hinic_hw_wq.h" +#include "hinic_hw_qp_ctxt.h" #include "hinic_hw_qp.h" #define SQ_DB_OFF SZ_2K +/* The number of cache line to prefetch Until threshold state */ +#define WQ_PREFETCH_MAX 2 +/* The number of cache line to prefetch After threshold state */ +#define WQ_PREFETCH_MIN 1 +/* Threshold state */ +#define WQ_PREFETCH_THRESHOLD 256 + +/* sizes of the SQ/RQ ctxt */ +#define Q_CTXT_SIZE 48 +#define CTXT_RSVD 240 + +#define SQ_CTXT_OFFSET(max_sqs, max_rqs, q_id) \ + (((max_rqs) + (max_sqs)) * CTXT_RSVD + (q_id) * Q_CTXT_SIZE) + +#define RQ_CTXT_OFFSET(max_sqs, max_rqs, q_id) \ + (((max_rqs) + (max_sqs)) * CTXT_RSVD + \ + (max_sqs + (q_id)) * Q_CTXT_SIZE) + +#define SIZE_16BYTES(size) (ALIGN(size, 16) >> 4) + +void hinic_qp_prepare_header(struct hinic_qp_ctxt_header *qp_ctxt_hdr, + enum hinic_qp_ctxt_type ctxt_type, + u16 num_queues, u16 max_queues) +{ + u16 max_sqs = max_queues; + u16 max_rqs = max_queues; + + qp_ctxt_hdr->num_queues = num_queues; + qp_ctxt_hdr->queue_type = ctxt_type; + + if (ctxt_type == HINIC_QP_CTXT_TYPE_SQ) + qp_ctxt_hdr->addr_offset = SQ_CTXT_OFFSET(max_sqs, max_rqs, 0); + else + qp_ctxt_hdr->addr_offset = RQ_CTXT_OFFSET(max_sqs, max_rqs, 0); + + qp_ctxt_hdr->addr_offset = SIZE_16BYTES(qp_ctxt_hdr->addr_offset); + + hinic_cpu_to_be32(qp_ctxt_hdr, sizeof(*qp_ctxt_hdr)); +} + +void hinic_sq_prepare_ctxt(struct hinic_sq_ctxt *sq_ctxt, + struct hinic_sq *sq, u16 global_qid) +{ + u32 wq_page_pfn_hi, wq_page_pfn_lo, wq_block_pfn_hi, wq_block_pfn_lo; + u64 wq_page_addr, wq_page_pfn, wq_block_pfn; + u16 pi_start, ci_start; + struct hinic_wq *wq; + + wq = sq->wq; + ci_start = atomic_read(&wq->cons_idx); + pi_start = atomic_read(&wq->prod_idx); + + /* Read the first page paddr from the WQ page paddr ptrs */ + wq_page_addr = be64_to_cpu(*wq->block_vaddr); + + wq_page_pfn = HINIC_WQ_PAGE_PFN(wq_page_addr); + wq_page_pfn_hi = upper_32_bits(wq_page_pfn); + wq_page_pfn_lo = lower_32_bits(wq_page_pfn); + + wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq->block_paddr); + wq_block_pfn_hi = upper_32_bits(wq_block_pfn); + wq_block_pfn_lo = lower_32_bits(wq_block_pfn); + + sq_ctxt->ceq_attr = HINIC_SQ_CTXT_CEQ_ATTR_SET(global_qid, + GLOBAL_SQ_ID) | + HINIC_SQ_CTXT_CEQ_ATTR_SET(0, EN); + + sq_ctxt->ci_wrapped = HINIC_SQ_CTXT_CI_SET(ci_start, IDX) | + HINIC_SQ_CTXT_CI_SET(1, WRAPPED); + + sq_ctxt->wq_hi_pfn_pi = + HINIC_SQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi, HI_PFN) | + HINIC_SQ_CTXT_WQ_PAGE_SET(pi_start, PI); + + sq_ctxt->wq_lo_pfn = wq_page_pfn_lo; + + sq_ctxt->pref_cache = + HINIC_SQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) | + HINIC_SQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) | + HINIC_SQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD, CACHE_THRESHOLD); + + sq_ctxt->pref_wrapped = 1; + + sq_ctxt->pref_wq_hi_pfn_ci = + HINIC_SQ_CTXT_PREF_SET(ci_start, CI) | + HINIC_SQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_HI_PFN); + + sq_ctxt->pref_wq_lo_pfn = wq_page_pfn_lo; + + sq_ctxt->wq_block_hi_pfn = + HINIC_SQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, HI_PFN); + + sq_ctxt->wq_block_lo_pfn = wq_block_pfn_lo; + + hinic_cpu_to_be32(sq_ctxt, sizeof(*sq_ctxt)); +} + +void hinic_rq_prepare_ctxt(struct hinic_rq_ctxt *rq_ctxt, + struct hinic_rq *rq, u16 global_qid) +{ + u32 wq_page_pfn_hi, wq_page_pfn_lo, wq_block_pfn_hi, wq_block_pfn_lo; + u64 wq_page_addr, wq_page_pfn, wq_block_pfn; + u16 pi_start, ci_start; + struct hinic_wq *wq; + + wq = rq->wq; + ci_start = atomic_read(&wq->cons_idx); + pi_start = atomic_read(&wq->prod_idx); + + /* Read the first page paddr from the WQ page paddr ptrs */ + wq_page_addr = be64_to_cpu(*wq->block_vaddr); + + wq_page_pfn = HINIC_WQ_PAGE_PFN(wq_page_addr); + wq_page_pfn_hi = upper_32_bits(wq_page_pfn); + wq_page_pfn_lo = lower_32_bits(wq_page_pfn); + + wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq->block_paddr); + wq_block_pfn_hi = upper_32_bits(wq_block_pfn); + wq_block_pfn_lo = lower_32_bits(wq_block_pfn); + + rq_ctxt->ceq_attr = HINIC_RQ_CTXT_CEQ_ATTR_SET(0, EN) | + HINIC_RQ_CTXT_CEQ_ATTR_SET(1, WRAPPED); + + rq_ctxt->pi_intr_attr = HINIC_RQ_CTXT_PI_SET(pi_start, IDX) | + HINIC_RQ_CTXT_PI_SET(rq->msix_entry, INTR); + + rq_ctxt->wq_hi_pfn_ci = HINIC_RQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi, + HI_PFN) | + HINIC_RQ_CTXT_WQ_PAGE_SET(ci_start, CI); + + rq_ctxt->wq_lo_pfn = wq_page_pfn_lo; + + rq_ctxt->pref_cache = + HINIC_RQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) | + HINIC_RQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) | + HINIC_RQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD, CACHE_THRESHOLD); + + rq_ctxt->pref_wrapped = 1; + + rq_ctxt->pref_wq_hi_pfn_ci = + HINIC_RQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_HI_PFN) | + HINIC_RQ_CTXT_PREF_SET(ci_start, CI); + + rq_ctxt->pref_wq_lo_pfn = wq_page_pfn_lo; + + rq_ctxt->pi_paddr_hi = upper_32_bits(rq->pi_dma_addr); + rq_ctxt->pi_paddr_lo = lower_32_bits(rq->pi_dma_addr); + + rq_ctxt->wq_block_hi_pfn = + HINIC_RQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, HI_PFN); + + rq_ctxt->wq_block_lo_pfn = wq_block_pfn_lo; + + hinic_cpu_to_be32(rq_ctxt, sizeof(*rq_ctxt)); +} + /** * alloc_sq_skb_arr - allocate sq array for saved skb * @sq: HW Send Queue diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h index c5ec30dcda96..56d1f8b9ca65 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -24,6 +24,7 @@ #include "hinic_hw_if.h" #include "hinic_hw_wqe.h" #include "hinic_hw_wq.h" +#include "hinic_hw_qp_ctxt.h" #define HINIC_SQ_WQEBB_SIZE 64 #define HINIC_RQ_WQEBB_SIZE 32 @@ -78,6 +79,16 @@ struct hinic_qp { u16 q_id; }; +void hinic_qp_prepare_header(struct hinic_qp_ctxt_header *qp_ctxt_hdr, + enum hinic_qp_ctxt_type ctxt_type, + u16 num_queues, u16 max_queues); + +void hinic_sq_prepare_ctxt(struct hinic_sq_ctxt *sq_ctxt, + struct hinic_sq *sq, u16 global_qid); + +void hinic_rq_prepare_ctxt(struct hinic_rq_ctxt *rq_ctxt, + struct hinic_rq *rq, u16 global_qid); + int hinic_init_sq(struct hinic_sq *sq, struct hinic_hwif *hwif, struct hinic_wq *wq, struct msix_entry *entry, void *ci_addr, dma_addr_t ci_dma_addr, void __iomem *db_base); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h new file mode 100644 index 000000000000..376abf00762b --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h @@ -0,0 +1,214 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_QP_CTXT_H +#define HINIC_HW_QP_CTXT_H + +#include + +#include "hinic_hw_cmdq.h" + +#define HINIC_SQ_CTXT_CEQ_ATTR_GLOBAL_SQ_ID_SHIFT 13 +#define HINIC_SQ_CTXT_CEQ_ATTR_EN_SHIFT 23 + +#define HINIC_SQ_CTXT_CEQ_ATTR_GLOBAL_SQ_ID_MASK 0x3FF +#define HINIC_SQ_CTXT_CEQ_ATTR_EN_MASK 0x1 + +#define HINIC_SQ_CTXT_CEQ_ATTR_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_CEQ_ATTR_##member##_MASK) \ + << HINIC_SQ_CTXT_CEQ_ATTR_##member##_SHIFT) + +#define HINIC_SQ_CTXT_CI_IDX_SHIFT 11 +#define HINIC_SQ_CTXT_CI_WRAPPED_SHIFT 23 + +#define HINIC_SQ_CTXT_CI_IDX_MASK 0xFFF +#define HINIC_SQ_CTXT_CI_WRAPPED_MASK 0x1 + +#define HINIC_SQ_CTXT_CI_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_CI_##member##_MASK) \ + << HINIC_SQ_CTXT_CI_##member##_SHIFT) + +#define HINIC_SQ_CTXT_WQ_PAGE_HI_PFN_SHIFT 0 +#define HINIC_SQ_CTXT_WQ_PAGE_PI_SHIFT 20 + +#define HINIC_SQ_CTXT_WQ_PAGE_HI_PFN_MASK 0xFFFFF +#define HINIC_SQ_CTXT_WQ_PAGE_PI_MASK 0xFFF + +#define HINIC_SQ_CTXT_WQ_PAGE_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_WQ_PAGE_##member##_MASK) \ + << HINIC_SQ_CTXT_WQ_PAGE_##member##_SHIFT) + +#define HINIC_SQ_CTXT_PREF_CACHE_THRESHOLD_SHIFT 0 +#define HINIC_SQ_CTXT_PREF_CACHE_MAX_SHIFT 14 +#define HINIC_SQ_CTXT_PREF_CACHE_MIN_SHIFT 25 + +#define HINIC_SQ_CTXT_PREF_CACHE_THRESHOLD_MASK 0x3FFF +#define HINIC_SQ_CTXT_PREF_CACHE_MAX_MASK 0x7FF +#define HINIC_SQ_CTXT_PREF_CACHE_MIN_MASK 0x7F + +#define HINIC_SQ_CTXT_PREF_WQ_HI_PFN_SHIFT 0 +#define HINIC_SQ_CTXT_PREF_CI_SHIFT 20 + +#define HINIC_SQ_CTXT_PREF_WQ_HI_PFN_MASK 0xFFFFF +#define HINIC_SQ_CTXT_PREF_CI_MASK 0xFFF + +#define HINIC_SQ_CTXT_PREF_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_PREF_##member##_MASK) \ + << HINIC_SQ_CTXT_PREF_##member##_SHIFT) + +#define HINIC_SQ_CTXT_WQ_BLOCK_HI_PFN_SHIFT 0 + +#define HINIC_SQ_CTXT_WQ_BLOCK_HI_PFN_MASK 0x7FFFFF + +#define HINIC_SQ_CTXT_WQ_BLOCK_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_WQ_BLOCK_##member##_MASK) \ + << HINIC_SQ_CTXT_WQ_BLOCK_##member##_SHIFT) + +#define HINIC_RQ_CTXT_CEQ_ATTR_EN_SHIFT 0 +#define HINIC_RQ_CTXT_CEQ_ATTR_WRAPPED_SHIFT 1 + +#define HINIC_RQ_CTXT_CEQ_ATTR_EN_MASK 0x1 +#define HINIC_RQ_CTXT_CEQ_ATTR_WRAPPED_MASK 0x1 + +#define HINIC_RQ_CTXT_CEQ_ATTR_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_CEQ_ATTR_##member##_MASK) \ + << HINIC_RQ_CTXT_CEQ_ATTR_##member##_SHIFT) + +#define HINIC_RQ_CTXT_PI_IDX_SHIFT 0 +#define HINIC_RQ_CTXT_PI_INTR_SHIFT 22 + +#define HINIC_RQ_CTXT_PI_IDX_MASK 0xFFF +#define HINIC_RQ_CTXT_PI_INTR_MASK 0x3FF + +#define HINIC_RQ_CTXT_PI_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_PI_##member##_MASK) << \ + HINIC_RQ_CTXT_PI_##member##_SHIFT) + +#define HINIC_RQ_CTXT_WQ_PAGE_HI_PFN_SHIFT 0 +#define HINIC_RQ_CTXT_WQ_PAGE_CI_SHIFT 20 + +#define HINIC_RQ_CTXT_WQ_PAGE_HI_PFN_MASK 0xFFFFF +#define HINIC_RQ_CTXT_WQ_PAGE_CI_MASK 0xFFF + +#define HINIC_RQ_CTXT_WQ_PAGE_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_WQ_PAGE_##member##_MASK) << \ + HINIC_RQ_CTXT_WQ_PAGE_##member##_SHIFT) + +#define HINIC_RQ_CTXT_PREF_CACHE_THRESHOLD_SHIFT 0 +#define HINIC_RQ_CTXT_PREF_CACHE_MAX_SHIFT 14 +#define HINIC_RQ_CTXT_PREF_CACHE_MIN_SHIFT 25 + +#define HINIC_RQ_CTXT_PREF_CACHE_THRESHOLD_MASK 0x3FFF +#define HINIC_RQ_CTXT_PREF_CACHE_MAX_MASK 0x7FF +#define HINIC_RQ_CTXT_PREF_CACHE_MIN_MASK 0x7F + +#define HINIC_RQ_CTXT_PREF_WQ_HI_PFN_SHIFT 0 +#define HINIC_RQ_CTXT_PREF_CI_SHIFT 20 + +#define HINIC_RQ_CTXT_PREF_WQ_HI_PFN_MASK 0xFFFFF +#define HINIC_RQ_CTXT_PREF_CI_MASK 0xFFF + +#define HINIC_RQ_CTXT_PREF_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_PREF_##member##_MASK) << \ + HINIC_RQ_CTXT_PREF_##member##_SHIFT) + +#define HINIC_RQ_CTXT_WQ_BLOCK_HI_PFN_SHIFT 0 + +#define HINIC_RQ_CTXT_WQ_BLOCK_HI_PFN_MASK 0x7FFFFF + +#define HINIC_RQ_CTXT_WQ_BLOCK_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_WQ_BLOCK_##member##_MASK) << \ + HINIC_RQ_CTXT_WQ_BLOCK_##member##_SHIFT) + +#define HINIC_SQ_CTXT_SIZE(num_sqs) (sizeof(struct hinic_qp_ctxt_header) \ + + (num_sqs) * sizeof(struct hinic_sq_ctxt)) + +#define HINIC_RQ_CTXT_SIZE(num_rqs) (sizeof(struct hinic_qp_ctxt_header) \ + + (num_rqs) * sizeof(struct hinic_rq_ctxt)) + +#define HINIC_WQ_PAGE_PFN_SHIFT 12 +#define HINIC_WQ_BLOCK_PFN_SHIFT 9 + +#define HINIC_WQ_PAGE_PFN(page_addr) ((page_addr) >> HINIC_WQ_PAGE_PFN_SHIFT) +#define HINIC_WQ_BLOCK_PFN(page_addr) ((page_addr) >> \ + HINIC_WQ_BLOCK_PFN_SHIFT) + +#define HINIC_Q_CTXT_MAX \ + ((HINIC_CMDQ_BUF_SIZE - sizeof(struct hinic_qp_ctxt_header)) \ + / sizeof(struct hinic_sq_ctxt)) + +enum hinic_qp_ctxt_type { + HINIC_QP_CTXT_TYPE_SQ, + HINIC_QP_CTXT_TYPE_RQ +}; + +struct hinic_qp_ctxt_header { + u16 num_queues; + u16 queue_type; + u32 addr_offset; +}; + +struct hinic_sq_ctxt { + u32 ceq_attr; + + u32 ci_wrapped; + + u32 wq_hi_pfn_pi; + u32 wq_lo_pfn; + + u32 pref_cache; + u32 pref_wrapped; + u32 pref_wq_hi_pfn_ci; + u32 pref_wq_lo_pfn; + + u32 rsvd0; + u32 rsvd1; + + u32 wq_block_hi_pfn; + u32 wq_block_lo_pfn; +}; + +struct hinic_rq_ctxt { + u32 ceq_attr; + + u32 pi_intr_attr; + + u32 wq_hi_pfn_ci; + u32 wq_lo_pfn; + + u32 pref_cache; + u32 pref_wrapped; + + u32 pref_wq_hi_pfn_ci; + u32 pref_wq_lo_pfn; + + u32 pi_paddr_hi; + u32 pi_paddr_lo; + + u32 wq_block_hi_pfn; + u32 wq_block_lo_pfn; +}; + +struct hinic_sq_ctxt_block { + struct hinic_qp_ctxt_header hdr; + struct hinic_sq_ctxt sq_ctxt[HINIC_Q_CTXT_MAX]; +}; + +struct hinic_rq_ctxt_block { + struct hinic_qp_ctxt_header hdr; + struct hinic_rq_ctxt rq_ctxt[HINIC_Q_CTXT_MAX]; +}; + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h index 7c114daf13ef..8ce259ace24a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h @@ -72,6 +72,15 @@ struct hinic_wqs { struct semaphore alloc_blocks_lock; }; +struct hinic_cmdq_pages { + /* The addresses are 64 bit in the HW */ + u64 page_paddr; + u64 *page_vaddr; + void **shadow_page_vaddr; + + struct hinic_hwif *hwif; +}; + int hinic_wqs_alloc(struct hinic_wqs *wqs, int num_wqs, struct hinic_hwif *hwif); -- cgit v1.2.3 From d0b9805e8222e86378c04d6bab366181b707631e Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:56:00 +0800 Subject: net-next/hinic: Initialize cmdq Create the work queues for cmdq and update the nic about the work queue contexts. cmdq commands are used for updating the nic about the qp contexts. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c | 282 +++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h | 53 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h | 2 + drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 5 + drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c | 156 ++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h | 8 + 6 files changed, 500 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c index 2fd3924f4eaf..0dccbe678c31 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c @@ -13,11 +13,49 @@ * */ +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "hinic_hw_if.h" +#include "hinic_hw_mgmt.h" +#include "hinic_hw_wq.h" #include "hinic_hw_cmdq.h" +#include "hinic_hw_io.h" +#include "hinic_hw_dev.h" + +#define CMDQ_DB_OFF SZ_2K + +#define CMDQ_WQEBB_SIZE 64 +#define CMDQ_DEPTH SZ_4K + +#define CMDQ_WQ_PAGE_SIZE SZ_4K + +#define WQE_LCMD_SIZE 64 +#define WQE_SCMD_SIZE 64 + +#define CMDQ_PFN(addr, page_size) ((addr) >> (ilog2(page_size))) + +#define cmdq_to_cmdqs(cmdq) container_of((cmdq) - (cmdq)->cmdq_type, \ + struct hinic_cmdqs, cmdq[0]) + +#define cmdqs_to_func_to_io(cmdqs) container_of(cmdqs, \ + struct hinic_func_to_io, \ + cmdqs) + +enum cmdq_wqe_type { + WQE_LCMD_TYPE = 0, + WQE_SCMD_TYPE = 1, +}; /** * hinic_alloc_cmdq_buf - alloc buffer for sending command @@ -29,8 +67,17 @@ int hinic_alloc_cmdq_buf(struct hinic_cmdqs *cmdqs, struct hinic_cmdq_buf *cmdq_buf) { - /* should be implemented */ - return -ENOMEM; + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + + cmdq_buf->buf = pci_pool_alloc(cmdqs->cmdq_buf_pool, GFP_KERNEL, + &cmdq_buf->dma_addr); + if (!cmdq_buf->buf) { + dev_err(&pdev->dev, "Failed to allocate cmd from the pool\n"); + return -ENOMEM; + } + + return 0; } /** @@ -41,7 +88,7 @@ int hinic_alloc_cmdq_buf(struct hinic_cmdqs *cmdqs, void hinic_free_cmdq_buf(struct hinic_cmdqs *cmdqs, struct hinic_cmdq_buf *cmdq_buf) { - /* should be implemented */ + pci_pool_free(cmdqs->cmdq_buf_pool, cmdq_buf->buf, cmdq_buf->dma_addr); } /** @@ -62,6 +109,169 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, return -EINVAL; } +/** + * cmdq_init_queue_ctxt - init the queue ctxt of a cmdq + * @cmdq_ctxt: cmdq ctxt to initialize + * @cmdq: the cmdq + * @cmdq_pages: the memory of the queue + **/ +static void cmdq_init_queue_ctxt(struct hinic_cmdq_ctxt *cmdq_ctxt, + struct hinic_cmdq *cmdq, + struct hinic_cmdq_pages *cmdq_pages) +{ + struct hinic_cmdq_ctxt_info *ctxt_info = &cmdq_ctxt->ctxt_info; + u64 wq_first_page_paddr, cmdq_first_block_paddr, pfn; + struct hinic_cmdqs *cmdqs = cmdq_to_cmdqs(cmdq); + struct hinic_wq *wq = cmdq->wq; + + /* The data in the HW is in Big Endian Format */ + wq_first_page_paddr = be64_to_cpu(*wq->block_vaddr); + + pfn = CMDQ_PFN(wq_first_page_paddr, wq->wq_page_size); + + ctxt_info->curr_wqe_page_pfn = + HINIC_CMDQ_CTXT_PAGE_INFO_SET(pfn, CURR_WQE_PAGE_PFN) | + HINIC_CMDQ_CTXT_PAGE_INFO_SET(HINIC_CEQ_ID_CMDQ, EQ_ID) | + HINIC_CMDQ_CTXT_PAGE_INFO_SET(1, CEQ_ARM) | + HINIC_CMDQ_CTXT_PAGE_INFO_SET(1, CEQ_EN) | + HINIC_CMDQ_CTXT_PAGE_INFO_SET(cmdq->wrapped, WRAPPED); + + /* block PFN - Read Modify Write */ + cmdq_first_block_paddr = cmdq_pages->page_paddr; + + pfn = CMDQ_PFN(cmdq_first_block_paddr, wq->wq_page_size); + + ctxt_info->wq_block_pfn = + HINIC_CMDQ_CTXT_BLOCK_INFO_SET(pfn, WQ_BLOCK_PFN) | + HINIC_CMDQ_CTXT_BLOCK_INFO_SET(atomic_read(&wq->cons_idx), CI); + + cmdq_ctxt->func_idx = HINIC_HWIF_FUNC_IDX(cmdqs->hwif); + cmdq_ctxt->cmdq_type = cmdq->cmdq_type; +} + +/** + * init_cmdq - initialize cmdq + * @cmdq: the cmdq + * @wq: the wq attaced to the cmdq + * @q_type: the cmdq type of the cmdq + * @db_area: doorbell area for the cmdq + * + * Return 0 - Success, negative - Failure + **/ +static int init_cmdq(struct hinic_cmdq *cmdq, struct hinic_wq *wq, + enum hinic_cmdq_type q_type, void __iomem *db_area) +{ + int err; + + cmdq->wq = wq; + cmdq->cmdq_type = q_type; + cmdq->wrapped = 1; + + spin_lock_init(&cmdq->cmdq_lock); + + cmdq->done = vzalloc(wq->q_depth * sizeof(*cmdq->done)); + if (!cmdq->done) + return -ENOMEM; + + cmdq->errcode = vzalloc(wq->q_depth * sizeof(*cmdq->errcode)); + if (!cmdq->errcode) { + err = -ENOMEM; + goto err_errcode; + } + + cmdq->db_base = db_area + CMDQ_DB_OFF; + return 0; + +err_errcode: + vfree(cmdq->done); + return err; +} + +/** + * free_cmdq - Free cmdq + * @cmdq: the cmdq to free + **/ +static void free_cmdq(struct hinic_cmdq *cmdq) +{ + vfree(cmdq->errcode); + vfree(cmdq->done); +} + +/** + * init_cmdqs_ctxt - write the cmdq ctxt to HW after init all cmdq + * @hwdev: the NIC HW device + * @cmdqs: cmdqs to write the ctxts for + * &db_area: db_area for all the cmdqs + * + * Return 0 - Success, negative - Failure + **/ +static int init_cmdqs_ctxt(struct hinic_hwdev *hwdev, + struct hinic_cmdqs *cmdqs, void __iomem **db_area) +{ + struct hinic_hwif *hwif = hwdev->hwif; + enum hinic_cmdq_type type, cmdq_type; + struct hinic_cmdq_ctxt *cmdq_ctxts; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + size_t cmdq_ctxts_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI function type\n"); + return -EINVAL; + } + + cmdq_ctxts_size = HINIC_MAX_CMDQ_TYPES * sizeof(*cmdq_ctxts); + cmdq_ctxts = devm_kzalloc(&pdev->dev, cmdq_ctxts_size, GFP_KERNEL); + if (!cmdq_ctxts) + return -ENOMEM; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + cmdq_type = HINIC_CMDQ_SYNC; + for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { + err = init_cmdq(&cmdqs->cmdq[cmdq_type], + &cmdqs->saved_wqs[cmdq_type], cmdq_type, + db_area[cmdq_type]); + if (err) { + dev_err(&pdev->dev, "Failed to initialize cmdq\n"); + goto err_init_cmdq; + } + + cmdq_init_queue_ctxt(&cmdq_ctxts[cmdq_type], + &cmdqs->cmdq[cmdq_type], + &cmdqs->cmdq_pages); + } + + /* Write the CMDQ ctxts */ + cmdq_type = HINIC_CMDQ_SYNC; + for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_CMDQ_CTXT_SET, + &cmdq_ctxts[cmdq_type], + sizeof(cmdq_ctxts[cmdq_type]), + NULL, NULL, HINIC_MGMT_MSG_SYNC); + if (err) { + dev_err(&pdev->dev, "Failed to set CMDQ CTXT type = %d\n", + cmdq_type); + goto err_write_cmdq_ctxt; + } + } + + devm_kfree(&pdev->dev, cmdq_ctxts); + return 0; + +err_write_cmdq_ctxt: + cmdq_type = HINIC_MAX_CMDQ_TYPES; + +err_init_cmdq: + for (type = HINIC_CMDQ_SYNC; type < cmdq_type; type++) + free_cmdq(&cmdqs->cmdq[type]); + + devm_kfree(&pdev->dev, cmdq_ctxts); + return err; +} + /** * hinic_init_cmdqs - init all cmdqs * @cmdqs: cmdqs to init @@ -73,8 +283,55 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, void __iomem **db_area) { - /* should be implemented */ - return -EINVAL; + struct hinic_func_to_io *func_to_io = cmdqs_to_func_to_io(cmdqs); + struct pci_dev *pdev = hwif->pdev; + struct hinic_hwdev *hwdev; + size_t saved_wqs_size; + u16 max_wqe_size; + int err; + + cmdqs->hwif = hwif; + cmdqs->cmdq_buf_pool = pci_pool_create("hinic_cmdq", pdev, + HINIC_CMDQ_BUF_SIZE, + HINIC_CMDQ_BUF_SIZE, 0); + if (!cmdqs->cmdq_buf_pool) + return -ENOMEM; + + saved_wqs_size = HINIC_MAX_CMDQ_TYPES * sizeof(struct hinic_wq); + cmdqs->saved_wqs = devm_kzalloc(&pdev->dev, saved_wqs_size, GFP_KERNEL); + if (!cmdqs->saved_wqs) { + err = -ENOMEM; + goto err_saved_wqs; + } + + max_wqe_size = WQE_LCMD_SIZE; + err = hinic_wqs_cmdq_alloc(&cmdqs->cmdq_pages, cmdqs->saved_wqs, hwif, + HINIC_MAX_CMDQ_TYPES, CMDQ_WQEBB_SIZE, + CMDQ_WQ_PAGE_SIZE, CMDQ_DEPTH, max_wqe_size); + if (err) { + dev_err(&pdev->dev, "Failed to allocate CMDQ wqs\n"); + goto err_cmdq_wqs; + } + + hwdev = container_of(func_to_io, struct hinic_hwdev, func_to_io); + err = init_cmdqs_ctxt(hwdev, cmdqs, db_area); + if (err) { + dev_err(&pdev->dev, "Failed to write cmdq ctxt\n"); + goto err_cmdq_ctxt; + } + + return 0; + +err_cmdq_ctxt: + hinic_wqs_cmdq_free(&cmdqs->cmdq_pages, cmdqs->saved_wqs, + HINIC_MAX_CMDQ_TYPES); + +err_cmdq_wqs: + devm_kfree(&pdev->dev, cmdqs->saved_wqs); + +err_saved_wqs: + pci_pool_destroy(cmdqs->cmdq_buf_pool); + return err; } /** @@ -83,5 +340,18 @@ int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, **/ void hinic_free_cmdqs(struct hinic_cmdqs *cmdqs) { - /* should be implemented */ + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + enum hinic_cmdq_type cmdq_type; + + cmdq_type = HINIC_CMDQ_SYNC; + for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) + free_cmdq(&cmdqs->cmdq[cmdq_type]); + + hinic_wqs_cmdq_free(&cmdqs->cmdq_pages, cmdqs->saved_wqs, + HINIC_MAX_CMDQ_TYPES); + + devm_kfree(&pdev->dev, cmdqs->saved_wqs); + + pci_pool_destroy(cmdqs->cmdq_buf_pool); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h index c9e97cad9b29..5ec59f1b4b0a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h @@ -24,6 +24,40 @@ #include "hinic_hw_if.h" #include "hinic_hw_wq.h" +#define HINIC_CMDQ_CTXT_CURR_WQE_PAGE_PFN_SHIFT 0 +#define HINIC_CMDQ_CTXT_EQ_ID_SHIFT 56 +#define HINIC_CMDQ_CTXT_CEQ_ARM_SHIFT 61 +#define HINIC_CMDQ_CTXT_CEQ_EN_SHIFT 62 +#define HINIC_CMDQ_CTXT_WRAPPED_SHIFT 63 + +#define HINIC_CMDQ_CTXT_CURR_WQE_PAGE_PFN_MASK 0xFFFFFFFFFFFFF +#define HINIC_CMDQ_CTXT_EQ_ID_MASK 0x1F +#define HINIC_CMDQ_CTXT_CEQ_ARM_MASK 0x1 +#define HINIC_CMDQ_CTXT_CEQ_EN_MASK 0x1 +#define HINIC_CMDQ_CTXT_WRAPPED_MASK 0x1 + +#define HINIC_CMDQ_CTXT_PAGE_INFO_SET(val, member) \ + (((u64)(val) & HINIC_CMDQ_CTXT_##member##_MASK) \ + << HINIC_CMDQ_CTXT_##member##_SHIFT) + +#define HINIC_CMDQ_CTXT_PAGE_INFO_CLEAR(val, member) \ + ((val) & (~((u64)HINIC_CMDQ_CTXT_##member##_MASK \ + << HINIC_CMDQ_CTXT_##member##_SHIFT))) + +#define HINIC_CMDQ_CTXT_WQ_BLOCK_PFN_SHIFT 0 +#define HINIC_CMDQ_CTXT_CI_SHIFT 52 + +#define HINIC_CMDQ_CTXT_WQ_BLOCK_PFN_MASK 0xFFFFFFFFFFFFF +#define HINIC_CMDQ_CTXT_CI_MASK 0xFFF + +#define HINIC_CMDQ_CTXT_BLOCK_INFO_SET(val, member) \ + (((u64)(val) & HINIC_CMDQ_CTXT_##member##_MASK) \ + << HINIC_CMDQ_CTXT_##member##_SHIFT) + +#define HINIC_CMDQ_CTXT_BLOCK_INFO_CLEAR(val, member) \ + ((val) & (~((u64)HINIC_CMDQ_CTXT_##member##_MASK \ + << HINIC_CMDQ_CTXT_##member##_SHIFT))) + #define HINIC_CMDQ_BUF_SIZE 2048 enum hinic_cmdq_type { @@ -38,6 +72,25 @@ struct hinic_cmdq_buf { size_t size; }; +struct hinic_cmdq_ctxt_info { + u64 curr_wqe_page_pfn; + u64 wq_block_pfn; +}; + +struct hinic_cmdq_ctxt { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 cmdq_type; + u8 rsvd1[1]; + + u8 rsvd2[4]; + + struct hinic_cmdq_ctxt_info ctxt_info; +}; + struct hinic_cmdq { struct hinic_wq *wq; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h index 7f50b2f6cc22..ca584c05d30e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h @@ -106,6 +106,8 @@ #define HINIC_EQ_PAGE_SIZE SZ_4K +#define HINIC_CEQ_ID_CMDQ 0 + enum hinic_eq_type { HINIC_AEQ, }; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h index 8021406cca7c..90116c2db819 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -68,6 +68,11 @@ enum hinic_cfg_cmd { HINIC_CFG_NIC_CAP = 0, }; +enum hinic_comm_cmd { + HINIC_COMM_CMD_CMDQ_CTXT_SET = 0x10, + HINIC_COMM_CMD_CMDQ_CTXT_GET = 0x11, +}; + enum hinic_mgmt_cb_state { HINIC_MGMT_CB_ENABLED = BIT(0), HINIC_MGMT_CB_RUNNING = BIT(1), diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c index fc72b76e744b..638aab790bd6 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c @@ -27,6 +27,7 @@ #include "hinic_hw_if.h" #include "hinic_hw_wq.h" +#include "hinic_hw_cmdq.h" #define WQS_BLOCKS_PER_PAGE 4 @@ -42,6 +43,11 @@ #define WQ_PAGE_ADDR_SIZE sizeof(u64) #define WQ_MAX_PAGES (WQ_BLOCK_SIZE / WQ_PAGE_ADDR_SIZE) +#define CMDQ_BLOCK_SIZE 512 +#define CMDQ_PAGE_SIZE 4096 + +#define CMDQ_WQ_MAX_PAGES (CMDQ_BLOCK_SIZE / WQ_PAGE_ADDR_SIZE) + #define WQ_BASE_VADDR(wqs, wq) \ ((void *)((wqs)->page_vaddr[(wq)->page_idx]) \ + (wq)->block_idx * WQ_BLOCK_SIZE) @@ -54,6 +60,18 @@ ((void *)((wqs)->shadow_page_vaddr[(wq)->page_idx]) \ + (wq)->block_idx * WQ_BLOCK_SIZE) +#define CMDQ_BASE_VADDR(cmdq_pages, wq) \ + ((void *)((cmdq_pages)->page_vaddr) \ + + (wq)->block_idx * CMDQ_BLOCK_SIZE) + +#define CMDQ_BASE_PADDR(cmdq_pages, wq) \ + ((cmdq_pages)->page_paddr \ + + (wq)->block_idx * CMDQ_BLOCK_SIZE) + +#define CMDQ_BASE_ADDR(cmdq_pages, wq) \ + ((void *)((cmdq_pages)->shadow_page_vaddr) \ + + (wq)->block_idx * CMDQ_BLOCK_SIZE) + /** * queue_alloc_page - allocate page for Queue * @hwif: HW interface for allocating DMA @@ -122,6 +140,37 @@ static void wqs_free_page(struct hinic_wqs *wqs, int page_idx) vfree(wqs->shadow_page_vaddr[page_idx]); } +/** + * cmdq_allocate_page - allocate page for cmdq + * @cmdq_pages: the pages of the cmdq queue struct to hold the page + * + * Return 0 - Success, negative - Failure + **/ +static int cmdq_allocate_page(struct hinic_cmdq_pages *cmdq_pages) +{ + return queue_alloc_page(cmdq_pages->hwif, &cmdq_pages->page_vaddr, + &cmdq_pages->page_paddr, + &cmdq_pages->shadow_page_vaddr, + CMDQ_PAGE_SIZE); +} + +/** + * cmdq_free_page - free page from cmdq + * @cmdq_pages: the pages of the cmdq queue struct that hold the page + * + * Return 0 - Success, negative - Failure + **/ +static void cmdq_free_page(struct hinic_cmdq_pages *cmdq_pages) +{ + struct hinic_hwif *hwif = cmdq_pages->hwif; + struct pci_dev *pdev = hwif->pdev; + + dma_free_coherent(&pdev->dev, CMDQ_PAGE_SIZE, + cmdq_pages->page_vaddr, + (dma_addr_t)cmdq_pages->page_paddr); + vfree(cmdq_pages->shadow_page_vaddr); +} + static int alloc_page_arrays(struct hinic_wqs *wqs) { struct hinic_hwif *hwif = wqs->hwif; @@ -514,3 +563,110 @@ void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq) wqs_return_block(wqs, wq->page_idx, wq->block_idx); } + +/** + * hinic_wqs_cmdq_alloc - Allocate wqs for cmdqs + * @cmdq_pages: will hold the pages of the cmdq + * @wq: returned wqs + * @hwif: HW interface + * @cmdq_blocks: number of cmdq blocks/wq to allocate + * @wqebb_size: Work Queue Block Byte Size + * @wq_page_size: the page size in the Work Queue + * @q_depth: number of wqebbs in WQ + * @max_wqe_size: maximum WQE size that will be used in the WQ + * + * Return 0 - Success, negative - Failure + **/ +int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages, + struct hinic_wq *wq, struct hinic_hwif *hwif, + int cmdq_blocks, u16 wqebb_size, u16 wq_page_size, + u16 q_depth, u16 max_wqe_size) +{ + struct pci_dev *pdev = hwif->pdev; + u16 num_wqebbs_per_page; + int i, j, err = -ENOMEM; + + if (wqebb_size == 0) { + dev_err(&pdev->dev, "wqebb_size must be > 0\n"); + return -EINVAL; + } + + if (wq_page_size == 0) { + dev_err(&pdev->dev, "wq_page_size must be > 0\n"); + return -EINVAL; + } + + if (q_depth & (q_depth - 1)) { + dev_err(&pdev->dev, "WQ q_depth must be power of 2\n"); + return -EINVAL; + } + + num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size; + + if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) { + dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n"); + return -EINVAL; + } + + cmdq_pages->hwif = hwif; + + err = cmdq_allocate_page(cmdq_pages); + if (err) { + dev_err(&pdev->dev, "Failed to allocate CMDQ page\n"); + return err; + } + + for (i = 0; i < cmdq_blocks; i++) { + wq[i].hwif = hwif; + wq[i].page_idx = 0; + wq[i].block_idx = i; + + wq[i].wqebb_size = wqebb_size; + wq[i].wq_page_size = wq_page_size; + wq[i].q_depth = q_depth; + wq[i].max_wqe_size = max_wqe_size; + wq[i].num_wqebbs_per_page = num_wqebbs_per_page; + + wq[i].block_vaddr = CMDQ_BASE_VADDR(cmdq_pages, &wq[i]); + wq[i].shadow_block_vaddr = CMDQ_BASE_ADDR(cmdq_pages, &wq[i]); + wq[i].block_paddr = CMDQ_BASE_PADDR(cmdq_pages, &wq[i]); + + err = alloc_wq_pages(&wq[i], cmdq_pages->hwif, + CMDQ_WQ_MAX_PAGES); + if (err) { + dev_err(&pdev->dev, "Failed to alloc CMDQ blocks\n"); + goto err_cmdq_block; + } + + atomic_set(&wq[i].cons_idx, 0); + atomic_set(&wq[i].prod_idx, 0); + atomic_set(&wq[i].delta, q_depth); + wq[i].mask = q_depth - 1; + } + + return 0; + +err_cmdq_block: + for (j = 0; j < i; j++) + free_wq_pages(&wq[j], cmdq_pages->hwif, wq[j].num_q_pages); + + cmdq_free_page(cmdq_pages); + return err; +} + +/** + * hinic_wqs_cmdq_free - Free wqs from cmdqs + * @cmdq_pages: hold the pages of the cmdq + * @wq: wqs to free + * @cmdq_blocks: number of wqs to free + **/ +void hinic_wqs_cmdq_free(struct hinic_cmdq_pages *cmdq_pages, + struct hinic_wq *wq, int cmdq_blocks) +{ + int i; + + for (i = 0; i < cmdq_blocks; i++) + free_wq_pages(&wq[i], cmdq_pages->hwif, wq[i].num_q_pages); + + cmdq_free_page(cmdq_pages); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h index 8ce259ace24a..a3c4469d0efe 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h @@ -81,6 +81,14 @@ struct hinic_cmdq_pages { struct hinic_hwif *hwif; }; +int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages, + struct hinic_wq *wq, struct hinic_hwif *hwif, + int cmdq_blocks, u16 wqebb_size, u16 wq_page_size, + u16 q_depth, u16 max_wqe_size); + +void hinic_wqs_cmdq_free(struct hinic_cmdq_pages *cmdq_pages, + struct hinic_wq *wq, int cmdq_blocks); + int hinic_wqs_alloc(struct hinic_wqs *wqs, int num_wqs, struct hinic_hwif *hwif); -- cgit v1.2.3 From fc9319e4025d49875fdb97c06618de2c0088ac31 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:56:01 +0800 Subject: net-next/hinic: Add ceqs Initialize the completion event queues and handle ceq events by calling the registered handlers. Used for cmdq command completion. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c | 16 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 29 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 7 +- drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c | 290 +++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h | 75 ++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_io.c | 15 +- drivers/net/ethernet/huawei/hinic/hinic_hw_io.h | 3 + 7 files changed, 427 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c index 0dccbe678c31..ec24b95747c1 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c @@ -27,6 +27,7 @@ #include #include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" #include "hinic_hw_mgmt.h" #include "hinic_hw_wq.h" #include "hinic_hw_cmdq.h" @@ -109,6 +110,16 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, return -EINVAL; } +/** + * cmdq_ceq_handler - cmdq completion event handler + * @handle: private data for the handler(cmdqs) + * @ceqe_data: ceq element data + **/ +static void cmdq_ceq_handler(void *handle, u32 ceqe_data) +{ + /* should be implemented */ +} + /** * cmdq_init_queue_ctxt - init the queue ctxt of a cmdq * @cmdq_ctxt: cmdq ctxt to initialize @@ -320,6 +331,8 @@ int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, goto err_cmdq_ctxt; } + hinic_ceq_register_cb(&func_to_io->ceqs, HINIC_CEQ_CMDQ, cmdqs, + cmdq_ceq_handler); return 0; err_cmdq_ctxt: @@ -340,10 +353,13 @@ err_saved_wqs: **/ void hinic_free_cmdqs(struct hinic_cmdqs *cmdqs) { + struct hinic_func_to_io *func_to_io = cmdqs_to_func_to_io(cmdqs); struct hinic_hwif *hwif = cmdqs->hwif; struct pci_dev *pdev = hwif->pdev; enum hinic_cmdq_type cmdq_type; + hinic_ceq_unregister_cb(&func_to_io->ceqs, HINIC_CEQ_CMDQ); + cmdq_type = HINIC_CMDQ_SYNC; for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) free_cmdq(&cmdqs->cmdq[cmdq_type]); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h index 1f57301ce527..10b8c7b650dc 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -81,27 +81,44 @@ /* EQ registers */ #define HINIC_AEQ_MTT_OFF_BASE_ADDR 0x200 +#define HINIC_CEQ_MTT_OFF_BASE_ADDR 0x400 #define HINIC_EQ_MTT_OFF_STRIDE 0x40 #define HINIC_CSR_AEQ_MTT_OFF(id) \ (HINIC_AEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE) +#define HINIC_CSR_CEQ_MTT_OFF(id) \ + (HINIC_CEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE) + #define HINIC_CSR_EQ_PAGE_OFF_STRIDE 8 #define HINIC_CSR_AEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \ (HINIC_CSR_AEQ_MTT_OFF(q_id) + \ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE) +#define HINIC_CSR_CEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \ + (HINIC_CSR_CEQ_MTT_OFF(q_id) + \ + (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE) + #define HINIC_CSR_AEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \ (HINIC_CSR_AEQ_MTT_OFF(q_id) + \ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4) +#define HINIC_CSR_CEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \ + (HINIC_CSR_CEQ_MTT_OFF(q_id) + \ + (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4) + #define HINIC_AEQ_CTRL_0_ADDR_BASE 0xE00 #define HINIC_AEQ_CTRL_1_ADDR_BASE 0xE04 #define HINIC_AEQ_CONS_IDX_ADDR_BASE 0xE08 #define HINIC_AEQ_PROD_IDX_ADDR_BASE 0xE0C +#define HINIC_CEQ_CTRL_0_ADDR_BASE 0x1000 +#define HINIC_CEQ_CTRL_1_ADDR_BASE 0x1004 +#define HINIC_CEQ_CONS_IDX_ADDR_BASE 0x1008 +#define HINIC_CEQ_PROD_IDX_ADDR_BASE 0x100C + #define HINIC_EQ_OFF_STRIDE 0x80 #define HINIC_CSR_AEQ_CTRL_0_ADDR(idx) \ @@ -116,4 +133,16 @@ #define HINIC_CSR_AEQ_PROD_IDX_ADDR(idx) \ (HINIC_AEQ_PROD_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) +#define HINIC_CSR_CEQ_CTRL_0_ADDR(idx) \ + (HINIC_CEQ_CTRL_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_CEQ_CTRL_1_ADDR(idx) \ + (HINIC_CEQ_CTRL_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_CEQ_CONS_IDX_ADDR(idx) \ + (HINIC_CEQ_CONS_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_CEQ_PROD_IDX_ADDR(idx) \ + (HINIC_CEQ_PROD_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index f29fea1dc9d2..2f698f1a89a3 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -277,6 +277,7 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev) struct hinic_cap *nic_cap = &hwdev->nic_cap; struct hinic_hwif *hwif = hwdev->hwif; int err, num_aeqs, num_ceqs, num_qps; + struct msix_entry *ceq_msix_entries; struct msix_entry *sq_msix_entries; struct msix_entry *rq_msix_entries; struct pci_dev *pdev = hwif->pdev; @@ -290,7 +291,11 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev) num_aeqs = HINIC_HWIF_NUM_AEQS(hwif); num_ceqs = HINIC_HWIF_NUM_CEQS(hwif); - err = hinic_io_init(func_to_io, hwif, nic_cap->max_qps, 0, NULL); + + ceq_msix_entries = &hwdev->msix_entries[num_aeqs]; + + err = hinic_io_init(func_to_io, hwif, nic_cap->max_qps, num_ceqs, + ceq_msix_entries); if (err) { dev_err(&pdev->dev, "Failed to init IO channel\n"); return err; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c index 6b03bc6ae940..cd09e6ef3aea 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c @@ -37,14 +37,21 @@ #define GET_EQ_NUM_ELEMS_IN_PG(eq, pg_size) ((pg_size) / (eq)->elem_size) -#define EQ_CONS_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_CONS_IDX_ADDR((eq)->q_id) -#define EQ_PROD_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_PROD_IDX_ADDR((eq)->q_id) +#define EQ_CONS_IDX_REG_ADDR(eq) (((eq)->type == HINIC_AEQ) ? \ + HINIC_CSR_AEQ_CONS_IDX_ADDR((eq)->q_id) : \ + HINIC_CSR_CEQ_CONS_IDX_ADDR((eq)->q_id)) -#define EQ_HI_PHYS_ADDR_REG(eq, pg_num) \ - HINIC_CSR_AEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num) +#define EQ_PROD_IDX_REG_ADDR(eq) (((eq)->type == HINIC_AEQ) ? \ + HINIC_CSR_AEQ_PROD_IDX_ADDR((eq)->q_id) : \ + HINIC_CSR_CEQ_PROD_IDX_ADDR((eq)->q_id)) -#define EQ_LO_PHYS_ADDR_REG(eq, pg_num) \ - HINIC_CSR_AEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num) +#define EQ_HI_PHYS_ADDR_REG(eq, pg_num) (((eq)->type == HINIC_AEQ) ? \ + HINIC_CSR_AEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num) : \ + HINIC_CSR_CEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num)) + +#define EQ_LO_PHYS_ADDR_REG(eq, pg_num) (((eq)->type == HINIC_AEQ) ? \ + HINIC_CSR_AEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num) : \ + HINIC_CSR_CEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num)) #define GET_EQ_ELEMENT(eq, idx) \ ((eq)->virt_addr[(idx) / (eq)->num_elem_in_pg] + \ @@ -53,8 +60,13 @@ #define GET_AEQ_ELEM(eq, idx) ((struct hinic_aeq_elem *) \ GET_EQ_ELEMENT(eq, idx)) +#define GET_CEQ_ELEM(eq, idx) ((u32 *) \ + GET_EQ_ELEMENT(eq, idx)) + #define GET_CURR_AEQ_ELEM(eq) GET_AEQ_ELEM(eq, (eq)->cons_idx) +#define GET_CURR_CEQ_ELEM(eq) GET_CEQ_ELEM(eq, (eq)->cons_idx) + #define PAGE_IN_4K(page_size) ((page_size) >> 12) #define EQ_SET_HW_PAGE_SIZE_VAL(eq) (ilog2(PAGE_IN_4K((eq)->page_size))) @@ -63,13 +75,29 @@ #define EQ_MAX_PAGES 8 +#define CEQE_TYPE_SHIFT 23 +#define CEQE_TYPE_MASK 0x7 + +#define CEQE_TYPE(ceqe) (((ceqe) >> CEQE_TYPE_SHIFT) & \ + CEQE_TYPE_MASK) + +#define CEQE_DATA_MASK 0x3FFFFFF +#define CEQE_DATA(ceqe) ((ceqe) & CEQE_DATA_MASK) + #define aeq_to_aeqs(eq) \ container_of((eq) - (eq)->q_id, struct hinic_aeqs, aeq[0]) +#define ceq_to_ceqs(eq) \ + container_of((eq) - (eq)->q_id, struct hinic_ceqs, ceq[0]) + #define work_to_aeq_work(work) \ container_of(work, struct hinic_eq_work, work) #define DMA_ATTR_AEQ_DEFAULT 0 +#define DMA_ATTR_CEQ_DEFAULT 0 + +/* No coalescence */ +#define THRESH_CEQ_DEFAULT 0 enum eq_int_mode { EQ_INT_MODE_ARMED, @@ -118,6 +146,42 @@ void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs, hwe_cb->hwe_handler = NULL; } +/** + * hinic_ceq_register_cb - register CEQ callback for specific event + * @ceqs: pointer to Completion eqs part of the chip + * @event: ceq event to register callback for it + * @handle: private data will be used by the callback + * @handler: callback function + **/ +void hinic_ceq_register_cb(struct hinic_ceqs *ceqs, + enum hinic_ceq_type event, void *handle, + void (*handler)(void *handle, u32 ceqe_data)) +{ + struct hinic_ceq_cb *ceq_cb = &ceqs->ceq_cb[event]; + + ceq_cb->handler = handler; + ceq_cb->handle = handle; + ceq_cb->ceqe_state = HINIC_EQE_ENABLED; +} + +/** + * hinic_ceq_unregister_cb - unregister the CEQ callback for specific event + * @ceqs: pointer to Completion eqs part of the chip + * @event: ceq event to unregister callback for it + **/ +void hinic_ceq_unregister_cb(struct hinic_ceqs *ceqs, + enum hinic_ceq_type event) +{ + struct hinic_ceq_cb *ceq_cb = &ceqs->ceq_cb[event]; + + ceq_cb->ceqe_state &= ~HINIC_EQE_ENABLED; + + while (ceq_cb->ceqe_state & HINIC_EQE_RUNNING) + schedule(); + + ceq_cb->handler = NULL; +} + static u8 eq_cons_idx_checksum_set(u32 val) { u8 checksum = 0; @@ -215,6 +279,70 @@ static void aeq_irq_handler(struct hinic_eq *eq) } } +/** + * ceq_event_handler - handler for the ceq events + * @ceqs: ceqs part of the chip + * @ceqe: ceq element that describes the event + **/ +static void ceq_event_handler(struct hinic_ceqs *ceqs, u32 ceqe) +{ + struct hinic_hwif *hwif = ceqs->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_ceq_cb *ceq_cb; + enum hinic_ceq_type event; + unsigned long eqe_state; + + event = CEQE_TYPE(ceqe); + if (event >= HINIC_MAX_CEQ_EVENTS) { + dev_err(&pdev->dev, "Unknown CEQ event, event = %d\n", event); + return; + } + + ceq_cb = &ceqs->ceq_cb[event]; + + eqe_state = cmpxchg(&ceq_cb->ceqe_state, + HINIC_EQE_ENABLED, + HINIC_EQE_ENABLED | HINIC_EQE_RUNNING); + + if ((eqe_state == HINIC_EQE_ENABLED) && (ceq_cb->handler)) + ceq_cb->handler(ceq_cb->handle, CEQE_DATA(ceqe)); + else + dev_err(&pdev->dev, "Unhandled CEQ Event %d\n", event); + + ceq_cb->ceqe_state &= ~HINIC_EQE_RUNNING; +} + +/** + * ceq_irq_handler - handler for the CEQ event + * @eq: the Completion Event Queue that received the event + **/ +static void ceq_irq_handler(struct hinic_eq *eq) +{ + struct hinic_ceqs *ceqs = ceq_to_ceqs(eq); + u32 ceqe; + int i; + + for (i = 0; i < eq->q_len; i++) { + ceqe = *(GET_CURR_CEQ_ELEM(eq)); + + /* Data in HW is in Big endian Format */ + ceqe = be32_to_cpu(ceqe); + + /* HW toggles the wrapped bit, when it adds eq element event */ + if (HINIC_EQ_ELEM_DESC_GET(ceqe, WRAPPED) == eq->wrapped) + break; + + ceq_event_handler(ceqs, ceqe); + + eq->cons_idx++; + + if (eq->cons_idx == eq->q_len) { + eq->cons_idx = 0; + eq->wrapped = !eq->wrapped; + } + } +} + /** * eq_irq_handler - handler for the EQ event * @data: the Event Queue that received the event @@ -225,6 +353,8 @@ static void eq_irq_handler(void *data) if (eq->type == HINIC_AEQ) aeq_irq_handler(eq); + else if (eq->type == HINIC_CEQ) + ceq_irq_handler(eq); eq_update_ci(eq); } @@ -242,6 +372,17 @@ static void eq_irq_work(struct work_struct *work) eq_irq_handler(aeq); } +/** + * ceq_tasklet - the tasklet of the EQ that received the event + * @ceq_data: the eq + **/ +static void ceq_tasklet(unsigned long ceq_data) +{ + struct hinic_eq *ceq = (struct hinic_eq *)ceq_data; + + eq_irq_handler(ceq); +} + /** * aeq_interrupt - aeq interrupt handler * @irq: irq number @@ -265,6 +406,23 @@ static irqreturn_t aeq_interrupt(int irq, void *data) return IRQ_HANDLED; } +/** + * ceq_interrupt - ceq interrupt handler + * @irq: irq number + * @data: the Completion Event Queue that collected the event + **/ +static irqreturn_t ceq_interrupt(int irq, void *data) +{ + struct hinic_eq *ceq = data; + + /* clear resend timer cnt register */ + hinic_msix_attr_cnt_clear(ceq->hwif, ceq->msix_entry.entry); + + tasklet_schedule(&ceq->ceq_tasklet); + + return IRQ_HANDLED; +} + void set_ctrl0(struct hinic_eq *eq) { struct msix_entry *msix_entry = &eq->msix_entry; @@ -290,6 +448,28 @@ void set_ctrl0(struct hinic_eq *eq) val |= ctrl0; + hinic_hwif_write_reg(eq->hwif, addr, val); + } else if (type == HINIC_CEQ) { + /* RMW Ctrl0 */ + addr = HINIC_CSR_CEQ_CTRL_0_ADDR(eq->q_id); + + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_CEQ_CTRL_0_CLEAR(val, INTR_IDX) & + HINIC_CEQ_CTRL_0_CLEAR(val, DMA_ATTR) & + HINIC_CEQ_CTRL_0_CLEAR(val, KICK_THRESH) & + HINIC_CEQ_CTRL_0_CLEAR(val, PCI_INTF_IDX) & + HINIC_CEQ_CTRL_0_CLEAR(val, INTR_MODE); + + ctrl0 = HINIC_CEQ_CTRL_0_SET(msix_entry->entry, INTR_IDX) | + HINIC_CEQ_CTRL_0_SET(DMA_ATTR_CEQ_DEFAULT, DMA_ATTR) | + HINIC_CEQ_CTRL_0_SET(THRESH_CEQ_DEFAULT, KICK_THRESH) | + HINIC_CEQ_CTRL_0_SET(HINIC_HWIF_PCI_INTF(eq->hwif), + PCI_INTF_IDX) | + HINIC_CEQ_CTRL_0_SET(EQ_INT_MODE_ARMED, INTR_MODE); + + val |= ctrl0; + hinic_hwif_write_reg(eq->hwif, addr, val); } } @@ -319,6 +499,23 @@ void set_ctrl1(struct hinic_eq *eq) val |= ctrl1; + hinic_hwif_write_reg(eq->hwif, addr, val); + } else if (type == HINIC_CEQ) { + /* RMW Ctrl1 */ + addr = HINIC_CSR_CEQ_CTRL_1_ADDR(eq->q_id); + + page_size_val = EQ_SET_HW_PAGE_SIZE_VAL(eq); + + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_CEQ_CTRL_1_CLEAR(val, LEN) & + HINIC_CEQ_CTRL_1_CLEAR(val, PAGE_SIZE); + + ctrl1 = HINIC_CEQ_CTRL_1_SET(eq->q_len, LEN) | + HINIC_CEQ_CTRL_1_SET(page_size_val, PAGE_SIZE); + + val |= ctrl1; + hinic_hwif_write_reg(eq->hwif, addr, val); } } @@ -351,6 +548,24 @@ static void aeq_elements_init(struct hinic_eq *eq, u32 init_val) wmb(); /* Write the initilzation values */ } +/** + * ceq_elements_init - Initialize all the elements in the ceq + * @eq: the event queue + * @init_val: value to init with it the elements + **/ +static void ceq_elements_init(struct hinic_eq *eq, u32 init_val) +{ + u32 *ceqe; + int i; + + for (i = 0; i < eq->q_len; i++) { + ceqe = GET_CEQ_ELEM(eq, i); + *(ceqe) = cpu_to_be32(init_val); + } + + wmb(); /* Write the initilzation values */ +} + /** * alloc_eq_pages - allocate the pages for the queue * @eq: the event queue @@ -402,6 +617,8 @@ static int alloc_eq_pages(struct hinic_eq *eq) if (eq->type == HINIC_AEQ) aeq_elements_init(eq, init_val); + else if (eq->type == HINIC_CEQ) + ceq_elements_init(eq, init_val); return 0; @@ -471,6 +688,8 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif, if (type == HINIC_AEQ) { eq->elem_size = HINIC_AEQE_SIZE; + } else if (type == HINIC_CEQ) { + eq->elem_size = HINIC_CEQE_SIZE; } else { dev_err(&pdev->dev, "Invalid EQ type\n"); return -EINVAL; @@ -504,6 +723,9 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif, struct hinic_eq_work *aeq_work = &eq->aeq_work; INIT_WORK(&aeq_work->work, eq_irq_work); + } else if (type == HINIC_CEQ) { + tasklet_init(&eq->ceq_tasklet, ceq_tasklet, + (unsigned long)eq); } /* set the attributes of the msix entry */ @@ -517,6 +739,9 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif, if (type == HINIC_AEQ) err = request_irq(entry.vector, aeq_interrupt, 0, "hinic_aeq", eq); + else if (type == HINIC_CEQ) + err = request_irq(entry.vector, ceq_interrupt, 0, + "hinic_ceq", eq); if (err) { dev_err(&pdev->dev, "Failed to request irq for the EQ\n"); @@ -544,6 +769,8 @@ static void remove_eq(struct hinic_eq *eq) struct hinic_eq_work *aeq_work = &eq->aeq_work; cancel_work_sync(&aeq_work->work); + } else if (eq->type == HINIC_CEQ) { + tasklet_kill(&eq->ceq_tasklet); } free_eq_pages(eq); @@ -606,3 +833,54 @@ void hinic_aeqs_free(struct hinic_aeqs *aeqs) destroy_workqueue(aeqs->workq); } + +/** + * hinic_ceqs_init - init all the ceqs + * @ceqs: ceqs part of the chip + * @hwif: the hardware interface of a pci function device + * @num_ceqs: number of CEQs + * @q_len: number of EQ elements + * @page_size: the page size of the event queue + * @msix_entries: msix entries associated with the event queues + * + * Return 0 - Success, Negative - Failure + **/ +int hinic_ceqs_init(struct hinic_ceqs *ceqs, struct hinic_hwif *hwif, + int num_ceqs, u32 q_len, u32 page_size, + struct msix_entry *msix_entries) +{ + struct pci_dev *pdev = hwif->pdev; + int i, q_id, err; + + ceqs->hwif = hwif; + ceqs->num_ceqs = num_ceqs; + + for (q_id = 0; q_id < num_ceqs; q_id++) { + err = init_eq(&ceqs->ceq[q_id], hwif, HINIC_CEQ, q_id, q_len, + page_size, msix_entries[q_id]); + if (err) { + dev_err(&pdev->dev, "Failed to init ceq %d\n", q_id); + goto err_init_ceq; + } + } + + return 0; + +err_init_ceq: + for (i = 0; i < q_id; i++) + remove_eq(&ceqs->ceq[i]); + + return err; +} + +/** + * hinic_ceqs_free - free all the ceqs + * @ceqs: ceqs part of the chip + **/ +void hinic_ceqs_free(struct hinic_ceqs *ceqs) +{ + int q_id; + + for (q_id = 0; q_id < ceqs->num_ceqs; q_id++) + remove_eq(&ceqs->ceq[q_id]); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h index ca584c05d30e..ecb9c2bc6dc8 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "hinic_hw_if.h" @@ -58,6 +59,40 @@ ((val) & (~(HINIC_AEQ_CTRL_1_##member##_MASK \ << HINIC_AEQ_CTRL_1_##member##_SHIFT))) +#define HINIC_CEQ_CTRL_0_INTR_IDX_SHIFT 0 +#define HINIC_CEQ_CTRL_0_DMA_ATTR_SHIFT 12 +#define HINIC_CEQ_CTRL_0_KICK_THRESH_SHIFT 20 +#define HINIC_CEQ_CTRL_0_PCI_INTF_IDX_SHIFT 24 +#define HINIC_CEQ_CTRL_0_INTR_MODE_SHIFT 31 + +#define HINIC_CEQ_CTRL_0_INTR_IDX_MASK 0x3FF +#define HINIC_CEQ_CTRL_0_DMA_ATTR_MASK 0x3F +#define HINIC_CEQ_CTRL_0_KICK_THRESH_MASK 0xF +#define HINIC_CEQ_CTRL_0_PCI_INTF_IDX_MASK 0x3 +#define HINIC_CEQ_CTRL_0_INTR_MODE_MASK 0x1 + +#define HINIC_CEQ_CTRL_0_SET(val, member) \ + (((u32)(val) & HINIC_CEQ_CTRL_0_##member##_MASK) << \ + HINIC_CEQ_CTRL_0_##member##_SHIFT) + +#define HINIC_CEQ_CTRL_0_CLEAR(val, member) \ + ((val) & (~(HINIC_CEQ_CTRL_0_##member##_MASK \ + << HINIC_CEQ_CTRL_0_##member##_SHIFT))) + +#define HINIC_CEQ_CTRL_1_LEN_SHIFT 0 +#define HINIC_CEQ_CTRL_1_PAGE_SIZE_SHIFT 28 + +#define HINIC_CEQ_CTRL_1_LEN_MASK 0x1FFFFF +#define HINIC_CEQ_CTRL_1_PAGE_SIZE_MASK 0xF + +#define HINIC_CEQ_CTRL_1_SET(val, member) \ + (((u32)(val) & HINIC_CEQ_CTRL_1_##member##_MASK) << \ + HINIC_CEQ_CTRL_1_##member##_SHIFT) + +#define HINIC_CEQ_CTRL_1_CLEAR(val, member) \ + ((val) & (~(HINIC_CEQ_CTRL_1_##member##_MASK \ + << HINIC_CEQ_CTRL_1_##member##_SHIFT))) + #define HINIC_EQ_ELEM_DESC_TYPE_SHIFT 0 #define HINIC_EQ_ELEM_DESC_SRC_SHIFT 7 #define HINIC_EQ_ELEM_DESC_SIZE_SHIFT 8 @@ -95,14 +130,17 @@ << HINIC_EQ_CI_##member##_SHIFT))) #define HINIC_MAX_AEQS 4 +#define HINIC_MAX_CEQS 32 #define HINIC_AEQE_SIZE 64 +#define HINIC_CEQE_SIZE 4 #define HINIC_AEQE_DESC_SIZE 4 #define HINIC_AEQE_DATA_SIZE \ (HINIC_AEQE_SIZE - HINIC_AEQE_DESC_SIZE) #define HINIC_DEFAULT_AEQ_LEN 64 +#define HINIC_DEFAULT_CEQ_LEN 1024 #define HINIC_EQ_PAGE_SIZE SZ_4K @@ -110,6 +148,7 @@ enum hinic_eq_type { HINIC_AEQ, + HINIC_CEQ, }; enum hinic_aeq_type { @@ -118,6 +157,12 @@ enum hinic_aeq_type { HINIC_MAX_AEQ_EVENTS, }; +enum hinic_ceq_type { + HINIC_CEQ_CMDQ = 3, + + HINIC_MAX_CEQ_EVENTS, +}; + enum hinic_eqe_state { HINIC_EQE_ENABLED = BIT(0), HINIC_EQE_RUNNING = BIT(1), @@ -154,6 +199,8 @@ struct hinic_eq { void **virt_addr; struct hinic_eq_work aeq_work; + + struct tasklet_struct ceq_tasklet; }; struct hinic_hw_event_cb { @@ -173,6 +220,21 @@ struct hinic_aeqs { struct workqueue_struct *workq; }; +struct hinic_ceq_cb { + void (*handler)(void *handle, u32 ceqe_data); + void *handle; + enum hinic_eqe_state ceqe_state; +}; + +struct hinic_ceqs { + struct hinic_hwif *hwif; + + struct hinic_eq ceq[HINIC_MAX_CEQS]; + int num_ceqs; + + struct hinic_ceq_cb ceq_cb[HINIC_MAX_CEQ_EVENTS]; +}; + void hinic_aeq_register_hw_cb(struct hinic_aeqs *aeqs, enum hinic_aeq_type event, void *handle, void (*hwe_handler)(void *handle, void *data, @@ -181,10 +243,23 @@ void hinic_aeq_register_hw_cb(struct hinic_aeqs *aeqs, void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs, enum hinic_aeq_type event); +void hinic_ceq_register_cb(struct hinic_ceqs *ceqs, + enum hinic_ceq_type event, void *handle, + void (*ceq_cb)(void *handle, u32 ceqe_data)); + +void hinic_ceq_unregister_cb(struct hinic_ceqs *ceqs, + enum hinic_ceq_type event); + int hinic_aeqs_init(struct hinic_aeqs *aeqs, struct hinic_hwif *hwif, int num_aeqs, u32 q_len, u32 page_size, struct msix_entry *msix_entries); void hinic_aeqs_free(struct hinic_aeqs *aeqs); +int hinic_ceqs_init(struct hinic_ceqs *ceqs, struct hinic_hwif *hwif, + int num_ceqs, u32 q_len, u32 page_size, + struct msix_entry *msix_entries); + +void hinic_ceqs_free(struct hinic_ceqs *ceqs); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c index bb4b93fe622a..8e5897669a3a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c @@ -25,6 +25,7 @@ #include #include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" #include "hinic_hw_wqe.h" #include "hinic_hw_wq.h" #include "hinic_hw_cmdq.h" @@ -455,10 +456,18 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io, func_to_io->qps = NULL; func_to_io->max_qps = max_qps; + err = hinic_ceqs_init(&func_to_io->ceqs, hwif, num_ceqs, + HINIC_DEFAULT_CEQ_LEN, HINIC_EQ_PAGE_SIZE, + ceq_msix_entries); + if (err) { + dev_err(&pdev->dev, "Failed to init CEQs\n"); + return err; + } + err = hinic_wqs_alloc(&func_to_io->wqs, 2 * max_qps, hwif); if (err) { dev_err(&pdev->dev, "Failed to allocate WQS for IO\n"); - return err; + goto err_wqs_alloc; } func_to_io->db_base = pci_ioremap_bar(pdev, HINIC_PCI_DB_BAR); @@ -499,6 +508,9 @@ err_db_area: err_db_ioremap: hinic_wqs_free(&func_to_io->wqs); + +err_wqs_alloc: + hinic_ceqs_free(&func_to_io->ceqs); return err; } @@ -517,4 +529,5 @@ void hinic_io_free(struct hinic_func_to_io *func_to_io) iounmap(func_to_io->db_base); hinic_wqs_free(&func_to_io->wqs); + hinic_ceqs_free(&func_to_io->ceqs); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h index 60d77b343fa7..cfc21baf0977 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h @@ -22,6 +22,7 @@ #include #include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" #include "hinic_hw_wq.h" #include "hinic_hw_cmdq.h" #include "hinic_hw_qp.h" @@ -46,6 +47,8 @@ struct hinic_free_db_area { struct hinic_func_to_io { struct hinic_hwif *hwif; + struct hinic_ceqs ceqs; + struct hinic_wqs wqs; struct hinic_wq *sq_wq; -- cgit v1.2.3 From 76baca2e92f4ed478ef14d68da118484f134632d Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:56:02 +0800 Subject: net-next/hinic: Add cmdq commands Add cmdq commands for setting queue pair contexts in the nic. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_common.c | 25 ++ drivers/net/ethernet/huawei/hinic/hinic_common.h | 9 + drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c | 282 +++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h | 38 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_io.h | 10 + drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c | 194 +++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h | 12 + drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h | 115 +++++++++ 8 files changed, 683 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_common.c b/drivers/net/ethernet/huawei/hinic/hinic_common.c index 1915ad63deec..02c74fd8380e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_common.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_common.c @@ -13,6 +13,7 @@ * */ +#include #include #include @@ -53,3 +54,27 @@ void hinic_be32_to_cpu(void *data, int len) mem++; } } + +/** + * hinic_set_sge - set dma area in scatter gather entry + * @sge: scatter gather entry + * @addr: dma address + * @len: length of relevant data in the dma address + **/ +void hinic_set_sge(struct hinic_sge *sge, dma_addr_t addr, int len) +{ + sge->hi_addr = upper_32_bits(addr); + sge->lo_addr = lower_32_bits(addr); + sge->len = len; +} + +/** + * hinic_sge_to_dma - get dma address from scatter gather entry + * @sge: scatter gather entry + * + * Return dma address of sg entry + **/ +dma_addr_t hinic_sge_to_dma(struct hinic_sge *sge) +{ + return (dma_addr_t)((((u64)sge->hi_addr) << 32) | sge->lo_addr); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_common.h b/drivers/net/ethernet/huawei/hinic/hinic_common.h index 0f2f4ff70c97..2c06b76e94a1 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_common.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_common.h @@ -16,6 +16,11 @@ #ifndef HINIC_COMMON_H #define HINIC_COMMON_H +#include + +#define UPPER_8_BITS(data) (((data) >> 8) & 0xFF) +#define LOWER_8_BITS(data) ((data) & 0xFF) + struct hinic_sge { u32 hi_addr; u32 lo_addr; @@ -26,4 +31,8 @@ void hinic_cpu_to_be32(void *data, int len); void hinic_be32_to_cpu(void *data, int len); +void hinic_set_sge(struct hinic_sge *sge, dma_addr_t addr, int len); + +dma_addr_t hinic_sge_to_dma(struct hinic_sge *sge); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c index ec24b95747c1..07ce78745098 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c @@ -24,19 +24,34 @@ #include #include #include +#include +#include +#include #include +#include +#include "hinic_common.h" #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" #include "hinic_hw_mgmt.h" +#include "hinic_hw_wqe.h" #include "hinic_hw_wq.h" #include "hinic_hw_cmdq.h" #include "hinic_hw_io.h" #include "hinic_hw_dev.h" +#define CMDQ_DB_PI_OFF(pi) (((u16)LOWER_8_BITS(pi)) << 3) + +#define CMDQ_DB_ADDR(db_base, pi) ((db_base) + CMDQ_DB_PI_OFF(pi)) + +#define CMDQ_WQE_HEADER(wqe) ((struct hinic_cmdq_header *)(wqe)) + +#define FIRST_DATA_TO_WRITE_LAST sizeof(u64) + #define CMDQ_DB_OFF SZ_2K #define CMDQ_WQEBB_SIZE 64 +#define CMDQ_WQE_SIZE 64 #define CMDQ_DEPTH SZ_4K #define CMDQ_WQ_PAGE_SIZE SZ_4K @@ -44,6 +59,10 @@ #define WQE_LCMD_SIZE 64 #define WQE_SCMD_SIZE 64 +#define COMPLETE_LEN 3 + +#define CMDQ_TIMEOUT 1000 + #define CMDQ_PFN(addr, page_size) ((addr) >> (ilog2(page_size))) #define cmdq_to_cmdqs(cmdq) container_of((cmdq) - (cmdq)->cmdq_type, \ @@ -58,6 +77,40 @@ enum cmdq_wqe_type { WQE_SCMD_TYPE = 1, }; +enum completion_format { + COMPLETE_DIRECT = 0, + COMPLETE_SGE = 1, +}; + +enum data_format { + DATA_SGE = 0, + DATA_DIRECT = 1, +}; + +enum bufdesc_len { + BUFDESC_LCMD_LEN = 2, /* 16 bytes - 2(8 byte unit) */ + BUFDESC_SCMD_LEN = 3, /* 24 bytes - 3(8 byte unit) */ +}; + +enum ctrl_sect_len { + CTRL_SECT_LEN = 1, /* 4 bytes (ctrl) - 1(8 byte unit) */ + CTRL_DIRECT_SECT_LEN = 2, /* 12 bytes (ctrl + rsvd) - 2(8 byte unit) */ +}; + +enum cmdq_scmd_type { + CMDQ_SET_ARM_CMD = 2, +}; + +enum cmdq_cmd_type { + CMDQ_CMD_SYNC_DIRECT_RESP = 0, + CMDQ_CMD_SYNC_SGE_RESP = 1, +}; + +enum completion_request { + NO_CEQ = 0, + CEQ_SET = 1, +}; + /** * hinic_alloc_cmdq_buf - alloc buffer for sending command * @cmdqs: the cmdqs @@ -92,6 +145,221 @@ void hinic_free_cmdq_buf(struct hinic_cmdqs *cmdqs, pci_pool_free(cmdqs->cmdq_buf_pool, cmdq_buf->buf, cmdq_buf->dma_addr); } +static void cmdq_set_sge_completion(struct hinic_cmdq_completion *completion, + struct hinic_cmdq_buf *buf_out) +{ + struct hinic_sge_resp *sge_resp = &completion->sge_resp; + + hinic_set_sge(&sge_resp->sge, buf_out->dma_addr, buf_out->size); +} + +static void cmdq_prepare_wqe_ctrl(struct hinic_cmdq_wqe *wqe, int wrapped, + enum hinic_cmd_ack_type ack_type, + enum hinic_mod_type mod, u8 cmd, u16 prod_idx, + enum completion_format complete_format, + enum data_format data_format, + enum bufdesc_len buf_len) +{ + struct hinic_cmdq_wqe_lcmd *wqe_lcmd; + struct hinic_cmdq_wqe_scmd *wqe_scmd; + enum ctrl_sect_len ctrl_len; + struct hinic_ctrl *ctrl; + u32 saved_data; + + if (data_format == DATA_SGE) { + wqe_lcmd = &wqe->wqe_lcmd; + + wqe_lcmd->status.status_info = 0; + ctrl = &wqe_lcmd->ctrl; + ctrl_len = CTRL_SECT_LEN; + } else { + wqe_scmd = &wqe->direct_wqe.wqe_scmd; + + wqe_scmd->status.status_info = 0; + ctrl = &wqe_scmd->ctrl; + ctrl_len = CTRL_DIRECT_SECT_LEN; + } + + ctrl->ctrl_info = HINIC_CMDQ_CTRL_SET(prod_idx, PI) | + HINIC_CMDQ_CTRL_SET(cmd, CMD) | + HINIC_CMDQ_CTRL_SET(mod, MOD) | + HINIC_CMDQ_CTRL_SET(ack_type, ACK_TYPE); + + CMDQ_WQE_HEADER(wqe)->header_info = + HINIC_CMDQ_WQE_HEADER_SET(buf_len, BUFDESC_LEN) | + HINIC_CMDQ_WQE_HEADER_SET(complete_format, COMPLETE_FMT) | + HINIC_CMDQ_WQE_HEADER_SET(data_format, DATA_FMT) | + HINIC_CMDQ_WQE_HEADER_SET(CEQ_SET, COMPLETE_REQ) | + HINIC_CMDQ_WQE_HEADER_SET(COMPLETE_LEN, COMPLETE_SECT_LEN) | + HINIC_CMDQ_WQE_HEADER_SET(ctrl_len, CTRL_LEN) | + HINIC_CMDQ_WQE_HEADER_SET(wrapped, TOGGLED_WRAPPED); + + saved_data = CMDQ_WQE_HEADER(wqe)->saved_data; + saved_data = HINIC_SAVED_DATA_CLEAR(saved_data, ARM); + + if ((cmd == CMDQ_SET_ARM_CMD) && (mod == HINIC_MOD_COMM)) + CMDQ_WQE_HEADER(wqe)->saved_data |= + HINIC_SAVED_DATA_SET(1, ARM); + else + CMDQ_WQE_HEADER(wqe)->saved_data = saved_data; +} + +static void cmdq_set_lcmd_bufdesc(struct hinic_cmdq_wqe_lcmd *wqe_lcmd, + struct hinic_cmdq_buf *buf_in) +{ + hinic_set_sge(&wqe_lcmd->buf_desc.sge, buf_in->dma_addr, buf_in->size); +} + +static void cmdq_set_lcmd_wqe(struct hinic_cmdq_wqe *wqe, + enum cmdq_cmd_type cmd_type, + struct hinic_cmdq_buf *buf_in, + struct hinic_cmdq_buf *buf_out, int wrapped, + enum hinic_cmd_ack_type ack_type, + enum hinic_mod_type mod, u8 cmd, u16 prod_idx) +{ + struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &wqe->wqe_lcmd; + enum completion_format complete_format; + + switch (cmd_type) { + case CMDQ_CMD_SYNC_SGE_RESP: + complete_format = COMPLETE_SGE; + cmdq_set_sge_completion(&wqe_lcmd->completion, buf_out); + break; + case CMDQ_CMD_SYNC_DIRECT_RESP: + complete_format = COMPLETE_DIRECT; + wqe_lcmd->completion.direct_resp = 0; + break; + } + + cmdq_prepare_wqe_ctrl(wqe, wrapped, ack_type, mod, cmd, + prod_idx, complete_format, DATA_SGE, + BUFDESC_LCMD_LEN); + + cmdq_set_lcmd_bufdesc(wqe_lcmd, buf_in); +} + +static void cmdq_wqe_fill(void *dst, void *src) +{ + memcpy(dst + FIRST_DATA_TO_WRITE_LAST, src + FIRST_DATA_TO_WRITE_LAST, + CMDQ_WQE_SIZE - FIRST_DATA_TO_WRITE_LAST); + + wmb(); /* The first 8 bytes should be written last */ + + *(u64 *)dst = *(u64 *)src; +} + +static void cmdq_fill_db(u32 *db_info, + enum hinic_cmdq_type cmdq_type, u16 prod_idx) +{ + *db_info = HINIC_CMDQ_DB_INFO_SET(UPPER_8_BITS(prod_idx), HI_PROD_IDX) | + HINIC_CMDQ_DB_INFO_SET(HINIC_CTRL_PATH, PATH) | + HINIC_CMDQ_DB_INFO_SET(cmdq_type, CMDQ_TYPE) | + HINIC_CMDQ_DB_INFO_SET(HINIC_DB_CMDQ_TYPE, DB_TYPE); +} + +static void cmdq_set_db(struct hinic_cmdq *cmdq, + enum hinic_cmdq_type cmdq_type, u16 prod_idx) +{ + u32 db_info; + + cmdq_fill_db(&db_info, cmdq_type, prod_idx); + + /* The data that is written to HW should be in Big Endian Format */ + db_info = cpu_to_be32(db_info); + + wmb(); /* write all before the doorbell */ + + writel(db_info, CMDQ_DB_ADDR(cmdq->db_base, prod_idx)); +} + +static int cmdq_sync_cmd_direct_resp(struct hinic_cmdq *cmdq, + enum hinic_mod_type mod, u8 cmd, + struct hinic_cmdq_buf *buf_in, + u64 *resp) +{ + struct hinic_cmdq_wqe *curr_cmdq_wqe, cmdq_wqe; + u16 curr_prod_idx, next_prod_idx; + int errcode, wrapped, num_wqebbs; + struct hinic_wq *wq = cmdq->wq; + struct hinic_hw_wqe *hw_wqe; + struct completion done; + + /* Keep doorbell index correct. bh - for tasklet(ceq). */ + spin_lock_bh(&cmdq->cmdq_lock); + + /* WQE_SIZE = WQEBB_SIZE, we will get the wq element and not shadow*/ + hw_wqe = hinic_get_wqe(wq, WQE_LCMD_SIZE, &curr_prod_idx); + if (IS_ERR(hw_wqe)) { + spin_unlock_bh(&cmdq->cmdq_lock); + return -EBUSY; + } + + curr_cmdq_wqe = &hw_wqe->cmdq_wqe; + + wrapped = cmdq->wrapped; + + num_wqebbs = ALIGN(WQE_LCMD_SIZE, wq->wqebb_size) / wq->wqebb_size; + next_prod_idx = curr_prod_idx + num_wqebbs; + if (next_prod_idx >= wq->q_depth) { + cmdq->wrapped = !cmdq->wrapped; + next_prod_idx -= wq->q_depth; + } + + cmdq->errcode[curr_prod_idx] = &errcode; + + init_completion(&done); + cmdq->done[curr_prod_idx] = &done; + + cmdq_set_lcmd_wqe(&cmdq_wqe, CMDQ_CMD_SYNC_DIRECT_RESP, buf_in, NULL, + wrapped, HINIC_CMD_ACK_TYPE_CMDQ, mod, cmd, + curr_prod_idx); + + /* The data that is written to HW should be in Big Endian Format */ + hinic_cpu_to_be32(&cmdq_wqe, WQE_LCMD_SIZE); + + /* CMDQ WQE is not shadow, therefore wqe will be written to wq */ + cmdq_wqe_fill(curr_cmdq_wqe, &cmdq_wqe); + + cmdq_set_db(cmdq, HINIC_CMDQ_SYNC, next_prod_idx); + + spin_unlock_bh(&cmdq->cmdq_lock); + + if (!wait_for_completion_timeout(&done, CMDQ_TIMEOUT)) { + spin_lock_bh(&cmdq->cmdq_lock); + + if (cmdq->errcode[curr_prod_idx] == &errcode) + cmdq->errcode[curr_prod_idx] = NULL; + + if (cmdq->done[curr_prod_idx] == &done) + cmdq->done[curr_prod_idx] = NULL; + + spin_unlock_bh(&cmdq->cmdq_lock); + + return -ETIMEDOUT; + } + + smp_rmb(); /* read error code after completion */ + + if (resp) { + struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &curr_cmdq_wqe->wqe_lcmd; + + *resp = cpu_to_be64(wqe_lcmd->completion.direct_resp); + } + + if (errcode != 0) + return -EFAULT; + + return 0; +} + +static int cmdq_params_valid(struct hinic_cmdq_buf *buf_in) +{ + if (buf_in->size > HINIC_CMDQ_MAX_DATA_SIZE) + return -EINVAL; + + return 0; +} + /** * hinic_cmdq_direct_resp - send command with direct data as resp * @cmdqs: the cmdqs @@ -106,8 +374,18 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, enum hinic_mod_type mod, u8 cmd, struct hinic_cmdq_buf *buf_in, u64 *resp) { - /* should be implemented */ - return -EINVAL; + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + err = cmdq_params_valid(buf_in); + if (err) { + dev_err(&pdev->dev, "Invalid CMDQ parameters\n"); + return err; + } + + return cmdq_sync_cmd_direct_resp(&cmdqs->cmdq[HINIC_CMDQ_SYNC], + mod, cmd, buf_in, resp); } /** diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h index 5ec59f1b4b0a..e11a4f094997 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h @@ -58,14 +58,52 @@ ((val) & (~((u64)HINIC_CMDQ_CTXT_##member##_MASK \ << HINIC_CMDQ_CTXT_##member##_SHIFT))) +#define HINIC_SAVED_DATA_ARM_SHIFT 31 + +#define HINIC_SAVED_DATA_ARM_MASK 0x1 + +#define HINIC_SAVED_DATA_SET(val, member) \ + (((u32)(val) & HINIC_SAVED_DATA_##member##_MASK) \ + << HINIC_SAVED_DATA_##member##_SHIFT) + +#define HINIC_SAVED_DATA_GET(val, member) \ + (((val) >> HINIC_SAVED_DATA_##member##_SHIFT) \ + & HINIC_SAVED_DATA_##member##_MASK) + +#define HINIC_SAVED_DATA_CLEAR(val, member) \ + ((val) & (~(HINIC_SAVED_DATA_##member##_MASK \ + << HINIC_SAVED_DATA_##member##_SHIFT))) + +#define HINIC_CMDQ_DB_INFO_HI_PROD_IDX_SHIFT 0 +#define HINIC_CMDQ_DB_INFO_PATH_SHIFT 23 +#define HINIC_CMDQ_DB_INFO_CMDQ_TYPE_SHIFT 24 +#define HINIC_CMDQ_DB_INFO_DB_TYPE_SHIFT 27 + +#define HINIC_CMDQ_DB_INFO_HI_PROD_IDX_MASK 0xFF +#define HINIC_CMDQ_DB_INFO_PATH_MASK 0x1 +#define HINIC_CMDQ_DB_INFO_CMDQ_TYPE_MASK 0x7 +#define HINIC_CMDQ_DB_INFO_DB_TYPE_MASK 0x1F + +#define HINIC_CMDQ_DB_INFO_SET(val, member) \ + (((u32)(val) & HINIC_CMDQ_DB_INFO_##member##_MASK) \ + << HINIC_CMDQ_DB_INFO_##member##_SHIFT) + #define HINIC_CMDQ_BUF_SIZE 2048 +#define HINIC_CMDQ_BUF_HW_RSVD 8 +#define HINIC_CMDQ_MAX_DATA_SIZE (HINIC_CMDQ_BUF_SIZE - \ + HINIC_CMDQ_BUF_HW_RSVD) + enum hinic_cmdq_type { HINIC_CMDQ_SYNC, HINIC_MAX_CMDQ_TYPES, }; +enum hinic_cmd_ack_type { + HINIC_CMD_ACK_TYPE_CMDQ, +}; + struct hinic_cmdq_buf { void *buf; dma_addr_t dma_addr; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h index cfc21baf0977..adb64179d47d 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h @@ -32,6 +32,16 @@ #define HINIC_DB_MAX_AREAS (HINIC_DB_SIZE / HINIC_DB_PAGE_SIZE) +enum hinic_db_type { + HINIC_DB_CMDQ_TYPE, + HINIC_DB_SQ_TYPE, +}; + +enum hinic_io_path { + HINIC_CTRL_PATH, + HINIC_DATA_PATH, +}; + struct hinic_free_db_area { int db_idx[HINIC_DB_MAX_AREAS]; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c index 638aab790bd6..6ceae958db28 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c @@ -23,9 +23,11 @@ #include #include #include +#include #include #include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" #include "hinic_hw_wq.h" #include "hinic_hw_cmdq.h" @@ -72,6 +74,25 @@ ((void *)((cmdq_pages)->shadow_page_vaddr) \ + (wq)->block_idx * CMDQ_BLOCK_SIZE) +#define WQE_PAGE_OFF(wq, idx) (((idx) & ((wq)->num_wqebbs_per_page - 1)) * \ + (wq)->wqebb_size) + +#define WQE_PAGE_NUM(wq, idx) (((idx) / ((wq)->num_wqebbs_per_page)) \ + & ((wq)->num_q_pages - 1)) + +#define WQ_PAGE_ADDR(wq, idx) \ + ((wq)->shadow_block_vaddr[WQE_PAGE_NUM(wq, idx)]) + +#define MASKED_WQE_IDX(wq, idx) ((idx) & (wq)->mask) + +#define WQE_IN_RANGE(wqe, start, end) \ + (((unsigned long)(wqe) >= (unsigned long)(start)) && \ + ((unsigned long)(wqe) < (unsigned long)(end))) + +#define WQE_SHADOW_PAGE(wq, wqe) \ + (((unsigned long)(wqe) - (unsigned long)(wq)->shadow_wqe) \ + / (wq)->max_wqe_size) + /** * queue_alloc_page - allocate page for Queue * @hwif: HW interface for allocating DMA @@ -670,3 +691,176 @@ void hinic_wqs_cmdq_free(struct hinic_cmdq_pages *cmdq_pages, cmdq_free_page(cmdq_pages); } + +static void copy_wqe_to_shadow(struct hinic_wq *wq, void *shadow_addr, + int num_wqebbs, u16 idx) +{ + void *wqebb_addr; + int i; + + for (i = 0; i < num_wqebbs; i++, idx++) { + idx = MASKED_WQE_IDX(wq, idx); + wqebb_addr = WQ_PAGE_ADDR(wq, idx) + + WQE_PAGE_OFF(wq, idx); + + memcpy(shadow_addr, wqebb_addr, wq->wqebb_size); + + shadow_addr += wq->wqebb_size; + } +} + +static void copy_wqe_from_shadow(struct hinic_wq *wq, void *shadow_addr, + int num_wqebbs, u16 idx) +{ + void *wqebb_addr; + int i; + + for (i = 0; i < num_wqebbs; i++, idx++) { + idx = MASKED_WQE_IDX(wq, idx); + wqebb_addr = WQ_PAGE_ADDR(wq, idx) + + WQE_PAGE_OFF(wq, idx); + + memcpy(wqebb_addr, shadow_addr, wq->wqebb_size); + shadow_addr += wq->wqebb_size; + } +} + +/** + * hinic_get_wqe - get wqe ptr in the current pi and update the pi + * @wq: wq to get wqe from + * @wqe_size: wqe size + * @prod_idx: returned pi + * + * Return wqe pointer + **/ +struct hinic_hw_wqe *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size, + u16 *prod_idx) +{ + int curr_pg, end_pg, num_wqebbs; + u16 curr_prod_idx, end_prod_idx; + + *prod_idx = MASKED_WQE_IDX(wq, atomic_read(&wq->prod_idx)); + + num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + + if (atomic_sub_return(num_wqebbs, &wq->delta) <= 0) { + atomic_add(num_wqebbs, &wq->delta); + return ERR_PTR(-EBUSY); + } + + end_prod_idx = atomic_add_return(num_wqebbs, &wq->prod_idx); + + end_prod_idx = MASKED_WQE_IDX(wq, end_prod_idx); + curr_prod_idx = end_prod_idx - num_wqebbs; + curr_prod_idx = MASKED_WQE_IDX(wq, curr_prod_idx); + + /* end prod index points to the next wqebb, therefore minus 1 */ + end_prod_idx = MASKED_WQE_IDX(wq, end_prod_idx - 1); + + curr_pg = WQE_PAGE_NUM(wq, curr_prod_idx); + end_pg = WQE_PAGE_NUM(wq, end_prod_idx); + + *prod_idx = curr_prod_idx; + + if (curr_pg != end_pg) { + void *shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size]; + + copy_wqe_to_shadow(wq, shadow_addr, num_wqebbs, *prod_idx); + + wq->shadow_idx[curr_pg] = *prod_idx; + return shadow_addr; + } + + return WQ_PAGE_ADDR(wq, *prod_idx) + WQE_PAGE_OFF(wq, *prod_idx); +} + +/** + * hinic_put_wqe - return the wqe place to use for a new wqe + * @wq: wq to return wqe + * @wqe_size: wqe size + **/ +void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size) +{ + int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + + atomic_add(num_wqebbs, &wq->cons_idx); + + atomic_add(num_wqebbs, &wq->delta); +} + +/** + * hinic_read_wqe - read wqe ptr in the current ci + * @wq: wq to get read from + * @wqe_size: wqe size + * @cons_idx: returned ci + * + * Return wqe pointer + **/ +struct hinic_hw_wqe *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size, + u16 *cons_idx) +{ + int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + u16 curr_cons_idx, end_cons_idx; + int curr_pg, end_pg; + + if ((atomic_read(&wq->delta) + num_wqebbs) > wq->q_depth) + return ERR_PTR(-EBUSY); + + curr_cons_idx = atomic_read(&wq->cons_idx); + + curr_cons_idx = MASKED_WQE_IDX(wq, curr_cons_idx); + end_cons_idx = MASKED_WQE_IDX(wq, curr_cons_idx + num_wqebbs - 1); + + curr_pg = WQE_PAGE_NUM(wq, curr_cons_idx); + end_pg = WQE_PAGE_NUM(wq, end_cons_idx); + + *cons_idx = curr_cons_idx; + + if (curr_pg != end_pg) { + void *shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size]; + + copy_wqe_to_shadow(wq, shadow_addr, num_wqebbs, *cons_idx); + return shadow_addr; + } + + return WQ_PAGE_ADDR(wq, *cons_idx) + WQE_PAGE_OFF(wq, *cons_idx); +} + +/** + * wqe_shadow - check if a wqe is shadow + * @wq: wq of the wqe + * @wqe: the wqe for shadow checking + * + * Return true - shadow, false - Not shadow + **/ +static inline bool wqe_shadow(struct hinic_wq *wq, struct hinic_hw_wqe *wqe) +{ + size_t wqe_shadow_size = wq->num_q_pages * wq->max_wqe_size; + + return WQE_IN_RANGE(wqe, wq->shadow_wqe, + &wq->shadow_wqe[wqe_shadow_size]); +} + +/** + * hinic_write_wqe - write the wqe to the wq + * @wq: wq to write wqe to + * @wqe: wqe to write + * @wqe_size: wqe size + **/ +void hinic_write_wqe(struct hinic_wq *wq, struct hinic_hw_wqe *wqe, + unsigned int wqe_size) +{ + int curr_pg, num_wqebbs; + void *shadow_addr; + u16 prod_idx; + + if (wqe_shadow(wq, wqe)) { + curr_pg = WQE_SHADOW_PAGE(wq, wqe); + + prod_idx = wq->shadow_idx[curr_pg]; + num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size]; + + copy_wqe_from_shadow(wq, shadow_addr, num_wqebbs, prod_idx); + } +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h index a3c4469d0efe..f01477a2c165 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h @@ -21,6 +21,7 @@ #include #include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" struct hinic_free_block { int page_idx; @@ -100,4 +101,15 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq, void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq); +struct hinic_hw_wqe *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size, + u16 *prod_idx); + +void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size); + +struct hinic_hw_wqe *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size, + u16 *cons_idx); + +void hinic_write_wqe(struct hinic_wq *wq, struct hinic_hw_wqe *wqe, + unsigned int wqe_size); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h index d727c4dc9d6c..bc73485483c5 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h @@ -18,6 +18,50 @@ #include "hinic_common.h" +#define HINIC_CMDQ_CTRL_PI_SHIFT 0 +#define HINIC_CMDQ_CTRL_CMD_SHIFT 16 +#define HINIC_CMDQ_CTRL_MOD_SHIFT 24 +#define HINIC_CMDQ_CTRL_ACK_TYPE_SHIFT 29 +#define HINIC_CMDQ_CTRL_HW_BUSY_BIT_SHIFT 31 + +#define HINIC_CMDQ_CTRL_PI_MASK 0xFFFF +#define HINIC_CMDQ_CTRL_CMD_MASK 0xFF +#define HINIC_CMDQ_CTRL_MOD_MASK 0x1F +#define HINIC_CMDQ_CTRL_ACK_TYPE_MASK 0x3 +#define HINIC_CMDQ_CTRL_HW_BUSY_BIT_MASK 0x1 + +#define HINIC_CMDQ_CTRL_SET(val, member) \ + (((u32)(val) & HINIC_CMDQ_CTRL_##member##_MASK) \ + << HINIC_CMDQ_CTRL_##member##_SHIFT) + +#define HINIC_CMDQ_CTRL_GET(val, member) \ + (((val) >> HINIC_CMDQ_CTRL_##member##_SHIFT) \ + & HINIC_CMDQ_CTRL_##member##_MASK) + +#define HINIC_CMDQ_WQE_HEADER_BUFDESC_LEN_SHIFT 0 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_FMT_SHIFT 15 +#define HINIC_CMDQ_WQE_HEADER_DATA_FMT_SHIFT 22 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_REQ_SHIFT 23 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_SECT_LEN_SHIFT 27 +#define HINIC_CMDQ_WQE_HEADER_CTRL_LEN_SHIFT 29 +#define HINIC_CMDQ_WQE_HEADER_TOGGLED_WRAPPED_SHIFT 31 + +#define HINIC_CMDQ_WQE_HEADER_BUFDESC_LEN_MASK 0xFF +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_FMT_MASK 0x1 +#define HINIC_CMDQ_WQE_HEADER_DATA_FMT_MASK 0x1 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_REQ_MASK 0x1 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_SECT_LEN_MASK 0x3 +#define HINIC_CMDQ_WQE_HEADER_CTRL_LEN_MASK 0x3 +#define HINIC_CMDQ_WQE_HEADER_TOGGLED_WRAPPED_MASK 0x1 + +#define HINIC_CMDQ_WQE_HEADER_SET(val, member) \ + (((u32)(val) & HINIC_CMDQ_WQE_HEADER_##member##_MASK) \ + << HINIC_CMDQ_WQE_HEADER_##member##_SHIFT) + +#define HINIC_CMDQ_WQE_HEADER_GET(val, member) \ + (((val) >> HINIC_CMDQ_WQE_HEADER_##member##_SHIFT) \ + & HINIC_CMDQ_WQE_HEADER_##member##_MASK) + #define HINIC_SQ_CTRL_BUFDESC_SECT_LEN_SHIFT 0 #define HINIC_SQ_CTRL_TASKSECT_LEN_SHIFT 16 #define HINIC_SQ_CTRL_DATA_FORMAT_SHIFT 22 @@ -143,6 +187,8 @@ sizeof(struct hinic_sq_task) + \ (nr_sges) * sizeof(struct hinic_sq_bufdesc)) +#define HINIC_SCMD_DATA_LEN 16 + #define HINIC_MAX_SQ_BUFDESCS 17 #define HINIC_SQ_WQE_MAX_SIZE 320 @@ -184,6 +230,74 @@ enum hinc_tunnel_l4type { HINIC_TUNNEL_L4TYPE_UNKNOWN = 0, }; +struct hinic_cmdq_header { + u32 header_info; + u32 saved_data; +}; + +struct hinic_status { + u32 status_info; +}; + +struct hinic_ctrl { + u32 ctrl_info; +}; + +struct hinic_sge_resp { + struct hinic_sge sge; + u32 rsvd; +}; + +struct hinic_cmdq_completion { + /* HW Format */ + union { + struct hinic_sge_resp sge_resp; + u64 direct_resp; + }; +}; + +struct hinic_scmd_bufdesc { + u32 buf_len; + u32 rsvd; + u8 data[HINIC_SCMD_DATA_LEN]; +}; + +struct hinic_lcmd_bufdesc { + struct hinic_sge sge; + u32 rsvd1; + u64 rsvd2; + u64 rsvd3; +}; + +struct hinic_cmdq_wqe_scmd { + struct hinic_cmdq_header header; + u64 rsvd; + struct hinic_status status; + struct hinic_ctrl ctrl; + struct hinic_cmdq_completion completion; + struct hinic_scmd_bufdesc buf_desc; +}; + +struct hinic_cmdq_wqe_lcmd { + struct hinic_cmdq_header header; + struct hinic_status status; + struct hinic_ctrl ctrl; + struct hinic_cmdq_completion completion; + struct hinic_lcmd_bufdesc buf_desc; +}; + +struct hinic_cmdq_direct_wqe { + struct hinic_cmdq_wqe_scmd wqe_scmd; +}; + +struct hinic_cmdq_wqe { + /* HW Format */ + union { + struct hinic_cmdq_direct_wqe direct_wqe; + struct hinic_cmdq_wqe_lcmd wqe_lcmd; + }; +}; + struct hinic_sq_ctrl { u32 ctrl_info; u32 queue_info; @@ -245,6 +359,7 @@ struct hinic_rq_wqe { struct hinic_hw_wqe { /* HW Format */ union { + struct hinic_cmdq_wqe cmdq_wqe; struct hinic_sq_wqe sq_wqe; struct hinic_rq_wqe rq_wqe; }; -- cgit v1.2.3 From 7ef37fe4c1a156a394174bd1b5d849cef2b8b4fa Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:56:03 +0800 Subject: net-next/hinic: Add cmdq completion handler Add cmdq completion handler for getting a notification about the completion of cmdq commands. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c | 297 +++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h | 12 + 2 files changed, 308 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c index 07ce78745098..7d95f0866fb0 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c @@ -40,12 +40,31 @@ #include "hinic_hw_io.h" #include "hinic_hw_dev.h" +#define CMDQ_CEQE_TYPE_SHIFT 0 + +#define CMDQ_CEQE_TYPE_MASK 0x7 + +#define CMDQ_CEQE_GET(val, member) \ + (((val) >> CMDQ_CEQE_##member##_SHIFT) \ + & CMDQ_CEQE_##member##_MASK) + +#define CMDQ_WQE_ERRCODE_VAL_SHIFT 20 + +#define CMDQ_WQE_ERRCODE_VAL_MASK 0xF + +#define CMDQ_WQE_ERRCODE_GET(val, member) \ + (((val) >> CMDQ_WQE_ERRCODE_##member##_SHIFT) \ + & CMDQ_WQE_ERRCODE_##member##_MASK) + #define CMDQ_DB_PI_OFF(pi) (((u16)LOWER_8_BITS(pi)) << 3) #define CMDQ_DB_ADDR(db_base, pi) ((db_base) + CMDQ_DB_PI_OFF(pi)) #define CMDQ_WQE_HEADER(wqe) ((struct hinic_cmdq_header *)(wqe)) +#define CMDQ_WQE_COMPLETED(ctrl_info) \ + HINIC_CMDQ_CTRL_GET(ctrl_info, HW_BUSY_BIT) + #define FIRST_DATA_TO_WRITE_LAST sizeof(u64) #define CMDQ_DB_OFF SZ_2K @@ -145,6 +164,22 @@ void hinic_free_cmdq_buf(struct hinic_cmdqs *cmdqs, pci_pool_free(cmdqs->cmdq_buf_pool, cmdq_buf->buf, cmdq_buf->dma_addr); } +static unsigned int cmdq_wqe_size_from_bdlen(enum bufdesc_len len) +{ + unsigned int wqe_size = 0; + + switch (len) { + case BUFDESC_LCMD_LEN: + wqe_size = WQE_LCMD_SIZE; + break; + case BUFDESC_SCMD_LEN: + wqe_size = WQE_SCMD_SIZE; + break; + } + + return wqe_size; +} + static void cmdq_set_sge_completion(struct hinic_cmdq_completion *completion, struct hinic_cmdq_buf *buf_out) { @@ -210,6 +245,15 @@ static void cmdq_set_lcmd_bufdesc(struct hinic_cmdq_wqe_lcmd *wqe_lcmd, hinic_set_sge(&wqe_lcmd->buf_desc.sge, buf_in->dma_addr, buf_in->size); } +static void cmdq_set_direct_wqe_data(struct hinic_cmdq_direct_wqe *wqe, + void *buf_in, u32 in_size) +{ + struct hinic_cmdq_wqe_scmd *wqe_scmd = &wqe->wqe_scmd; + + wqe_scmd->buf_desc.buf_len = in_size; + memcpy(wqe_scmd->buf_desc.data, buf_in, in_size); +} + static void cmdq_set_lcmd_wqe(struct hinic_cmdq_wqe *wqe, enum cmdq_cmd_type cmd_type, struct hinic_cmdq_buf *buf_in, @@ -238,6 +282,36 @@ static void cmdq_set_lcmd_wqe(struct hinic_cmdq_wqe *wqe, cmdq_set_lcmd_bufdesc(wqe_lcmd, buf_in); } +static void cmdq_set_direct_wqe(struct hinic_cmdq_wqe *wqe, + enum cmdq_cmd_type cmd_type, + void *buf_in, u16 in_size, + struct hinic_cmdq_buf *buf_out, int wrapped, + enum hinic_cmd_ack_type ack_type, + enum hinic_mod_type mod, u8 cmd, u16 prod_idx) +{ + struct hinic_cmdq_direct_wqe *direct_wqe = &wqe->direct_wqe; + enum completion_format complete_format; + struct hinic_cmdq_wqe_scmd *wqe_scmd; + + wqe_scmd = &direct_wqe->wqe_scmd; + + switch (cmd_type) { + case CMDQ_CMD_SYNC_SGE_RESP: + complete_format = COMPLETE_SGE; + cmdq_set_sge_completion(&wqe_scmd->completion, buf_out); + break; + case CMDQ_CMD_SYNC_DIRECT_RESP: + complete_format = COMPLETE_DIRECT; + wqe_scmd->completion.direct_resp = 0; + break; + } + + cmdq_prepare_wqe_ctrl(wqe, wrapped, ack_type, mod, cmd, prod_idx, + complete_format, DATA_DIRECT, BUFDESC_SCMD_LEN); + + cmdq_set_direct_wqe_data(direct_wqe, buf_in, in_size); +} + static void cmdq_wqe_fill(void *dst, void *src) { memcpy(dst + FIRST_DATA_TO_WRITE_LAST, src + FIRST_DATA_TO_WRITE_LAST, @@ -352,6 +426,52 @@ static int cmdq_sync_cmd_direct_resp(struct hinic_cmdq *cmdq, return 0; } +static int cmdq_set_arm_bit(struct hinic_cmdq *cmdq, void *buf_in, + u16 in_size) +{ + struct hinic_cmdq_wqe *curr_cmdq_wqe, cmdq_wqe; + u16 curr_prod_idx, next_prod_idx; + struct hinic_wq *wq = cmdq->wq; + struct hinic_hw_wqe *hw_wqe; + int wrapped, num_wqebbs; + + /* Keep doorbell index correct */ + spin_lock(&cmdq->cmdq_lock); + + /* WQE_SIZE = WQEBB_SIZE, we will get the wq element and not shadow*/ + hw_wqe = hinic_get_wqe(wq, WQE_SCMD_SIZE, &curr_prod_idx); + if (IS_ERR(hw_wqe)) { + spin_unlock(&cmdq->cmdq_lock); + return -EBUSY; + } + + curr_cmdq_wqe = &hw_wqe->cmdq_wqe; + + wrapped = cmdq->wrapped; + + num_wqebbs = ALIGN(WQE_SCMD_SIZE, wq->wqebb_size) / wq->wqebb_size; + next_prod_idx = curr_prod_idx + num_wqebbs; + if (next_prod_idx >= wq->q_depth) { + cmdq->wrapped = !cmdq->wrapped; + next_prod_idx -= wq->q_depth; + } + + cmdq_set_direct_wqe(&cmdq_wqe, CMDQ_CMD_SYNC_DIRECT_RESP, buf_in, + in_size, NULL, wrapped, HINIC_CMD_ACK_TYPE_CMDQ, + HINIC_MOD_COMM, CMDQ_SET_ARM_CMD, curr_prod_idx); + + /* The data that is written to HW should be in Big Endian Format */ + hinic_cpu_to_be32(&cmdq_wqe, WQE_SCMD_SIZE); + + /* cmdq wqe is not shadow, therefore wqe will be written to wq */ + cmdq_wqe_fill(curr_cmdq_wqe, &cmdq_wqe); + + cmdq_set_db(cmdq, HINIC_CMDQ_SYNC, next_prod_idx); + + spin_unlock(&cmdq->cmdq_lock); + return 0; +} + static int cmdq_params_valid(struct hinic_cmdq_buf *buf_in) { if (buf_in->size > HINIC_CMDQ_MAX_DATA_SIZE) @@ -388,6 +508,139 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, mod, cmd, buf_in, resp); } +/** + * hinic_set_arm_bit - set arm bit for enable interrupt again + * @cmdqs: the cmdqs + * @q_type: type of queue to set the arm bit for + * @q_id: the queue number + * + * Return 0 - Success, negative - Failure + **/ +int hinic_set_arm_bit(struct hinic_cmdqs *cmdqs, + enum hinic_set_arm_qtype q_type, u32 q_id) +{ + struct hinic_cmdq *cmdq = &cmdqs->cmdq[HINIC_CMDQ_SYNC]; + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_cmdq_arm_bit arm_bit; + int err; + + arm_bit.q_type = q_type; + arm_bit.q_id = q_id; + + err = cmdq_set_arm_bit(cmdq, &arm_bit, sizeof(arm_bit)); + if (err) { + dev_err(&pdev->dev, "Failed to set arm for qid %d\n", q_id); + return err; + } + + return 0; +} + +static void clear_wqe_complete_bit(struct hinic_cmdq *cmdq, + struct hinic_cmdq_wqe *wqe) +{ + u32 header_info = be32_to_cpu(CMDQ_WQE_HEADER(wqe)->header_info); + unsigned int bufdesc_len, wqe_size; + struct hinic_ctrl *ctrl; + + bufdesc_len = HINIC_CMDQ_WQE_HEADER_GET(header_info, BUFDESC_LEN); + wqe_size = cmdq_wqe_size_from_bdlen(bufdesc_len); + if (wqe_size == WQE_LCMD_SIZE) { + struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &wqe->wqe_lcmd; + + ctrl = &wqe_lcmd->ctrl; + } else { + struct hinic_cmdq_direct_wqe *direct_wqe = &wqe->direct_wqe; + struct hinic_cmdq_wqe_scmd *wqe_scmd; + + wqe_scmd = &direct_wqe->wqe_scmd; + ctrl = &wqe_scmd->ctrl; + } + + /* clear HW busy bit */ + ctrl->ctrl_info = 0; + + wmb(); /* verify wqe is clear */ +} + +/** + * cmdq_arm_ceq_handler - cmdq completion event handler for arm command + * @cmdq: the cmdq of the arm command + * @wqe: the wqe of the arm command + * + * Return 0 - Success, negative - Failure + **/ +static int cmdq_arm_ceq_handler(struct hinic_cmdq *cmdq, + struct hinic_cmdq_wqe *wqe) +{ + struct hinic_cmdq_direct_wqe *direct_wqe = &wqe->direct_wqe; + struct hinic_cmdq_wqe_scmd *wqe_scmd; + struct hinic_ctrl *ctrl; + u32 ctrl_info; + + wqe_scmd = &direct_wqe->wqe_scmd; + ctrl = &wqe_scmd->ctrl; + ctrl_info = be32_to_cpu(ctrl->ctrl_info); + + /* HW should toggle the HW BUSY BIT */ + if (!CMDQ_WQE_COMPLETED(ctrl_info)) + return -EBUSY; + + clear_wqe_complete_bit(cmdq, wqe); + + hinic_put_wqe(cmdq->wq, WQE_SCMD_SIZE); + return 0; +} + +static void cmdq_update_errcode(struct hinic_cmdq *cmdq, u16 prod_idx, + int errcode) +{ + if (cmdq->errcode[prod_idx]) + *cmdq->errcode[prod_idx] = errcode; +} + +/** + * cmdq_arm_ceq_handler - cmdq completion event handler for sync command + * @cmdq: the cmdq of the command + * @cons_idx: the consumer index to update the error code for + * @errcode: the error code + **/ +static void cmdq_sync_cmd_handler(struct hinic_cmdq *cmdq, u16 cons_idx, + int errcode) +{ + u16 prod_idx = cons_idx; + + spin_lock(&cmdq->cmdq_lock); + cmdq_update_errcode(cmdq, prod_idx, errcode); + + wmb(); /* write all before update for the command request */ + + if (cmdq->done[prod_idx]) + complete(cmdq->done[prod_idx]); + spin_unlock(&cmdq->cmdq_lock); +} + +static int cmdq_cmd_ceq_handler(struct hinic_cmdq *cmdq, u16 ci, + struct hinic_cmdq_wqe *cmdq_wqe) +{ + struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &cmdq_wqe->wqe_lcmd; + struct hinic_status *status = &wqe_lcmd->status; + struct hinic_ctrl *ctrl = &wqe_lcmd->ctrl; + int errcode; + + if (!CMDQ_WQE_COMPLETED(be32_to_cpu(ctrl->ctrl_info))) + return -EBUSY; + + errcode = CMDQ_WQE_ERRCODE_GET(be32_to_cpu(status->status_info), VAL); + + cmdq_sync_cmd_handler(cmdq, ci, errcode); + + clear_wqe_complete_bit(cmdq, cmdq_wqe); + hinic_put_wqe(cmdq->wq, WQE_LCMD_SIZE); + return 0; +} + /** * cmdq_ceq_handler - cmdq completion event handler * @handle: private data for the handler(cmdqs) @@ -395,7 +648,49 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, **/ static void cmdq_ceq_handler(void *handle, u32 ceqe_data) { - /* should be implemented */ + enum hinic_cmdq_type cmdq_type = CMDQ_CEQE_GET(ceqe_data, TYPE); + struct hinic_cmdqs *cmdqs = (struct hinic_cmdqs *)handle; + struct hinic_cmdq *cmdq = &cmdqs->cmdq[cmdq_type]; + struct hinic_cmdq_header *header; + struct hinic_hw_wqe *hw_wqe; + int err, set_arm = 0; + u32 saved_data; + u16 ci; + + /* Read the smallest wqe size for getting wqe size */ + while ((hw_wqe = hinic_read_wqe(cmdq->wq, WQE_SCMD_SIZE, &ci))) { + if (IS_ERR(hw_wqe)) + break; + + header = CMDQ_WQE_HEADER(&hw_wqe->cmdq_wqe); + saved_data = be32_to_cpu(header->saved_data); + + if (HINIC_SAVED_DATA_GET(saved_data, ARM)) { + /* arm_bit was set until here */ + set_arm = 0; + + if (cmdq_arm_ceq_handler(cmdq, &hw_wqe->cmdq_wqe)) + break; + } else { + set_arm = 1; + + hw_wqe = hinic_read_wqe(cmdq->wq, WQE_LCMD_SIZE, &ci); + if (IS_ERR(hw_wqe)) + break; + + if (cmdq_cmd_ceq_handler(cmdq, ci, &hw_wqe->cmdq_wqe)) + break; + } + } + + if (set_arm) { + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + + err = hinic_set_arm_bit(cmdqs, HINIC_SET_ARM_CMDQ, cmdq_type); + if (err) + dev_err(&pdev->dev, "Failed to set arm for CMDQ\n"); + } } /** diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h index e11a4f094997..b35583400cb6 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h @@ -100,6 +100,10 @@ enum hinic_cmdq_type { HINIC_MAX_CMDQ_TYPES, }; +enum hinic_set_arm_qtype { + HINIC_SET_ARM_CMDQ, +}; + enum hinic_cmd_ack_type { HINIC_CMD_ACK_TYPE_CMDQ, }; @@ -110,6 +114,11 @@ struct hinic_cmdq_buf { size_t size; }; +struct hinic_cmdq_arm_bit { + u32 q_type; + u32 q_id; +}; + struct hinic_cmdq_ctxt_info { u64 curr_wqe_page_pfn; u64 wq_block_pfn; @@ -167,6 +176,9 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, enum hinic_mod_type mod, u8 cmd, struct hinic_cmdq_buf *buf_in, u64 *out_param); +int hinic_set_arm_bit(struct hinic_cmdqs *cmdqs, + enum hinic_set_arm_qtype q_type, u32 q_id); + int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, void __iomem **db_area); -- cgit v1.2.3 From e2585ea775380ec2b2b1bf9619a5a3a6d26aa72b Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:56:04 +0800 Subject: net-next/hinic: Add Rx handler Set the io resources in the nic and handle rx events by qp operations. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 1 + drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 1 + drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 361 +++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 77 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.c | 36 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 35 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 13 + drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c | 208 +++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h | 29 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c | 12 + drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h | 2 + drivers/net/ethernet/huawei/hinic/hinic_main.c | 27 ++ drivers/net/ethernet/huawei/hinic/hinic_port.c | 32 ++ drivers/net/ethernet/huawei/hinic/hinic_port.h | 19 + drivers/net/ethernet/huawei/hinic/hinic_rx.c | 418 ++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_rx.h | 7 + 16 files changed, 1278 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 5b8231dc3ff1..3d0f6cf2508f 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -43,6 +43,7 @@ struct hinic_dev { struct hinic_hwdev *hwdev; u32 msg_enable; + unsigned int rx_weight; unsigned int flags; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h index 10b8c7b650dc..f39b184f674d 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -20,6 +20,7 @@ #define HINIC_CSR_FUNC_ATTR0_ADDR 0x0 #define HINIC_CSR_FUNC_ATTR1_ADDR 0x4 +#define HINIC_CSR_FUNC_ATTR4_ADDR 0x10 #define HINIC_CSR_FUNC_ATTR5_ADDR 0x14 #define HINIC_DMA_ATTR_BASE 0xC80 diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index 2f698f1a89a3..77d43431e573 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include #include #include "hinic_hw_if.h" @@ -30,6 +33,10 @@ #include "hinic_hw_io.h" #include "hinic_hw_dev.h" +#define IO_STATUS_TIMEOUT 100 +#define OUTBOUND_STATE_TIMEOUT 100 +#define DB_STATE_TIMEOUT 100 + #define MAX_IRQS(max_qps, num_aeqs, num_ceqs) \ (2 * (max_qps) + (num_aeqs) + (num_ceqs)) @@ -37,6 +44,15 @@ enum intr_type { INTR_MSIX_TYPE, }; +enum io_status { + IO_STOPPED = 0, + IO_RUNNING = 1, +}; + +enum hw_ioctxt_set_cmdq_depth { + HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT, +}; + /* HW struct */ struct hinic_dev_cap { u8 status; @@ -51,6 +67,31 @@ struct hinic_dev_cap { u8 rsvd3[208]; }; +struct rx_buf_sz { + int idx; + size_t sz; +}; + +static struct rx_buf_sz rx_buf_sz_table[] = { + {0, 32}, + {1, 64}, + {2, 96}, + {3, 128}, + {4, 192}, + {5, 256}, + {6, 384}, + {7, 512}, + {8, 768}, + {9, 1024}, + {10, 1536}, + {11, 2048}, + {12, 3072}, + {13, 4096}, + {14, 8192}, + {15, 16384}, + {-1, -1}, +}; + /** * get_capability - convert device capabilities to NIC capabilities * @hwdev: the HW device to set and convert device capabilities for @@ -235,6 +276,252 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, HINIC_MGMT_MSG_SYNC); } +/** + * init_fw_ctxt- Init Firmware tables before network mgmt and io operations + * @hwdev: the NIC HW device + * + * Return 0 - Success, negative - Failure + **/ +static int init_fw_ctxt(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_cmd_fw_ctxt fw_ctxt; + struct hinic_pfhwdev *pfhwdev; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + fw_ctxt.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + fw_ctxt.rx_buf_sz = HINIC_RX_BUF_SZ; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_FWCTXT_INIT, + &fw_ctxt, sizeof(fw_ctxt), + &fw_ctxt, &out_size); + if (err || (out_size != sizeof(fw_ctxt)) || fw_ctxt.status) { + dev_err(&pdev->dev, "Failed to init FW ctxt, ret = %d\n", + fw_ctxt.status); + return -EFAULT; + } + + return 0; +} + +/** + * set_hw_ioctxt - set the shape of the IO queues in FW + * @hwdev: the NIC HW device + * @rq_depth: rq depth + * @sq_depth: sq depth + * + * Return 0 - Success, negative - Failure + **/ +static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int rq_depth, + unsigned int sq_depth) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_cmd_hw_ioctxt hw_ioctxt; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + int i; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + hw_ioctxt.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + hw_ioctxt.set_cmdq_depth = HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT; + hw_ioctxt.cmdq_depth = 0; + + hw_ioctxt.rq_depth = ilog2(rq_depth); + + for (i = 0; ; i++) { + if ((rx_buf_sz_table[i].sz == HINIC_RX_BUF_SZ) || + (rx_buf_sz_table[i].sz == -1)) { + hw_ioctxt.rx_buf_sz_idx = rx_buf_sz_table[i].idx; + break; + } + } + + if (hw_ioctxt.rx_buf_sz_idx == -1) + return -EINVAL; + + hw_ioctxt.sq_depth = ilog2(sq_depth); + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_HWCTXT_SET, + &hw_ioctxt, sizeof(hw_ioctxt), NULL, + NULL, HINIC_MGMT_MSG_SYNC); +} + +static int wait_for_outbound_state(struct hinic_hwdev *hwdev) +{ + enum hinic_outbound_state outbound_state; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + unsigned long end; + + end = jiffies + msecs_to_jiffies(OUTBOUND_STATE_TIMEOUT); + do { + outbound_state = hinic_outbound_state_get(hwif); + + if (outbound_state == HINIC_OUTBOUND_ENABLE) + return 0; + + msleep(20); + } while (time_before(jiffies, end)); + + dev_err(&pdev->dev, "Wait for OUTBOUND - Timeout\n"); + return -EFAULT; +} + +static int wait_for_db_state(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + enum hinic_db_state db_state; + unsigned long end; + + end = jiffies + msecs_to_jiffies(DB_STATE_TIMEOUT); + do { + db_state = hinic_db_state_get(hwif); + + if (db_state == HINIC_DB_ENABLE) + return 0; + + msleep(20); + } while (time_before(jiffies, end)); + + dev_err(&pdev->dev, "Wait for DB - Timeout\n"); + return -EFAULT; +} + +static int wait_for_io_stopped(struct hinic_hwdev *hwdev) +{ + struct hinic_cmd_io_status cmd_io_status; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + unsigned long end; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + cmd_io_status.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + end = jiffies + msecs_to_jiffies(IO_STATUS_TIMEOUT); + do { + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_IO_STATUS_GET, + &cmd_io_status, sizeof(cmd_io_status), + &cmd_io_status, &out_size, + HINIC_MGMT_MSG_SYNC); + if ((err) || (out_size != sizeof(cmd_io_status))) { + dev_err(&pdev->dev, "Failed to get IO status, ret = %d\n", + err); + return err; + } + + if (cmd_io_status.status == IO_STOPPED) { + dev_info(&pdev->dev, "IO stopped\n"); + return 0; + } + + msleep(20); + } while (time_before(jiffies, end)); + + dev_err(&pdev->dev, "Wait for IO stopped - Timeout\n"); + return -ETIMEDOUT; +} + +/** + * clear_io_resource - set the IO resources as not active in the NIC + * @hwdev: the NIC HW device + * + * Return 0 - Success, negative - Failure + **/ +static int clear_io_resources(struct hinic_hwdev *hwdev) +{ + struct hinic_cmd_clear_io_res cmd_clear_io_res; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + err = wait_for_io_stopped(hwdev); + if (err) { + dev_err(&pdev->dev, "IO has not stopped yet\n"); + return err; + } + + cmd_clear_io_res.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_IO_RES_CLEAR, &cmd_clear_io_res, + sizeof(cmd_clear_io_res), NULL, NULL, + HINIC_MGMT_MSG_SYNC); + if (err) { + dev_err(&pdev->dev, "Failed to clear IO resources\n"); + return err; + } + + return 0; +} + +/** + * set_resources_state - set the state of the resources in the NIC + * @hwdev: the NIC HW device + * @state: the state to set + * + * Return 0 - Success, negative - Failure + **/ +static int set_resources_state(struct hinic_hwdev *hwdev, + enum hinic_res_state state) +{ + struct hinic_cmd_set_res_state res_state; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + res_state.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + res_state.state = state; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, + HINIC_MOD_COMM, + HINIC_COMM_CMD_RES_STATE_SET, + &res_state, sizeof(res_state), NULL, + NULL, HINIC_MGMT_MSG_SYNC); +} + /** * get_base_qpn - get the first qp number * @hwdev: the NIC HW device @@ -312,8 +599,23 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev) goto err_create_qps; } + err = wait_for_db_state(hwdev); + if (err) { + dev_warn(&pdev->dev, "db - disabled, try again\n"); + hinic_db_state_set(hwif, HINIC_DB_ENABLE); + } + + err = set_hw_ioctxt(hwdev, HINIC_SQ_DEPTH, HINIC_RQ_DEPTH); + if (err) { + dev_err(&pdev->dev, "Failed to set HW IO ctxt\n"); + goto err_hw_ioctxt; + } + return 0; +err_hw_ioctxt: + hinic_io_destroy_qps(func_to_io, num_qps); + err_create_qps: hinic_io_free(func_to_io); return err; @@ -329,6 +631,8 @@ void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev) struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; struct hinic_cap *nic_cap = &hwdev->nic_cap; + clear_io_resources(hwdev); + hinic_io_destroy_qps(func_to_io, nic_cap->num_qps); hinic_io_free(func_to_io); } @@ -532,6 +836,12 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) goto err_init_msix; } + err = wait_for_outbound_state(hwdev); + if (err) { + dev_warn(&pdev->dev, "outbound - disabled, try again\n"); + hinic_outbound_state_set(hwif, HINIC_OUTBOUND_ENABLE); + } + num_aeqs = HINIC_HWIF_NUM_AEQS(hwif); err = hinic_aeqs_init(&hwdev->aeqs, hwif, num_aeqs, @@ -554,8 +864,22 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) goto err_dev_cap; } + err = init_fw_ctxt(hwdev); + if (err) { + dev_err(&pdev->dev, "Failed to init function table\n"); + goto err_init_fw_ctxt; + } + + err = set_resources_state(hwdev, HINIC_RES_ACTIVE); + if (err) { + dev_err(&pdev->dev, "Failed to set resources state\n"); + goto err_resources_state; + } + return hwdev; +err_resources_state: +err_init_fw_ctxt: err_dev_cap: free_pfhwdev(pfhwdev); @@ -582,6 +906,8 @@ void hinic_free_hwdev(struct hinic_hwdev *hwdev) struct hinic_pfhwdev, hwdev); + set_resources_state(hwdev, HINIC_RES_CLEAN); + free_pfhwdev(pfhwdev); hinic_aeqs_free(&hwdev->aeqs); @@ -639,3 +965,38 @@ struct hinic_rq *hinic_hwdev_get_rq(struct hinic_hwdev *hwdev, int i) return &qp->rq; } + +/** + * hinic_hwdev_msix_cnt_set - clear message attribute counters for msix entry + * @hwdev: the NIC HW device + * @msix_index: msix_index + * + * Return 0 - Success, negative - Failure + **/ +int hinic_hwdev_msix_cnt_set(struct hinic_hwdev *hwdev, u16 msix_index) +{ + return hinic_msix_attr_cnt_clear(hwdev->hwif, msix_index); +} + +/** + * hinic_hwdev_msix_set - set message attribute for msix entry + * @hwdev: the NIC HW device + * @msix_index: msix_index + * @pending_limit: the maximum pending interrupt events (unit 8) + * @coalesc_timer: coalesc period for interrupt (unit 8 us) + * @lli_timer: replenishing period for low latency credit (unit 8 us) + * @lli_credit_limit: maximum credits for low latency msix messages (unit 8) + * @resend_timer: maximum wait for resending msix (unit coalesc period) + * + * Return 0 - Success, negative - Failure + **/ +int hinic_hwdev_msix_set(struct hinic_hwdev *hwdev, u16 msix_index, + u8 pending_limit, u8 coalesc_timer, + u8 lli_timer_cfg, u8 lli_credit_limit, + u8 resend_timer) +{ + return hinic_msix_attr_set(hwdev->hwif, msix_index, + pending_limit, coalesc_timer, + lli_timer_cfg, lli_credit_limit, + resend_timer); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 81c2c6e92898..e7277d19db58 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -74,6 +74,76 @@ enum hinic_cb_state { HINIC_CB_RUNNING = BIT(1), }; +enum hinic_res_state { + HINIC_RES_CLEAN = 0, + HINIC_RES_ACTIVE = 1, +}; + +struct hinic_cmd_fw_ctxt { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rx_buf_sz; + + u32 rsvd1; +}; + +struct hinic_cmd_hw_ioctxt { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + + u16 rsvd1; + + u8 set_cmdq_depth; + u8 cmdq_depth; + + u8 rsvd2; + u8 rsvd3; + u8 rsvd4; + u8 rsvd5; + + u16 rq_depth; + u16 rx_buf_sz_idx; + u16 sq_depth; +}; + +struct hinic_cmd_io_status { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 rsvd1; + u8 rsvd2; + u32 io_status; +}; + +struct hinic_cmd_clear_io_res { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 rsvd1; + u8 rsvd2; +}; + +struct hinic_cmd_set_res_state { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 state; + u8 rsvd1; + u32 rsvd2; +}; + struct hinic_cmd_base_qpn { u8 status; u8 version; @@ -137,4 +207,11 @@ struct hinic_sq *hinic_hwdev_get_sq(struct hinic_hwdev *hwdev, int i); struct hinic_rq *hinic_hwdev_get_rq(struct hinic_hwdev *hwdev, int i); +int hinic_hwdev_msix_cnt_set(struct hinic_hwdev *hwdev, u16 msix_index); + +int hinic_hwdev_msix_set(struct hinic_hwdev *hwdev, u16 msix_index, + u8 pending_limit, u8 coalesc_timer, + u8 lli_timer_cfg, u8 lli_credit_limit, + u8 resend_timer); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c index b340695bff8b..823a17061a97 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c @@ -132,6 +132,42 @@ void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action) hinic_hwif_write_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR, attr5); } +enum hinic_outbound_state hinic_outbound_state_get(struct hinic_hwif *hwif) +{ + u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); + + return HINIC_FA4_GET(attr4, OUTBOUND_STATE); +} + +void hinic_outbound_state_set(struct hinic_hwif *hwif, + enum hinic_outbound_state outbound_state) +{ + u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); + + attr4 = HINIC_FA4_CLEAR(attr4, OUTBOUND_STATE); + attr4 |= HINIC_FA4_SET(outbound_state, OUTBOUND_STATE); + + hinic_hwif_write_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR, attr4); +} + +enum hinic_db_state hinic_db_state_get(struct hinic_hwif *hwif) +{ + u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); + + return HINIC_FA4_GET(attr4, DB_STATE); +} + +void hinic_db_state_set(struct hinic_hwif *hwif, + enum hinic_db_state db_state) +{ + u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); + + attr4 = HINIC_FA4_CLEAR(attr4, DB_STATE); + attr4 |= HINIC_FA4_SET(db_state, DB_STATE); + + hinic_hwif_write_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR, attr4); +} + /** * hwif_ready - test if the HW is ready for use * @hwif: the HW interface of a pci function device diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index 8f5919540c19..5b4760c0e9f5 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -73,6 +73,21 @@ #define HINIC_FA1_GET(val, member) \ (((val) >> HINIC_FA1_##member##_SHIFT) & HINIC_FA1_##member##_MASK) +#define HINIC_FA4_OUTBOUND_STATE_SHIFT 0 +#define HINIC_FA4_DB_STATE_SHIFT 1 + +#define HINIC_FA4_OUTBOUND_STATE_MASK 0x1 +#define HINIC_FA4_DB_STATE_MASK 0x1 + +#define HINIC_FA4_GET(val, member) \ + (((val) >> HINIC_FA4_##member##_SHIFT) & HINIC_FA4_##member##_MASK) + +#define HINIC_FA4_SET(val, member) \ + ((((u32)val) & HINIC_FA4_##member##_MASK) << HINIC_FA4_##member##_SHIFT) + +#define HINIC_FA4_CLEAR(val, member) \ + ((val) & (~(HINIC_FA4_##member##_MASK << HINIC_FA4_##member##_SHIFT))) + #define HINIC_FA5_PF_ACTION_SHIFT 0 #define HINIC_FA5_PF_ACTION_MASK 0xFFFF @@ -182,6 +197,16 @@ enum hinic_pf_action { HINIC_PF_MGMT_ACTIVE = 0x11, }; +enum hinic_outbound_state { + HINIC_OUTBOUND_ENABLE = 0, + HINIC_OUTBOUND_DISABLE = 1, +}; + +enum hinic_db_state { + HINIC_DB_ENABLE = 0, + HINIC_DB_DISABLE = 1, +}; + struct hinic_func_attr { u16 func_idx; u8 pf_idx; @@ -230,6 +255,16 @@ int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index); void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action); +enum hinic_outbound_state hinic_outbound_state_get(struct hinic_hwif *hwif); + +void hinic_outbound_state_set(struct hinic_hwif *hwif, + enum hinic_outbound_state outbound_state); + +enum hinic_db_state hinic_db_state_get(struct hinic_hwif *hwif); + +void hinic_db_state_set(struct hinic_hwif *hwif, + enum hinic_db_state db_state); + int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev); void hinic_free_hwif(struct hinic_hwif *hwif); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h index 90116c2db819..320711e8dee6 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -69,8 +69,21 @@ enum hinic_cfg_cmd { }; enum hinic_comm_cmd { + HINIC_COMM_CMD_IO_STATUS_GET = 0x3, + HINIC_COMM_CMD_CMDQ_CTXT_SET = 0x10, HINIC_COMM_CMD_CMDQ_CTXT_GET = 0x11, + + HINIC_COMM_CMD_HWCTXT_SET = 0x12, + HINIC_COMM_CMD_HWCTXT_GET = 0x13, + + HINIC_COMM_CMD_SQ_HI_CI_SET = 0x14, + + HINIC_COMM_CMD_RES_STATE_SET = 0x24, + + HINIC_COMM_CMD_IO_RES_CLEAR = 0x29, + + HINIC_COMM_CMD_MAX = 0x32, }; enum hinic_mgmt_cb_state { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c index 13e0ff3533a4..6c5c6ea0ab49 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c @@ -22,10 +22,13 @@ #include #include #include +#include +#include #include #include "hinic_common.h" #include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" #include "hinic_hw_wq.h" #include "hinic_hw_qp_ctxt.h" #include "hinic_hw_qp.h" @@ -51,6 +54,13 @@ (max_sqs + (q_id)) * Q_CTXT_SIZE) #define SIZE_16BYTES(size) (ALIGN(size, 16) >> 4) +#define SIZE_8BYTES(size) (ALIGN(size, 8) >> 3) + +#define RQ_MASKED_IDX(rq, idx) ((idx) & (rq)->wq->mask) + +enum rq_completion_fmt { + RQ_COMPLETE_SGE = 1 +}; void hinic_qp_prepare_header(struct hinic_qp_ctxt_header *qp_ctxt_hdr, enum hinic_qp_ctxt_type ctxt_type, @@ -424,3 +434,201 @@ void hinic_clean_rq(struct hinic_rq *rq) free_rq_cqe(rq); free_rq_skb_arr(rq); } + +/** + * hinic_get_rq_free_wqebbs - return number of free wqebbs for use + * @rq: recv queue + * + * Return number of free wqebbs + **/ +int hinic_get_rq_free_wqebbs(struct hinic_rq *rq) +{ + struct hinic_wq *wq = rq->wq; + + return atomic_read(&wq->delta) - 1; +} + +/** + * hinic_rq_get_wqe - get wqe ptr in the current pi and update the pi + * @rq: rq to get wqe from + * @wqe_size: wqe size + * @prod_idx: returned pi + * + * Return wqe pointer + **/ +struct hinic_rq_wqe *hinic_rq_get_wqe(struct hinic_rq *rq, + unsigned int wqe_size, u16 *prod_idx) +{ + struct hinic_hw_wqe *hw_wqe = hinic_get_wqe(rq->wq, wqe_size, + prod_idx); + + if (IS_ERR(hw_wqe)) + return NULL; + + return &hw_wqe->rq_wqe; +} + +/** + * hinic_rq_write_wqe - write the wqe to the rq + * @rq: recv queue + * @prod_idx: pi of the wqe + * @rq_wqe: the wqe to write + * @skb: skb to save + **/ +void hinic_rq_write_wqe(struct hinic_rq *rq, u16 prod_idx, + struct hinic_rq_wqe *rq_wqe, struct sk_buff *skb) +{ + struct hinic_hw_wqe *hw_wqe = (struct hinic_hw_wqe *)rq_wqe; + + rq->saved_skb[prod_idx] = skb; + + /* The data in the HW should be in Big Endian Format */ + hinic_cpu_to_be32(rq_wqe, sizeof(*rq_wqe)); + + hinic_write_wqe(rq->wq, hw_wqe, sizeof(*rq_wqe)); +} + +/** + * hinic_rq_read_wqe - read wqe ptr in the current ci and update the ci + * @rq: recv queue + * @wqe_size: the size of the wqe + * @skb: return saved skb + * @cons_idx: consumer index of the wqe + * + * Return wqe in ci position + **/ +struct hinic_rq_wqe *hinic_rq_read_wqe(struct hinic_rq *rq, + unsigned int wqe_size, + struct sk_buff **skb, u16 *cons_idx) +{ + struct hinic_hw_wqe *hw_wqe; + struct hinic_rq_cqe *cqe; + int rx_done; + u32 status; + + hw_wqe = hinic_read_wqe(rq->wq, wqe_size, cons_idx); + if (IS_ERR(hw_wqe)) + return NULL; + + cqe = rq->cqe[*cons_idx]; + + status = be32_to_cpu(cqe->status); + + rx_done = HINIC_RQ_CQE_STATUS_GET(status, RXDONE); + if (!rx_done) + return NULL; + + *skb = rq->saved_skb[*cons_idx]; + + return &hw_wqe->rq_wqe; +} + +/** + * hinic_rq_read_next_wqe - increment ci and read the wqe in ci position + * @rq: recv queue + * @wqe_size: the size of the wqe + * @skb: return saved skb + * @cons_idx: consumer index in the wq + * + * Return wqe in incremented ci position + **/ +struct hinic_rq_wqe *hinic_rq_read_next_wqe(struct hinic_rq *rq, + unsigned int wqe_size, + struct sk_buff **skb, + u16 *cons_idx) +{ + struct hinic_wq *wq = rq->wq; + struct hinic_hw_wqe *hw_wqe; + unsigned int num_wqebbs; + + wqe_size = ALIGN(wqe_size, wq->wqebb_size); + num_wqebbs = wqe_size / wq->wqebb_size; + + *cons_idx = RQ_MASKED_IDX(rq, *cons_idx + num_wqebbs); + + *skb = rq->saved_skb[*cons_idx]; + + hw_wqe = hinic_read_wqe_direct(wq, *cons_idx); + + return &hw_wqe->rq_wqe; +} + +/** + * hinic_put_wqe - release the ci for new wqes + * @rq: recv queue + * @cons_idx: consumer index of the wqe + * @wqe_size: the size of the wqe + **/ +void hinic_rq_put_wqe(struct hinic_rq *rq, u16 cons_idx, + unsigned int wqe_size) +{ + struct hinic_rq_cqe *cqe = rq->cqe[cons_idx]; + u32 status = be32_to_cpu(cqe->status); + + status = HINIC_RQ_CQE_STATUS_CLEAR(status, RXDONE); + + /* Rx WQE size is 1 WQEBB, no wq shadow*/ + cqe->status = cpu_to_be32(status); + + wmb(); /* clear done flag */ + + hinic_put_wqe(rq->wq, wqe_size); +} + +/** + * hinic_rq_get_sge - get sge from the wqe + * @rq: recv queue + * @rq_wqe: wqe to get the sge from its buf address + * @cons_idx: consumer index + * @sge: returned sge + **/ +void hinic_rq_get_sge(struct hinic_rq *rq, struct hinic_rq_wqe *rq_wqe, + u16 cons_idx, struct hinic_sge *sge) +{ + struct hinic_rq_cqe *cqe = rq->cqe[cons_idx]; + u32 len = be32_to_cpu(cqe->len); + + sge->hi_addr = be32_to_cpu(rq_wqe->buf_desc.hi_addr); + sge->lo_addr = be32_to_cpu(rq_wqe->buf_desc.lo_addr); + sge->len = HINIC_RQ_CQE_SGE_GET(len, LEN); +} + +/** + * hinic_rq_prepare_wqe - prepare wqe before insert to the queue + * @rq: recv queue + * @prod_idx: pi value + * @rq_wqe: the wqe + * @sge: sge for use by the wqe for recv buf address + **/ +void hinic_rq_prepare_wqe(struct hinic_rq *rq, u16 prod_idx, + struct hinic_rq_wqe *rq_wqe, struct hinic_sge *sge) +{ + struct hinic_rq_cqe_sect *cqe_sect = &rq_wqe->cqe_sect; + struct hinic_rq_bufdesc *buf_desc = &rq_wqe->buf_desc; + struct hinic_rq_cqe *cqe = rq->cqe[prod_idx]; + struct hinic_rq_ctrl *ctrl = &rq_wqe->ctrl; + dma_addr_t cqe_dma = rq->cqe_dma[prod_idx]; + + ctrl->ctrl_info = + HINIC_RQ_CTRL_SET(SIZE_8BYTES(sizeof(*ctrl)), LEN) | + HINIC_RQ_CTRL_SET(SIZE_8BYTES(sizeof(*cqe_sect)), + COMPLETE_LEN) | + HINIC_RQ_CTRL_SET(SIZE_8BYTES(sizeof(*buf_desc)), + BUFDESC_SECT_LEN) | + HINIC_RQ_CTRL_SET(RQ_COMPLETE_SGE, COMPLETE_FORMAT); + + hinic_set_sge(&cqe_sect->sge, cqe_dma, sizeof(*cqe)); + + buf_desc->hi_addr = sge->hi_addr; + buf_desc->lo_addr = sge->lo_addr; +} + +/** + * hinic_rq_update - update pi of the rq + * @rq: recv queue + * @prod_idx: pi value + **/ +void hinic_rq_update(struct hinic_rq *rq, u16 prod_idx) +{ + *rq->pi_virt_addr = cpu_to_be16(RQ_MASKED_IDX(rq, prod_idx + 1)); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h index 56d1f8b9ca65..696f0df6559a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -21,6 +21,7 @@ #include #include +#include "hinic_common.h" #include "hinic_hw_if.h" #include "hinic_hw_wqe.h" #include "hinic_hw_wq.h" @@ -100,4 +101,32 @@ int hinic_init_rq(struct hinic_rq *rq, struct hinic_hwif *hwif, void hinic_clean_rq(struct hinic_rq *rq); +int hinic_get_rq_free_wqebbs(struct hinic_rq *rq); + +struct hinic_rq_wqe *hinic_rq_get_wqe(struct hinic_rq *rq, + unsigned int wqe_size, u16 *prod_idx); + +void hinic_rq_write_wqe(struct hinic_rq *rq, u16 prod_idx, + struct hinic_rq_wqe *wqe, struct sk_buff *skb); + +struct hinic_rq_wqe *hinic_rq_read_wqe(struct hinic_rq *rq, + unsigned int wqe_size, + struct sk_buff **skb, u16 *cons_idx); + +struct hinic_rq_wqe *hinic_rq_read_next_wqe(struct hinic_rq *rq, + unsigned int wqe_size, + struct sk_buff **skb, + u16 *cons_idx); + +void hinic_rq_put_wqe(struct hinic_rq *rq, u16 cons_idx, + unsigned int wqe_size); + +void hinic_rq_get_sge(struct hinic_rq *rq, struct hinic_rq_wqe *wqe, + u16 cons_idx, struct hinic_sge *sge); + +void hinic_rq_prepare_wqe(struct hinic_rq *rq, u16 prod_idx, + struct hinic_rq_wqe *wqe, struct hinic_sge *sge); + +void hinic_rq_update(struct hinic_rq *rq, u16 prod_idx); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c index 6ceae958db28..3e3181c089bd 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c @@ -826,6 +826,18 @@ struct hinic_hw_wqe *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size, return WQ_PAGE_ADDR(wq, *cons_idx) + WQE_PAGE_OFF(wq, *cons_idx); } +/** + * hinic_read_wqe_direct - read wqe directly from ci position + * @wq: wq + * @cons_idx: ci position + * + * Return wqe + **/ +struct hinic_hw_wqe *hinic_read_wqe_direct(struct hinic_wq *wq, u16 cons_idx) +{ + return WQ_PAGE_ADDR(wq, cons_idx) + WQE_PAGE_OFF(wq, cons_idx); +} + /** * wqe_shadow - check if a wqe is shadow * @wq: wq of the wqe diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h index f01477a2c165..9c030a0f035e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h @@ -109,6 +109,8 @@ void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size); struct hinic_hw_wqe *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size, u16 *cons_idx); +struct hinic_hw_wqe *hinic_read_wqe_direct(struct hinic_wq *wq, u16 cons_idx); + void hinic_write_wqe(struct hinic_wq *wq, struct hinic_hw_wqe *wqe, unsigned int wqe_size); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 22d5b61b0426..53b13f8d0b8f 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,10 @@ MODULE_AUTHOR("Huawei Technologies CO., Ltd"); MODULE_DESCRIPTION("Huawei Intelligent NIC driver"); MODULE_LICENSE("GPL"); +static unsigned int rx_weight = 64; +module_param(rx_weight, uint, 0644); +MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)"); + #define PCI_DEVICE_ID_HI1822_PF 0x1822 #define HINIC_WQ_NAME "hinic_dev" @@ -220,6 +225,13 @@ static int hinic_open(struct net_device *netdev) goto err_port_state; } + err = hinic_port_set_func_state(nic_dev, HINIC_FUNC_PORT_ENABLE); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to set func port state\n"); + goto err_func_port_state; + } + /* Wait up to 3 sec between port enable to link state */ msleep(3000); @@ -250,6 +262,12 @@ static int hinic_open(struct net_device *netdev) err_port_link: up(&nic_dev->mgmt_lock); + ret = hinic_port_set_func_state(nic_dev, HINIC_FUNC_PORT_DISABLE); + if (ret) + netif_warn(nic_dev, drv, netdev, + "Failed to revert func port state\n"); + +err_func_port_state: ret = hinic_port_set_state(nic_dev, HINIC_PORT_DISABLE); if (ret) netif_warn(nic_dev, drv, netdev, @@ -283,6 +301,14 @@ static int hinic_close(struct net_device *netdev) up(&nic_dev->mgmt_lock); + err = hinic_port_set_func_state(nic_dev, HINIC_FUNC_PORT_DISABLE); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to set func port state\n"); + nic_dev->flags |= (flags & HINIC_INTF_UP); + return err; + } + err = hinic_port_set_state(nic_dev, HINIC_PORT_DISABLE); if (err) { netif_err(nic_dev, drv, netdev, "Failed to set port state\n"); @@ -664,6 +690,7 @@ static int nic_dev_init(struct pci_dev *pdev) nic_dev->flags = 0; nic_dev->txqs = NULL; nic_dev->rxqs = NULL; + nic_dev->rx_weight = rx_weight; sema_init(&nic_dev->mgmt_lock, 1); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 0dafede7169e..528ec6febd04 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -314,3 +314,35 @@ int hinic_port_set_state(struct hinic_dev *nic_dev, enum hinic_port_state state) return 0; } + +/** + * hinic_port_set_func_state- set func device state + * @nic_dev: nic device + * @state: the state to set + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_set_func_state(struct hinic_dev *nic_dev, + enum hinic_func_port_state state) +{ + struct hinic_port_func_state_cmd func_state; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + func_state.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + func_state.state = state; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_FUNC_STATE, + &func_state, sizeof(func_state), + &func_state, &out_size); + if (err || (out_size != sizeof(func_state)) || func_state.status) { + dev_err(&pdev->dev, "Failed to set port func state, ret = %d\n", + func_state.status); + return -EFAULT; + } + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 3a8da8eadb9b..17f9d7fc5a0a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -40,6 +40,11 @@ enum hinic_port_state { HINIC_PORT_ENABLE = 3, }; +enum hinic_func_port_state { + HINIC_FUNC_PORT_DISABLE = 0, + HINIC_FUNC_PORT_ENABLE = 2, +}; + struct hinic_port_mac_cmd { u8 status; u8 version; @@ -109,6 +114,17 @@ struct hinic_port_link_status { u8 rsvd2; }; +struct hinic_port_func_state_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rsvd1; + u8 state; + u8 rsvd2[3]; +}; + int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, u16 vlan_id); @@ -131,4 +147,7 @@ int hinic_port_link_state(struct hinic_dev *nic_dev, int hinic_port_set_state(struct hinic_dev *nic_dev, enum hinic_port_state state); +int hinic_port_set_func_state(struct hinic_dev *nic_dev, + enum hinic_func_port_state state); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c index 3c79f65d44da..b1212e498f95 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c @@ -13,11 +13,35 @@ * */ +#include +#include +#include +#include +#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include "hinic_common.h" +#include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" #include "hinic_hw_qp.h" +#include "hinic_hw_dev.h" #include "hinic_rx.h" +#include "hinic_dev.h" + +#define RX_IRQ_NO_PENDING 0 +#define RX_IRQ_NO_COALESC 0 +#define RX_IRQ_NO_LLI_TIMER 0 +#define RX_IRQ_NO_CREDIT 0 +#define RX_IRQ_NO_RESEND_TIMER 0 /** * hinic_rxq_clean_stats - Clean the statistics of specific queue @@ -45,6 +69,361 @@ static void rxq_stats_init(struct hinic_rxq *rxq) hinic_rxq_clean_stats(rxq); } +/** + * rx_alloc_skb - allocate skb and map it to dma address + * @rxq: rx queue + * @dma_addr: returned dma address for the skb + * + * Return skb + **/ +static struct sk_buff *rx_alloc_skb(struct hinic_rxq *rxq, + dma_addr_t *dma_addr) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct sk_buff *skb; + dma_addr_t addr; + int err; + + skb = netdev_alloc_skb_ip_align(rxq->netdev, rxq->rq->buf_sz); + if (!skb) { + netdev_err(rxq->netdev, "Failed to allocate Rx SKB\n"); + return NULL; + } + + addr = dma_map_single(&pdev->dev, skb->data, rxq->rq->buf_sz, + DMA_FROM_DEVICE); + err = dma_mapping_error(&pdev->dev, addr); + if (err) { + dev_err(&pdev->dev, "Failed to map Rx DMA, err = %d\n", err); + goto err_rx_map; + } + + *dma_addr = addr; + return skb; + +err_rx_map: + dev_kfree_skb_any(skb); + return NULL; +} + +/** + * rx_unmap_skb - unmap the dma address of the skb + * @rxq: rx queue + * @dma_addr: dma address of the skb + **/ +static void rx_unmap_skb(struct hinic_rxq *rxq, dma_addr_t dma_addr) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + + dma_unmap_single(&pdev->dev, dma_addr, rxq->rq->buf_sz, + DMA_FROM_DEVICE); +} + +/** + * rx_free_skb - unmap and free skb + * @rxq: rx queue + * @skb: skb to free + * @dma_addr: dma address of the skb + **/ +static void rx_free_skb(struct hinic_rxq *rxq, struct sk_buff *skb, + dma_addr_t dma_addr) +{ + rx_unmap_skb(rxq, dma_addr); + dev_kfree_skb_any(skb); +} + +/** + * rx_alloc_pkts - allocate pkts in rx queue + * @rxq: rx queue + * + * Return number of skbs allocated + **/ +static int rx_alloc_pkts(struct hinic_rxq *rxq) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + struct hinic_rq_wqe *rq_wqe; + unsigned int free_wqebbs; + struct hinic_sge sge; + dma_addr_t dma_addr; + struct sk_buff *skb; + int i, alloc_more; + u16 prod_idx; + + free_wqebbs = hinic_get_rq_free_wqebbs(rxq->rq); + alloc_more = 0; + + /* Limit the allocation chunks */ + if (free_wqebbs > nic_dev->rx_weight) + free_wqebbs = nic_dev->rx_weight; + + for (i = 0; i < free_wqebbs; i++) { + skb = rx_alloc_skb(rxq, &dma_addr); + if (!skb) { + netdev_err(rxq->netdev, "Failed to alloc Rx skb\n"); + alloc_more = 1; + goto skb_out; + } + + hinic_set_sge(&sge, dma_addr, skb->len); + + rq_wqe = hinic_rq_get_wqe(rxq->rq, HINIC_RQ_WQE_SIZE, + &prod_idx); + if (!rq_wqe) { + rx_free_skb(rxq, skb, dma_addr); + alloc_more = 1; + goto skb_out; + } + + hinic_rq_prepare_wqe(rxq->rq, prod_idx, rq_wqe, &sge); + + hinic_rq_write_wqe(rxq->rq, prod_idx, rq_wqe, skb); + } + +skb_out: + if (i) { + wmb(); /* write all the wqes before update PI */ + + hinic_rq_update(rxq->rq, prod_idx); + } + + if (alloc_more) + tasklet_schedule(&rxq->rx_task); + + return i; +} + +/** + * free_all_rx_skbs - free all skbs in rx queue + * @rxq: rx queue + **/ +static void free_all_rx_skbs(struct hinic_rxq *rxq) +{ + struct hinic_rq *rq = rxq->rq; + struct hinic_hw_wqe *hw_wqe; + struct hinic_sge sge; + u16 ci; + + while ((hw_wqe = hinic_read_wqe(rq->wq, HINIC_RQ_WQE_SIZE, &ci))) { + if (IS_ERR(hw_wqe)) + break; + + hinic_rq_get_sge(rq, &hw_wqe->rq_wqe, ci, &sge); + + hinic_put_wqe(rq->wq, HINIC_RQ_WQE_SIZE); + + rx_free_skb(rxq, rq->saved_skb[ci], hinic_sge_to_dma(&sge)); + } +} + +/** + * rx_alloc_task - tasklet for queue allocation + * @data: rx queue + **/ +static void rx_alloc_task(unsigned long data) +{ + struct hinic_rxq *rxq = (struct hinic_rxq *)data; + + (void)rx_alloc_pkts(rxq); +} + +/** + * rx_recv_jumbo_pkt - Rx handler for jumbo pkt + * @rxq: rx queue + * @head_skb: the first skb in the list + * @left_pkt_len: left size of the pkt exclude head skb + * @ci: consumer index + * + * Return number of wqes that used for the left of the pkt + **/ +static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb, + unsigned int left_pkt_len, u16 ci) +{ + struct sk_buff *skb, *curr_skb = head_skb; + struct hinic_rq_wqe *rq_wqe; + unsigned int curr_len; + struct hinic_sge sge; + int num_wqes = 0; + + while (left_pkt_len > 0) { + rq_wqe = hinic_rq_read_next_wqe(rxq->rq, HINIC_RQ_WQE_SIZE, + &skb, &ci); + + num_wqes++; + + hinic_rq_get_sge(rxq->rq, rq_wqe, ci, &sge); + + rx_unmap_skb(rxq, hinic_sge_to_dma(&sge)); + + prefetch(skb->data); + + curr_len = (left_pkt_len > HINIC_RX_BUF_SZ) ? HINIC_RX_BUF_SZ : + left_pkt_len; + + left_pkt_len -= curr_len; + + __skb_put(skb, curr_len); + + if (curr_skb == head_skb) + skb_shinfo(head_skb)->frag_list = skb; + else + curr_skb->next = skb; + + head_skb->len += skb->len; + head_skb->data_len += skb->len; + head_skb->truesize += skb->truesize; + + curr_skb = skb; + } + + return num_wqes; +} + +/** + * rxq_recv - Rx handler + * @rxq: rx queue + * @budget: maximum pkts to process + * + * Return number of pkts received + **/ +static int rxq_recv(struct hinic_rxq *rxq, int budget) +{ + struct hinic_qp *qp = container_of(rxq->rq, struct hinic_qp, rq); + u64 pkt_len = 0, rx_bytes = 0; + struct hinic_rq_wqe *rq_wqe; + int num_wqes, pkts = 0; + struct hinic_sge sge; + struct sk_buff *skb; + u16 ci; + + while (pkts < budget) { + num_wqes = 0; + + rq_wqe = hinic_rq_read_wqe(rxq->rq, HINIC_RQ_WQE_SIZE, &skb, + &ci); + if (!rq_wqe) + break; + + hinic_rq_get_sge(rxq->rq, rq_wqe, ci, &sge); + + rx_unmap_skb(rxq, hinic_sge_to_dma(&sge)); + + prefetch(skb->data); + + pkt_len = sge.len; + + if (pkt_len <= HINIC_RX_BUF_SZ) { + __skb_put(skb, pkt_len); + } else { + __skb_put(skb, HINIC_RX_BUF_SZ); + num_wqes = rx_recv_jumbo_pkt(rxq, skb, pkt_len - + HINIC_RX_BUF_SZ, ci); + } + + hinic_rq_put_wqe(rxq->rq, ci, + (num_wqes + 1) * HINIC_RQ_WQE_SIZE); + + skb_record_rx_queue(skb, qp->q_id); + skb->protocol = eth_type_trans(skb, rxq->netdev); + + napi_gro_receive(&rxq->napi, skb); + + pkts++; + rx_bytes += pkt_len; + } + + if (pkts) + tasklet_schedule(&rxq->rx_task); /* hinic_rx_alloc_pkts */ + + u64_stats_update_begin(&rxq->rxq_stats.syncp); + rxq->rxq_stats.pkts += pkts; + rxq->rxq_stats.bytes += rx_bytes; + u64_stats_update_end(&rxq->rxq_stats.syncp); + + return pkts; +} + +static int rx_poll(struct napi_struct *napi, int budget) +{ + struct hinic_rxq *rxq = container_of(napi, struct hinic_rxq, napi); + struct hinic_rq *rq = rxq->rq; + int pkts; + + pkts = rxq_recv(rxq, budget); + if (pkts >= budget) + return budget; + + napi_complete(napi); + enable_irq(rq->irq); + return pkts; +} + +static void rx_add_napi(struct hinic_rxq *rxq) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + + netif_napi_add(rxq->netdev, &rxq->napi, rx_poll, nic_dev->rx_weight); + napi_enable(&rxq->napi); +} + +static void rx_del_napi(struct hinic_rxq *rxq) +{ + napi_disable(&rxq->napi); + netif_napi_del(&rxq->napi); +} + +static irqreturn_t rx_irq(int irq, void *data) +{ + struct hinic_rxq *rxq = (struct hinic_rxq *)data; + struct hinic_rq *rq = rxq->rq; + struct hinic_dev *nic_dev; + + /* Disable the interrupt until napi will be completed */ + disable_irq_nosync(rq->irq); + + nic_dev = netdev_priv(rxq->netdev); + hinic_hwdev_msix_cnt_set(nic_dev->hwdev, rq->msix_entry); + + napi_schedule(&rxq->napi); + return IRQ_HANDLED; +} + +static int rx_request_irq(struct hinic_rxq *rxq) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_rq *rq = rxq->rq; + int err; + + rx_add_napi(rxq); + + hinic_hwdev_msix_set(hwdev, rq->msix_entry, + RX_IRQ_NO_PENDING, RX_IRQ_NO_COALESC, + RX_IRQ_NO_LLI_TIMER, RX_IRQ_NO_CREDIT, + RX_IRQ_NO_RESEND_TIMER); + + err = request_irq(rq->irq, rx_irq, 0, rxq->irq_name, rxq); + if (err) { + rx_del_napi(rxq); + return err; + } + + return 0; +} + +static void rx_free_irq(struct hinic_rxq *rxq) +{ + struct hinic_rq *rq = rxq->rq; + + free_irq(rq->irq, rxq); + rx_del_napi(rxq); +} + /** * hinic_init_rxq - Initialize the Rx Queue * @rxq: Logical Rx Queue @@ -56,11 +435,43 @@ static void rxq_stats_init(struct hinic_rxq *rxq) int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, struct net_device *netdev) { + struct hinic_qp *qp = container_of(rq, struct hinic_qp, rq); + int err, pkts, irqname_len; + rxq->netdev = netdev; rxq->rq = rq; rxq_stats_init(rxq); + + irqname_len = snprintf(NULL, 0, "hinic_rxq%d", qp->q_id) + 1; + rxq->irq_name = devm_kzalloc(&netdev->dev, irqname_len, GFP_KERNEL); + if (!rxq->irq_name) + return -ENOMEM; + + sprintf(rxq->irq_name, "hinic_rxq%d", qp->q_id); + + tasklet_init(&rxq->rx_task, rx_alloc_task, (unsigned long)rxq); + + pkts = rx_alloc_pkts(rxq); + if (!pkts) { + err = -ENOMEM; + goto err_rx_pkts; + } + + err = rx_request_irq(rxq); + if (err) { + netdev_err(netdev, "Failed to request Rx irq\n"); + goto err_req_rx_irq; + } + return 0; + +err_req_rx_irq: +err_rx_pkts: + tasklet_kill(&rxq->rx_task); + free_all_rx_skbs(rxq); + devm_kfree(&netdev->dev, rxq->irq_name); + return err; } /** @@ -69,4 +480,11 @@ int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, **/ void hinic_clean_rxq(struct hinic_rxq *rxq) { + struct net_device *netdev = rxq->netdev; + + rx_free_irq(rxq); + + tasklet_kill(&rxq->rx_task); + free_all_rx_skbs(rxq); + devm_kfree(&netdev->dev, rxq->irq_name); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.h b/drivers/net/ethernet/huawei/hinic/hinic_rx.h index fbd0246165dc..538c8861e8dd 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "hinic_hw_qp.h" @@ -34,6 +35,12 @@ struct hinic_rxq { struct hinic_rq *rq; struct hinic_rxq_stats rxq_stats; + + char *irq_name; + + struct tasklet_struct rx_task; + + struct napi_struct napi; }; void hinic_rxq_clean_stats(struct hinic_rxq *rxq); -- cgit v1.2.3 From 00e57a6d4ad345a3910cfd24a5403d49a70d7705 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:56:05 +0800 Subject: net-next/hinic: Add Tx operation Add transmit operation for sending data by qp operations. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 1 + drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 46 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 22 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c | 257 +++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h | 48 +++ drivers/net/ethernet/huawei/hinic/hinic_main.c | 12 +- drivers/net/ethernet/huawei/hinic/hinic_tx.c | 406 +++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_tx.h | 11 + 8 files changed, 799 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 3d0f6cf2508f..15d0c2e3797c 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -43,6 +43,7 @@ struct hinic_dev { struct hinic_hwdev *hwdev; u32 msg_enable; + unsigned int tx_weight; unsigned int rx_weight; unsigned int flags; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index 77d43431e573..09dec6de8dd5 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -40,6 +40,8 @@ #define MAX_IRQS(max_qps, num_aeqs, num_ceqs) \ (2 * (max_qps) + (num_aeqs) + (num_ceqs)) +#define ADDR_IN_4BYTES(addr) ((addr) >> 2) + enum intr_type { INTR_MSIX_TYPE, }; @@ -1000,3 +1002,47 @@ int hinic_hwdev_msix_set(struct hinic_hwdev *hwdev, u16 msix_index, lli_timer_cfg, lli_credit_limit, resend_timer); } + +/** + * hinic_hwdev_hw_ci_addr_set - set cons idx addr and attributes in HW for sq + * @hwdev: the NIC HW device + * @sq: send queue + * @pending_limit: the maximum pending update ci events (unit 8) + * @coalesc_timer: coalesc period for update ci (unit 8 us) + * + * Return 0 - Success, negative - Failure + **/ +int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq, + u8 pending_limit, u8 coalesc_timer) +{ + struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq); + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + struct hinic_cmd_hw_ci hw_ci; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + hw_ci.dma_attr_off = 0; + hw_ci.pending_limit = pending_limit; + hw_ci.coalesc_timer = coalesc_timer; + + hw_ci.msix_en = 1; + hw_ci.msix_entry_idx = sq->msix_entry; + + hw_ci.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + hw_ci.sq_id = qp->q_id; + + hw_ci.ci_addr = ADDR_IN_4BYTES(sq->hw_ci_dma_addr); + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, + HINIC_MOD_COMM, + HINIC_COMM_CMD_SQ_HI_CI_SET, + &hw_ci, sizeof(hw_ci), NULL, + NULL, HINIC_MGMT_MSG_SYNC); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index e7277d19db58..0f5563f3b779 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -153,6 +153,25 @@ struct hinic_cmd_base_qpn { u16 qpn; }; +struct hinic_cmd_hw_ci { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + + u8 dma_attr_off; + u8 pending_limit; + u8 coalesc_timer; + + u8 msix_en; + u16 msix_entry_idx; + + u32 sq_id; + u32 rsvd1; + u64 ci_addr; +}; + struct hinic_hwdev { struct hinic_hwif *hwif; struct msix_entry *msix_entries; @@ -214,4 +233,7 @@ int hinic_hwdev_msix_set(struct hinic_hwdev *hwdev, u16 msix_index, u8 lli_timer_cfg, u8 lli_credit_limit, u8 resend_timer); +int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq, + u8 pending_limit, u8 coalesc_timer); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c index 6c5c6ea0ab49..b9db6d649743 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ #include "hinic_hw_wq.h" #include "hinic_hw_qp_ctxt.h" #include "hinic_hw_qp.h" +#include "hinic_hw_io.h" #define SQ_DB_OFF SZ_2K @@ -53,11 +55,27 @@ (((max_rqs) + (max_sqs)) * CTXT_RSVD + \ (max_sqs + (q_id)) * Q_CTXT_SIZE) -#define SIZE_16BYTES(size) (ALIGN(size, 16) >> 4) -#define SIZE_8BYTES(size) (ALIGN(size, 8) >> 3) +#define SIZE_16BYTES(size) (ALIGN(size, 16) >> 4) +#define SIZE_8BYTES(size) (ALIGN(size, 8) >> 3) +#define SECT_SIZE_FROM_8BYTES(size) ((size) << 3) +#define SQ_DB_PI_HI_SHIFT 8 +#define SQ_DB_PI_HI(prod_idx) ((prod_idx) >> SQ_DB_PI_HI_SHIFT) + +#define SQ_DB_PI_LOW_MASK 0xFF +#define SQ_DB_PI_LOW(prod_idx) ((prod_idx) & SQ_DB_PI_LOW_MASK) + +#define SQ_DB_ADDR(sq, pi) ((u64 *)((sq)->db_base) + SQ_DB_PI_LOW(pi)) + +#define SQ_MASKED_IDX(sq, idx) ((idx) & (sq)->wq->mask) #define RQ_MASKED_IDX(rq, idx) ((idx) & (rq)->wq->mask) +#define TX_MAX_MSS_DEFAULT 0x3E00 + +enum sq_wqe_type { + SQ_NORMAL_WQE = 0, +}; + enum rq_completion_fmt { RQ_COMPLETE_SGE = 1 }; @@ -435,6 +453,19 @@ void hinic_clean_rq(struct hinic_rq *rq) free_rq_skb_arr(rq); } +/** + * hinic_get_sq_free_wqebbs - return number of free wqebbs for use + * @sq: send queue + * + * Return number of free wqebbs + **/ +int hinic_get_sq_free_wqebbs(struct hinic_sq *sq) +{ + struct hinic_wq *wq = sq->wq; + + return atomic_read(&wq->delta) - 1; +} + /** * hinic_get_rq_free_wqebbs - return number of free wqebbs for use * @rq: recv queue @@ -448,6 +479,228 @@ int hinic_get_rq_free_wqebbs(struct hinic_rq *rq) return atomic_read(&wq->delta) - 1; } +static void sq_prepare_ctrl(struct hinic_sq_ctrl *ctrl, u16 prod_idx, + int nr_descs) +{ + u32 ctrl_size, task_size, bufdesc_size; + + ctrl_size = SIZE_8BYTES(sizeof(struct hinic_sq_ctrl)); + task_size = SIZE_8BYTES(sizeof(struct hinic_sq_task)); + bufdesc_size = nr_descs * sizeof(struct hinic_sq_bufdesc); + bufdesc_size = SIZE_8BYTES(bufdesc_size); + + ctrl->ctrl_info = HINIC_SQ_CTRL_SET(bufdesc_size, BUFDESC_SECT_LEN) | + HINIC_SQ_CTRL_SET(task_size, TASKSECT_LEN) | + HINIC_SQ_CTRL_SET(SQ_NORMAL_WQE, DATA_FORMAT) | + HINIC_SQ_CTRL_SET(ctrl_size, LEN); + + ctrl->queue_info = HINIC_SQ_CTRL_SET(TX_MAX_MSS_DEFAULT, + QUEUE_INFO_MSS); +} + +static void sq_prepare_task(struct hinic_sq_task *task) +{ + task->pkt_info0 = + HINIC_SQ_TASK_INFO0_SET(0, L2HDR_LEN) | + HINIC_SQ_TASK_INFO0_SET(HINIC_L4_OFF_DISABLE, L4_OFFLOAD) | + HINIC_SQ_TASK_INFO0_SET(HINIC_OUTER_L3TYPE_UNKNOWN, + INNER_L3TYPE) | + HINIC_SQ_TASK_INFO0_SET(HINIC_VLAN_OFF_DISABLE, + VLAN_OFFLOAD) | + HINIC_SQ_TASK_INFO0_SET(HINIC_PKT_NOT_PARSED, PARSE_FLAG); + + task->pkt_info1 = + HINIC_SQ_TASK_INFO1_SET(HINIC_MEDIA_UNKNOWN, MEDIA_TYPE) | + HINIC_SQ_TASK_INFO1_SET(0, INNER_L4_LEN) | + HINIC_SQ_TASK_INFO1_SET(0, INNER_L3_LEN); + + task->pkt_info2 = + HINIC_SQ_TASK_INFO2_SET(0, TUNNEL_L4_LEN) | + HINIC_SQ_TASK_INFO2_SET(0, OUTER_L3_LEN) | + HINIC_SQ_TASK_INFO2_SET(HINIC_TUNNEL_L4TYPE_UNKNOWN, + TUNNEL_L4TYPE) | + HINIC_SQ_TASK_INFO2_SET(HINIC_OUTER_L3TYPE_UNKNOWN, + OUTER_L3TYPE); + + task->ufo_v6_identify = 0; + + task->pkt_info4 = HINIC_SQ_TASK_INFO4_SET(HINIC_L2TYPE_ETH, L2TYPE); + + task->zero_pad = 0; +} + +/** + * hinic_sq_prepare_wqe - prepare wqe before insert to the queue + * @sq: send queue + * @prod_idx: pi value + * @sq_wqe: wqe to prepare + * @sges: sges for use by the wqe for send for buf addresses + * @nr_sges: number of sges + **/ +void hinic_sq_prepare_wqe(struct hinic_sq *sq, u16 prod_idx, + struct hinic_sq_wqe *sq_wqe, struct hinic_sge *sges, + int nr_sges) +{ + int i; + + sq_prepare_ctrl(&sq_wqe->ctrl, prod_idx, nr_sges); + + sq_prepare_task(&sq_wqe->task); + + for (i = 0; i < nr_sges; i++) + sq_wqe->buf_descs[i].sge = sges[i]; +} + +/** + * sq_prepare_db - prepare doorbell to write + * @sq: send queue + * @prod_idx: pi value for the doorbell + * @cos: cos of the doorbell + * + * Return db value + **/ +static u32 sq_prepare_db(struct hinic_sq *sq, u16 prod_idx, unsigned int cos) +{ + struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq); + u8 hi_prod_idx = SQ_DB_PI_HI(SQ_MASKED_IDX(sq, prod_idx)); + + /* Data should be written to HW in Big Endian Format */ + return cpu_to_be32(HINIC_SQ_DB_INFO_SET(hi_prod_idx, PI_HI) | + HINIC_SQ_DB_INFO_SET(HINIC_DB_SQ_TYPE, TYPE) | + HINIC_SQ_DB_INFO_SET(HINIC_DATA_PATH, PATH) | + HINIC_SQ_DB_INFO_SET(cos, COS) | + HINIC_SQ_DB_INFO_SET(qp->q_id, QID)); +} + +/** + * hinic_sq_write_db- write doorbell + * @sq: send queue + * @prod_idx: pi value for the doorbell + * @wqe_size: wqe size + * @cos: cos of the wqe + **/ +void hinic_sq_write_db(struct hinic_sq *sq, u16 prod_idx, unsigned int wqe_size, + unsigned int cos) +{ + struct hinic_wq *wq = sq->wq; + + /* increment prod_idx to the next */ + prod_idx += ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + + wmb(); /* Write all before the doorbell */ + + writel(sq_prepare_db(sq, prod_idx, cos), SQ_DB_ADDR(sq, prod_idx)); +} + +/** + * hinic_sq_get_wqe - get wqe ptr in the current pi and update the pi + * @sq: sq to get wqe from + * @wqe_size: wqe size + * @prod_idx: returned pi + * + * Return wqe pointer + **/ +struct hinic_sq_wqe *hinic_sq_get_wqe(struct hinic_sq *sq, + unsigned int wqe_size, u16 *prod_idx) +{ + struct hinic_hw_wqe *hw_wqe = hinic_get_wqe(sq->wq, wqe_size, + prod_idx); + + if (IS_ERR(hw_wqe)) + return NULL; + + return &hw_wqe->sq_wqe; +} + +/** + * hinic_sq_write_wqe - write the wqe to the sq + * @sq: send queue + * @prod_idx: pi of the wqe + * @sq_wqe: the wqe to write + * @skb: skb to save + * @wqe_size: the size of the wqe + **/ +void hinic_sq_write_wqe(struct hinic_sq *sq, u16 prod_idx, + struct hinic_sq_wqe *sq_wqe, + struct sk_buff *skb, unsigned int wqe_size) +{ + struct hinic_hw_wqe *hw_wqe = (struct hinic_hw_wqe *)sq_wqe; + + sq->saved_skb[prod_idx] = skb; + + /* The data in the HW should be in Big Endian Format */ + hinic_cpu_to_be32(sq_wqe, wqe_size); + + hinic_write_wqe(sq->wq, hw_wqe, wqe_size); +} + +/** + * hinic_sq_read_wqe - read wqe ptr in the current ci and update the ci + * @sq: send queue + * @skb: return skb that was saved + * @wqe_size: the size of the wqe + * @cons_idx: consumer index of the wqe + * + * Return wqe in ci position + **/ +struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq, + struct sk_buff **skb, + unsigned int *wqe_size, u16 *cons_idx) +{ + struct hinic_hw_wqe *hw_wqe; + struct hinic_sq_wqe *sq_wqe; + struct hinic_sq_ctrl *ctrl; + unsigned int buf_sect_len; + u32 ctrl_info; + + /* read the ctrl section for getting wqe size */ + hw_wqe = hinic_read_wqe(sq->wq, sizeof(*ctrl), cons_idx); + if (IS_ERR(hw_wqe)) + return NULL; + + sq_wqe = &hw_wqe->sq_wqe; + ctrl = &sq_wqe->ctrl; + ctrl_info = be32_to_cpu(ctrl->ctrl_info); + buf_sect_len = HINIC_SQ_CTRL_GET(ctrl_info, BUFDESC_SECT_LEN); + + *wqe_size = sizeof(*ctrl) + sizeof(sq_wqe->task); + *wqe_size += SECT_SIZE_FROM_8BYTES(buf_sect_len); + + *skb = sq->saved_skb[*cons_idx]; + + /* using the real wqe size to read wqe again */ + hw_wqe = hinic_read_wqe(sq->wq, *wqe_size, cons_idx); + + return &hw_wqe->sq_wqe; +} + +/** + * hinic_sq_put_wqe - release the ci for new wqes + * @sq: send queue + * @wqe_size: the size of the wqe + **/ +void hinic_sq_put_wqe(struct hinic_sq *sq, unsigned int wqe_size) +{ + hinic_put_wqe(sq->wq, wqe_size); +} + +/** + * hinic_sq_get_sges - get sges from the wqe + * @sq_wqe: wqe to get the sges from its buffer addresses + * @sges: returned sges + * @nr_sges: number sges to return + **/ +void hinic_sq_get_sges(struct hinic_sq_wqe *sq_wqe, struct hinic_sge *sges, + int nr_sges) +{ + int i; + + for (i = 0; i < nr_sges && i < HINIC_MAX_SQ_BUFDESCS; i++) { + sges[i] = sq_wqe->buf_descs[i].sge; + hinic_be32_to_cpu(&sges[i], sizeof(sges[i])); + } +} + /** * hinic_rq_get_wqe - get wqe ptr in the current pi and update the pi * @rq: rq to get wqe from diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h index 696f0df6559a..e642a8a8cb7f 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -16,6 +16,7 @@ #ifndef HINIC_HW_QP_H #define HINIC_HW_QP_H +#include #include #include #include @@ -27,6 +28,22 @@ #include "hinic_hw_wq.h" #include "hinic_hw_qp_ctxt.h" +#define HINIC_SQ_DB_INFO_PI_HI_SHIFT 0 +#define HINIC_SQ_DB_INFO_QID_SHIFT 8 +#define HINIC_SQ_DB_INFO_PATH_SHIFT 23 +#define HINIC_SQ_DB_INFO_COS_SHIFT 24 +#define HINIC_SQ_DB_INFO_TYPE_SHIFT 27 + +#define HINIC_SQ_DB_INFO_PI_HI_MASK 0xFF +#define HINIC_SQ_DB_INFO_QID_MASK 0x3FF +#define HINIC_SQ_DB_INFO_PATH_MASK 0x1 +#define HINIC_SQ_DB_INFO_COS_MASK 0x7 +#define HINIC_SQ_DB_INFO_TYPE_MASK 0x1F + +#define HINIC_SQ_DB_INFO_SET(val, member) \ + (((u32)(val) & HINIC_SQ_DB_INFO_##member##_MASK) \ + << HINIC_SQ_DB_INFO_##member##_SHIFT) + #define HINIC_SQ_WQEBB_SIZE 64 #define HINIC_RQ_WQEBB_SIZE 32 @@ -38,6 +55,12 @@ #define HINIC_RX_BUF_SZ 2048 +#define HINIC_MIN_TX_WQE_SIZE(wq) \ + ALIGN(HINIC_SQ_WQE_SIZE(1), (wq)->wqebb_size) + +#define HINIC_MIN_TX_NUM_WQEBBS(sq) \ + (HINIC_MIN_TX_WQE_SIZE((sq)->wq) / (sq)->wq->wqebb_size) + struct hinic_sq { struct hinic_hwif *hwif; @@ -101,8 +124,33 @@ int hinic_init_rq(struct hinic_rq *rq, struct hinic_hwif *hwif, void hinic_clean_rq(struct hinic_rq *rq); +int hinic_get_sq_free_wqebbs(struct hinic_sq *sq); + int hinic_get_rq_free_wqebbs(struct hinic_rq *rq); +void hinic_sq_prepare_wqe(struct hinic_sq *sq, u16 prod_idx, + struct hinic_sq_wqe *wqe, struct hinic_sge *sges, + int nr_sges); + +void hinic_sq_write_db(struct hinic_sq *sq, u16 prod_idx, unsigned int wqe_size, + unsigned int cos); + +struct hinic_sq_wqe *hinic_sq_get_wqe(struct hinic_sq *sq, + unsigned int wqe_size, u16 *prod_idx); + +void hinic_sq_write_wqe(struct hinic_sq *sq, u16 prod_idx, + struct hinic_sq_wqe *wqe, struct sk_buff *skb, + unsigned int wqe_size); + +struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq, + struct sk_buff **skb, + unsigned int *wqe_size, u16 *cons_idx); + +void hinic_sq_put_wqe(struct hinic_sq *sq, unsigned int wqe_size); + +void hinic_sq_get_sges(struct hinic_sq_wqe *wqe, struct hinic_sge *sges, + int nr_sges); + struct hinic_rq_wqe *hinic_rq_get_wqe(struct hinic_rq *rq, unsigned int wqe_size, u16 *prod_idx); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 53b13f8d0b8f..599d8b590e9a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -43,6 +43,10 @@ MODULE_AUTHOR("Huawei Technologies CO., Ltd"); MODULE_DESCRIPTION("Huawei Intelligent NIC driver"); MODULE_LICENSE("GPL"); +static unsigned int tx_weight = 64; +module_param(tx_weight, uint, 0644); +MODULE_PARM_DESC(tx_weight, "Number Tx packets for NAPI budget (default=64)"); + static unsigned int rx_weight = 64; module_param(rx_weight, uint, 0644); MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)"); @@ -569,9 +573,11 @@ static void hinic_set_rx_mode(struct net_device *netdev) queue_work(nic_dev->workq, &rx_mode_work->work); } -netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +static void hinic_tx_timeout(struct net_device *netdev) { - return NETDEV_TX_BUSY; + struct hinic_dev *nic_dev = netdev_priv(netdev); + + netif_err(nic_dev, drv, netdev, "Tx timeout\n"); } static const struct net_device_ops hinic_netdev_ops = { @@ -584,6 +590,7 @@ static const struct net_device_ops hinic_netdev_ops = { .ndo_vlan_rx_kill_vid = hinic_vlan_rx_kill_vid, .ndo_set_rx_mode = hinic_set_rx_mode, .ndo_start_xmit = hinic_xmit_frame, + .ndo_tx_timeout = hinic_tx_timeout, /* more operations should be filled */ }; @@ -690,6 +697,7 @@ static int nic_dev_init(struct pci_dev *pdev) nic_dev->flags = 0; nic_dev->txqs = NULL; nic_dev->rxqs = NULL; + nic_dev->tx_weight = tx_weight; nic_dev->rx_weight = rx_weight; sema_init(&nic_dev->mgmt_lock, 1); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index 9835912039c9..90ab2d971383 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -13,12 +13,42 @@ * */ +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hinic_common.h" +#include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" #include "hinic_hw_qp.h" +#include "hinic_hw_dev.h" +#include "hinic_dev.h" #include "hinic_tx.h" +#define TX_IRQ_NO_PENDING 0 +#define TX_IRQ_NO_COALESC 0 +#define TX_IRQ_NO_LLI_TIMER 0 +#define TX_IRQ_NO_CREDIT 0 +#define TX_IRQ_NO_RESEND_TIMER 0 + +#define CI_UPDATE_NO_PENDING 0 +#define CI_UPDATE_NO_COALESC 0 + +#define HW_CONS_IDX(sq) be16_to_cpu(*(u16 *)((sq)->hw_ci_addr)) + +#define MIN_SKB_LEN 64 + /** * hinic_txq_clean_stats - Clean the statistics of specific queue * @txq: Logical Tx Queue @@ -48,6 +78,321 @@ static void txq_stats_init(struct hinic_txq *txq) hinic_txq_clean_stats(txq); } +/** + * tx_map_skb - dma mapping for skb and return sges + * @nic_dev: nic device + * @skb: the skb + * @sges: returned sges + * + * Return 0 - Success, negative - Failure + **/ +static int tx_map_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, + struct hinic_sge *sges) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct skb_frag_struct *frag; + dma_addr_t dma_addr; + int i, j; + + dma_addr = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb), + DMA_TO_DEVICE); + if (dma_mapping_error(&pdev->dev, dma_addr)) { + dev_err(&pdev->dev, "Failed to map Tx skb data\n"); + return -EFAULT; + } + + hinic_set_sge(&sges[0], dma_addr, skb_headlen(skb)); + + for (i = 0 ; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + + dma_addr = skb_frag_dma_map(&pdev->dev, frag, 0, + skb_frag_size(frag), + DMA_TO_DEVICE); + if (dma_mapping_error(&pdev->dev, dma_addr)) { + dev_err(&pdev->dev, "Failed to map Tx skb frag\n"); + goto err_tx_map; + } + + hinic_set_sge(&sges[i + 1], dma_addr, skb_frag_size(frag)); + } + + return 0; + +err_tx_map: + for (j = 0; j < i; j++) + dma_unmap_page(&pdev->dev, hinic_sge_to_dma(&sges[j + 1]), + sges[j + 1].len, DMA_TO_DEVICE); + + dma_unmap_single(&pdev->dev, hinic_sge_to_dma(&sges[0]), sges[0].len, + DMA_TO_DEVICE); + return -EFAULT; +} + +/** + * tx_unmap_skb - unmap the dma address of the skb + * @nic_dev: nic device + * @skb: the skb + * @sges: the sges that are connected to the skb + **/ +static void tx_unmap_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, + struct hinic_sge *sges) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + int i; + + for (i = 0; i < skb_shinfo(skb)->nr_frags ; i++) + dma_unmap_page(&pdev->dev, hinic_sge_to_dma(&sges[i + 1]), + sges[i + 1].len, DMA_TO_DEVICE); + + dma_unmap_single(&pdev->dev, hinic_sge_to_dma(&sges[0]), sges[0].len, + DMA_TO_DEVICE); +} + +netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct netdev_queue *netdev_txq; + int nr_sges, err = NETDEV_TX_OK; + struct hinic_sq_wqe *sq_wqe; + unsigned int wqe_size; + struct hinic_txq *txq; + struct hinic_qp *qp; + u16 prod_idx; + + txq = &nic_dev->txqs[skb->queue_mapping]; + qp = container_of(txq->sq, struct hinic_qp, sq); + + if (skb->len < MIN_SKB_LEN) { + if (skb_pad(skb, MIN_SKB_LEN - skb->len)) { + netdev_err(netdev, "Failed to pad skb\n"); + goto skb_error; + } + + skb->len = MIN_SKB_LEN; + } + + nr_sges = skb_shinfo(skb)->nr_frags + 1; + if (nr_sges > txq->max_sges) { + netdev_err(netdev, "Too many Tx sges\n"); + goto skb_error; + } + + err = tx_map_skb(nic_dev, skb, txq->sges); + if (err) + goto skb_error; + + wqe_size = HINIC_SQ_WQE_SIZE(nr_sges); + + sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); + if (!sq_wqe) { + tx_unmap_skb(nic_dev, skb, txq->sges); + + netif_stop_subqueue(netdev, qp->q_id); + + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_busy++; + u64_stats_update_end(&txq->txq_stats.syncp); + err = NETDEV_TX_BUSY; + goto flush_skbs; + } + + hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges); + + hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size); + +flush_skbs: + netdev_txq = netdev_get_tx_queue(netdev, skb->queue_mapping); + if ((!skb->xmit_more) || (netif_xmit_stopped(netdev_txq))) + hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0); + + return err; + +skb_error: + dev_kfree_skb_any(skb); + + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_dropped++; + u64_stats_update_end(&txq->txq_stats.syncp); + return err; +} + +/** + * tx_free_skb - unmap and free skb + * @nic_dev: nic device + * @skb: the skb + * @sges: the sges that are connected to the skb + **/ +static void tx_free_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, + struct hinic_sge *sges) +{ + tx_unmap_skb(nic_dev, skb, sges); + + dev_kfree_skb_any(skb); +} + +/** + * free_all_rx_skbs - free all skbs in tx queue + * @txq: tx queue + **/ +static void free_all_tx_skbs(struct hinic_txq *txq) +{ + struct hinic_dev *nic_dev = netdev_priv(txq->netdev); + struct hinic_sq *sq = txq->sq; + struct hinic_sq_wqe *sq_wqe; + unsigned int wqe_size; + struct sk_buff *skb; + int nr_sges; + u16 ci; + + while ((sq_wqe = hinic_sq_read_wqe(sq, &skb, &wqe_size, &ci))) { + nr_sges = skb_shinfo(skb)->nr_frags + 1; + + hinic_sq_get_sges(sq_wqe, txq->free_sges, nr_sges); + + hinic_sq_put_wqe(sq, wqe_size); + + tx_free_skb(nic_dev, skb, txq->free_sges); + } +} + +/** + * free_tx_poll - free finished tx skbs in tx queue that connected to napi + * @napi: napi + * @budget: number of tx + * + * Return 0 - Success, negative - Failure + **/ +static int free_tx_poll(struct napi_struct *napi, int budget) +{ + struct hinic_txq *txq = container_of(napi, struct hinic_txq, napi); + struct hinic_qp *qp = container_of(txq->sq, struct hinic_qp, sq); + struct hinic_dev *nic_dev = netdev_priv(txq->netdev); + struct netdev_queue *netdev_txq; + struct hinic_sq *sq = txq->sq; + struct hinic_wq *wq = sq->wq; + struct hinic_sq_wqe *sq_wqe; + unsigned int wqe_size; + int nr_sges, pkts = 0; + struct sk_buff *skb; + u64 tx_bytes = 0; + u16 hw_ci, sw_ci; + + do { + hw_ci = HW_CONS_IDX(sq) & wq->mask; + + sq_wqe = hinic_sq_read_wqe(sq, &skb, &wqe_size, &sw_ci); + if ((!sq_wqe) || + (((hw_ci - sw_ci) & wq->mask) * wq->wqebb_size < wqe_size)) + break; + + tx_bytes += skb->len; + pkts++; + + nr_sges = skb_shinfo(skb)->nr_frags + 1; + + hinic_sq_get_sges(sq_wqe, txq->free_sges, nr_sges); + + hinic_sq_put_wqe(sq, wqe_size); + + tx_free_skb(nic_dev, skb, txq->free_sges); + } while (pkts < budget); + + if (__netif_subqueue_stopped(nic_dev->netdev, qp->q_id) && + hinic_get_sq_free_wqebbs(sq) >= HINIC_MIN_TX_NUM_WQEBBS(sq)) { + netdev_txq = netdev_get_tx_queue(txq->netdev, qp->q_id); + + __netif_tx_lock(netdev_txq, smp_processor_id()); + + netif_wake_subqueue(nic_dev->netdev, qp->q_id); + + __netif_tx_unlock(netdev_txq); + + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_wake++; + u64_stats_update_end(&txq->txq_stats.syncp); + } + + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.bytes += tx_bytes; + txq->txq_stats.pkts += pkts; + u64_stats_update_end(&txq->txq_stats.syncp); + + if (pkts < budget) { + napi_complete(napi); + enable_irq(sq->irq); + return pkts; + } + + return budget; +} + +static void tx_napi_add(struct hinic_txq *txq, int weight) +{ + netif_napi_add(txq->netdev, &txq->napi, free_tx_poll, weight); + napi_enable(&txq->napi); +} + +static void tx_napi_del(struct hinic_txq *txq) +{ + napi_disable(&txq->napi); + netif_napi_del(&txq->napi); +} + +static irqreturn_t tx_irq(int irq, void *data) +{ + struct hinic_txq *txq = data; + struct hinic_dev *nic_dev; + + nic_dev = netdev_priv(txq->netdev); + + /* Disable the interrupt until napi will be completed */ + disable_irq_nosync(txq->sq->irq); + + hinic_hwdev_msix_cnt_set(nic_dev->hwdev, txq->sq->msix_entry); + + napi_schedule(&txq->napi); + return IRQ_HANDLED; +} + +static int tx_request_irq(struct hinic_txq *txq) +{ + struct hinic_dev *nic_dev = netdev_priv(txq->netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_sq *sq = txq->sq; + int err; + + tx_napi_add(txq, nic_dev->tx_weight); + + hinic_hwdev_msix_set(nic_dev->hwdev, sq->msix_entry, + TX_IRQ_NO_PENDING, TX_IRQ_NO_COALESC, + TX_IRQ_NO_LLI_TIMER, TX_IRQ_NO_CREDIT, + TX_IRQ_NO_RESEND_TIMER); + + err = request_irq(sq->irq, tx_irq, 0, txq->irq_name, txq); + if (err) { + dev_err(&pdev->dev, "Failed to request Tx irq\n"); + tx_napi_del(txq); + return err; + } + + return 0; +} + +static void tx_free_irq(struct hinic_txq *txq) +{ + struct hinic_sq *sq = txq->sq; + + free_irq(sq->irq, txq); + tx_napi_del(txq); +} + /** * hinic_init_txq - Initialize the Tx Queue * @txq: Logical Tx Queue @@ -59,11 +404,63 @@ static void txq_stats_init(struct hinic_txq *txq) int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, struct net_device *netdev) { + struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq); + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + int err, irqname_len; + size_t sges_size; + txq->netdev = netdev; txq->sq = sq; txq_stats_init(txq); + + txq->max_sges = HINIC_MAX_SQ_BUFDESCS; + + sges_size = txq->max_sges * sizeof(*txq->sges); + txq->sges = devm_kzalloc(&netdev->dev, sges_size, GFP_KERNEL); + if (!txq->sges) + return -ENOMEM; + + sges_size = txq->max_sges * sizeof(*txq->free_sges); + txq->free_sges = devm_kzalloc(&netdev->dev, sges_size, GFP_KERNEL); + if (!txq->free_sges) { + err = -ENOMEM; + goto err_alloc_free_sges; + } + + irqname_len = snprintf(NULL, 0, "hinic_txq%d", qp->q_id) + 1; + txq->irq_name = devm_kzalloc(&netdev->dev, irqname_len, GFP_KERNEL); + if (!txq->irq_name) { + err = -ENOMEM; + goto err_alloc_irqname; + } + + sprintf(txq->irq_name, "hinic_txq%d", qp->q_id); + + err = hinic_hwdev_hw_ci_addr_set(hwdev, sq, CI_UPDATE_NO_PENDING, + CI_UPDATE_NO_COALESC); + if (err) + goto err_hw_ci; + + err = tx_request_irq(txq); + if (err) { + netdev_err(netdev, "Failed to request Tx irq\n"); + goto err_req_tx_irq; + } + return 0; + +err_req_tx_irq: +err_hw_ci: + devm_kfree(&netdev->dev, txq->irq_name); + +err_alloc_irqname: + devm_kfree(&netdev->dev, txq->free_sges); + +err_alloc_free_sges: + devm_kfree(&netdev->dev, txq->sges); + return err; } /** @@ -72,4 +469,13 @@ int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, **/ void hinic_clean_txq(struct hinic_txq *txq) { + struct net_device *netdev = txq->netdev; + + tx_free_irq(txq); + + free_all_tx_skbs(txq); + + devm_kfree(&netdev->dev, txq->irq_name); + devm_kfree(&netdev->dev, txq->free_sges); + devm_kfree(&netdev->dev, txq->sges); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.h b/drivers/net/ethernet/huawei/hinic/hinic_tx.h index bbdb4b62d940..7123c7f7e06a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.h @@ -18,8 +18,10 @@ #include #include +#include #include +#include "hinic_common.h" #include "hinic_hw_qp.h" struct hinic_txq_stats { @@ -37,10 +39,19 @@ struct hinic_txq { struct hinic_sq *sq; struct hinic_txq_stats txq_stats; + + int max_sges; + struct hinic_sge *sges; + struct hinic_sge *free_sges; + + char *irq_name; + struct napi_struct napi; }; void hinic_txq_clean_stats(struct hinic_txq *txq); +netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); + int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, struct net_device *netdev); -- cgit v1.2.3 From edd384f682cc2981420628b769a1929db680f02f Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:56:06 +0800 Subject: net-next/hinic: Add ethtool and stats Add ethtool operations and statistics operations. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 3 + drivers/net/ethernet/huawei/hinic/hinic_main.c | 218 ++++++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_port.c | 31 ++++ drivers/net/ethernet/huawei/hinic/hinic_port.h | 45 +++++ drivers/net/ethernet/huawei/hinic/hinic_rx.c | 19 +++ drivers/net/ethernet/huawei/hinic/hinic_rx.h | 2 + drivers/net/ethernet/huawei/hinic/hinic_tx.c | 22 +++ drivers/net/ethernet/huawei/hinic/hinic_tx.h | 2 + 8 files changed, 341 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 15d0c2e3797c..5186cc9023aa 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -56,6 +56,9 @@ struct hinic_dev { struct hinic_txq *txqs; struct hinic_rxq *rxqs; + + struct hinic_txq_stats tx_stats; + struct hinic_rxq_stats rx_stats; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 599d8b590e9a..a417ca2d441c 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -69,6 +69,186 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)"); static int change_mac_addr(struct net_device *netdev, const u8 *addr); +static void set_link_speed(struct ethtool_link_ksettings *link_ksettings, + enum hinic_speed speed) +{ + switch (speed) { + case HINIC_SPEED_10MB_LINK: + link_ksettings->base.speed = SPEED_10; + break; + + case HINIC_SPEED_100MB_LINK: + link_ksettings->base.speed = SPEED_100; + break; + + case HINIC_SPEED_1000MB_LINK: + link_ksettings->base.speed = SPEED_1000; + break; + + case HINIC_SPEED_10GB_LINK: + link_ksettings->base.speed = SPEED_10000; + break; + + case HINIC_SPEED_25GB_LINK: + link_ksettings->base.speed = SPEED_25000; + break; + + case HINIC_SPEED_40GB_LINK: + link_ksettings->base.speed = SPEED_40000; + break; + + case HINIC_SPEED_100GB_LINK: + link_ksettings->base.speed = SPEED_100000; + break; + + default: + link_ksettings->base.speed = SPEED_UNKNOWN; + break; + } +} + +static int hinic_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings + *link_ksettings) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + enum hinic_port_link_state link_state; + struct hinic_port_cap port_cap; + int err; + + ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); + ethtool_link_ksettings_add_link_mode(link_ksettings, supported, + Autoneg); + + link_ksettings->base.speed = SPEED_UNKNOWN; + link_ksettings->base.autoneg = AUTONEG_DISABLE; + link_ksettings->base.duplex = DUPLEX_UNKNOWN; + + err = hinic_port_get_cap(nic_dev, &port_cap); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to get port capabilities\n"); + return err; + } + + err = hinic_port_link_state(nic_dev, &link_state); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to get port link state\n"); + return err; + } + + if (link_state != HINIC_LINK_STATE_UP) { + netif_info(nic_dev, drv, netdev, "No link\n"); + return err; + } + + set_link_speed(link_ksettings, port_cap.speed); + + if (!!(port_cap.autoneg_cap & HINIC_AUTONEG_SUPPORTED)) + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, Autoneg); + + if (port_cap.autoneg_state == HINIC_AUTONEG_ACTIVE) + link_ksettings->base.autoneg = AUTONEG_ENABLE; + + link_ksettings->base.duplex = (port_cap.duplex == HINIC_DUPLEX_FULL) ? + DUPLEX_FULL : DUPLEX_HALF; + return 0; +} + +static void hinic_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + + strlcpy(info->driver, HINIC_DRV_NAME, sizeof(info->driver)); + strlcpy(info->bus_info, pci_name(hwif->pdev), sizeof(info->bus_info)); +} + +static void hinic_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + ring->rx_max_pending = HINIC_RQ_DEPTH; + ring->tx_max_pending = HINIC_SQ_DEPTH; + ring->rx_pending = HINIC_RQ_DEPTH; + ring->tx_pending = HINIC_SQ_DEPTH; +} + +static void hinic_get_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + + channels->max_rx = hwdev->nic_cap.max_qps; + channels->max_tx = hwdev->nic_cap.max_qps; + channels->max_other = 0; + channels->max_combined = 0; + channels->rx_count = hinic_hwdev_num_qps(hwdev); + channels->tx_count = hinic_hwdev_num_qps(hwdev); + channels->other_count = 0; + channels->combined_count = 0; +} + +static const struct ethtool_ops hinic_ethtool_ops = { + .get_link_ksettings = hinic_get_link_ksettings, + .get_drvinfo = hinic_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ringparam = hinic_get_ringparam, + .get_channels = hinic_get_channels, +}; + +static void update_rx_stats(struct hinic_dev *nic_dev, struct hinic_rxq *rxq) +{ + struct hinic_rxq_stats *nic_rx_stats = &nic_dev->rx_stats; + struct hinic_rxq_stats rx_stats; + + u64_stats_init(&rx_stats.syncp); + + hinic_rxq_get_stats(rxq, &rx_stats); + + u64_stats_update_begin(&nic_rx_stats->syncp); + nic_rx_stats->bytes += rx_stats.bytes; + nic_rx_stats->pkts += rx_stats.pkts; + u64_stats_update_end(&nic_rx_stats->syncp); + + hinic_rxq_clean_stats(rxq); +} + +static void update_tx_stats(struct hinic_dev *nic_dev, struct hinic_txq *txq) +{ + struct hinic_txq_stats *nic_tx_stats = &nic_dev->tx_stats; + struct hinic_txq_stats tx_stats; + + u64_stats_init(&tx_stats.syncp); + + hinic_txq_get_stats(txq, &tx_stats); + + u64_stats_update_begin(&nic_tx_stats->syncp); + nic_tx_stats->bytes += tx_stats.bytes; + nic_tx_stats->pkts += tx_stats.pkts; + nic_tx_stats->tx_busy += tx_stats.tx_busy; + nic_tx_stats->tx_wake += tx_stats.tx_wake; + nic_tx_stats->tx_dropped += tx_stats.tx_dropped; + u64_stats_update_end(&nic_tx_stats->syncp); + + hinic_txq_clean_stats(txq); +} + +static void update_nic_stats(struct hinic_dev *nic_dev) +{ + int i, num_qps = hinic_hwdev_num_qps(nic_dev->hwdev); + + for (i = 0; i < num_qps; i++) + update_rx_stats(nic_dev, &nic_dev->rxqs[i]); + + for (i = 0; i < num_qps; i++) + update_tx_stats(nic_dev, &nic_dev->txqs[i]); +} + /** * create_txqs - Create the Logical Tx Queues of specific NIC device * @nic_dev: the specific NIC device @@ -303,6 +483,8 @@ static int hinic_close(struct net_device *netdev) netif_carrier_off(netdev); netif_tx_disable(netdev); + update_nic_stats(nic_dev); + up(&nic_dev->mgmt_lock); err = hinic_port_set_func_state(nic_dev, HINIC_FUNC_PORT_DISABLE); @@ -580,6 +762,31 @@ static void hinic_tx_timeout(struct net_device *netdev) netif_err(nic_dev, drv, netdev, "Tx timeout\n"); } +static void hinic_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_rxq_stats *nic_rx_stats; + struct hinic_txq_stats *nic_tx_stats; + + nic_rx_stats = &nic_dev->rx_stats; + nic_tx_stats = &nic_dev->tx_stats; + + down(&nic_dev->mgmt_lock); + + if (nic_dev->flags & HINIC_INTF_UP) + update_nic_stats(nic_dev); + + up(&nic_dev->mgmt_lock); + + stats->rx_bytes = nic_rx_stats->bytes; + stats->rx_packets = nic_rx_stats->pkts; + + stats->tx_bytes = nic_tx_stats->bytes; + stats->tx_packets = nic_tx_stats->pkts; + stats->tx_errors = nic_tx_stats->tx_dropped; +} + static const struct net_device_ops hinic_netdev_ops = { .ndo_open = hinic_open, .ndo_stop = hinic_close, @@ -591,7 +798,7 @@ static const struct net_device_ops hinic_netdev_ops = { .ndo_set_rx_mode = hinic_set_rx_mode, .ndo_start_xmit = hinic_xmit_frame, .ndo_tx_timeout = hinic_tx_timeout, - /* more operations should be filled */ + .ndo_get_stats64 = hinic_get_stats64, }; static void netdev_features_init(struct net_device *netdev) @@ -663,6 +870,8 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size, static int nic_dev_init(struct pci_dev *pdev) { struct hinic_rx_mode_work *rx_mode_work; + struct hinic_txq_stats *tx_stats; + struct hinic_rxq_stats *rx_stats; struct hinic_dev *nic_dev; struct net_device *netdev; struct hinic_hwdev *hwdev; @@ -689,6 +898,7 @@ static int nic_dev_init(struct pci_dev *pdev) } netdev->netdev_ops = &hinic_netdev_ops; + netdev->ethtool_ops = &hinic_ethtool_ops; nic_dev = netdev_priv(netdev); nic_dev->netdev = netdev; @@ -702,6 +912,12 @@ static int nic_dev_init(struct pci_dev *pdev) sema_init(&nic_dev->mgmt_lock, 1); + tx_stats = &nic_dev->tx_stats; + rx_stats = &nic_dev->rx_stats; + + u64_stats_init(&tx_stats->syncp); + u64_stats_init(&rx_stats->syncp); + nic_dev->vlan_bitmap = devm_kzalloc(&pdev->dev, VLAN_BITMAP_SIZE(nic_dev), GFP_KERNEL); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 528ec6febd04..4d4e3f05fb5f 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -346,3 +346,34 @@ int hinic_port_set_func_state(struct hinic_dev *nic_dev, return 0; } + +/** + * hinic_port_get_cap - get port capabilities + * @nic_dev: nic device + * @port_cap: returned port capabilities + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_get_cap(struct hinic_dev *nic_dev, + struct hinic_port_cap *port_cap) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + port_cap->func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_CAP, + port_cap, sizeof(*port_cap), + port_cap, &out_size); + if (err || (out_size != sizeof(*port_cap)) || port_cap->status) { + dev_err(&pdev->dev, + "Failed to get port capabilities, ret = %d\n", + port_cap->status); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 17f9d7fc5a0a..9404365195dd 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -45,6 +45,33 @@ enum hinic_func_port_state { HINIC_FUNC_PORT_ENABLE = 2, }; +enum hinic_autoneg_cap { + HINIC_AUTONEG_UNSUPPORTED, + HINIC_AUTONEG_SUPPORTED, +}; + +enum hinic_autoneg_state { + HINIC_AUTONEG_DISABLED, + HINIC_AUTONEG_ACTIVE, +}; + +enum hinic_duplex { + HINIC_DUPLEX_HALF, + HINIC_DUPLEX_FULL, +}; + +enum hinic_speed { + HINIC_SPEED_10MB_LINK = 0, + HINIC_SPEED_100MB_LINK, + HINIC_SPEED_1000MB_LINK, + HINIC_SPEED_10GB_LINK, + HINIC_SPEED_25GB_LINK, + HINIC_SPEED_40GB_LINK, + HINIC_SPEED_100GB_LINK, + + HINIC_SPEED_UNKNOWN = 0xFF, +}; + struct hinic_port_mac_cmd { u8 status; u8 version; @@ -125,6 +152,21 @@ struct hinic_port_func_state_cmd { u8 rsvd2[3]; }; +struct hinic_port_cap { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rsvd1; + u8 port_type; + u8 autoneg_cap; + u8 autoneg_state; + u8 duplex; + u8 speed; + u8 rsvd2[3]; +}; + int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, u16 vlan_id); @@ -150,4 +192,7 @@ int hinic_port_set_state(struct hinic_dev *nic_dev, int hinic_port_set_func_state(struct hinic_dev *nic_dev, enum hinic_func_port_state state); +int hinic_port_get_cap(struct hinic_dev *nic_dev, + struct hinic_port_cap *port_cap); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c index b1212e498f95..1d4f712b15a8 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c @@ -57,6 +57,25 @@ void hinic_rxq_clean_stats(struct hinic_rxq *rxq) u64_stats_update_end(&rxq_stats->syncp); } +/** + * hinic_rxq_get_stats - get statistics of Rx Queue + * @rxq: Logical Rx Queue + * @stats: return updated stats here + **/ +void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats) +{ + struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; + unsigned int start; + + u64_stats_update_begin(&stats->syncp); + do { + start = u64_stats_fetch_begin(&rxq_stats->syncp); + stats->pkts = rxq_stats->pkts; + stats->bytes = rxq_stats->bytes; + } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); + u64_stats_update_end(&stats->syncp); +} + /** * rxq_stats_init - Initialize the statistics of specific queue * @rxq: Logical Rx Queue diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.h b/drivers/net/ethernet/huawei/hinic/hinic_rx.h index 538c8861e8dd..27c9af4b1c12 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.h @@ -45,6 +45,8 @@ struct hinic_rxq { void hinic_rxq_clean_stats(struct hinic_rxq *rxq); +void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats); + int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, struct net_device *netdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index 90ab2d971383..5bf6a32faa46 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -66,6 +66,28 @@ void hinic_txq_clean_stats(struct hinic_txq *txq) u64_stats_update_end(&txq_stats->syncp); } +/** + * hinic_txq_get_stats - get statistics of Tx Queue + * @txq: Logical Tx Queue + * @stats: return updated stats here + **/ +void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats) +{ + struct hinic_txq_stats *txq_stats = &txq->txq_stats; + unsigned int start; + + u64_stats_update_begin(&stats->syncp); + do { + start = u64_stats_fetch_begin(&txq_stats->syncp); + stats->pkts = txq_stats->pkts; + stats->bytes = txq_stats->bytes; + stats->tx_busy = txq_stats->tx_busy; + stats->tx_wake = txq_stats->tx_wake; + stats->tx_dropped = txq_stats->tx_dropped; + } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); + u64_stats_update_end(&stats->syncp); +} + /** * txq_stats_init - Initialize the statistics of specific queue * @txq: Logical Tx Queue diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.h b/drivers/net/ethernet/huawei/hinic/hinic_tx.h index 7123c7f7e06a..1fa55dce5aa7 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.h @@ -50,6 +50,8 @@ struct hinic_txq { void hinic_txq_clean_stats(struct hinic_txq *txq); +void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats); + netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, -- cgit v1.2.3 From b553400bb4a65ee4c8bc5c0202cede3c46524e55 Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 21 Aug 2017 23:56:07 +0800 Subject: net-next/hinic: Add netpoll Add more netdev operation - netpoll. Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_main.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index a417ca2d441c..ae7ad48c9f65 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -787,6 +787,23 @@ static void hinic_get_stats64(struct net_device *netdev, stats->tx_errors = nic_tx_stats->tx_dropped; } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void hinic_netpoll(struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int i, num_qps; + + num_qps = hinic_hwdev_num_qps(nic_dev->hwdev); + for (i = 0; i < num_qps; i++) { + struct hinic_txq *txq = &nic_dev->txqs[i]; + struct hinic_rxq *rxq = &nic_dev->rxqs[i]; + + napi_schedule(&txq->napi); + napi_schedule(&rxq->napi); + } +} +#endif + static const struct net_device_ops hinic_netdev_ops = { .ndo_open = hinic_open, .ndo_stop = hinic_close, @@ -799,6 +816,9 @@ static const struct net_device_ops hinic_netdev_ops = { .ndo_start_xmit = hinic_xmit_frame, .ndo_tx_timeout = hinic_tx_timeout, .ndo_get_stats64 = hinic_get_stats64, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = hinic_netpoll, +#endif }; static void netdev_features_init(struct net_device *netdev) -- cgit v1.2.3 From a8e8b7ff35175ab0a55ef0fa8560c3d5ffdb2818 Mon Sep 17 00:00:00 2001 From: Salil Date: Mon, 21 Aug 2017 17:05:24 +0100 Subject: net: hns3: Add support to change MTU in HNS3 hardware This patch adds the following support to the HNS3 driver: 1. Support to change the Maximum Transmission Unit of a port in the HNS NIC hardware. 2. Initializes the supported MTU range for the netdevice. Signed-off-by: lipeng Signed-off-by: Salil Mehta Reviewed-by: Andrew Lunn Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 34 ++++++++++++++++++++++ .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h | 1 + 2 files changed, 35 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index e731f87f3c46..1c3e29447891 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1278,11 +1278,42 @@ static int hns3_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, return ret; } +static int hns3_nic_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + bool if_running = netif_running(netdev); + int ret; + + if (!h->ae_algo->ops->set_mtu) + return -EOPNOTSUPP; + + /* if this was called with netdev up then bring netdevice down */ + if (if_running) { + (void)hns3_nic_net_stop(netdev); + msleep(100); + } + + ret = h->ae_algo->ops->set_mtu(h, new_mtu); + if (ret) { + netdev_err(netdev, "failed to change MTU in hardware %d\n", + ret); + return ret; + } + + /* if the netdev was running earlier, bring it up again */ + if (if_running && hns3_nic_net_open(netdev)) + ret = -EINVAL; + + return ret; +} + static const struct net_device_ops hns3_nic_netdev_ops = { .ndo_open = hns3_nic_net_open, .ndo_stop = hns3_nic_net_stop, .ndo_start_xmit = hns3_nic_net_xmit, .ndo_set_mac_address = hns3_nic_net_set_mac_address, + .ndo_change_mtu = hns3_nic_change_mtu, .ndo_set_features = hns3_nic_set_features, .ndo_get_stats64 = hns3_nic_get_stats64, .ndo_setup_tc = hns3_nic_setup_tc, @@ -2752,6 +2783,9 @@ static int hns3_client_init(struct hnae3_handle *handle) goto out_reg_netdev_fail; } + /* MTU range: (ETH_MIN_MTU(kernel default) - 9706) */ + netdev->max_mtu = HNS3_MAX_MTU - (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + return ret; out_reg_netdev_fail: diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h index a6e8f15a4669..7e8746189747 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h @@ -76,6 +76,7 @@ enum hns3_nic_state { #define HNS3_RING_NAME_LEN 16 #define HNS3_BUFFER_SIZE_2048 2048 #define HNS3_RING_MAX_PENDING 32768 +#define HNS3_MAX_MTU 9728 #define HNS3_BD_SIZE_512_TYPE 0 #define HNS3_BD_SIZE_1024_TYPE 1 -- cgit v1.2.3 From b6d08bd81d3044dcd73ea45fa6c28cc049224d41 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Mon, 21 Aug 2017 22:36:50 +0530 Subject: net: ethernet: make ptp_clock_info const Make these const as they are only used in a copy operation. Done using Coccinelle. @match disable optional_qualifier@ identifier s; @@ static struct ptp_clock_info s = {...}; @ref@ position p; identifier match.s; @@ s@p @good1@ position ref.p; identifier match.s,f,c; expression e; @@ ( e = s@p | e = s@p.f | c(...,s@p.f,...) | c(...,s@p,...) ) @bad depends on !good1@ position ref.p; identifier match.s; @@ s@p @depends on forall !bad disable optional_qualifier@ identifier match.s; @@ static + const struct ptp_clock_info s; Signed-off-by: Bhumika Goyal Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/bfin_mac.c | 2 +- drivers/net/ethernet/cadence/macb_ptp.c | 2 +- drivers/net/ethernet/freescale/gianfar_ptp.c | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c | 2 +- drivers/net/ethernet/ti/cpts.c | 2 +- drivers/net/ethernet/tile/tilegx.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index a9ac58c351a0..a251de8d9a91 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -986,7 +986,7 @@ static int bfin_ptp_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } -static struct ptp_clock_info bfin_ptp_caps = { +static const struct ptp_clock_info bfin_ptp_caps = { .owner = THIS_MODULE, .name = "BF518 clock", .max_adj = 0, diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c index 67cca08472b7..2220c771092b 100755 --- a/drivers/net/ethernet/cadence/macb_ptp.c +++ b/drivers/net/ethernet/cadence/macb_ptp.c @@ -192,7 +192,7 @@ static int gem_ptp_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } -static struct ptp_clock_info gem_ptp_caps_template = { +static const struct ptp_clock_info gem_ptp_caps_template = { .owner = THIS_MODULE, .name = GEM_PTP_TIMER_NAME, .max_adj = 0, diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c index 721be13081f9..544114281ea7 100644 --- a/drivers/net/ethernet/freescale/gianfar_ptp.c +++ b/drivers/net/ethernet/freescale/gianfar_ptp.c @@ -411,7 +411,7 @@ static int ptp_gianfar_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } -static struct ptp_clock_info ptp_gianfar_caps = { +static const struct ptp_clock_info ptp_gianfar_caps = { .owner = THIS_MODULE, .name = "gianfar clock", .max_adj = 512000, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c index d71bd80c5b5b..e471a903c654 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -152,7 +152,7 @@ static int stmmac_enable(struct ptp_clock_info *ptp, } /* structure describing a PTP hardware clock */ -static struct ptp_clock_info stmmac_ptp_clock_ops = { +static const struct ptp_clock_info stmmac_ptp_clock_ops = { .owner = THIS_MODULE, .name = "stmmac_ptp_clock", .max_adj = 62500000, diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index c2121d214f08..e7b76f6b4f67 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -298,7 +298,7 @@ static long cpts_overflow_check(struct ptp_clock_info *ptp) return (long)delay; } -static struct ptp_clock_info cpts_info = { +static const struct ptp_clock_info cpts_info = { .owner = THIS_MODULE, .name = "CTPS timer", .max_adj = 1000000, diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c index aec95382ea5c..c00102b8145a 100644 --- a/drivers/net/ethernet/tile/tilegx.c +++ b/drivers/net/ethernet/tile/tilegx.c @@ -873,7 +873,7 @@ static int ptp_mpipe_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } -static struct ptp_clock_info ptp_mpipe_caps = { +static const struct ptp_clock_info ptp_mpipe_caps = { .owner = THIS_MODULE, .name = "mPIPE clock", .max_adj = 999999999, -- cgit v1.2.3 From ad530a1d403a70b5473578e17b65d14132926b86 Mon Sep 17 00:00:00 2001 From: Veerasenareddy Burru Date: Mon, 21 Aug 2017 12:35:56 -0700 Subject: liquidio: move macro definition to a proper place The macro LIO_CMD_WAIT_TM is not specific to the PF driver; it can be used by the VF driver too, so move its definition from a PF-specific header file to one that's common to PF and VF. Signed-off-by: Veerasenareddy Burru Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h | 2 -- drivers/net/ethernet/cavium/liquidio/liquidio_common.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h index dee604651ba7..2aba5247b6d8 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h @@ -24,8 +24,6 @@ #include "cn23xx_pf_regs.h" -#define LIO_CMD_WAIT_TM 100 - /* Register address and configuration for a CN23XX devices. * If device specific changes need to be made then add a struct to include * device specific fields as shown in the commented section diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 906e30aadadc..d0076c191cee 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -237,6 +237,8 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry, #define OCTNET_CMD_VLAN_FILTER_ENABLE 0x1 #define OCTNET_CMD_VLAN_FILTER_DISABLE 0x0 +#define LIO_CMD_WAIT_TM 100 + /* RX(packets coming from wire) Checksum verification flags */ /* TCP/UDP csum */ #define CNNIC_L4SUM_VERIFIED 0x1 -- cgit v1.2.3 From ee5b1fac5641515a80ed1b15a84713629b29516d Mon Sep 17 00:00:00 2001 From: Veerasenareddy Burru Date: Mon, 21 Aug 2017 12:35:59 -0700 Subject: liquidio: make VF driver notify NIC firmware of MTU change Signed-off-by: Veerasenareddy Burru Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 27 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 0402b18d4689..2e993ce43b66 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1544,14 +1544,31 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) */ static int liquidio_change_mtu(struct net_device *netdev, int new_mtu) { - struct lio *lio = GET_LIO(netdev); + struct octnic_ctrl_pkt nctrl; + struct octeon_device *oct; + struct lio *lio; + int ret = 0; - lio->mtu = new_mtu; + lio = GET_LIO(netdev); + oct = lio->oct_dev; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); - netif_info(lio, probe, lio->netdev, "MTU Changed from %d to %d\n", - netdev->mtu, new_mtu); + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = OCTNET_CMD_CHANGE_MTU; + nctrl.ncmd.s.param1 = new_mtu; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.wait_time = LIO_CMD_WAIT_TM; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, "Failed to set MTU\n"); + return -EIO; + } - netdev->mtu = new_mtu; + lio->mtu = new_mtu; return 0; } -- cgit v1.2.3 From fcba1569a0c87cea40404af9a29b319f4e491839 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Mon, 21 Aug 2017 19:22:37 -0700 Subject: hv_netvsc: Clean up unused parameter from netvsc_get_hash() The parameter "sk" is not in use. Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index b33f0507c373..4677d214cc1f 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -193,7 +193,7 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, /* Azure hosts don't support non-TCP port numbers in hashing yet. We compute * hash for non-TCP traffic with only IP numbers. */ -static inline u32 netvsc_get_hash(struct sk_buff *skb, struct sock *sk) +static inline u32 netvsc_get_hash(struct sk_buff *skb) { struct flow_keys flow; u32 hash; @@ -227,7 +227,7 @@ static inline int netvsc_get_tx_queue(struct net_device *ndev, struct sock *sk = skb->sk; int q_idx; - q_idx = ndc->tx_send_table[netvsc_get_hash(skb, sk) & + q_idx = ndc->tx_send_table[netvsc_get_hash(skb) & (VRSS_SEND_TAB_SIZE - 1)]; /* If queue index changed record the new value */ -- cgit v1.2.3 From 4c0e2cbfd9b519722529526f4d87f8aab3c32ffd Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Mon, 21 Aug 2017 19:22:38 -0700 Subject: hv_netvsc: Clean up unused parameter from netvsc_get_rss_hash_opts() The parameter "nvdev" is not in use. Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 4677d214cc1f..d8612b1a8e4e 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1228,8 +1228,7 @@ static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data) } static int -netvsc_get_rss_hash_opts(struct netvsc_device *nvdev, - struct ethtool_rxnfc *info) +netvsc_get_rss_hash_opts(struct ethtool_rxnfc *info) { info->data = RXH_IP_SRC | RXH_IP_DST; @@ -1267,7 +1266,7 @@ netvsc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, return 0; case ETHTOOL_GRXFH: - return netvsc_get_rss_hash_opts(nvdev, info); + return netvsc_get_rss_hash_opts(info); } return -EOPNOTSUPP; } -- cgit v1.2.3 From 4823eb2f3af44e2b9f7f02bed5a211e9ce79051f Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Mon, 21 Aug 2017 19:22:39 -0700 Subject: hv_netvsc: Add ethtool handler to set and get UDP hash levels The patch add the functions to switch UDP hash level between L3 and L4 by ethtool command. UDP over IPv4 and v6 can be set differently. The default hash level is L4. We currently only allow switching TX hash level from within the guests. On Azure, fragmented UDP packets have high loss rate with L4 hashing. Using L3 hashing is recommended in this case. For example, for UDP over IPv4 on eth0: To include UDP port numbers in hasing: ethtool -N eth0 rx-flow-hash udp4 sdfn To exclude UDP port numbers in hasing: ethtool -N eth0 rx-flow-hash udp4 sd To show UDP hash level: ethtool -n eth0 rx-flow-hash udp4 Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 2 ++ drivers/net/hyperv/netvsc_drv.c | 78 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 72 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 9198dd1240ed..ff1c0c8d5e0d 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -720,6 +720,8 @@ struct net_device_context { u32 tx_send_table[VRSS_SEND_TAB_SIZE]; /* Ethtool settings */ + bool udp4_l4_hash; + bool udp6_l4_hash; u8 duplex; u32 speed; struct netvsc_ethtool_stats eth_stats; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index d8612b1a8e4e..c0c4c9195a3f 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -190,10 +190,12 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, return ppi; } -/* Azure hosts don't support non-TCP port numbers in hashing yet. We compute - * hash for non-TCP traffic with only IP numbers. +/* Azure hosts don't support non-TCP port numbers in hashing for fragmented + * packets. We can use ethtool to change UDP hash level when necessary. */ -static inline u32 netvsc_get_hash(struct sk_buff *skb) +static inline u32 netvsc_get_hash( + struct sk_buff *skb, + const struct net_device_context *ndc) { struct flow_keys flow; u32 hash; @@ -204,7 +206,11 @@ static inline u32 netvsc_get_hash(struct sk_buff *skb) if (!skb_flow_dissect_flow_keys(skb, &flow, 0)) return 0; - if (flow.basic.ip_proto == IPPROTO_TCP) { + if (flow.basic.ip_proto == IPPROTO_TCP || + (flow.basic.ip_proto == IPPROTO_UDP && + ((flow.basic.n_proto == htons(ETH_P_IP) && ndc->udp4_l4_hash) || + (flow.basic.n_proto == htons(ETH_P_IPV6) && + ndc->udp6_l4_hash)))) { return skb_get_hash(skb); } else { if (flow.basic.n_proto == htons(ETH_P_IP)) @@ -227,7 +233,7 @@ static inline int netvsc_get_tx_queue(struct net_device *ndev, struct sock *sk = skb->sk; int q_idx; - q_idx = ndc->tx_send_table[netvsc_get_hash(skb) & + q_idx = ndc->tx_send_table[netvsc_get_hash(skb, ndc) & (VRSS_SEND_TAB_SIZE - 1)]; /* If queue index changed record the new value */ @@ -891,6 +897,9 @@ static void netvsc_init_settings(struct net_device *dev) { struct net_device_context *ndc = netdev_priv(dev); + ndc->udp4_l4_hash = true; + ndc->udp6_l4_hash = true; + ndc->speed = SPEED_UNKNOWN; ndc->duplex = DUPLEX_FULL; } @@ -1228,7 +1237,8 @@ static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data) } static int -netvsc_get_rss_hash_opts(struct ethtool_rxnfc *info) +netvsc_get_rss_hash_opts(struct net_device_context *ndc, + struct ethtool_rxnfc *info) { info->data = RXH_IP_SRC | RXH_IP_DST; @@ -1236,9 +1246,20 @@ netvsc_get_rss_hash_opts(struct ethtool_rxnfc *info) case TCP_V4_FLOW: case TCP_V6_FLOW: info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; - /* fallthrough */ + break; + case UDP_V4_FLOW: + if (ndc->udp4_l4_hash) + info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + + break; + case UDP_V6_FLOW: + if (ndc->udp6_l4_hash) + info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + + break; + case IPV4_FLOW: case IPV6_FLOW: break; @@ -1266,11 +1287,51 @@ netvsc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, return 0; case ETHTOOL_GRXFH: - return netvsc_get_rss_hash_opts(info); + return netvsc_get_rss_hash_opts(ndc, info); } return -EOPNOTSUPP; } +static int netvsc_set_rss_hash_opts(struct net_device_context *ndc, + struct ethtool_rxnfc *info) +{ + if (info->data == (RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3)) { + if (info->flow_type == UDP_V4_FLOW) + ndc->udp4_l4_hash = true; + else if (info->flow_type == UDP_V6_FLOW) + ndc->udp6_l4_hash = true; + else + return -EOPNOTSUPP; + + return 0; + } + + if (info->data == (RXH_IP_SRC | RXH_IP_DST)) { + if (info->flow_type == UDP_V4_FLOW) + ndc->udp4_l4_hash = false; + else if (info->flow_type == UDP_V6_FLOW) + ndc->udp6_l4_hash = false; + else + return -EOPNOTSUPP; + + return 0; + } + + return -EOPNOTSUPP; +} + +static int +netvsc_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *info) +{ + struct net_device_context *ndc = netdev_priv(ndev); + + if (info->cmd == ETHTOOL_SRXFH) + return netvsc_set_rss_hash_opts(ndc, info); + + return -EOPNOTSUPP; +} + #ifdef CONFIG_NET_POLL_CONTROLLER static void netvsc_poll_controller(struct net_device *dev) { @@ -1469,6 +1530,7 @@ static const struct ethtool_ops ethtool_ops = { .set_channels = netvsc_set_channels, .get_ts_info = ethtool_op_get_ts_info, .get_rxnfc = netvsc_get_rxnfc, + .set_rxnfc = netvsc_set_rxnfc, .get_rxfh_key_size = netvsc_get_rxfh_key_size, .get_rxfh_indir_size = netvsc_rss_indir_size, .get_rxfh = netvsc_get_rxfh, -- cgit v1.2.3 From 58e0c0db85097923a46c5dae56d526f61b26c75e Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Tue, 22 Aug 2017 13:41:19 +0530 Subject: net: ethernet: ax88796: make mdiobb_ops const Make this const as it is only stored in a const field of a mdiobb_ctrl structure. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- drivers/net/ethernet/8390/ax88796.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 05d9d3e2e92e..245554707163 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -585,7 +585,7 @@ static int ax_bb_get_data(struct mdiobb_ctrl *ctrl) return reg_memr & AX_MEMR_MDI ? 1 : 0; } -static struct mdiobb_ops bb_ops = { +static const struct mdiobb_ops bb_ops = { .owner = THIS_MODULE, .set_mdc = ax_bb_mdc, .set_mdio_dir = ax_bb_dir, -- cgit v1.2.3 From 94494733ba02653d376839cd6ebc761f0cefc4f2 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Tue, 22 Aug 2017 13:45:59 +0530 Subject: net: ethernet: freescale: fs_enet: make mdiobb_ops const Make this const as it is only stored in a const field of a mdiobb_ctrl structure. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c index 1f015edcca22..c8e5d889bd81 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c @@ -100,7 +100,7 @@ static inline void mdc(struct mdiobb_ctrl *ctrl, int what) in_be32(bitbang->dat); } -static struct mdiobb_ops bb_ops = { +static const struct mdiobb_ops bb_ops = { .owner = THIS_MODULE, .set_mdc = mdc, .set_mdio_dir = mdio_dir, -- cgit v1.2.3 From 41a130f7c61d2adb303852d3fca938ad138a96d8 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Tue, 22 Aug 2017 13:43:29 +0530 Subject: net: mdio-gpio: make mdiobb_ops const Make this const as it is only stored in a const field of a mdiobb_ctrl structure. Signed-off-by: Bhumika Goyal Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 7faa79b254ef..4333c6e14742 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -116,7 +116,7 @@ static void mdc_set(struct mdiobb_ctrl *ctrl, int what) gpiod_set_value(bitbang->mdc, what); } -static struct mdiobb_ops mdio_gpio_ops = { +static const struct mdiobb_ops mdio_gpio_ops = { .owner = THIS_MODULE, .set_mdc = mdc_set, .set_mdio_dir = mdio_dir, -- cgit v1.2.3 From 81b6630ff7210356fe1843572543c76674e90450 Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Tue, 22 Aug 2017 19:08:21 +0200 Subject: net: mvpp2: unify register definitions coding style Cosmetic patch to use the same formatting rules on all register definitions. Signed-off-by: Antoine Tenart Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 88 ++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 44 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 39bc8fbbdd65..34c679f25fec 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -193,18 +193,18 @@ #define MVPP2_MAX_ISR_RX_THRESHOLD 0xfffff0 #define MVPP21_ISR_RXQ_GROUP_REG(port) (0x5400 + 4 * (port)) -#define MVPP22_ISR_RXQ_GROUP_INDEX_REG 0x5400 +#define MVPP22_ISR_RXQ_GROUP_INDEX_REG 0x5400 #define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf -#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 -#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET 7 +#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 +#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET 7 #define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf -#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 +#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 -#define MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG 0x5404 -#define MVPP22_ISR_RXQ_SUB_GROUP_STARTQ_MASK 0x1f -#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_MASK 0xf00 -#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET 8 +#define MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG 0x5404 +#define MVPP22_ISR_RXQ_SUB_GROUP_STARTQ_MASK 0x1f +#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_MASK 0xf00 +#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET 8 #define MVPP2_ISR_ENABLE_REG(port) (0x5420 + 4 * (port)) #define MVPP2_ISR_ENABLE_INTERRUPT(mask) ((mask) & 0xffff) @@ -272,7 +272,7 @@ #define MVPP2_BM_VIRT_RLS_REG 0x64c0 #define MVPP22_BM_ADDR_HIGH_RLS_REG 0x64c4 #define MVPP22_BM_ADDR_HIGH_PHYS_RLS_MASK 0xff -#define MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK 0xff00 +#define MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK 0xff00 #define MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT 8 /* TX Scheduler registers */ @@ -314,57 +314,57 @@ /* Per-port registers */ #define MVPP2_GMAC_CTRL_0_REG 0x0 -#define MVPP2_GMAC_PORT_EN_MASK BIT(0) -#define MVPP2_GMAC_MAX_RX_SIZE_OFFS 2 -#define MVPP2_GMAC_MAX_RX_SIZE_MASK 0x7ffc -#define MVPP2_GMAC_MIB_CNTR_EN_MASK BIT(15) +#define MVPP2_GMAC_PORT_EN_MASK BIT(0) +#define MVPP2_GMAC_MAX_RX_SIZE_OFFS 2 +#define MVPP2_GMAC_MAX_RX_SIZE_MASK 0x7ffc +#define MVPP2_GMAC_MIB_CNTR_EN_MASK BIT(15) #define MVPP2_GMAC_CTRL_1_REG 0x4 -#define MVPP2_GMAC_PERIODIC_XON_EN_MASK BIT(1) -#define MVPP2_GMAC_GMII_LB_EN_MASK BIT(5) -#define MVPP2_GMAC_PCS_LB_EN_BIT 6 -#define MVPP2_GMAC_PCS_LB_EN_MASK BIT(6) -#define MVPP2_GMAC_SA_LOW_OFFS 7 +#define MVPP2_GMAC_PERIODIC_XON_EN_MASK BIT(1) +#define MVPP2_GMAC_GMII_LB_EN_MASK BIT(5) +#define MVPP2_GMAC_PCS_LB_EN_BIT 6 +#define MVPP2_GMAC_PCS_LB_EN_MASK BIT(6) +#define MVPP2_GMAC_SA_LOW_OFFS 7 #define MVPP2_GMAC_CTRL_2_REG 0x8 -#define MVPP2_GMAC_INBAND_AN_MASK BIT(0) -#define MVPP2_GMAC_PCS_ENABLE_MASK BIT(3) -#define MVPP2_GMAC_PORT_RGMII_MASK BIT(4) -#define MVPP2_GMAC_PORT_RESET_MASK BIT(6) +#define MVPP2_GMAC_INBAND_AN_MASK BIT(0) +#define MVPP2_GMAC_PCS_ENABLE_MASK BIT(3) +#define MVPP2_GMAC_PORT_RGMII_MASK BIT(4) +#define MVPP2_GMAC_PORT_RESET_MASK BIT(6) #define MVPP2_GMAC_AUTONEG_CONFIG 0xc -#define MVPP2_GMAC_FORCE_LINK_DOWN BIT(0) -#define MVPP2_GMAC_FORCE_LINK_PASS BIT(1) -#define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5) -#define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6) -#define MVPP2_GMAC_AN_SPEED_EN BIT(7) -#define MVPP2_GMAC_FC_ADV_EN BIT(9) -#define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) -#define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) +#define MVPP2_GMAC_FORCE_LINK_DOWN BIT(0) +#define MVPP2_GMAC_FORCE_LINK_PASS BIT(1) +#define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5) +#define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6) +#define MVPP2_GMAC_AN_SPEED_EN BIT(7) +#define MVPP2_GMAC_FC_ADV_EN BIT(9) +#define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) +#define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) #define MVPP2_GMAC_PORT_FIFO_CFG_1_REG 0x1c -#define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6 -#define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 -#define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ +#define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6 +#define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 +#define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK) #define MVPP22_GMAC_CTRL_4_REG 0x90 -#define MVPP22_CTRL4_EXT_PIN_GMII_SEL BIT(0) -#define MVPP22_CTRL4_DP_CLK_SEL BIT(5) -#define MVPP22_CTRL4_SYNC_BYPASS BIT(6) -#define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7) +#define MVPP22_CTRL4_EXT_PIN_GMII_SEL BIT(0) +#define MVPP22_CTRL4_DP_CLK_SEL BIT(5) +#define MVPP22_CTRL4_SYNC_BYPASS BIT(6) +#define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7) /* Per-port XGMAC registers. PPv2.2 only, only for GOP port 0, * relative to port->base. */ #define MVPP22_XLG_CTRL0_REG 0x100 -#define MVPP22_XLG_CTRL0_PORT_EN BIT(0) -#define MVPP22_XLG_CTRL0_MAC_RESET_DIS BIT(1) -#define MVPP22_XLG_CTRL0_MIB_CNT_DIS BIT(14) +#define MVPP22_XLG_CTRL0_PORT_EN BIT(0) +#define MVPP22_XLG_CTRL0_MAC_RESET_DIS BIT(1) +#define MVPP22_XLG_CTRL0_MIB_CNT_DIS BIT(14) #define MVPP22_XLG_CTRL3_REG 0x11c -#define MVPP22_XLG_CTRL3_MACMODESELECT_MASK (7 << 13) -#define MVPP22_XLG_CTRL3_MACMODESELECT_GMAC (0 << 13) -#define MVPP22_XLG_CTRL3_MACMODESELECT_10G (1 << 13) +#define MVPP22_XLG_CTRL3_MACMODESELECT_MASK (7 << 13) +#define MVPP22_XLG_CTRL3_MACMODESELECT_GMAC (0 << 13) +#define MVPP22_XLG_CTRL3_MACMODESELECT_10G (1 << 13) /* SMI registers. PPv2.2 only, relative to priv->iface_base. */ #define MVPP22_SMI_MISC_CFG_REG 0x1204 -#define MVPP22_SMI_POLLING_EN BIT(10) +#define MVPP22_SMI_POLLING_EN BIT(10) #define MVPP22_GMAC_BASE(port) (0x7000 + (port) * 0x1000 + 0xe00) -- cgit v1.2.3 From 1068ec79d9cb5481ccfa6ffacdcf174636227b5d Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Tue, 22 Aug 2017 19:08:22 +0200 Subject: net: mvpp2: fix the synchronization module bypass macro name The macro defining the bit to toggle to bypass or not the synchronization module is wrongly named. Writing 1 will disable bypass. This patch s/MVPP22_CTRL4_SYNC_BYPASS/MVPP22_CTRL4_SYNC_BYPASS_DIS/. Signed-off-by: Antoine Tenart Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 34c679f25fec..03b7ced1082f 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -346,7 +346,7 @@ #define MVPP22_GMAC_CTRL_4_REG 0x90 #define MVPP22_CTRL4_EXT_PIN_GMII_SEL BIT(0) #define MVPP22_CTRL4_DP_CLK_SEL BIT(5) -#define MVPP22_CTRL4_SYNC_BYPASS BIT(6) +#define MVPP22_CTRL4_SYNC_BYPASS_DIS BIT(6) #define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7) /* Per-port XGMAC registers. PPv2.2 only, only for GOP port 0, @@ -4269,7 +4269,7 @@ static void mvpp22_port_mii_set(struct mvpp2_port *port) else val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL; val &= ~MVPP22_CTRL4_DP_CLK_SEL; - val |= MVPP22_CTRL4_SYNC_BYPASS; + val |= MVPP22_CTRL4_SYNC_BYPASS_DIS; val |= MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); } -- cgit v1.2.3 From 2055d6268d755fdc3f96f1d0bbf22c6164dacfbf Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Tue, 22 Aug 2017 19:08:23 +0200 Subject: net: mvpp2: move the mii configuration in the ndo_open path This moves the mii configuration in the ndo_open path, to allow handling different mii configurations later and to switch between these configurations at runtime. Signed-off-by: Antoine Tenart Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 03b7ced1082f..087eb793ab4d 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5987,6 +5987,7 @@ static void mvpp2_start_dev(struct mvpp2_port *port) /* Enable interrupts on all CPUs */ mvpp2_interrupts_enable(port); + mvpp2_port_mii_set(port); mvpp2_port_enable(port); phy_start(ndev->phydev); netif_tx_start_all_queues(port->dev); @@ -6949,7 +6950,6 @@ static int mvpp2_port_probe(struct platform_device *pdev, goto err_free_stats; } - mvpp2_port_mii_set(port); mvpp2_port_periodic_xon_disable(port); if (priv->hw_version == MVPP21) -- cgit v1.2.3 From 3919357fb0bbdbc18366cf59cbf0f16c608f81fe Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Tue, 22 Aug 2017 19:08:24 +0200 Subject: net: mvpp2: initialize the GMAC when using a port This adds a routine to initialize the GMAC at the port level when using a port. This wasn't done until this commit, and the mvpp2 driver was relying on the bootloader/firmware initialization. This doesn't mean everything is configured in the mvpp2 driver now, but it helps reducing the gap. Signed-off-by: Antoine Tenart Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 123 ++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 25 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 087eb793ab4d..2ab7366ac90f 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -315,6 +315,7 @@ /* Per-port registers */ #define MVPP2_GMAC_CTRL_0_REG 0x0 #define MVPP2_GMAC_PORT_EN_MASK BIT(0) +#define MVPP2_GMAC_PORT_TYPE_MASK BIT(1) #define MVPP2_GMAC_MAX_RX_SIZE_OFFS 2 #define MVPP2_GMAC_MAX_RX_SIZE_MASK 0x7ffc #define MVPP2_GMAC_MIB_CNTR_EN_MASK BIT(15) @@ -326,16 +327,21 @@ #define MVPP2_GMAC_SA_LOW_OFFS 7 #define MVPP2_GMAC_CTRL_2_REG 0x8 #define MVPP2_GMAC_INBAND_AN_MASK BIT(0) +#define MVPP2_GMAC_FLOW_CTRL_MASK GENMASK(2, 1) #define MVPP2_GMAC_PCS_ENABLE_MASK BIT(3) #define MVPP2_GMAC_PORT_RGMII_MASK BIT(4) +#define MVPP2_GMAC_DISABLE_PADDING BIT(5) #define MVPP2_GMAC_PORT_RESET_MASK BIT(6) #define MVPP2_GMAC_AUTONEG_CONFIG 0xc #define MVPP2_GMAC_FORCE_LINK_DOWN BIT(0) #define MVPP2_GMAC_FORCE_LINK_PASS BIT(1) +#define MVPP2_GMAC_IN_BAND_AUTONEG BIT(2) +#define MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS BIT(3) #define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5) #define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6) #define MVPP2_GMAC_AN_SPEED_EN BIT(7) #define MVPP2_GMAC_FC_ADV_EN BIT(9) +#define MVPP2_GMAC_FLOW_CTRL_AUTONEG BIT(11) #define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) #define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) #define MVPP2_GMAC_PORT_FIFO_CFG_1_REG 0x1c @@ -4245,6 +4251,92 @@ mvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask) /* Port configuration routines */ +static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port) +{ + u32 val; + + if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); + val |= MVPP22_CTRL4_SYNC_BYPASS_DIS | MVPP22_CTRL4_DP_CLK_SEL | + MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; + val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL; + writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); + + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + val |= MVPP2_GMAC_DISABLE_PADDING; + val &= ~MVPP2_GMAC_FLOW_CTRL_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + } else if (port->phy_interface == PHY_INTERFACE_MODE_RGMII || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) { + val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); + val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL | + MVPP22_CTRL4_SYNC_BYPASS_DIS | + MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; + val &= ~MVPP22_CTRL4_DP_CLK_SEL; + writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); + + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + val &= ~MVPP2_GMAC_DISABLE_PADDING; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + } + + /* The port is connected to a copper PHY */ + val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); + val &= ~MVPP2_GMAC_PORT_TYPE_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); + + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS | + MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG | + MVPP2_GMAC_AN_DUPLEX_EN; + if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) + val |= MVPP2_GMAC_IN_BAND_AUTONEG; + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); +} + +static void mvpp2_port_mii_gmac_configure(struct mvpp2_port *port) +{ + u32 val; + + /* Force link down */ + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val &= ~MVPP2_GMAC_FORCE_LINK_PASS; + val |= MVPP2_GMAC_FORCE_LINK_DOWN; + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + + /* Set the GMAC in a reset state */ + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + val |= MVPP2_GMAC_PORT_RESET_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + + /* Configure the PCS and in-band AN */ + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK; + } else if (port->phy_interface == PHY_INTERFACE_MODE_RGMII || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) { + val &= ~MVPP2_GMAC_PCS_ENABLE_MASK; + val |= MVPP2_GMAC_PORT_RGMII_MASK; + } + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + + mvpp2_port_mii_gmac_configure_mode(port); + + /* Unset the GMAC reset state */ + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + val &= ~MVPP2_GMAC_PORT_RESET_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + + /* Stop forcing link down */ + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val &= ~MVPP2_GMAC_FORCE_LINK_DOWN; + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); +} + static void mvpp22_port_mii_set(struct mvpp2_port *port) { u32 val; @@ -4262,38 +4354,19 @@ static void mvpp22_port_mii_set(struct mvpp2_port *port) writel(val, port->base + MVPP22_XLG_CTRL3_REG); } - - val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); - if (port->phy_interface == PHY_INTERFACE_MODE_RGMII) - val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL; - else - val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL; - val &= ~MVPP22_CTRL4_DP_CLK_SEL; - val |= MVPP22_CTRL4_SYNC_BYPASS_DIS; - val |= MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; - writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); } static void mvpp2_port_mii_set(struct mvpp2_port *port) { - u32 val; - if (port->priv->hw_version == MVPP22) mvpp22_port_mii_set(port); - val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); - - switch (port->phy_interface) { - case PHY_INTERFACE_MODE_SGMII: - val |= MVPP2_GMAC_INBAND_AN_MASK; - break; - case PHY_INTERFACE_MODE_RGMII: - val |= MVPP2_GMAC_PORT_RGMII_MASK; - default: - val &= ~MVPP2_GMAC_PCS_ENABLE_MASK; - } - - writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + if (port->phy_interface == PHY_INTERFACE_MODE_RGMII || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) + mvpp2_port_mii_gmac_configure(port); } static void mvpp2_port_fc_adv_enable(struct mvpp2_port *port) -- cgit v1.2.3 From 77321959feb9bdcfeeb2a2154d6fb831fb1991c2 Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Tue, 22 Aug 2017 19:08:25 +0200 Subject: net: mvpp2: initialize the XLG MAC when using a port This adds a routine to initialize the XLG MAC at the port level when using a port and the XAUI/10GKR interface mode. This wasn't done until this commit, and the mvpp2 driver was relying on the bootloader/firmware initialization. This doesn't mean everything is configured in the mvpp2 driver now, but it helps reducing the gap. Signed-off-by: Antoine Tenart Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 2ab7366ac90f..64e0a1bae7d0 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -361,6 +361,7 @@ #define MVPP22_XLG_CTRL0_REG 0x100 #define MVPP22_XLG_CTRL0_PORT_EN BIT(0) #define MVPP22_XLG_CTRL0_MAC_RESET_DIS BIT(1) +#define MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN BIT(7) #define MVPP22_XLG_CTRL0_MIB_CNT_DIS BIT(14) #define MVPP22_XLG_CTRL3_REG 0x11c @@ -368,6 +369,11 @@ #define MVPP22_XLG_CTRL3_MACMODESELECT_GMAC (0 << 13) #define MVPP22_XLG_CTRL3_MACMODESELECT_10G (1 << 13) +#define MVPP22_XLG_CTRL4_REG 0x184 +#define MVPP22_XLG_CTRL4_FWD_FC BIT(5) +#define MVPP22_XLG_CTRL4_FWD_PFC BIT(6) +#define MVPP22_XLG_CTRL4_MACMODSELECT_GMAC BIT(12) + /* SMI registers. PPv2.2 only, relative to priv->iface_base. */ #define MVPP22_SMI_MISC_CFG_REG 0x1204 #define MVPP22_SMI_POLLING_EN BIT(10) @@ -4337,6 +4343,23 @@ static void mvpp2_port_mii_gmac_configure(struct mvpp2_port *port) writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); } +static void mvpp2_port_mii_xlg_configure(struct mvpp2_port *port) +{ + u32 val; + + if (port->gop_id != 0) + return; + + val = readl(port->base + MVPP22_XLG_CTRL0_REG); + val |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN; + writel(val, port->base + MVPP22_XLG_CTRL0_REG); + + val = readl(port->base + MVPP22_XLG_CTRL4_REG); + val &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC; + val |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC; + writel(val, port->base + MVPP22_XLG_CTRL4_REG); +} + static void mvpp22_port_mii_set(struct mvpp2_port *port) { u32 val; @@ -4367,6 +4390,8 @@ static void mvpp2_port_mii_set(struct mvpp2_port *port) port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID || port->phy_interface == PHY_INTERFACE_MODE_SGMII) mvpp2_port_mii_gmac_configure(port); + else if (port->phy_interface == PHY_INTERFACE_MODE_10GKR) + mvpp2_port_mii_xlg_configure(port); } static void mvpp2_port_fc_adv_enable(struct mvpp2_port *port) -- cgit v1.2.3 From 76eb1b1de5b6467c78bb72311dbf29eea1f10a3a Mon Sep 17 00:00:00 2001 From: Stefan Chulski Date: Tue, 22 Aug 2017 19:08:26 +0200 Subject: net: mvpp2: set maximum packet size for 10G ports Set maximum packet size for XLG 10G ports. Missing maximum packet size for XLG configuration will cause kernel panic if oversized packet is received by port. Signed-off-by: Stefan Chulski Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 64e0a1bae7d0..b2d2b4c9d5af 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -363,7 +363,9 @@ #define MVPP22_XLG_CTRL0_MAC_RESET_DIS BIT(1) #define MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN BIT(7) #define MVPP22_XLG_CTRL0_MIB_CNT_DIS BIT(14) - +#define MVPP22_XLG_CTRL1_REG 0x104 +#define MVPP22_XLG_CTRL1_FRAMESIZELIMIT BIT(0) +#define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK 0x1fff #define MVPP22_XLG_CTRL3_REG 0x11c #define MVPP22_XLG_CTRL3_MACMODESELECT_MASK (7 << 13) #define MVPP22_XLG_CTRL3_MACMODESELECT_GMAC (0 << 13) @@ -4498,6 +4500,18 @@ static inline void mvpp2_gmac_max_rx_size_set(struct mvpp2_port *port) writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); } +/* Change maximum receive size of the port */ +static inline void mvpp2_xlg_max_rx_size_set(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP22_XLG_CTRL1_REG); + val &= ~MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK; + val |= ((port->pkt_size - MVPP2_MH_SIZE) / 2) << + MVPP22_XLG_CTRL1_FRAMESIZELIMIT; + writel(val, port->base + MVPP22_XLG_CTRL1_REG); +} + /* Set defaults to the MVPP2 port */ static void mvpp2_defaults_set(struct mvpp2_port *port) { @@ -6076,7 +6090,13 @@ static void mvpp2_start_dev(struct mvpp2_port *port) struct net_device *ndev = port->dev; int i; - mvpp2_gmac_max_rx_size_set(port); + if (port->gop_id == 0 && + (port->phy_interface == PHY_INTERFACE_MODE_XAUI || + port->phy_interface == PHY_INTERFACE_MODE_10GKR)) + mvpp2_xlg_max_rx_size_set(port); + else + mvpp2_gmac_max_rx_size_set(port); + mvpp2_txp_max_tx_size_set(port); for (i = 0; i < port->nqvecs; i++) -- cgit v1.2.3 From f84bf386f3956c4a02c773a59c9c61033c00a5b5 Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Tue, 22 Aug 2017 19:08:27 +0200 Subject: net: mvpp2: initialize the GoP The patch adds GoP (group of ports) initialization functions. The mvpp2 driver was relying on the firmware/bootloader initialization; this patch moves this setup to the mvpp2 driver. Signed-off-by: Antoine Tenart Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 170 +++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index b2d2b4c9d5af..02c62cbbfe51 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -388,6 +390,38 @@ #define MVPP2_QUEUE_NEXT_DESC(q, index) \ (((index) < (q)->last_desc) ? ((index) + 1) : 0) +/* XPCS registers. PPv2.2 only */ +#define MVPP22_MPCS_BASE(port) (0x7000 + (port) * 0x1000) +#define MVPP22_MPCS_CTRL 0x14 +#define MVPP22_MPCS_CTRL_FWD_ERR_CONN BIT(10) +#define MVPP22_MPCS_CLK_RESET 0x14c +#define MAC_CLK_RESET_SD_TX BIT(0) +#define MAC_CLK_RESET_SD_RX BIT(1) +#define MAC_CLK_RESET_MAC BIT(2) +#define MVPP22_MPCS_CLK_RESET_DIV_RATIO(n) ((n) << 4) +#define MVPP22_MPCS_CLK_RESET_DIV_SET BIT(11) + +/* XPCS registers. PPv2.2 only */ +#define MVPP22_XPCS_BASE(port) (0x7400 + (port) * 0x1000) +#define MVPP22_XPCS_CFG0 0x0 +#define MVPP22_XPCS_CFG0_PCS_MODE(n) ((n) << 3) +#define MVPP22_XPCS_CFG0_ACTIVE_LANE(n) ((n) << 5) + +/* System controller registers. Accessed through a regmap. */ +#define GENCONF_SOFT_RESET1 0x1108 +#define GENCONF_SOFT_RESET1_GOP BIT(6) +#define GENCONF_PORT_CTRL0 0x1110 +#define GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT BIT(1) +#define GENCONF_PORT_CTRL0_RX_DATA_SAMPLE BIT(29) +#define GENCONF_PORT_CTRL0_CLK_DIV_PHASE_CLR BIT(31) +#define GENCONF_PORT_CTRL1 0x1114 +#define GENCONF_PORT_CTRL1_EN(p) BIT(p) +#define GENCONF_PORT_CTRL1_RESET(p) (BIT(p) << 28) +#define GENCONF_CTRL0 0x1120 +#define GENCONF_CTRL0_PORT0_RGMII BIT(0) +#define GENCONF_CTRL0_PORT1_RGMII_MII BIT(1) +#define GENCONF_CTRL0_PORT1_RGMII BIT(2) + /* Various constants */ /* Coalescing */ @@ -731,6 +765,11 @@ struct mvpp2 { */ void __iomem *swth_base[MVPP2_MAX_THREADS]; + /* On PPv2.2, some port control registers are located into the system + * controller space. These registers are accessible through a regmap. + */ + struct regmap *sysctrl_base; + /* Common clocks */ struct clk *pp_clk; struct clk *gop_clk; @@ -4259,6 +4298,123 @@ mvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask) /* Port configuration routines */ +static void mvpp22_gop_init_rgmii(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + u32 val; + + regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val); + val |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT; + regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val); + + regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val); + if (port->gop_id == 2) + val |= GENCONF_CTRL0_PORT0_RGMII | GENCONF_CTRL0_PORT1_RGMII; + else if (port->gop_id == 3) + val |= GENCONF_CTRL0_PORT1_RGMII_MII; + regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val); +} + +static void mvpp22_gop_init_sgmii(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + u32 val; + + regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val); + val |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT | + GENCONF_PORT_CTRL0_RX_DATA_SAMPLE; + regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val); + + if (port->gop_id > 1) { + regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val); + if (port->gop_id == 2) + val &= ~GENCONF_CTRL0_PORT0_RGMII; + else if (port->gop_id == 3) + val &= ~GENCONF_CTRL0_PORT1_RGMII_MII; + regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val); + } +} + +static void mvpp22_gop_init_10gkr(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + void __iomem *mpcs = priv->iface_base + MVPP22_MPCS_BASE(port->gop_id); + void __iomem *xpcs = priv->iface_base + MVPP22_XPCS_BASE(port->gop_id); + u32 val; + + /* XPCS */ + val = readl(xpcs + MVPP22_XPCS_CFG0); + val &= ~(MVPP22_XPCS_CFG0_PCS_MODE(0x3) | + MVPP22_XPCS_CFG0_ACTIVE_LANE(0x3)); + val |= MVPP22_XPCS_CFG0_ACTIVE_LANE(2); + writel(val, xpcs + MVPP22_XPCS_CFG0); + + /* MPCS */ + val = readl(mpcs + MVPP22_MPCS_CTRL); + val &= ~MVPP22_MPCS_CTRL_FWD_ERR_CONN; + writel(val, mpcs + MVPP22_MPCS_CTRL); + + val = readl(mpcs + MVPP22_MPCS_CLK_RESET); + val &= ~(MVPP22_MPCS_CLK_RESET_DIV_RATIO(0x7) | MAC_CLK_RESET_MAC | + MAC_CLK_RESET_SD_RX | MAC_CLK_RESET_SD_TX); + val |= MVPP22_MPCS_CLK_RESET_DIV_RATIO(1); + writel(val, mpcs + MVPP22_MPCS_CLK_RESET); + + val &= ~MVPP22_MPCS_CLK_RESET_DIV_SET; + val |= MAC_CLK_RESET_MAC | MAC_CLK_RESET_SD_RX | MAC_CLK_RESET_SD_TX; + writel(val, mpcs + MVPP22_MPCS_CLK_RESET); +} + +static int mvpp22_gop_init(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + u32 val; + + if (!priv->sysctrl_base) + return 0; + + switch (port->phy_interface) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + if (port->gop_id == 0) + goto invalid_conf; + mvpp22_gop_init_rgmii(port); + break; + case PHY_INTERFACE_MODE_SGMII: + mvpp22_gop_init_sgmii(port); + break; + case PHY_INTERFACE_MODE_10GKR: + if (port->gop_id != 0) + goto invalid_conf; + mvpp22_gop_init_10gkr(port); + break; + default: + goto unsupported_conf; + } + + regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL1, &val); + val |= GENCONF_PORT_CTRL1_RESET(port->gop_id) | + GENCONF_PORT_CTRL1_EN(port->gop_id); + regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL1, val); + + regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val); + val |= GENCONF_PORT_CTRL0_CLK_DIV_PHASE_CLR; + regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val); + + regmap_read(priv->sysctrl_base, GENCONF_SOFT_RESET1, &val); + val |= GENCONF_SOFT_RESET1_GOP; + regmap_write(priv->sysctrl_base, GENCONF_SOFT_RESET1, val); + +unsupported_conf: + return 0; + +invalid_conf: + netdev_err(port->dev, "Invalid port configuration\n"); + return -EINVAL; +} + static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port) { u32 val; @@ -6105,6 +6261,9 @@ static void mvpp2_start_dev(struct mvpp2_port *port) /* Enable interrupts on all CPUs */ mvpp2_interrupts_enable(port); + if (port->priv->hw_version == MVPP22) + mvpp22_gop_init(port); + mvpp2_port_mii_set(port); mvpp2_port_enable(port); phy_start(ndev->phydev); @@ -7350,6 +7509,17 @@ static int mvpp2_probe(struct platform_device *pdev) priv->iface_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->iface_base)) return PTR_ERR(priv->iface_base); + + priv->sysctrl_base = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "marvell,system-controller"); + if (IS_ERR(priv->sysctrl_base)) + /* The system controller regmap is optional for dt + * compatibility reasons. When not provided, the + * configuration of the GoP relies on the + * firmware/bootloader. + */ + priv->sysctrl_base = NULL; } for (i = 0; i < MVPP2_MAX_THREADS; i++) { -- cgit v1.2.3 From 153890b41b0ad467a89a77d48bb96f6bc3b14120 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 22 Aug 2017 23:41:12 +0530 Subject: net: amd: constify zorro_device_id zorro_device_id are not supposed to change at runtime. All functions working with zorro_device_id provided by work with const zorro_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/a2065.c | 2 +- drivers/net/ethernet/amd/ariadne.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/a2065.c b/drivers/net/ethernet/amd/a2065.c index ee4b94e3cda9..e22f976a0d18 100644 --- a/drivers/net/ethernet/amd/a2065.c +++ b/drivers/net/ethernet/amd/a2065.c @@ -643,7 +643,7 @@ static int a2065_init_one(struct zorro_dev *z, static void a2065_remove_one(struct zorro_dev *z); -static struct zorro_device_id a2065_zorro_tbl[] = { +static const struct zorro_device_id a2065_zorro_tbl[] = { { ZORRO_PROD_CBM_A2065_1 }, { ZORRO_PROD_CBM_A2065_2 }, { ZORRO_PROD_AMERISTAR_A2065 }, diff --git a/drivers/net/ethernet/amd/ariadne.c b/drivers/net/ethernet/amd/ariadne.c index 5fd7b15b0574..4b6a5cb85dd2 100644 --- a/drivers/net/ethernet/amd/ariadne.c +++ b/drivers/net/ethernet/amd/ariadne.c @@ -692,7 +692,7 @@ static void ariadne_remove_one(struct zorro_dev *z) free_netdev(dev); } -static struct zorro_device_id ariadne_zorro_tbl[] = { +static const struct zorro_device_id ariadne_zorro_tbl[] = { { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE }, { 0 } }; -- cgit v1.2.3 From b28547728d4fd42a004df2b662724e16ff778db6 Mon Sep 17 00:00:00 2001 From: Felix Manlunas Date: Tue, 22 Aug 2017 12:46:37 -0700 Subject: liquidio: change manner of detecting whether or not NIC firmware is loaded In the NIC firmware, the 1-bit flag indicating "firmware is loaded" moved from SLI_SCRATCH_1 to SLI_SCRATCH_2 (these are Octeon general-purpose scratch registers). Make the PF driver conform to this change. Remove code that sets the "firmware is loaded" flag because it's now the firmware's job to do that. In the code that detects whether or not the firmware is loaded, don't just rely on checking the "firmware is loaded" flag because that may cause a rare false negative. Add code that deduces whether or not the firmware is loaded; that will never give a false negative. Also bump up driver version to match newer NIC firmware. Signed-off-by: Felix Manlunas Signed-off-by: Derek Chickles Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c | 15 +++++++++++++-- drivers/net/ethernet/cavium/liquidio/lio_main.c | 6 ------ drivers/net/ethernet/cavium/liquidio/liquidio_common.h | 3 ++- 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index fbc0d4e008f3..f6c0bad78cd4 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -1442,8 +1442,19 @@ int cn23xx_fw_loaded(struct octeon_device *oct) { u64 val; - val = octeon_read_csr64(oct, CN23XX_SLI_SCRATCH1); - return (val >> 1) & 1ULL; + /* If there's more than one active PF on this NIC, then that + * implies that the NIC firmware is loaded and running. This check + * prevents a rare false negative that might occur if we only relied + * on checking the SCR2_BIT_FW_LOADED flag. The false negative would + * happen if the PF driver sees SCR2_BIT_FW_LOADED as cleared even + * though the firmware was already loaded but still booting and has yet + * to set SCR2_BIT_FW_LOADED. + */ + if (atomic_read(oct->adapter_refcount) > 1) + return 1; + + val = octeon_read_csr64(oct, CN23XX_SLI_SCRATCH2); + return (val >> SCR2_BIT_FW_LOADED) & 1ULL; } void cn23xx_tell_vf_its_macaddr_changed(struct octeon_device *oct, int vfidx, diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index c2360fe8cef2..e7f54948173f 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -4125,12 +4125,6 @@ static int octeon_device_init(struct octeon_device *octeon_dev) dev_err(&octeon_dev->pci_dev->dev, "Could not load firmware to board\n"); return 1; } - /* set bit 1 of SLI_SCRATCH_1 to indicate that firmware is - * loaded - */ - if (OCTEON_CN23XX_PF(octeon_dev)) - octeon_write_csr64(octeon_dev, CN23XX_SLI_SCRATCH1, - 2ULL); } handshake[octeon_dev->octeon_id].init_ok = 1; diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index d0076c191cee..3788c8cd082a 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -28,7 +28,7 @@ #define LIQUIDIO_PACKAGE "" #define LIQUIDIO_BASE_MAJOR_VERSION 1 #define LIQUIDIO_BASE_MINOR_VERSION 6 -#define LIQUIDIO_BASE_MICRO_VERSION 0 +#define LIQUIDIO_BASE_MICRO_VERSION 1 #define LIQUIDIO_BASE_VERSION __stringify(LIQUIDIO_BASE_MAJOR_VERSION) "." \ __stringify(LIQUIDIO_BASE_MINOR_VERSION) #define LIQUIDIO_MICRO_VERSION "." __stringify(LIQUIDIO_BASE_MICRO_VERSION) @@ -106,6 +106,7 @@ enum octeon_tag_type { #define MAX_IOQ_INTERRUPTS_PER_PF (64 * 2) #define MAX_IOQ_INTERRUPTS_PER_VF (8 * 2) +#define SCR2_BIT_FW_LOADED 63 static inline u32 incr_index(u32 index, u32 count, u32 max) { -- cgit v1.2.3 From f9cbe9a556afca9e82df9aebe4412d93769566b5 Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Wed, 23 Aug 2017 09:46:54 +0200 Subject: net: define the TSO header size in net/tso.h The TSO header size was defined in many drivers. Factorize the code and define its size in net/tso.h. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 1 - drivers/net/ethernet/freescale/fec_main.c | 1 - drivers/net/ethernet/marvell/mv643xx_eth.c | 2 -- drivers/net/ethernet/marvell/mvneta.c | 3 --- include/net/tso.h | 2 ++ 5 files changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index 57858522c33c..67d1a3230773 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -277,7 +277,6 @@ struct snd_queue { u16 xdp_free_cnt; bool is_xdp; -#define TSO_HEADER_SIZE 128 /* For TSO segment's header */ char *tso_hdrs; dma_addr_t tso_hdrs_phys; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index df09b254553d..56f56d6ada9c 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -226,7 +226,6 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define COPYBREAK_DEFAULT 256 -#define TSO_HEADER_SIZE 128 /* Max number of allowed TCP segments for software TSO */ #define FEC_MAX_TSO_SEGS 100 #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 9c94ea9b2b80..fb2d533ae4ef 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -183,8 +183,6 @@ static char mv643xx_eth_driver_version[] = "1.4"; #define DEFAULT_TX_QUEUE_SIZE 512 #define SKB_DMA_REALIGN ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES) -#define TSO_HEADER_SIZE 128 - /* Max number of allowed TCP segments for software TSO */ #define MV643XX_MAX_TSO_SEGS 100 #define MV643XX_MAX_SKB_DESCS (MV643XX_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 0aab74c2a209..35ff1ecfcff0 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -281,9 +281,6 @@ */ #define MVNETA_RSS_LU_TABLE_SIZE 1 -/* TSO header size */ -#define TSO_HEADER_SIZE 128 - /* Max number of Rx descriptors */ #define MVNETA_MAX_RXD 128 diff --git a/include/net/tso.h b/include/net/tso.h index b7be852bfe9d..9a56c39e6d0a 100644 --- a/include/net/tso.h +++ b/include/net/tso.h @@ -3,6 +3,8 @@ #include +#define TSO_HEADER_SIZE 128 + struct tso_t { int next_frag_idx; void *data; -- cgit v1.2.3 From 85affd7e29e78dbf39f6b1e3a3ddf8432168e231 Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Wed, 23 Aug 2017 09:46:55 +0200 Subject: net: mvpp2: unify the txq size define use The txq size is defined by MVPP2_AGGR_TXQ_SIZE, which is sometime not used directly but through variables. As it is a fixed value use the define everywhere in the driver. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 02c62cbbfe51..eee878809c08 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5284,15 +5284,14 @@ static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause, /* Allocate and initialize descriptors for aggr TXQ */ static int mvpp2_aggr_txq_init(struct platform_device *pdev, - struct mvpp2_tx_queue *aggr_txq, - int desc_num, int cpu, + struct mvpp2_tx_queue *aggr_txq, int cpu, struct mvpp2 *priv) { u32 txq_dma; /* Allocate memory for TX descriptors */ aggr_txq->descs = dma_alloc_coherent(&pdev->dev, - desc_num * MVPP2_DESC_ALIGNED_SIZE, + MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE, &aggr_txq->descs_dma, GFP_KERNEL); if (!aggr_txq->descs) return -ENOMEM; @@ -5313,7 +5312,8 @@ static int mvpp2_aggr_txq_init(struct platform_device *pdev, MVPP22_AGGR_TXQ_DESC_ADDR_OFFS; mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu), txq_dma); - mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), desc_num); + mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), + MVPP2_AGGR_TXQ_SIZE); return 0; } @@ -7445,8 +7445,7 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) for_each_present_cpu(i) { priv->aggr_txqs[i].id = i; priv->aggr_txqs[i].size = MVPP2_AGGR_TXQ_SIZE; - err = mvpp2_aggr_txq_init(pdev, &priv->aggr_txqs[i], - MVPP2_AGGR_TXQ_SIZE, i, priv); + err = mvpp2_aggr_txq_init(pdev, &priv->aggr_txqs[i], i, priv); if (err < 0) return err; } -- cgit v1.2.3 From 186cd4d4e4144803652212eb0b7413141469feee Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Wed, 23 Aug 2017 09:46:56 +0200 Subject: net: mvpp2: software tso support The patch uses the tso API to implement the tso functionality in Marvell PPv2 driver. Using iperf and 10G ports, using TSO shows a significant performance improvement by a factor 2 to reach around 9.5Gbps in TX; as well as a significant CPU usage drop (from 25% to 15%). Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 171 ++++++++++++++++++++++++++++++++--- 1 file changed, 157 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index eee878809c08..7fa251bf91ae 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -35,6 +35,7 @@ #include #include #include +#include /* RX Fifo Registers */ #define MVPP2_RX_DATA_FIFO_SIZE_REG(port) (0x00 + 4 * (port)) @@ -1010,6 +1011,10 @@ struct mvpp2_txq_pcpu { /* Index of the TX DMA descriptor to be cleaned up */ int txq_get_index; + + /* DMA buffer for TSO headers */ + char *tso_headers; + dma_addr_t tso_headers_dma; }; struct mvpp2_tx_queue { @@ -5494,6 +5499,14 @@ static int mvpp2_txq_init(struct mvpp2_port *port, txq_pcpu->reserved_num = 0; txq_pcpu->txq_put_index = 0; txq_pcpu->txq_get_index = 0; + + txq_pcpu->tso_headers = + dma_alloc_coherent(port->dev->dev.parent, + MVPP2_AGGR_TXQ_SIZE * TSO_HEADER_SIZE, + &txq_pcpu->tso_headers_dma, + GFP_KERNEL); + if (!txq_pcpu->tso_headers) + goto cleanup; } return 0; @@ -5501,6 +5514,11 @@ cleanup: for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); kfree(txq_pcpu->buffs); + + dma_free_coherent(port->dev->dev.parent, + MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE, + txq_pcpu->tso_headers, + txq_pcpu->tso_headers_dma); } dma_free_coherent(port->dev->dev.parent, @@ -5520,6 +5538,11 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port, for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); kfree(txq_pcpu->buffs); + + dma_free_coherent(port->dev->dev.parent, + MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE, + txq_pcpu->tso_headers, + txq_pcpu->tso_headers_dma); } if (txq->descs) @@ -6049,6 +6072,123 @@ cleanup: return -ENOMEM; } +static inline void mvpp2_tso_put_hdr(struct sk_buff *skb, + struct net_device *dev, + struct mvpp2_tx_queue *txq, + struct mvpp2_tx_queue *aggr_txq, + struct mvpp2_txq_pcpu *txq_pcpu, + int hdr_sz) +{ + struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq); + dma_addr_t addr; + + mvpp2_txdesc_txq_set(port, tx_desc, txq->id); + mvpp2_txdesc_size_set(port, tx_desc, hdr_sz); + + addr = txq_pcpu->tso_headers_dma + + txq_pcpu->txq_put_index * TSO_HEADER_SIZE; + mvpp2_txdesc_offset_set(port, tx_desc, addr & MVPP2_TX_DESC_ALIGN); + mvpp2_txdesc_dma_addr_set(port, tx_desc, addr & ~MVPP2_TX_DESC_ALIGN); + + mvpp2_txdesc_cmd_set(port, tx_desc, mvpp2_skb_tx_csum(port, skb) | + MVPP2_TXD_F_DESC | + MVPP2_TXD_PADDING_DISABLE); + mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); +} + +static inline int mvpp2_tso_put_data(struct sk_buff *skb, + struct net_device *dev, struct tso_t *tso, + struct mvpp2_tx_queue *txq, + struct mvpp2_tx_queue *aggr_txq, + struct mvpp2_txq_pcpu *txq_pcpu, + int sz, bool left, bool last) +{ + struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq); + dma_addr_t buf_dma_addr; + + mvpp2_txdesc_txq_set(port, tx_desc, txq->id); + mvpp2_txdesc_size_set(port, tx_desc, sz); + + buf_dma_addr = dma_map_single(dev->dev.parent, tso->data, sz, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dev.parent, buf_dma_addr))) { + mvpp2_txq_desc_put(txq); + return -ENOMEM; + } + + mvpp2_txdesc_offset_set(port, tx_desc, + buf_dma_addr & MVPP2_TX_DESC_ALIGN); + mvpp2_txdesc_dma_addr_set(port, tx_desc, + buf_dma_addr & ~MVPP2_TX_DESC_ALIGN); + + if (!left) { + mvpp2_txdesc_cmd_set(port, tx_desc, MVPP2_TXD_L_DESC); + if (last) { + mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc); + return 0; + } + } else { + mvpp2_txdesc_cmd_set(port, tx_desc, 0); + } + + mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); + return 0; +} + +static int mvpp2_tx_tso(struct sk_buff *skb, struct net_device *dev, + struct mvpp2_tx_queue *txq, + struct mvpp2_tx_queue *aggr_txq, + struct mvpp2_txq_pcpu *txq_pcpu) +{ + struct mvpp2_port *port = netdev_priv(dev); + struct tso_t tso; + int hdr_sz = skb_transport_offset(skb) + tcp_hdrlen(skb); + int i, len, descs = 0; + + /* Check number of available descriptors */ + if (mvpp2_aggr_desc_num_check(port->priv, aggr_txq, + tso_count_descs(skb)) || + mvpp2_txq_reserved_desc_num_proc(port->priv, txq, txq_pcpu, + tso_count_descs(skb))) + return 0; + + tso_start(skb, &tso); + len = skb->len - hdr_sz; + while (len > 0) { + int left = min_t(int, skb_shinfo(skb)->gso_size, len); + char *hdr = txq_pcpu->tso_headers + + txq_pcpu->txq_put_index * TSO_HEADER_SIZE; + + len -= left; + descs++; + + tso_build_hdr(skb, hdr, &tso, left, len == 0); + mvpp2_tso_put_hdr(skb, dev, txq, aggr_txq, txq_pcpu, hdr_sz); + + while (left > 0) { + int sz = min_t(int, tso.size, left); + left -= sz; + descs++; + + if (mvpp2_tso_put_data(skb, dev, &tso, txq, aggr_txq, + txq_pcpu, sz, left, len == 0)) + goto release; + tso_build_data(skb, &tso, sz); + } + } + + return descs; + +release: + for (i = descs - 1; i >= 0; i--) { + struct mvpp2_tx_desc *tx_desc = txq->descs + i; + tx_desc_unmap_put(port, txq, tx_desc); + } + return 0; +} + /* Main tx processing */ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) { @@ -6066,6 +6206,10 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) txq_pcpu = this_cpu_ptr(txq->pcpu); aggr_txq = &port->priv->aggr_txqs[smp_processor_id()]; + if (skb_is_gso(skb)) { + frags = mvpp2_tx_tso(skb, dev, txq, aggr_txq, txq_pcpu); + goto out; + } frags = skb_shinfo(skb)->nr_frags + 1; /* Check number of available descriptors */ @@ -6115,22 +6259,21 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) } } - txq_pcpu->reserved_num -= frags; - txq_pcpu->count += frags; - aggr_txq->count += frags; - - /* Enable transmit */ - wmb(); - mvpp2_aggr_txq_pend_desc_add(port, frags); - - if (txq_pcpu->size - txq_pcpu->count < MAX_SKB_FRAGS + 1) { - struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id); - - netif_tx_stop_queue(nq); - } out: if (frags > 0) { struct mvpp2_pcpu_stats *stats = this_cpu_ptr(port->stats); + struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id); + + txq_pcpu->reserved_num -= frags; + txq_pcpu->count += frags; + aggr_txq->count += frags; + + /* Enable transmit */ + wmb(); + mvpp2_aggr_txq_pend_desc_add(port, frags); + + if (txq_pcpu->size - txq_pcpu->count < MAX_SKB_FRAGS + 1) + netif_tx_stop_queue(nq); u64_stats_update_begin(&stats->syncp); stats->tx_packets++; @@ -7255,7 +7398,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, } } - features = NETIF_F_SG | NETIF_F_IP_CSUM; + features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; dev->features = features | NETIF_F_RXCSUM; dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO; dev->vlan_features |= features; -- cgit v1.2.3 From 45b62742df6a27fba71f799cd1a2f721e1c0d584 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 23 Aug 2017 10:08:18 +0200 Subject: mlxsw: spectrum: Offload multichain TC rules Reflect chain index coming down from TC core and create a ruleset per chain. Note that only chain 0, being the implicit chain, is bound to the device for processing. The rest of chains have to be "jumped-to" by actions. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 3 --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 4 ++-- drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 28 +++++++++++++++------- .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 5 ++-- 4 files changed, 25 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 90a95cdc1626..6e641db16702 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1733,9 +1733,6 @@ mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, else return -EOPNOTSUPP; - if (f->common.chain_index) - return -EOPNOTSUPP; - switch (f->command) { case TC_CLSFLOWER_REPLACE: return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, f); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 8452d1db2f3f..de3aef971e2d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -440,8 +440,8 @@ struct mlxsw_sp_acl_ruleset; /* spectrum_acl.c */ struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl); struct mlxsw_sp_acl_ruleset * -mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, - struct net_device *dev, bool ingress, +mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, + bool ingress, u32 chain_index, enum mlxsw_sp_acl_profile profile); void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 508b5fcacd77..8ab331bdff02 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -74,6 +74,7 @@ struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl) struct mlxsw_sp_acl_ruleset_ht_key { struct net_device *dev; /* dev this ruleset is bound to */ bool ingress; + u32 chain_index; const struct mlxsw_sp_acl_profile_ops *ops; }; @@ -163,7 +164,8 @@ static void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset, - struct net_device *dev, bool ingress) + struct net_device *dev, bool ingress, + u32 chain_index) { const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; struct mlxsw_sp_acl *acl = mlxsw_sp->acl; @@ -171,13 +173,20 @@ static int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp, ruleset->ht_key.dev = dev; ruleset->ht_key.ingress = ingress; + ruleset->ht_key.chain_index = chain_index; err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node, mlxsw_sp_acl_ruleset_ht_params); if (err) return err; - err = ops->ruleset_bind(mlxsw_sp, ruleset->priv, dev, ingress); - if (err) - goto err_ops_ruleset_bind; + if (!ruleset->ht_key.chain_index) { + /* We only need ruleset with chain index 0, the implicit one, + * to be directly bound to device. The rest of the rulesets + * are bound by "Goto action set". + */ + err = ops->ruleset_bind(mlxsw_sp, ruleset->priv, dev, ingress); + if (err) + goto err_ops_ruleset_bind; + } return 0; err_ops_ruleset_bind: @@ -192,7 +201,8 @@ static void mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp, const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; struct mlxsw_sp_acl *acl = mlxsw_sp->acl; - ops->ruleset_unbind(mlxsw_sp, ruleset->priv); + if (!ruleset->ht_key.chain_index) + ops->ruleset_unbind(mlxsw_sp, ruleset->priv); rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node, mlxsw_sp_acl_ruleset_ht_params); } @@ -212,8 +222,8 @@ static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp, } struct mlxsw_sp_acl_ruleset * -mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, - struct net_device *dev, bool ingress, +mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, + bool ingress, u32 chain_index, enum mlxsw_sp_acl_profile profile) { const struct mlxsw_sp_acl_profile_ops *ops; @@ -229,6 +239,7 @@ mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, memset(&ht_key, 0, sizeof(ht_key)); ht_key.dev = dev; ht_key.ingress = ingress; + ht_key.chain_index = chain_index; ht_key.ops = ops; ruleset = rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key, mlxsw_sp_acl_ruleset_ht_params); @@ -239,7 +250,8 @@ mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, ruleset = mlxsw_sp_acl_ruleset_create(mlxsw_sp, ops); if (IS_ERR(ruleset)) return ruleset; - err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, ruleset, dev, ingress); + err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, ruleset, dev, + ingress, chain_index); if (err) goto err_ruleset_bind; return ruleset; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 95428b41c50f..34872aa52073 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -378,6 +378,7 @@ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, int err; ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, dev, ingress, + f->common.chain_index, MLXSW_SP_ACL_PROFILE_FLOWER); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); @@ -421,7 +422,7 @@ void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, struct mlxsw_sp_acl_rule *rule; ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, mlxsw_sp_port->dev, - ingress, + ingress, f->common.chain_index, MLXSW_SP_ACL_PROFILE_FLOWER); if (IS_ERR(ruleset)) return; @@ -447,7 +448,7 @@ int mlxsw_sp_flower_stats(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, int err; ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, mlxsw_sp_port->dev, - ingress, + ingress, f->common.chain_index, MLXSW_SP_ACL_PROFILE_FLOWER); if (WARN_ON(IS_ERR(ruleset))) return -EINVAL; -- cgit v1.2.3 From 0ade3b6457b5535548c2bdad44b5bde6ebd1b7e2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 23 Aug 2017 10:08:20 +0200 Subject: mlxsw: spectrum_acl: Allow to get group_id value for a ruleset For goto_chain action we need to know group_id of a ruleset to jump to. Provide infrastructure in order to get it. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 ++ drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 7 +++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c | 15 +++++++++++++++ 3 files changed, 24 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index de3aef971e2d..1866f692d3cd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -417,6 +417,7 @@ struct mlxsw_sp_acl_profile_ops { int (*ruleset_bind)(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv, struct net_device *dev, bool ingress); void (*ruleset_unbind)(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv); + u16 (*ruleset_group_id)(void *ruleset_priv); size_t rule_priv_size; int (*rule_add)(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv, void *rule_priv, @@ -445,6 +446,7 @@ mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, enum mlxsw_sp_acl_profile profile); void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset); +u16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset); struct mlxsw_sp_acl_rule_info * mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 8ab331bdff02..ef86f044295a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -267,6 +267,13 @@ void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset); } +u16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset) +{ + const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; + + return ops->ruleset_group_id(ruleset->priv); +} + static int mlxsw_sp_acl_rulei_counter_alloc(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c index bc5173f1b5c1..50b40de1fb91 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c @@ -295,6 +295,12 @@ mlxsw_sp_acl_tcam_group_unbind(struct mlxsw_sp *mlxsw_sp, mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl); } +static u16 +mlxsw_sp_acl_tcam_group_id(struct mlxsw_sp_acl_tcam_group *group) +{ + return group->id; +} + static unsigned int mlxsw_sp_acl_tcam_region_prio(struct mlxsw_sp_acl_tcam_region *region) { @@ -1063,6 +1069,14 @@ mlxsw_sp_acl_tcam_flower_ruleset_unbind(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_acl_tcam_group_unbind(mlxsw_sp, &ruleset->group); } +static u16 +mlxsw_sp_acl_tcam_flower_ruleset_group_id(void *ruleset_priv) +{ + struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv; + + return mlxsw_sp_acl_tcam_group_id(&ruleset->group); +} + static int mlxsw_sp_acl_tcam_flower_rule_add(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv, void *rule_priv, @@ -1099,6 +1113,7 @@ static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = { .ruleset_del = mlxsw_sp_acl_tcam_flower_ruleset_del, .ruleset_bind = mlxsw_sp_acl_tcam_flower_ruleset_bind, .ruleset_unbind = mlxsw_sp_acl_tcam_flower_ruleset_unbind, + .ruleset_group_id = mlxsw_sp_acl_tcam_flower_ruleset_group_id, .rule_priv_size = sizeof(struct mlxsw_sp_acl_tcam_flower_rule), .rule_add = mlxsw_sp_acl_tcam_flower_rule_add, .rule_del = mlxsw_sp_acl_tcam_flower_rule_del, -- cgit v1.2.3 From dbec8ee95ab3ed231cba59f452531d6ce01fe24f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 23 Aug 2017 10:08:21 +0200 Subject: mlxsw: spectrum_acl: Provide helper to lookup ruleset We need to lookup ruleset in order to offload goto_chain termination action. This patch adds it. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 4 ++ drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 45 ++++++++++++++++++---- 2 files changed, 41 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 1866f692d3cd..f8c7f7e930c5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -441,6 +441,10 @@ struct mlxsw_sp_acl_ruleset; /* spectrum_acl.c */ struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl); struct mlxsw_sp_acl_ruleset * +mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, + bool ingress, u32 chain_index, + enum mlxsw_sp_acl_profile profile); +struct mlxsw_sp_acl_ruleset * mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, bool ingress, u32 chain_index, enum mlxsw_sp_acl_profile profile); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index ef86f044295a..4b2455e3e079 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -221,6 +221,41 @@ static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset); } +static struct mlxsw_sp_acl_ruleset * +__mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp_acl *acl, struct net_device *dev, + bool ingress, u32 chain_index, + const struct mlxsw_sp_acl_profile_ops *ops) +{ + struct mlxsw_sp_acl_ruleset_ht_key ht_key; + + memset(&ht_key, 0, sizeof(ht_key)); + ht_key.dev = dev; + ht_key.ingress = ingress; + ht_key.chain_index = chain_index; + ht_key.ops = ops; + return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key, + mlxsw_sp_acl_ruleset_ht_params); +} + +struct mlxsw_sp_acl_ruleset * +mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, + bool ingress, u32 chain_index, + enum mlxsw_sp_acl_profile profile) +{ + const struct mlxsw_sp_acl_profile_ops *ops; + struct mlxsw_sp_acl *acl = mlxsw_sp->acl; + struct mlxsw_sp_acl_ruleset *ruleset; + + ops = acl->ops->profile_ops(mlxsw_sp, profile); + if (!ops) + return ERR_PTR(-EINVAL); + ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, dev, ingress, + chain_index, ops); + if (!ruleset) + return ERR_PTR(-ENOENT); + return ruleset; +} + struct mlxsw_sp_acl_ruleset * mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, bool ingress, u32 chain_index, @@ -228,7 +263,6 @@ mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, { const struct mlxsw_sp_acl_profile_ops *ops; struct mlxsw_sp_acl *acl = mlxsw_sp->acl; - struct mlxsw_sp_acl_ruleset_ht_key ht_key; struct mlxsw_sp_acl_ruleset *ruleset; int err; @@ -236,13 +270,8 @@ mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, if (!ops) return ERR_PTR(-EINVAL); - memset(&ht_key, 0, sizeof(ht_key)); - ht_key.dev = dev; - ht_key.ingress = ingress; - ht_key.chain_index = chain_index; - ht_key.ops = ops; - ruleset = rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key, - mlxsw_sp_acl_ruleset_ht_params); + ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, dev, ingress, + chain_index, ops); if (ruleset) { mlxsw_sp_acl_ruleset_ref_inc(ruleset); return ruleset; -- cgit v1.2.3 From 0ede6ba2a1de08a2a9a5c5ab78e31f1e6a333ed2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 23 Aug 2017 10:08:22 +0200 Subject: mlxsw: spectrum_flower: Offload goto_chain termination action If action is gact goto_chain, offload it to HW by jumping to another ruleset. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 23 ++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 34872aa52073..8aace9a06a5d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -45,7 +45,7 @@ #include "core_acl_flex_keys.h" static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, - struct net_device *dev, + struct net_device *dev, bool ingress, struct mlxsw_sp_acl_rule_info *rulei, struct tcf_exts *exts) { @@ -71,6 +71,20 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, err = mlxsw_sp_acl_rulei_act_trap(rulei); if (err) return err; + } else if (is_tcf_gact_goto_chain(a)) { + u32 chain_index = tcf_gact_goto_chain_index(a); + struct mlxsw_sp_acl_ruleset *ruleset; + u16 group_id; + + ruleset = mlxsw_sp_acl_ruleset_lookup(mlxsw_sp, dev, + ingress, + chain_index, + MLXSW_SP_ACL_PROFILE_FLOWER); + if (IS_ERR(ruleset)) + return PTR_ERR(ruleset); + + group_id = mlxsw_sp_acl_ruleset_group_id(ruleset); + mlxsw_sp_acl_rulei_act_jump(rulei, group_id); } else if (is_tcf_mirred_egress_redirect(a)) { int ifindex = tcf_mirred_ifindex(a); struct net_device *out_dev; @@ -246,7 +260,7 @@ static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp, } static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, - struct net_device *dev, + struct net_device *dev, bool ingress, struct mlxsw_sp_acl_rule_info *rulei, struct tc_cls_flower_offload *f) { @@ -364,7 +378,8 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, if (err) return err; - return mlxsw_sp_flower_parse_actions(mlxsw_sp, dev, rulei, f->exts); + return mlxsw_sp_flower_parse_actions(mlxsw_sp, dev, ingress, + rulei, f->exts); } int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, @@ -390,7 +405,7 @@ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, } rulei = mlxsw_sp_acl_rule_rulei(rule); - err = mlxsw_sp_flower_parse(mlxsw_sp, dev, rulei, f); + err = mlxsw_sp_flower_parse(mlxsw_sp, dev, ingress, rulei, f); if (err) goto err_flower_parse; -- cgit v1.2.3 From 5719e5eb31e485d9eac8e2303ef9ca2f42c49225 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 23 Aug 2017 10:59:40 +0100 Subject: net: hinic: make functions set_ctrl0 and set_ctrl1 static The functions set_ctrl0 and set_ctrl1 are local to the source and do not need to be in global scope, so make them static. Cleans up sparse warnings: symbol 'set_ctrl0' was not declared. Should it be static? symbol 'set_ctrl1' was not declared. Should it be static? Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c index cd09e6ef3aea..7cb8b9b94726 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c @@ -423,7 +423,7 @@ static irqreturn_t ceq_interrupt(int irq, void *data) return IRQ_HANDLED; } -void set_ctrl0(struct hinic_eq *eq) +static void set_ctrl0(struct hinic_eq *eq) { struct msix_entry *msix_entry = &eq->msix_entry; enum hinic_eq_type type = eq->type; @@ -474,7 +474,7 @@ void set_ctrl0(struct hinic_eq *eq) } } -void set_ctrl1(struct hinic_eq *eq) +static void set_ctrl1(struct hinic_eq *eq) { enum hinic_eq_type type = eq->type; u32 page_size_val, elem_size; -- cgit v1.2.3 From d382b9c007827987c7939e2bd828af43c7af9668 Mon Sep 17 00:00:00 2001 From: "Reizer, Eyal" Date: Sun, 20 Aug 2017 12:28:18 +0000 Subject: wlcore: add missing nvs file name info for wilink8 The following commits: commit c815fdebef44 ("wlcore: spi: Populate config firmware data") commit d776fc86b82f ("wlcore: sdio: Populate config firmware data") Populated the nvs entry for wilink6 and wilink7 only while it is still needed for wilink8 as well. This broke user space backward compatibility when upgrading from older kernels, as the alternate mac address would not be read from the nvs that is present in the file system (lib/firmware/ti-connectivity/wl1271-nvs.bin) causing mac address change of the wlan interface. This patch fix this and update the structure field with the same default nvs file name that has been used before. In addition, some distros hold a default wl1271-nvs.bin in the file system with a bogus mac address (deadbeef...) that overrides the mac address that is stored inside the device. Warn users about this bogus mac address and use the internal mac address Fixes: c815fdebef44 ("wlcore: spi: Populate config firmware data") Fixes: d776fc86b82f ("wlcore: sdio: Populate config firmware data") Signed-off-by: Eyal Reizer Reviewed-by: Sebastian Reichel Tested-by: Tony Lindgren Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wlcore/main.c | 23 +++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/sdio.c | 1 + drivers/net/wireless/ti/wlcore/spi.c | 1 + drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++ 4 files changed, 28 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 60aaa850fbd1..c346c021b999 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -6016,6 +6016,8 @@ static int wl1271_register_hw(struct wl1271 *wl) { int ret; u32 oui_addr = 0, nic_addr = 0; + struct platform_device *pdev = wl->pdev; + struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev); if (wl->mac80211_registered) return 0; @@ -6040,6 +6042,27 @@ static int wl1271_register_hw(struct wl1271 *wl) nic_addr = wl->fuse_nic_addr + 1; } + if (oui_addr == 0xdeadbe && nic_addr == 0xef0000) { + wl1271_warning("Detected unconfigured mac address in nvs, derive from fuse instead.\n"); + if (!strcmp(pdev_data->family->name, "wl18xx")) { + wl1271_warning("This default nvs file can be removed from the file system\n"); + } else { + wl1271_warning("Your device performance is not optimized.\n"); + wl1271_warning("Please use the calibrator tool to configure your device.\n"); + } + + if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) { + wl1271_warning("Fuse mac address is zero. using random mac\n"); + /* Use TI oui and a random nic */ + oui_addr = WLCORE_TI_OUI_ADDRESS; + nic_addr = get_random_int(); + } else { + oui_addr = wl->fuse_oui_addr; + /* fuse has the BD_ADDR, the WLAN addresses are the next two */ + nic_addr = wl->fuse_nic_addr + 1; + } + } + wl12xx_derive_mac_addresses(wl, oui_addr, nic_addr); ret = ieee80211_register_hw(wl->hw); diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index 2fb38717346f..f8a1fea64e25 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -230,6 +230,7 @@ static const struct wilink_family_data wl128x_data = { static const struct wilink_family_data wl18xx_data = { .name = "wl18xx", .cfg_name = "ti-connectivity/wl18xx-conf.bin", + .nvs_name = "ti-connectivity/wl1271-nvs.bin", }; static const struct of_device_id wlcore_sdio_of_match_table[] = { diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index fdabb9242cca..62ce54a949e9 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -92,6 +92,7 @@ static const struct wilink_family_data wl128x_data = { static const struct wilink_family_data wl18xx_data = { .name = "wl18xx", .cfg_name = "ti-connectivity/wl18xx-conf.bin", + .nvs_name = "ti-connectivity/wl1271-nvs.bin", }; struct wl12xx_spi_glue { diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 1827546ba807..95fbedc8ea34 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -40,6 +40,9 @@ /* wl12xx/wl18xx maximum transmission power (in dBm) */ #define WLCORE_MAX_TXPWR 25 +/* Texas Instruments pre assigned OUI */ +#define WLCORE_TI_OUI_ADDRESS 0x080028 + /* forward declaration */ struct wl1271_tx_hw_descr; enum wl_rx_buf_align; -- cgit v1.2.3 From 14cc696c0a4aac823e41b9c29d04d1936cbf969c Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 17 Aug 2017 19:22:19 -0500 Subject: rtlwifi: btcoex: 23b 1ant: fix duplicated code for different branches Refactor code in order to avoid identical code for different branches. This issue was detected with the help of Coccinelle. Addresses-Coverity-ID: 1415177 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c index 03998d2e9eb8..c04425236ce4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c @@ -600,14 +600,8 @@ static void halbtc8723b1ant_coex_table_with_type(struct btc_coexist *btcoexist, 0xffffff, 0x3); break; case 5: - if ((coex_sta->cck_ever_lock) && (coex_sta->scan_ap_num <= 5)) - halbtc8723b1ant_coex_table(btcoexist, force_exec, - 0x5a5a5a5a, 0x5aaa5a5a, - 0xffffff, 0x3); - else - halbtc8723b1ant_coex_table(btcoexist, force_exec, - 0x5a5a5a5a, 0x5aaa5a5a, - 0xffffff, 0x3); + halbtc8723b1ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a, + 0x5aaa5a5a, 0xffffff, 0x3); break; case 6: halbtc8723b1ant_coex_table(btcoexist, force_exec, 0x55555555, -- cgit v1.2.3 From d9ee6015e573176e599693b6d0125ad74b210623 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 18 Aug 2017 11:05:19 +0300 Subject: rtlwifi: make a couple arrays larger This is a static checker fix. "cal_num" is 10. We're declaring the tx_dt[] and rx_td[] arrays as 3 element arrays. The static checker complains that we do: tx_dt[cal] = (vdf_y[1]>>20)-(vdf_y[0]>>20); "cal" is the iterator and it is in the 0-9 range so it looks like we could corrupt memory. Signed-off-by: Dan Carpenter Acked-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c index aa3ccc740521..176deb2b5386 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c @@ -3773,10 +3773,11 @@ static void _rtl8821ae_iqk_tx(struct ieee80211_hw *hw, enum radio_path path) u32 tx_fail, rx_fail, delay_count, iqk_ready, cal_retry, cal = 0, temp_reg65; int tx_x = 0, tx_y = 0, rx_x = 0, rx_y = 0, tx_average = 0, rx_average = 0; int tx_x0[cal_num], tx_y0[cal_num], tx_x0_rxk[cal_num], - tx_y0_rxk[cal_num], rx_x0[cal_num], rx_y0[cal_num]; + tx_y0_rxk[cal_num], rx_x0[cal_num], rx_y0[cal_num], + tx_dt[cal_num], rx_dt[cal_num]; bool tx0iqkok = false, rx0iqkok = false; bool vdf_enable = false; - int i, k, vdf_y[3], vdf_x[3], tx_dt[3], rx_dt[3], + int i, k, vdf_y[3], vdf_x[3], ii, dx = 0, dy = 0, tx_finish = 0, rx_finish = 0; RT_TRACE(rtlpriv, COMP_IQK, DBG_LOUD, -- cgit v1.2.3 From b3b2f62c6238808f74e00767a3224f9be54d606f Mon Sep 17 00:00:00 2001 From: Christophe Jaillet Date: Tue, 22 Aug 2017 00:06:17 +0200 Subject: mt7601u: check memory allocation failure Check memory allocation failure and return -ENOMEM in such a case, as already done a few lines below. As 'dev->tx_q' can be NULL, we also need to check for that in 'mt7601u_free_tx()', and return early. Signed-off-by: Christophe JAILLET Acked-by: Jakub Kicinski Signed-off-by: Kalle Valo --- drivers/net/wireless/mediatek/mt7601u/dma.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c index 660267b359e4..7f3e3983b781 100644 --- a/drivers/net/wireless/mediatek/mt7601u/dma.c +++ b/drivers/net/wireless/mediatek/mt7601u/dma.c @@ -457,6 +457,9 @@ static void mt7601u_free_tx(struct mt7601u_dev *dev) { int i; + if (!dev->tx_q) + return; + for (i = 0; i < __MT_EP_OUT_MAX; i++) mt7601u_free_tx_queue(&dev->tx_q[i]); } @@ -484,6 +487,8 @@ static int mt7601u_alloc_tx(struct mt7601u_dev *dev) dev->tx_q = devm_kcalloc(dev->dev, __MT_EP_OUT_MAX, sizeof(*dev->tx_q), GFP_KERNEL); + if (!dev->tx_q) + return -ENOMEM; for (i = 0; i < __MT_EP_OUT_MAX; i++) if (mt7601u_alloc_tx_queue(dev, &dev->tx_q[i])) -- cgit v1.2.3 From 2cc8918eb987998ef55a2d961f7b86c13dcf425d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 22 Aug 2017 13:59:04 +0100 Subject: rtlwifi: rtl8821ae: fix spelling mistake: "faill" -> "failed" Trivial fix to spelling mistake in RT_TRACE message Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c index b84b4fa7b71c..f2b2c549e5b2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c @@ -98,7 +98,7 @@ static int _rtl8821ae_fw_free_to_go(struct ieee80211_hw *hw) if (counter >= FW_8821AE_POLLING_TIMEOUT_COUNT) { RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD, - "chksum report faill ! REG_MCUFWDL:0x%08x .\n", + "chksum report fail! REG_MCUFWDL:0x%08x .\n", value32); goto exit; } -- cgit v1.2.3 From 28b75415ad19fef232d8daab4d5de17d753f0b36 Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Wed, 23 Aug 2017 09:16:51 +0200 Subject: wireless: ipw2200: Replace PCI pool old API The PCI pool API is deprecated. This commit replaces the PCI pool old API by the appropriate function with the DMA pool API. Signed-off-by: Romain Perier Reviewed-by: Peter Senna Tschudin Acked-by: Stanislav Yakovlev Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/ipw2x00/ipw2200.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index c311b1a994c1..c57c567add4d 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -3209,7 +3209,7 @@ static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len) struct fw_chunk *chunk; int total_nr = 0; int i; - struct pci_pool *pool; + struct dma_pool *pool; void **virts; dma_addr_t *phys; @@ -3226,9 +3226,10 @@ static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len) kfree(virts); return -ENOMEM; } - pool = pci_pool_create("ipw2200", priv->pci_dev, CB_MAX_LENGTH, 0, 0); + pool = dma_pool_create("ipw2200", &priv->pci_dev->dev, CB_MAX_LENGTH, 0, + 0); if (!pool) { - IPW_ERROR("pci_pool_create failed\n"); + IPW_ERROR("dma_pool_create failed\n"); kfree(phys); kfree(virts); return -ENOMEM; @@ -3253,7 +3254,7 @@ static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len) nr = (chunk_len + CB_MAX_LENGTH - 1) / CB_MAX_LENGTH; for (i = 0; i < nr; i++) { - virts[total_nr] = pci_pool_alloc(pool, GFP_KERNEL, + virts[total_nr] = dma_pool_alloc(pool, GFP_KERNEL, &phys[total_nr]); if (!virts[total_nr]) { ret = -ENOMEM; @@ -3297,9 +3298,9 @@ static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len) } out: for (i = 0; i < total_nr; i++) - pci_pool_free(pool, virts[i], phys[i]); + dma_pool_free(pool, virts[i], phys[i]); - pci_pool_destroy(pool); + dma_pool_destroy(pool); kfree(phys); kfree(virts); -- cgit v1.2.3 From 9ea792a48cdd9229c9dd698f68dd4ee831bd8644 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 23 Aug 2017 15:29:34 +0530 Subject: net: rsi: mac80211: constify ieee80211_ops ieee80211_ops are not supposed to change at runtime. All functions working with ieee80211_ops provided by work with const ieee80211_ops. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 8b983d03f2da..e24c8b36b157 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -1503,7 +1503,7 @@ static void rsi_mac80211_rfkill_poll(struct ieee80211_hw *hw) mutex_unlock(&common->mutex); } -static struct ieee80211_ops mac80211_ops = { +static const struct ieee80211_ops mac80211_ops = { .tx = rsi_mac80211_tx, .start = rsi_mac80211_start, .stop = rsi_mac80211_stop, -- cgit v1.2.3 From 2c1dca3c6b222a389c5dfebfcdd6fe3c87e5aa75 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 23 Aug 2017 18:06:42 +0530 Subject: wireless: ipw2x00: make iw_handler_def const Make these const as they are only stored in the const field of a net_device structure. Signed-off-by: Bhumika Goyal Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 4 ++-- drivers/net/wireless/intel/ipw2x00/ipw2200.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index 77f3f92b3c85..19c442cb93e4 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -340,7 +340,7 @@ static int ipw2100_ucode_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw); static void ipw2100_wx_event_work(struct work_struct *work); static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev); -static struct iw_handler_def ipw2100_wx_handler_def; +static const struct iw_handler_def ipw2100_wx_handler_def; static inline void read_register(struct net_device *dev, u32 reg, u32 * val) { @@ -8273,7 +8273,7 @@ static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev) return (struct iw_statistics *)NULL; } -static struct iw_handler_def ipw2100_wx_handler_def = { +static const struct iw_handler_def ipw2100_wx_handler_def = { .standard = ipw2100_wx_handlers, .num_standard = ARRAY_SIZE(ipw2100_wx_handlers), .num_private = ARRAY_SIZE(ipw2100_private_handler), diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index c57c567add4d..8da87496cb58 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -10009,7 +10009,7 @@ static iw_handler ipw_priv_handler[] = { #endif }; -static struct iw_handler_def ipw_wx_handler_def = { +static const struct iw_handler_def ipw_wx_handler_def = { .standard = ipw_wx_handlers, .num_standard = ARRAY_SIZE(ipw_wx_handlers), .num_private = ARRAY_SIZE(ipw_priv_handler), -- cgit v1.2.3 From 3c0045837d613984c7948775fa315beb68703b5c Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 24 Aug 2017 15:54:11 +0300 Subject: net/mlx5: Add a blank line after declarations V2 The blank line should be after u32 val = ... and not after __be32 __iomem *addr = ... Fixes: ad5b39a95c83 ("net/mlx5: Add a blank line after declarations") Signed-off-by: Saeed Mahameed Reported-by: Joe Perches --- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index a08027b8f3ce..02da96f98352 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -188,8 +188,8 @@ static enum mlx5_dev_event port_subtype_event(u8 subtype) static void eq_update_ci(struct mlx5_eq *eq, int arm) { __be32 __iomem *addr = eq->doorbell + (arm ? 0 : 2); - u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24); + __raw_writel((__force u32)cpu_to_be32(val), addr); /* We still want ordering, just not swabbing, so add a barrier */ mb(); -- cgit v1.2.3 From 09800305504432eb00b8504645c05ac589f1c02c Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Tue, 22 Aug 2017 14:22:15 +0300 Subject: net/mlx5e: Fix wrong code indentation in conditional statement Fix the following checkpatch warning in en_ethtool.c: WARNING: suspect code indent for conditional statements (8, 9) + for (i = 0; i < NUM_PCIE_PERF_STALL_COUNTERS(priv); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, Signed-off-by: Gal Pressman Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index c30cf6b4736f..0dd7e9caf150 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -254,8 +254,8 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, u8 *data) pcie_perf_stats_desc64[i].format); for (i = 0; i < NUM_PCIE_PERF_STALL_COUNTERS(priv); i++) - strcpy(data + (idx++) * ETH_GSTRING_LEN, - pcie_perf_stall_stats_desc[i].format); + strcpy(data + (idx++) * ETH_GSTRING_LEN, + pcie_perf_stall_stats_desc[i].format); for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++) -- cgit v1.2.3 From 8ebabaa02fe13e534ae5b0a8aa495762c25ef279 Mon Sep 17 00:00:00 2001 From: Matan Barak Date: Sun, 28 May 2017 10:58:40 +0300 Subject: net/mlx5: Convert linear search for free index to ida When allocating a flow table entry, we need to allocate a free index in the flow group. Currently, this is done by traversing the existing flow table entries in the flow group, until a free index is found. Replacing this by using a ida, which allows us to find a free index much faster. Signed-off-by: Matan Barak Reviewed-by: Maor Gottlieb Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 56 ++++++++--------------- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 2 +- 2 files changed, 20 insertions(+), 38 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index e8690fe46bf2..af245fcdba70 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -360,6 +360,7 @@ static void del_flow_table(struct fs_node *node) err = mlx5_cmd_destroy_flow_table(dev, ft); if (err) mlx5_core_warn(dev, "flow steering can't destroy ft\n"); + ida_destroy(&ft->fte_allocator); fs_get_obj(prio, ft->node.parent); prio->num_ft--; } @@ -437,8 +438,8 @@ static void del_fte(struct fs_node *node) "flow steering can't delete fte in index %d of flow group id %d\n", fte->index, fg->id); + ida_simple_remove(&ft->fte_allocator, fte->index); fte->status = 0; - fg->num_ftes--; } static void del_flow_group(struct fs_node *node) @@ -523,6 +524,7 @@ static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_ft ft->flags = flags; INIT_LIST_HEAD(&ft->fwd_rules); mutex_init(&ft->lock); + ida_init(&ft->fte_allocator); return ft; } @@ -839,6 +841,7 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa destroy_ft: mlx5_cmd_destroy_flow_table(root->dev, ft); free_ft: + ida_destroy(&ft->fte_allocator); kfree(ft); unlock_root: mutex_unlock(&root->chain_lock); @@ -1102,41 +1105,26 @@ free_handle: return ERR_PTR(err); } -/* Assumed fg is locked */ -static unsigned int get_free_fte_index(struct mlx5_flow_group *fg, - struct list_head **prev) -{ - struct fs_fte *fte; - unsigned int start = fg->start_index; - - if (prev) - *prev = &fg->node.children; - - /* assumed list is sorted by index */ - fs_for_each_fte(fte, fg) { - if (fte->index != start) - return start; - start++; - if (prev) - *prev = &fte->node.list; - } - - return start; -} - -/* prev is output, prev->next = new_fte */ static struct fs_fte *create_fte(struct mlx5_flow_group *fg, u32 *match_value, - struct mlx5_flow_act *flow_act, - struct list_head **prev) + struct mlx5_flow_act *flow_act) { + struct mlx5_flow_table *ft; struct fs_fte *fte; int index; - index = get_free_fte_index(fg, prev); + fs_get_obj(ft, fg->node.parent); + index = ida_simple_get(&ft->fte_allocator, fg->start_index, + fg->start_index + fg->max_ftes, + GFP_KERNEL); + if (index < 0) + return ERR_PTR(index); + fte = alloc_fte(flow_act, match_value, index); - if (IS_ERR(fte)) + if (IS_ERR(fte)) { + ida_simple_remove(&ft->fte_allocator, index); return fte; + } return fte; } @@ -1234,7 +1222,6 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, { struct mlx5_flow_handle *handle; struct mlx5_flow_table *ft; - struct list_head *prev; struct fs_fte *fte; int i; @@ -1267,12 +1254,8 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, unlock_ref_node(&fte->node); } fs_get_obj(ft, fg->node.parent); - if (fg->num_ftes >= fg->max_ftes) { - handle = ERR_PTR(-ENOSPC); - goto unlock_fg; - } - fte = create_fte(fg, match_value, flow_act, &prev); + fte = create_fte(fg, match_value, flow_act); if (IS_ERR(fte)) { handle = (void *)fte; goto unlock_fg; @@ -1286,10 +1269,9 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, goto unlock_fg; } - fg->num_ftes++; - tree_add_node(&fte->node, &fg->node); - list_add(&fte->node.list, prev); + /* fte list isn't sorted */ + list_add_tail(&fte->node.list, &fg->node.children); add_rules: for (i = 0; i < handle->num_rules; i++) { if (atomic_read(&handle->rule[i]->node.refcount) == 1) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 9fb5a333df52..5fbae885558f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -118,6 +118,7 @@ struct mlx5_flow_table { /* FWD rules that point on this flow table */ struct list_head fwd_rules; u32 flags; + struct ida fte_allocator; }; struct mlx5_fc_cache { @@ -183,7 +184,6 @@ struct mlx5_flow_group { struct mlx5_flow_group_mask mask; u32 start_index; u32 max_ftes; - u32 num_ftes; u32 id; }; -- cgit v1.2.3 From 667cb65ae5ad01523505d48d6cfd92bd1d3c9785 Mon Sep 17 00:00:00 2001 From: Matan Barak Date: Mon, 7 Aug 2017 11:14:11 +0300 Subject: net/mlx5: Don't store reserved part in FTEs and FGs The current code stores fte_match_param in the software representation of FTEs and FGs. fte_match_param contains a large reserved area at the bottom of the struct. Since downstream patches are going to hash this part, we would like to avoid doing so on a reserved part. Signed-off-by: Matan Barak Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 8 -------- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 16 ++++++++++++++-- include/linux/mlx5/device.h | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 16b32f31d691..e0d0efd903bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -263,7 +263,7 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev, MLX5_SET(flow_context, in_flow_context, modify_header_id, fte->modify_id); in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context, match_value); - memcpy(in_match_value, &fte->val, MLX5_ST_SZ_BYTES(fte_match_param)); + memcpy(in_match_value, &fte->val, sizeof(fte->val)); in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination); if (fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index af245fcdba70..986c5e50e872 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -371,21 +371,14 @@ static void del_rule(struct fs_node *node) struct mlx5_flow_table *ft; struct mlx5_flow_group *fg; struct fs_fte *fte; - u32 *match_value; int modify_mask; struct mlx5_core_dev *dev = get_dev(node); - int match_len = MLX5_ST_SZ_BYTES(fte_match_param); int err; bool update_fte = false; - match_value = kvzalloc(match_len, GFP_KERNEL); - if (!match_value) - return; - fs_get_obj(rule, node); fs_get_obj(fte, rule->node.parent); fs_get_obj(fg, fte->node.parent); - memcpy(match_value, fte->val, sizeof(fte->val)); fs_get_obj(ft, fg->node.parent); list_del(&rule->node.list); if (rule->sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) { @@ -415,7 +408,6 @@ out: "%s can't del rule fg id=%d fte_index=%d\n", __func__, fg->id, fte->index); } - kvfree(match_value); } static void del_fte(struct fs_node *node) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 5fbae885558f..bfbc081d17dc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -144,10 +144,22 @@ struct mlx5_fc { struct mlx5_fc_cache cache ____cacheline_aligned_in_smp; }; +#define MLX5_FTE_MATCH_PARAM_RESERVED reserved_at_600 +/* Calculate the fte_match_param length and without the reserved length. + * Make sure the reserved field is the last. + */ +#define MLX5_ST_SZ_DW_MATCH_PARAM \ + ((MLX5_BYTE_OFF(fte_match_param, MLX5_FTE_MATCH_PARAM_RESERVED) / sizeof(u32)) + \ + BUILD_BUG_ON_ZERO(MLX5_ST_SZ_BYTES(fte_match_param) != \ + MLX5_FLD_SZ_BYTES(fte_match_param, \ + MLX5_FTE_MATCH_PARAM_RESERVED) +\ + MLX5_BYTE_OFF(fte_match_param, \ + MLX5_FTE_MATCH_PARAM_RESERVED))) + /* Type of children is mlx5_flow_rule */ struct fs_fte { struct fs_node node; - u32 val[MLX5_ST_SZ_DW(fte_match_param)]; + u32 val[MLX5_ST_SZ_DW_MATCH_PARAM]; u32 dests_size; u32 flow_tag; u32 index; @@ -175,7 +187,7 @@ struct mlx5_flow_namespace { struct mlx5_flow_group_mask { u8 match_criteria_enable; - u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)]; + u32 match_criteria[MLX5_ST_SZ_DW_MATCH_PARAM]; }; /* Type of children is fs_fte */ diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index f31a0b5377e1..3c7442b56460 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -48,7 +48,7 @@ /* helper macros */ #define __mlx5_nullp(typ) ((struct mlx5_ifc_##typ##_bits *)0) #define __mlx5_bit_sz(typ, fld) sizeof(__mlx5_nullp(typ)->fld) -#define __mlx5_bit_off(typ, fld) ((unsigned)(unsigned long)(&(__mlx5_nullp(typ)->fld))) +#define __mlx5_bit_off(typ, fld) (offsetof(struct mlx5_ifc_##typ##_bits, fld)) #define __mlx5_dw_off(typ, fld) (__mlx5_bit_off(typ, fld) / 32) #define __mlx5_64_off(typ, fld) (__mlx5_bit_off(typ, fld) / 64) #define __mlx5_dw_bit_off(typ, fld) (32 - __mlx5_bit_sz(typ, fld) - (__mlx5_bit_off(typ, fld) & 0x1f)) -- cgit v1.2.3 From 0d235c3fabb79bddc46527885985f0ae281a89f9 Mon Sep 17 00:00:00 2001 From: Matan Barak Date: Sun, 28 May 2017 12:09:06 +0300 Subject: net/mlx5: Add hash table to search FTEs in a flow-group When adding a flow table entry (fte) to a flow group (fg), we first need to check whether this fte exist. In such a case we just merge the destinations (if possible). Currently, this is done by traversing the fte list available in a fg. This could take a lot of time when using large flow groups. Speeding this up by using rhashtable, which is much faster. Signed-off-by: Matan Barak Reviewed-by: Maor Gottlieb Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 228 +++++++++++++++------- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 3 + 2 files changed, 160 insertions(+), 71 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 986c5e50e872..d8d45b006996 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -150,6 +150,14 @@ enum fs_i_mutex_lock_class { FS_MUTEX_CHILD }; +static const struct rhashtable_params rhash_fte = { + .key_len = FIELD_SIZEOF(struct fs_fte, val), + .key_offset = offsetof(struct fs_fte, val), + .head_offset = offsetof(struct fs_fte, hash), + .automatic_shrinking = true, + .min_size = 1, +}; + static void del_rule(struct fs_node *node); static void del_flow_table(struct fs_node *node); static void del_flow_group(struct fs_node *node); @@ -255,63 +263,59 @@ static struct fs_prio *find_prio(struct mlx5_flow_namespace *ns, return NULL; } -static bool masked_memcmp(void *mask, void *val1, void *val2, size_t size) +static bool check_last_reserved(const u32 *match_criteria) { - unsigned int i; - - for (i = 0; i < size; i++, mask++, val1++, val2++) - if ((*((u8 *)val1) & (*(u8 *)mask)) != - ((*(u8 *)val2) & (*(u8 *)mask))) - return false; + char *match_criteria_reserved = + MLX5_ADDR_OF(fte_match_param, match_criteria, MLX5_FTE_MATCH_PARAM_RESERVED); - return true; + return !match_criteria_reserved[0] && + !memcmp(match_criteria_reserved, match_criteria_reserved + 1, + MLX5_FLD_SZ_BYTES(fte_match_param, + MLX5_FTE_MATCH_PARAM_RESERVED) - 1); } -static bool compare_match_value(struct mlx5_flow_group_mask *mask, - void *fte_param1, void *fte_param2) +static bool check_valid_mask(u8 match_criteria_enable, const u32 *match_criteria) { - if (mask->match_criteria_enable & - 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) { - void *fte_match1 = MLX5_ADDR_OF(fte_match_param, - fte_param1, outer_headers); - void *fte_match2 = MLX5_ADDR_OF(fte_match_param, - fte_param2, outer_headers); - void *fte_mask = MLX5_ADDR_OF(fte_match_param, - mask->match_criteria, outer_headers); + if (match_criteria_enable & ~( + (1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) | + (1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) | + (1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS))) + return false; - if (!masked_memcmp(fte_mask, fte_match1, fte_match2, - MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4))) + if (!(match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS)) { + char *fg_type_mask = MLX5_ADDR_OF(fte_match_param, + match_criteria, outer_headers); + + if (fg_type_mask[0] || + memcmp(fg_type_mask, fg_type_mask + 1, + MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4) - 1)) return false; } - if (mask->match_criteria_enable & - 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) { - void *fte_match1 = MLX5_ADDR_OF(fte_match_param, - fte_param1, misc_parameters); - void *fte_match2 = MLX5_ADDR_OF(fte_match_param, - fte_param2, misc_parameters); - void *fte_mask = MLX5_ADDR_OF(fte_match_param, - mask->match_criteria, misc_parameters); + if (!(match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS)) { + char *fg_type_mask = MLX5_ADDR_OF(fte_match_param, + match_criteria, misc_parameters); - if (!masked_memcmp(fte_mask, fte_match1, fte_match2, - MLX5_ST_SZ_BYTES(fte_match_set_misc))) + if (fg_type_mask[0] || + memcmp(fg_type_mask, fg_type_mask + 1, + MLX5_ST_SZ_BYTES(fte_match_set_misc) - 1)) return false; } - if (mask->match_criteria_enable & - 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS) { - void *fte_match1 = MLX5_ADDR_OF(fte_match_param, - fte_param1, inner_headers); - void *fte_match2 = MLX5_ADDR_OF(fte_match_param, - fte_param2, inner_headers); - void *fte_mask = MLX5_ADDR_OF(fte_match_param, - mask->match_criteria, inner_headers); + if (!(match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS)) { + char *fg_type_mask = MLX5_ADDR_OF(fte_match_param, + match_criteria, inner_headers); - if (!masked_memcmp(fte_mask, fte_match1, fte_match2, - MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4))) + if (fg_type_mask[0] || + memcmp(fg_type_mask, fg_type_mask + 1, + MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4) - 1)) return false; } - return true; + + return check_last_reserved(match_criteria); } static bool compare_match_criteria(u8 match_criteria_enable1, @@ -410,6 +414,18 @@ out: } } +static void destroy_fte(struct fs_fte *fte, struct mlx5_flow_group *fg) +{ + struct mlx5_flow_table *ft; + int ret; + + ret = rhashtable_remove_fast(&fg->ftes_hash, &fte->hash, rhash_fte); + WARN_ON(ret); + fte->status = 0; + fs_get_obj(ft, fg->node.parent); + ida_simple_remove(&ft->fte_allocator, fte->index); +} + static void del_fte(struct fs_node *node) { struct mlx5_flow_table *ft; @@ -430,8 +446,7 @@ static void del_fte(struct fs_node *node) "flow steering can't delete fte in index %d of flow group id %d\n", fte->index, fg->id); - ida_simple_remove(&ft->fte_allocator, fte->index); - fte->status = 0; + destroy_fte(fte, fg); } static void del_flow_group(struct fs_node *node) @@ -447,6 +462,7 @@ static void del_flow_group(struct fs_node *node) if (ft->autogroup.active) ft->autogroup.num_groups--; + rhashtable_destroy(&fg->ftes_hash); if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id)) mlx5_core_warn(dev, "flow steering can't destroy fg %d of ft %d\n", fg->id, ft->id); @@ -481,10 +497,17 @@ static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in) u8 match_criteria_enable = MLX5_GET(create_flow_group_in, create_fg_in, match_criteria_enable); + int ret; + fg = kzalloc(sizeof(*fg), GFP_KERNEL); if (!fg) return ERR_PTR(-ENOMEM); + ret = rhashtable_init(&fg->ftes_hash, &rhash_fte); + if (ret) { + kfree(fg); + return ERR_PTR(ret); + } fg->mask.match_criteria_enable = match_criteria_enable; memcpy(&fg->mask.match_criteria, match_criteria, sizeof(fg->mask.match_criteria)); @@ -920,10 +943,8 @@ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table * return fg; err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id); - if (err) { - kfree(fg); - return ERR_PTR(err); - } + if (err) + goto err_free_fg; if (ft->autogroup.active) ft->autogroup.num_groups++; @@ -934,13 +955,27 @@ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table * list_add(&fg->node.list, prev_fg); return fg; + +err_free_fg: + rhashtable_destroy(&fg->ftes_hash); + kfree(fg); + + return ERR_PTR(err); } struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft, u32 *fg_in) { + void *match_criteria = MLX5_ADDR_OF(create_flow_group_in, + fg_in, match_criteria); + u8 match_criteria_enable = MLX5_GET(create_flow_group_in, + fg_in, + match_criteria_enable); struct mlx5_flow_group *fg; + if (!check_valid_mask(match_criteria_enable, match_criteria)) + return ERR_PTR(-EINVAL); + if (ft->autogroup.active) return ERR_PTR(-EPERM); @@ -1104,6 +1139,7 @@ static struct fs_fte *create_fte(struct mlx5_flow_group *fg, struct mlx5_flow_table *ft; struct fs_fte *fte; int index; + int ret; fs_get_obj(ft, fg->node.parent); index = ida_simple_get(&ft->fte_allocator, fg->start_index, @@ -1114,11 +1150,20 @@ static struct fs_fte *create_fte(struct mlx5_flow_group *fg, fte = alloc_fte(flow_act, match_value, index); if (IS_ERR(fte)) { - ida_simple_remove(&ft->fte_allocator, index); - return fte; + ret = PTR_ERR(fte); + goto err_alloc; } + ret = rhashtable_insert_fast(&fg->ftes_hash, &fte->hash, rhash_fte); + if (ret) + goto err_hash; return fte; + +err_hash: + kfree(fte); +err_alloc: + ida_simple_remove(&ft->fte_allocator, index); + return ERR_PTR(ret); } static struct mlx5_flow_group *create_autogroup(struct mlx5_flow_table *ft, @@ -1206,42 +1251,78 @@ static struct mlx5_flow_rule *find_flow_rule(struct fs_fte *fte, return NULL; } +static bool check_conflicting_actions(u32 action1, u32 action2) +{ + u32 xored_actions = action1 ^ action2; + + /* if one rule only wants to count, it's ok */ + if (action1 == MLX5_FLOW_CONTEXT_ACTION_COUNT || + action2 == MLX5_FLOW_CONTEXT_ACTION_COUNT) + return false; + + if (xored_actions & (MLX5_FLOW_CONTEXT_ACTION_DROP | + MLX5_FLOW_CONTEXT_ACTION_ENCAP | + MLX5_FLOW_CONTEXT_ACTION_DECAP)) + return true; + + return false; +} + +static int check_conflicting_ftes(struct fs_fte *fte, const struct mlx5_flow_act *flow_act) +{ + if (check_conflicting_actions(flow_act->action, fte->action)) { + mlx5_core_warn(get_dev(&fte->node), + "Found two FTEs with conflicting actions\n"); + return -EEXIST; + } + + if (fte->flow_tag != flow_act->flow_tag) { + mlx5_core_warn(get_dev(&fte->node), + "FTE flow tag %u already exists with different flow tag %u\n", + fte->flow_tag, + flow_act->flow_tag); + return -EEXIST; + } + + return 0; +} + static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, u32 *match_value, struct mlx5_flow_act *flow_act, struct mlx5_flow_destination *dest, int dest_num) { + u32 masked_val[sizeof(fg->mask.match_criteria)]; struct mlx5_flow_handle *handle; struct mlx5_flow_table *ft; struct fs_fte *fte; int i; nested_lock_ref_node(&fg->node, FS_MUTEX_PARENT); - fs_for_each_fte(fte, fg) { + for (i = 0; i < sizeof(masked_val); i++) + masked_val[i] = match_value[i] & fg->mask.match_criteria[i]; + fte = rhashtable_lookup_fast(&fg->ftes_hash, masked_val, rhash_fte); + if (fte) { + int old_action; + int ret; + nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); - if (compare_match_value(&fg->mask, match_value, &fte->val) && - (flow_act->action & fte->action)) { - int old_action = fte->action; - - if (fte->flow_tag != flow_act->flow_tag) { - mlx5_core_warn(get_dev(&fte->node), - "FTE flow tag %u already exists with different flow tag %u\n", - fte->flow_tag, - flow_act->flow_tag); - handle = ERR_PTR(-EEXIST); - goto unlock_fte; - } + ret = check_conflicting_ftes(fte, flow_act); + if (ret) { + handle = ERR_PTR(ret); + goto unlock_fte; + } - fte->action |= flow_act->action; - handle = add_rule_fte(fte, fg, dest, dest_num, - old_action != flow_act->action); - if (IS_ERR(handle)) { - fte->action = old_action; - goto unlock_fte; - } else { - goto add_rules; - } + old_action = fte->action; + fte->action |= flow_act->action; + handle = add_rule_fte(fte, fg, dest, dest_num, + old_action != flow_act->action); + if (IS_ERR(handle)) { + fte->action = old_action; + goto unlock_fte; + } else { + goto add_rules; } unlock_ref_node(&fte->node); } @@ -1257,6 +1338,7 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, handle = add_rule_fte(fte, fg, dest, dest_num, false); if (IS_ERR(handle)) { unlock_ref_node(&fte->node); + destroy_fte(fte, fg); kfree(fte); goto unlock_fg; } @@ -1332,6 +1414,10 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, struct mlx5_flow_handle *rule; int i; + if (!check_valid_mask(spec->match_criteria_enable, + spec->match_criteria)) + return ERR_PTR(-EINVAL); + for (i = 0; i < dest_num; i++) { if (!dest_is_valid(&dest[i], flow_act->action, ft)) return ERR_PTR(-EINVAL); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index bfbc081d17dc..62709a3865d2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -34,6 +34,7 @@ #define _MLX5_FS_CORE_ #include +#include enum fs_node_type { FS_TYPE_NAMESPACE, @@ -168,6 +169,7 @@ struct fs_fte { u32 modify_id; enum fs_fte_status status; struct mlx5_fc *counter; + struct rhash_head hash; }; /* Type of children is mlx5_flow_table/namespace */ @@ -197,6 +199,7 @@ struct mlx5_flow_group { u32 start_index; u32 max_ftes; u32 id; + struct rhashtable ftes_hash; }; struct mlx5_flow_root_namespace { -- cgit v1.2.3 From 693c6883bbc4f7649750eb9ce49d0b799d073186 Mon Sep 17 00:00:00 2001 From: Matan Barak Date: Sun, 28 May 2017 16:45:31 +0300 Subject: net/mlx5: Add hash table for flow groups in flow table When adding a flow table entry (fte) to a flow table (ft), we first need to find its flow group (fg). Currently, this is done by traversing a linear list of all flow groups in the flow table. Furthermore, since multiple flow groups which correspond to the same fte mask may exist in the same ft, we can't just stop at the first match. Converting the linear list to rhltable in order to speed things up. The last four patches increases the steering rules update rate by a factor of more than 7 (for insertion of 50K steering rules). Signed-off-by: Matan Barak Reviewed-by: Maor Gottlieb Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 187 +++++++++++++++++----- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 2 + 2 files changed, 152 insertions(+), 37 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index d8d45b006996..9704c2eb82a1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -158,6 +158,15 @@ static const struct rhashtable_params rhash_fte = { .min_size = 1, }; +static const struct rhashtable_params rhash_fg = { + .key_len = FIELD_SIZEOF(struct mlx5_flow_group, mask), + .key_offset = offsetof(struct mlx5_flow_group, mask), + .head_offset = offsetof(struct mlx5_flow_group, hash), + .automatic_shrinking = true, + .min_size = 1, + +}; + static void del_rule(struct fs_node *node); static void del_flow_table(struct fs_node *node); static void del_flow_group(struct fs_node *node); @@ -318,12 +327,22 @@ static bool check_valid_mask(u8 match_criteria_enable, const u32 *match_criteria return check_last_reserved(match_criteria); } -static bool compare_match_criteria(u8 match_criteria_enable1, - u8 match_criteria_enable2, - void *mask1, void *mask2) +static bool check_valid_spec(const struct mlx5_flow_spec *spec) { - return match_criteria_enable1 == match_criteria_enable2 && - !memcmp(mask1, mask2, MLX5_ST_SZ_BYTES(fte_match_param)); + int i; + + if (!check_valid_mask(spec->match_criteria_enable, spec->match_criteria)) { + pr_warn("mlx5_core: Match criteria given mismatches match_criteria_enable\n"); + return false; + } + + for (i = 0; i < MLX5_ST_SZ_DW_MATCH_PARAM; i++) + if (spec->match_value[i] & ~spec->match_criteria[i]) { + pr_warn("mlx5_core: match_value differs from match_criteria\n"); + return false; + } + + return check_last_reserved(spec->match_value); } static struct mlx5_flow_root_namespace *find_root(struct fs_node *node) @@ -365,6 +384,7 @@ static void del_flow_table(struct fs_node *node) if (err) mlx5_core_warn(dev, "flow steering can't destroy ft\n"); ida_destroy(&ft->fte_allocator); + rhltable_destroy(&ft->fgs_hash); fs_get_obj(prio, ft->node.parent); prio->num_ft--; } @@ -454,6 +474,7 @@ static void del_flow_group(struct fs_node *node) struct mlx5_flow_group *fg; struct mlx5_flow_table *ft; struct mlx5_core_dev *dev; + int err; fs_get_obj(fg, node); fs_get_obj(ft, fg->node.parent); @@ -463,6 +484,10 @@ static void del_flow_group(struct fs_node *node) ft->autogroup.num_groups--; rhashtable_destroy(&fg->ftes_hash); + err = rhltable_remove(&ft->fgs_hash, + &fg->hash, + rhash_fg); + WARN_ON(err); if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id)) mlx5_core_warn(dev, "flow steering can't destroy fg %d of ft %d\n", fg->id, ft->id); @@ -525,10 +550,17 @@ static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_ft u32 flags) { struct mlx5_flow_table *ft; + int ret; ft = kzalloc(sizeof(*ft), GFP_KERNEL); if (!ft) - return NULL; + return ERR_PTR(-ENOMEM); + + ret = rhltable_init(&ft->fgs_hash, &rhash_fg); + if (ret) { + kfree(ft); + return ERR_PTR(ret); + } ft->level = level; ft->node.type = FS_TYPE_FLOW_TABLE; @@ -829,8 +861,8 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa ft_attr->max_fte ? roundup_pow_of_two(ft_attr->max_fte) : 0, root->table_type, op_mod, ft_attr->flags); - if (!ft) { - err = -ENOMEM; + if (IS_ERR(ft)) { + err = PTR_ERR(ft); goto unlock_root; } @@ -942,10 +974,14 @@ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table * if (IS_ERR(fg)) return fg; - err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id); + err = rhltable_insert(&ft->fgs_hash, &fg->hash, rhash_fg); if (err) goto err_free_fg; + err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id); + if (err) + goto err_remove_fg; + if (ft->autogroup.active) ft->autogroup.num_groups++; /* Add node to tree */ @@ -956,6 +992,10 @@ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table * return fg; +err_remove_fg: + WARN_ON(rhltable_remove(&ft->fgs_hash, + &fg->hash, + rhash_fg)); err_free_fg: rhashtable_destroy(&fg->ftes_hash); kfree(fg); @@ -1291,18 +1331,13 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, u32 *match_value, struct mlx5_flow_act *flow_act, struct mlx5_flow_destination *dest, - int dest_num) + int dest_num, + struct fs_fte *fte) { - u32 masked_val[sizeof(fg->mask.match_criteria)]; struct mlx5_flow_handle *handle; struct mlx5_flow_table *ft; - struct fs_fte *fte; int i; - nested_lock_ref_node(&fg->node, FS_MUTEX_PARENT); - for (i = 0; i < sizeof(masked_val); i++) - masked_val[i] = match_value[i] & fg->mask.match_criteria[i]; - fte = rhashtable_lookup_fast(&fg->ftes_hash, masked_val, rhash_fte); if (fte) { int old_action; int ret; @@ -1324,15 +1359,12 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, } else { goto add_rules; } - unlock_ref_node(&fte->node); } fs_get_obj(ft, fg->node.parent); fte = create_fte(fg, match_value, flow_act); - if (IS_ERR(fte)) { - handle = (void *)fte; - goto unlock_fg; - } + if (IS_ERR(fte)) + return (void *)fte; tree_init_node(&fte->node, 0, del_fte); nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); handle = add_rule_fte(fte, fg, dest, dest_num, false); @@ -1340,7 +1372,7 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, unlock_ref_node(&fte->node); destroy_fte(fte, fg); kfree(fte); - goto unlock_fg; + return handle; } tree_add_node(&fte->node, &fg->node); @@ -1353,8 +1385,6 @@ add_rules: } unlock_fte: unlock_ref_node(&fte->node); -unlock_fg: - unlock_ref_node(&fg->node); return handle; } @@ -1402,6 +1432,96 @@ static bool dest_is_valid(struct mlx5_flow_destination *dest, return true; } +static struct mlx5_flow_handle * +try_add_to_existing_fg(struct mlx5_flow_table *ft, + struct mlx5_flow_spec *spec, + struct mlx5_flow_act *flow_act, + struct mlx5_flow_destination *dest, + int dest_num) +{ + struct mlx5_flow_group *g; + struct mlx5_flow_handle *rule = ERR_PTR(-ENOENT); + struct rhlist_head *tmp, *list; + struct match_list { + struct list_head list; + struct mlx5_flow_group *g; + } match_list, *iter; + LIST_HEAD(match_head); + + rcu_read_lock(); + /* Collect all fgs which has a matching match_criteria */ + list = rhltable_lookup(&ft->fgs_hash, spec, rhash_fg); + rhl_for_each_entry_rcu(g, tmp, list, hash) { + struct match_list *curr_match; + + if (likely(list_empty(&match_head))) { + match_list.g = g; + list_add_tail(&match_list.list, &match_head); + continue; + } + curr_match = kmalloc(sizeof(*curr_match), GFP_ATOMIC); + + if (!curr_match) { + rcu_read_unlock(); + rule = ERR_PTR(-ENOMEM); + goto free_list; + } + curr_match->g = g; + list_add_tail(&curr_match->list, &match_head); + } + rcu_read_unlock(); + + /* Try to find a fg that already contains a matching fte */ + list_for_each_entry(iter, &match_head, list) { + struct fs_fte *fte; + + g = iter->g; + nested_lock_ref_node(&g->node, FS_MUTEX_PARENT); + fte = rhashtable_lookup_fast(&g->ftes_hash, spec->match_value, + rhash_fte); + if (fte) { + rule = add_rule_fg(g, spec->match_value, + flow_act, dest, dest_num, fte); + unlock_ref_node(&g->node); + goto free_list; + } + unlock_ref_node(&g->node); + } + + /* No group with matching fte found. Try to add a new fte to any + * matching fg. + */ + list_for_each_entry(iter, &match_head, list) { + g = iter->g; + + nested_lock_ref_node(&g->node, FS_MUTEX_PARENT); + rule = add_rule_fg(g, spec->match_value, + flow_act, dest, dest_num, NULL); + if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC) { + unlock_ref_node(&g->node); + goto free_list; + } + unlock_ref_node(&g->node); + } + +free_list: + if (!list_empty(&match_head)) { + struct match_list *match_tmp; + + /* The most common case is having one FG. Since we want to + * optimize this case, we save the first on the stack. + * Therefore, no need to free it. + */ + list_del(&list_first_entry(&match_head, typeof(*iter), list)->list); + list_for_each_entry_safe(iter, match_tmp, &match_head, list) { + list_del(&iter->list); + kfree(iter); + } + } + + return rule; +} + static struct mlx5_flow_handle * _mlx5_add_flow_rules(struct mlx5_flow_table *ft, struct mlx5_flow_spec *spec, @@ -1414,8 +1534,7 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, struct mlx5_flow_handle *rule; int i; - if (!check_valid_mask(spec->match_criteria_enable, - spec->match_criteria)) + if (!check_valid_spec(spec)) return ERR_PTR(-EINVAL); for (i = 0; i < dest_num; i++) { @@ -1424,16 +1543,9 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, } nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT); - fs_for_each_fg(g, ft) - if (compare_match_criteria(g->mask.match_criteria_enable, - spec->match_criteria_enable, - g->mask.match_criteria, - spec->match_criteria)) { - rule = add_rule_fg(g, spec->match_value, - flow_act, dest, dest_num); - if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC) - goto unlock; - } + rule = try_add_to_existing_fg(ft, spec, flow_act, dest, dest_num); + if (!IS_ERR(rule)) + goto unlock; g = create_autogroup(ft, spec->match_criteria_enable, spec->match_criteria); @@ -1442,7 +1554,8 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, goto unlock; } - rule = add_rule_fg(g, spec->match_value, flow_act, dest, dest_num); + rule = add_rule_fg(g, spec->match_value, flow_act, dest, + dest_num, NULL); if (IS_ERR(rule)) { /* Remove assumes refcount > 0 and autogroup creates a group * with a refcount = 0. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 62709a3865d2..5509a752f98e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -120,6 +120,7 @@ struct mlx5_flow_table { struct list_head fwd_rules; u32 flags; struct ida fte_allocator; + struct rhltable fgs_hash; }; struct mlx5_fc_cache { @@ -200,6 +201,7 @@ struct mlx5_flow_group { u32 max_ftes; u32 id; struct rhashtable ftes_hash; + struct rhlist_head hash; }; struct mlx5_flow_root_namespace { -- cgit v1.2.3 From 4c03e69ab1ef31877cef63575a1869b130f9c5ce Mon Sep 17 00:00:00 2001 From: Matan Barak Date: Sun, 28 May 2017 10:32:09 +0300 Subject: net/mlx5: Add tracepoints Add a tracepoint infrastructure for mlx5_core driver. Implemented flow steering tracepoints: 1. Add flow group 2. Remove flow group 3. Add flow table entry 4. Remove flow table entry 5. Add flow table rule 6. Remove flow table rule Signed-off-by: Matan Barak Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 5 +- .../net/ethernet/mellanox/mlx5/core/diag/Makefile | 1 + .../mellanox/mlx5/core/diag/fs_tracepoint.c | 261 +++++++++++++++++++ .../mellanox/mlx5/core/diag/fs_tracepoint.h | 282 +++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 11 +- 5 files changed, 558 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/diag/Makefile create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 22ed657d263a..87a3099808f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -4,7 +4,8 @@ subdir-ccflags-y += -I$(src) mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o \ - fs_counters.o rl.o lag.o dev.o wq.o lib/gid.o + fs_counters.o rl.o lag.o dev.o wq.o lib/gid.o \ + diag/fs_tracepoint.o mlx5_core-$(CONFIG_MLX5_ACCEL) += accel/ipsec.o @@ -25,3 +26,5 @@ mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ en_accel/ipsec_stats.o + +CFLAGS_tracepoint.o := -I$(src) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/diag/Makefile new file mode 100644 index 000000000000..d8e17110f25d --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/Makefile @@ -0,0 +1 @@ +subdir-ccflags-y += -I$(src)/.. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c new file mode 100644 index 000000000000..0be4575b58a2 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define CREATE_TRACE_POINTS + +#include "fs_tracepoint.h" +#include + +#define DECLARE_MASK_VAL(type, name) struct {type m; type v; } name +#define MASK_VAL(type, spec, name, mask, val, fld) \ + DECLARE_MASK_VAL(type, name) = \ + {.m = MLX5_GET(spec, mask, fld),\ + .v = MLX5_GET(spec, val, fld)} +#define MASK_VAL_BE(type, spec, name, mask, val, fld) \ + DECLARE_MASK_VAL(type, name) = \ + {.m = MLX5_GET_BE(type, spec, mask, fld),\ + .v = MLX5_GET_BE(type, spec, val, fld)} +#define GET_MASKED_VAL(name) (name.m & name.v) + +#define GET_MASK_VAL(name, type, mask, val, fld) \ + (name.m = MLX5_GET(type, mask, fld), \ + name.v = MLX5_GET(type, val, fld), \ + name.m & name.v) +#define PRINT_MASKED_VAL(name, p, format) { \ + if (name.m) \ + trace_seq_printf(p, __stringify(name) "=" format " ", name.v); \ + } +#define PRINT_MASKED_VALP(name, cast, p, format) { \ + if (name.m) \ + trace_seq_printf(p, __stringify(name) "=" format " ", \ + (cast)&name.v);\ + } + +static void print_lyr_2_4_hdrs(struct trace_seq *p, + const u32 *mask, const u32 *value) +{ +#define MASK_VAL_L2(type, name, fld) \ + MASK_VAL(type, fte_match_set_lyr_2_4, name, mask, value, fld) + DECLARE_MASK_VAL(u64, smac) = { + .m = MLX5_GET(fte_match_set_lyr_2_4, mask, smac_47_16) << 16 | + MLX5_GET(fte_match_set_lyr_2_4, mask, smac_15_0), + .v = MLX5_GET(fte_match_set_lyr_2_4, value, smac_47_16) << 16 | + MLX5_GET(fte_match_set_lyr_2_4, value, smac_15_0)}; + DECLARE_MASK_VAL(u64, dmac) = { + .m = MLX5_GET(fte_match_set_lyr_2_4, mask, dmac_47_16) << 16 | + MLX5_GET(fte_match_set_lyr_2_4, mask, dmac_15_0), + .v = MLX5_GET(fte_match_set_lyr_2_4, value, dmac_47_16) << 16 | + MLX5_GET(fte_match_set_lyr_2_4, value, dmac_15_0)}; + MASK_VAL_L2(u16, ethertype, ethertype); + + PRINT_MASKED_VALP(smac, u8 *, p, "%pM"); + PRINT_MASKED_VALP(dmac, u8 *, p, "%pM"); + PRINT_MASKED_VAL(ethertype, p, "%04x"); + + if (ethertype.m == 0xffff) { + if (ethertype.v == ETH_P_IP) { +#define MASK_VAL_L2_BE(type, name, fld) \ + MASK_VAL_BE(type, fte_match_set_lyr_2_4, name, mask, value, fld) + MASK_VAL_L2_BE(u32, src_ipv4, + src_ipv4_src_ipv6.ipv4_layout.ipv4); + MASK_VAL_L2_BE(u32, dst_ipv4, + dst_ipv4_dst_ipv6.ipv4_layout.ipv4); + + PRINT_MASKED_VALP(src_ipv4, typeof(&src_ipv4.v), p, + "%pI4"); + PRINT_MASKED_VALP(dst_ipv4, typeof(&dst_ipv4.v), p, + "%pI4"); + } else if (ethertype.v == ETH_P_IPV6) { + static const struct in6_addr full_ones = { + .in6_u.u6_addr32 = {htonl(0xffffffff), + htonl(0xffffffff), + htonl(0xffffffff), + htonl(0xffffffff)}, + }; + DECLARE_MASK_VAL(struct in6_addr, src_ipv6); + DECLARE_MASK_VAL(struct in6_addr, dst_ipv6); + + memcpy(src_ipv6.m.in6_u.u6_addr8, + MLX5_ADDR_OF(fte_match_set_lyr_2_4, mask, + src_ipv4_src_ipv6.ipv6_layout.ipv6), + sizeof(src_ipv6.m)); + memcpy(dst_ipv6.m.in6_u.u6_addr8, + MLX5_ADDR_OF(fte_match_set_lyr_2_4, mask, + dst_ipv4_dst_ipv6.ipv6_layout.ipv6), + sizeof(dst_ipv6.m)); + memcpy(src_ipv6.v.in6_u.u6_addr8, + MLX5_ADDR_OF(fte_match_set_lyr_2_4, value, + src_ipv4_src_ipv6.ipv6_layout.ipv6), + sizeof(src_ipv6.v)); + memcpy(dst_ipv6.v.in6_u.u6_addr8, + MLX5_ADDR_OF(fte_match_set_lyr_2_4, value, + dst_ipv4_dst_ipv6.ipv6_layout.ipv6), + sizeof(dst_ipv6.v)); + + if (!memcmp(&src_ipv6.m, &full_ones, sizeof(full_ones))) + trace_seq_printf(p, "src_ipv6=%pI6 ", + &src_ipv6.v); + if (!memcmp(&dst_ipv6.m, &full_ones, sizeof(full_ones))) + trace_seq_printf(p, "dst_ipv6=%pI6 ", + &dst_ipv6.v); + } + } + +#define PRINT_MASKED_VAL_L2(type, name, fld, p, format) {\ + MASK_VAL_L2(type, name, fld); \ + PRINT_MASKED_VAL(name, p, format); \ +} + + PRINT_MASKED_VAL_L2(u8, ip_protocol, ip_protocol, p, "%02x"); + PRINT_MASKED_VAL_L2(u16, tcp_flags, tcp_flags, p, "%x"); + PRINT_MASKED_VAL_L2(u16, tcp_sport, tcp_sport, p, "%u"); + PRINT_MASKED_VAL_L2(u16, tcp_dport, tcp_dport, p, "%u"); + PRINT_MASKED_VAL_L2(u16, udp_sport, udp_sport, p, "%u"); + PRINT_MASKED_VAL_L2(u16, udp_dport, udp_dport, p, "%u"); + PRINT_MASKED_VAL_L2(u16, first_vid, first_vid, p, "%04x"); + PRINT_MASKED_VAL_L2(u8, first_prio, first_prio, p, "%x"); + PRINT_MASKED_VAL_L2(u8, first_cfi, first_cfi, p, "%d"); + PRINT_MASKED_VAL_L2(u8, ip_dscp, ip_dscp, p, "%02x"); + PRINT_MASKED_VAL_L2(u8, ip_ecn, ip_ecn, p, "%x"); + PRINT_MASKED_VAL_L2(u8, cvlan_tag, cvlan_tag, p, "%d"); + PRINT_MASKED_VAL_L2(u8, svlan_tag, svlan_tag, p, "%d"); + PRINT_MASKED_VAL_L2(u8, frag, frag, p, "%d"); +} + +static void print_misc_parameters_hdrs(struct trace_seq *p, + const u32 *mask, const u32 *value) +{ +#define MASK_VAL_MISC(type, name, fld) \ + MASK_VAL(type, fte_match_set_misc, name, mask, value, fld) +#define PRINT_MASKED_VAL_MISC(type, name, fld, p, format) {\ + MASK_VAL_MISC(type, name, fld); \ + PRINT_MASKED_VAL(name, p, format); \ +} + DECLARE_MASK_VAL(u64, gre_key) = { + .m = MLX5_GET(fte_match_set_misc, mask, gre_key_h) << 8 | + MLX5_GET(fte_match_set_misc, mask, gre_key_l), + .v = MLX5_GET(fte_match_set_misc, value, gre_key_h) << 8 | + MLX5_GET(fte_match_set_misc, value, gre_key_l)}; + + PRINT_MASKED_VAL(gre_key, p, "%llu"); + PRINT_MASKED_VAL_MISC(u32, source_sqn, source_sqn, p, "%u"); + PRINT_MASKED_VAL_MISC(u16, source_port, source_port, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, outer_second_prio, outer_second_prio, + p, "%u"); + PRINT_MASKED_VAL_MISC(u8, outer_second_cfi, outer_second_cfi, p, "%u"); + PRINT_MASKED_VAL_MISC(u16, outer_second_vid, outer_second_vid, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, inner_second_prio, inner_second_prio, + p, "%u"); + PRINT_MASKED_VAL_MISC(u8, inner_second_cfi, inner_second_cfi, p, "%u"); + PRINT_MASKED_VAL_MISC(u16, inner_second_vid, inner_second_vid, p, "%u"); + + PRINT_MASKED_VAL_MISC(u8, outer_second_cvlan_tag, + outer_second_cvlan_tag, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, inner_second_cvlan_tag, + inner_second_cvlan_tag, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, outer_second_svlan_tag, + outer_second_svlan_tag, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, inner_second_svlan_tag, + inner_second_svlan_tag, p, "%u"); + + PRINT_MASKED_VAL_MISC(u8, gre_protocol, gre_protocol, p, "%u"); + + PRINT_MASKED_VAL_MISC(u32, vxlan_vni, vxlan_vni, p, "%u"); + PRINT_MASKED_VAL_MISC(u32, outer_ipv6_flow_label, outer_ipv6_flow_label, + p, "%x"); + PRINT_MASKED_VAL_MISC(u32, inner_ipv6_flow_label, inner_ipv6_flow_label, + p, "%x"); +} + +const char *parse_fs_hdrs(struct trace_seq *p, + u8 match_criteria_enable, + const u32 *mask_outer, + const u32 *mask_misc, + const u32 *mask_inner, + const u32 *value_outer, + const u32 *value_misc, + const u32 *value_inner) +{ + const char *ret = trace_seq_buffer_ptr(p); + + if (match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) { + trace_seq_printf(p, "[outer] "); + print_lyr_2_4_hdrs(p, mask_outer, value_outer); + } + + if (match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) { + trace_seq_printf(p, "[misc] "); + print_misc_parameters_hdrs(p, mask_misc, value_misc); + } + if (match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS) { + trace_seq_printf(p, "[inner] "); + print_lyr_2_4_hdrs(p, mask_inner, value_inner); + } + trace_seq_putc(p, 0); + return ret; +} + +const char *parse_fs_dst(struct trace_seq *p, + const struct mlx5_flow_destination *dst, + u32 counter_id) +{ + const char *ret = trace_seq_buffer_ptr(p); + + switch (dst->type) { + case MLX5_FLOW_DESTINATION_TYPE_VPORT: + trace_seq_printf(p, "vport=%u\n", dst->vport_num); + break; + case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE: + trace_seq_printf(p, "ft=%p\n", dst->ft); + break; + case MLX5_FLOW_DESTINATION_TYPE_TIR: + trace_seq_printf(p, "tir=%u\n", dst->tir_num); + break; + case MLX5_FLOW_DESTINATION_TYPE_COUNTER: + trace_seq_printf(p, "counter_id=%u\n", counter_id); + break; + } + + trace_seq_putc(p, 0); + return ret; +} + +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_add_fg); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_del_fg); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_set_fte); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_del_fte); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_add_rule); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_del_rule); + diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h new file mode 100644 index 000000000000..1e3a6c3e4132 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(_MLX5_FS_TP_) || defined(TRACE_HEADER_MULTI_READ) +#define _MLX5_FS_TP_ + +#include +#include +#include "../fs_core.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mlx5 + +#define __parse_fs_hdrs(match_criteria_enable, mouter, mmisc, minner, vouter, \ + vinner, vmisc) \ + parse_fs_hdrs(p, match_criteria_enable, mouter, mmisc, minner, vouter,\ + vinner, vmisc) + +const char *parse_fs_hdrs(struct trace_seq *p, + u8 match_criteria_enable, + const u32 *mask_outer, + const u32 *mask_misc, + const u32 *mask_inner, + const u32 *value_outer, + const u32 *value_misc, + const u32 *value_inner); + +#define __parse_fs_dst(dst, counter_id) \ + parse_fs_dst(p, (const struct mlx5_flow_destination *)dst, counter_id) + +const char *parse_fs_dst(struct trace_seq *p, + const struct mlx5_flow_destination *dst, + u32 counter_id); + +TRACE_EVENT(mlx5_fs_add_fg, + TP_PROTO(const struct mlx5_flow_group *fg), + TP_ARGS(fg), + TP_STRUCT__entry( + __field(const struct mlx5_flow_group *, fg) + __field(const struct mlx5_flow_table *, ft) + __field(u32, start_index) + __field(u32, end_index) + __field(u32, id) + __field(u8, mask_enable) + __array(u32, mask_outer, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, mask_inner, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, mask_misc, MLX5_ST_SZ_DW(fte_match_set_misc)) + ), + TP_fast_assign( + __entry->fg = fg; + fs_get_obj(__entry->ft, fg->node.parent); + __entry->start_index = fg->start_index; + __entry->end_index = fg->start_index + fg->max_ftes; + __entry->id = fg->id; + __entry->mask_enable = fg->mask.match_criteria_enable; + memcpy(__entry->mask_outer, + MLX5_ADDR_OF(fte_match_param, + &fg->mask.match_criteria, + outer_headers), + sizeof(__entry->mask_outer)); + memcpy(__entry->mask_inner, + MLX5_ADDR_OF(fte_match_param, + &fg->mask.match_criteria, + inner_headers), + sizeof(__entry->mask_inner)); + memcpy(__entry->mask_misc, + MLX5_ADDR_OF(fte_match_param, + &fg->mask.match_criteria, + misc_parameters), + sizeof(__entry->mask_misc)); + + ), + TP_printk("fg=%p ft=%p id=%u start=%u end=%u bit_mask=%02x %s\n", + __entry->fg, __entry->ft, __entry->id, + __entry->start_index, __entry->end_index, + __entry->mask_enable, + __parse_fs_hdrs(__entry->mask_enable, + __entry->mask_outer, + __entry->mask_misc, + __entry->mask_inner, + __entry->mask_outer, + __entry->mask_misc, + __entry->mask_inner)) + ); + +TRACE_EVENT(mlx5_fs_del_fg, + TP_PROTO(const struct mlx5_flow_group *fg), + TP_ARGS(fg), + TP_STRUCT__entry( + __field(const struct mlx5_flow_group *, fg) + __field(u32, id) + ), + TP_fast_assign( + __entry->fg = fg; + __entry->id = fg->id; + + ), + TP_printk("fg=%p id=%u\n", + __entry->fg, __entry->id) + ); + +#define ACTION_FLAGS \ + {MLX5_FLOW_CONTEXT_ACTION_ALLOW, "ALLOW"},\ + {MLX5_FLOW_CONTEXT_ACTION_DROP, "DROP"},\ + {MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, "FWD"},\ + {MLX5_FLOW_CONTEXT_ACTION_COUNT, "CNT"},\ + {MLX5_FLOW_CONTEXT_ACTION_ENCAP, "ENCAP"},\ + {MLX5_FLOW_CONTEXT_ACTION_DECAP, "DECAP"},\ + {MLX5_FLOW_CONTEXT_ACTION_MOD_HDR, "MOD_HDR"},\ + {MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO, "NEXT_PRIO"} + +TRACE_EVENT(mlx5_fs_set_fte, + TP_PROTO(const struct fs_fte *fte, bool new_fte), + TP_ARGS(fte, new_fte), + TP_STRUCT__entry( + __field(const struct fs_fte *, fte) + __field(const struct mlx5_flow_group *, fg) + __field(u32, group_index) + __field(u32, index) + __field(u32, action) + __field(u32, flow_tag) + __field(u8, mask_enable) + __field(bool, new_fte) + __array(u32, mask_outer, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, mask_inner, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, mask_misc, MLX5_ST_SZ_DW(fte_match_set_misc)) + __array(u32, value_outer, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, value_inner, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, value_misc, MLX5_ST_SZ_DW(fte_match_set_misc)) + ), + TP_fast_assign( + __entry->fte = fte; + __entry->new_fte = new_fte; + fs_get_obj(__entry->fg, fte->node.parent); + __entry->group_index = __entry->fg->id; + __entry->index = fte->index; + __entry->action = fte->action; + __entry->mask_enable = __entry->fg->mask.match_criteria_enable; + __entry->flow_tag = fte->flow_tag; + memcpy(__entry->mask_outer, + MLX5_ADDR_OF(fte_match_param, + &__entry->fg->mask.match_criteria, + outer_headers), + sizeof(__entry->mask_outer)); + memcpy(__entry->mask_inner, + MLX5_ADDR_OF(fte_match_param, + &__entry->fg->mask.match_criteria, + inner_headers), + sizeof(__entry->mask_inner)); + memcpy(__entry->mask_misc, + MLX5_ADDR_OF(fte_match_param, + &__entry->fg->mask.match_criteria, + misc_parameters), + sizeof(__entry->mask_misc)); + memcpy(__entry->value_outer, + MLX5_ADDR_OF(fte_match_param, + &fte->val, + outer_headers), + sizeof(__entry->value_outer)); + memcpy(__entry->value_inner, + MLX5_ADDR_OF(fte_match_param, + &fte->val, + inner_headers), + sizeof(__entry->value_inner)); + memcpy(__entry->value_misc, + MLX5_ADDR_OF(fte_match_param, + &fte->val, + misc_parameters), + sizeof(__entry->value_misc)); + + ), + TP_printk("op=%s fte=%p fg=%p index=%u group_index=%u action=<%s> flow_tag=%x %s\n", + __entry->new_fte ? "add" : "set", + __entry->fte, __entry->fg, __entry->index, + __entry->group_index, __print_flags(__entry->action, "|", + ACTION_FLAGS), + __entry->flow_tag, + __parse_fs_hdrs(__entry->mask_enable, + __entry->mask_outer, + __entry->mask_misc, + __entry->mask_inner, + __entry->value_outer, + __entry->value_misc, + __entry->value_inner)) + ); + +TRACE_EVENT(mlx5_fs_del_fte, + TP_PROTO(const struct fs_fte *fte), + TP_ARGS(fte), + TP_STRUCT__entry( + __field(const struct fs_fte *, fte) + __field(u32, index) + ), + TP_fast_assign( + __entry->fte = fte; + __entry->index = fte->index; + + ), + TP_printk("fte=%p index=%u\n", + __entry->fte, __entry->index) + ); + +TRACE_EVENT(mlx5_fs_add_rule, + TP_PROTO(const struct mlx5_flow_rule *rule), + TP_ARGS(rule), + TP_STRUCT__entry( + __field(const struct mlx5_flow_rule *, rule) + __field(const struct fs_fte *, fte) + __field(u32, sw_action) + __field(u32, index) + __field(u32, counter_id) + __array(u8, destination, sizeof(struct mlx5_flow_destination)) + ), + TP_fast_assign( + __entry->rule = rule; + fs_get_obj(__entry->fte, rule->node.parent); + __entry->index = __entry->fte->dests_size - 1; + __entry->sw_action = rule->sw_action; + memcpy(__entry->destination, + &rule->dest_attr, + sizeof(__entry->destination)); + if (rule->dest_attr.type & MLX5_FLOW_DESTINATION_TYPE_COUNTER && + rule->dest_attr.counter) + __entry->counter_id = + rule->dest_attr.counter->id; + ), + TP_printk("rule=%p fte=%p index=%u sw_action=<%s> [dst] %s\n", + __entry->rule, __entry->fte, __entry->index, + __print_flags(__entry->sw_action, "|", ACTION_FLAGS), + __parse_fs_dst(__entry->destination, __entry->counter_id)) + ); + +TRACE_EVENT(mlx5_fs_del_rule, + TP_PROTO(const struct mlx5_flow_rule *rule), + TP_ARGS(rule), + TP_STRUCT__entry( + __field(const struct mlx5_flow_rule *, rule) + __field(const struct fs_fte *, fte) + ), + TP_fast_assign( + __entry->rule = rule; + fs_get_obj(__entry->fte, rule->node.parent); + ), + TP_printk("rule=%p fte=%p\n", + __entry->rule, __entry->fte) + ); +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ./diag +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE fs_tracepoint +#include diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 9704c2eb82a1..d731d57a996a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -36,6 +36,7 @@ #include "mlx5_core.h" #include "fs_core.h" #include "fs_cmd.h" +#include "diag/fs_tracepoint.h" #define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\ sizeof(struct init_tree_node)) @@ -404,6 +405,7 @@ static void del_rule(struct fs_node *node) fs_get_obj(fte, rule->node.parent); fs_get_obj(fg, fte->node.parent); fs_get_obj(ft, fg->node.parent); + trace_mlx5_fs_del_rule(rule); list_del(&rule->node.list); if (rule->sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) { mutex_lock(&rule->dest_attr.ft->lock); @@ -457,6 +459,7 @@ static void del_fte(struct fs_node *node) fs_get_obj(fte, node); fs_get_obj(fg, fte->node.parent); fs_get_obj(ft, fg->node.parent); + trace_mlx5_fs_del_fte(fte); dev = get_dev(&ft->node); err = mlx5_cmd_delete_fte(dev, ft, @@ -479,6 +482,7 @@ static void del_flow_group(struct fs_node *node) fs_get_obj(fg, node); fs_get_obj(ft, fg->node.parent); dev = get_dev(&ft->node); + trace_mlx5_fs_del_fg(fg); if (ft->autogroup.active) ft->autogroup.num_groups--; @@ -990,6 +994,7 @@ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table * /* Add node to group list */ list_add(&fg->node.list, prev_fg); + trace_mlx5_fs_add_fg(fg); return fg; err_remove_fg: @@ -1357,6 +1362,7 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, fte->action = old_action; goto unlock_fte; } else { + trace_mlx5_fs_set_fte(fte, false); goto add_rules; } } @@ -1378,10 +1384,13 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, tree_add_node(&fte->node, &fg->node); /* fte list isn't sorted */ list_add_tail(&fte->node.list, &fg->node.children); + trace_mlx5_fs_set_fte(fte, true); add_rules: for (i = 0; i < handle->num_rules; i++) { - if (atomic_read(&handle->rule[i]->node.refcount) == 1) + if (atomic_read(&handle->rule[i]->node.refcount) == 1) { tree_add_node(&handle->rule[i]->node, &fte->node); + trace_mlx5_fs_add_rule(handle->rule[i]); + } } unlock_fte: unlock_ref_node(&fte->node); -- cgit v1.2.3 From 23ca5ec3afefbd9330bd8d65cb2526ade21a6b96 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:01 +0200 Subject: mlxsw: spectrum_dpipe: Fix erif table op name space Fix ERIF's table operations name space. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index af2c65a3fd9f..e9e1f7aeb04f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -215,8 +215,8 @@ static int mlxsw_sp_erif_entry_get(struct mlxsw_sp *mlxsw_sp, } static int -mlxsw_sp_table_erif_entries_dump(void *priv, bool counters_enabled, - struct devlink_dpipe_dump_ctx *dump_ctx) +mlxsw_sp_dpipe_table_erif_entries_dump(void *priv, bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx) { struct devlink_dpipe_value match_value, action_value; struct devlink_dpipe_action action = {0}; @@ -279,7 +279,7 @@ err_entry_get: return err; } -static int mlxsw_sp_table_erif_counters_update(void *priv, bool enable) +static int mlxsw_sp_dpipe_table_erif_counters_update(void *priv, bool enable) { struct mlxsw_sp *mlxsw_sp = priv; int i; @@ -304,8 +304,8 @@ static int mlxsw_sp_table_erif_counters_update(void *priv, bool enable) static struct devlink_dpipe_table_ops mlxsw_sp_erif_ops = { .matches_dump = mlxsw_sp_dpipe_table_erif_matches_dump, .actions_dump = mlxsw_sp_dpipe_table_erif_actions_dump, - .entries_dump = mlxsw_sp_table_erif_entries_dump, - .counters_set_update = mlxsw_sp_table_erif_counters_update, + .entries_dump = mlxsw_sp_dpipe_table_erif_entries_dump, + .counters_set_update = mlxsw_sp_dpipe_table_erif_counters_update, }; static int mlxsw_sp_dpipe_erif_table_init(struct mlxsw_sp *mlxsw_sp) -- cgit v1.2.3 From ffd3cdccf214cf0df08856a6738544076c4cd548 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:02 +0200 Subject: devlink: Add support for dynamic table size Up until now the dpipe table's size was static and known at registration time. The host table does not have constant size and it is resized in dynamic manner. In order to support this behavior the size is changed to be obtained dynamically via an op. This patch also adjust the current dpipe table for the new API. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 13 +++++++++---- include/net/devlink.h | 7 +++---- net/core/devlink.c | 12 +++++++----- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index e9e1f7aeb04f..9eb265bd5430 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -301,24 +301,29 @@ static int mlxsw_sp_dpipe_table_erif_counters_update(void *priv, bool enable) return 0; } +static u64 mlxsw_sp_dpipe_table_erif_size_get(void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); +} + static struct devlink_dpipe_table_ops mlxsw_sp_erif_ops = { .matches_dump = mlxsw_sp_dpipe_table_erif_matches_dump, .actions_dump = mlxsw_sp_dpipe_table_erif_actions_dump, .entries_dump = mlxsw_sp_dpipe_table_erif_entries_dump, .counters_set_update = mlxsw_sp_dpipe_table_erif_counters_update, + .size_get = mlxsw_sp_dpipe_table_erif_size_get, }; static int mlxsw_sp_dpipe_erif_table_init(struct mlxsw_sp *mlxsw_sp) { struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); - u64 table_size; - table_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); return devlink_dpipe_table_register(devlink, MLXSW_SP_DPIPE_TABLE_NAME_ERIF, &mlxsw_sp_erif_ops, - mlxsw_sp, table_size, - false); + mlxsw_sp, false); } static void mlxsw_sp_dpipe_erif_table_fini(struct mlxsw_sp *mlxsw_sp) diff --git a/include/net/devlink.h b/include/net/devlink.h index 8ff8a6f77f29..e96272b2cfec 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -178,7 +178,6 @@ struct devlink_dpipe_table_ops; * struct devlink_dpipe_table - table object * @priv: private * @name: table name - * @size: maximum number of entries * @counters_enabled: indicates if counters are active * @counter_control_extern: indicates if counter control is in dpipe or * external tool @@ -189,7 +188,6 @@ struct devlink_dpipe_table { void *priv; struct list_head list; const char *name; - u64 size; bool counters_enabled; bool counter_control_extern; struct devlink_dpipe_table_ops *table_ops; @@ -204,6 +202,7 @@ struct devlink_dpipe_table { * @counters_set_update - when changing the counter status hardware sync * maybe needed to allocate/free counter related * resources + * @size_get - get size */ struct devlink_dpipe_table_ops { int (*actions_dump)(void *priv, struct sk_buff *skb); @@ -211,6 +210,7 @@ struct devlink_dpipe_table_ops { int (*entries_dump)(void *priv, bool counters_enabled, struct devlink_dpipe_dump_ctx *dump_ctx); int (*counters_set_update)(void *priv, bool enable); + u64 (*size_get)(void *priv); }; /** @@ -311,8 +311,7 @@ void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index); int devlink_dpipe_table_register(struct devlink *devlink, const char *table_name, struct devlink_dpipe_table_ops *table_ops, - void *priv, u64 size, - bool counter_control_extern); + void *priv, bool counter_control_extern); void devlink_dpipe_table_unregister(struct devlink *devlink, const char *table_name); int devlink_dpipe_headers_register(struct devlink *devlink, diff --git a/net/core/devlink.c b/net/core/devlink.c index fcf5dd662882..87062ff36ab4 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1647,13 +1647,15 @@ static int devlink_dpipe_table_put(struct sk_buff *skb, struct devlink_dpipe_table *table) { struct nlattr *table_attr; + u64 table_size; + table_size = table->table_ops->size_get(table->priv); table_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE); if (!table_attr) return -EMSGSIZE; if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_TABLE_NAME, table->name) || - nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table->size, + nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table_size, DEVLINK_ATTR_PAD)) goto nla_put_failure; if (nla_put_u8(skb, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, @@ -2718,20 +2720,21 @@ EXPORT_SYMBOL_GPL(devlink_dpipe_table_counter_enabled); * @table_name: table name * @table_ops: table ops * @priv: priv - * @size: size * @counter_control_extern: external control for counters */ int devlink_dpipe_table_register(struct devlink *devlink, const char *table_name, struct devlink_dpipe_table_ops *table_ops, - void *priv, u64 size, - bool counter_control_extern) + void *priv, bool counter_control_extern) { struct devlink_dpipe_table *table; if (devlink_dpipe_table_find(&devlink->dpipe_table_list, table_name)) return -EEXIST; + if (WARN_ON(!table_ops->size_get)) + return -EINVAL; + table = kzalloc(sizeof(*table), GFP_KERNEL); if (!table) return -ENOMEM; @@ -2739,7 +2742,6 @@ int devlink_dpipe_table_register(struct devlink *devlink, table->name = table_name; table->table_ops = table_ops; table->priv = priv; - table->size = size; table->counter_control_extern = counter_control_extern; mutex_lock(&devlink_mutex); -- cgit v1.2.3 From 3580732448f128c39e7325912bc4368ade5dce7d Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:03 +0200 Subject: devlink: Move dpipe entry clear function into devlink The entry clear routine can be shared between the drivers, thus it is moved inside devlink. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 24 ++-------------------- include/net/devlink.h | 6 ++++++ net/core/devlink.c | 22 ++++++++++++++++++++ 3 files changed, 30 insertions(+), 22 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 9eb265bd5430..1305df9c6bf9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -114,26 +114,6 @@ static int mlxsw_sp_dpipe_table_erif_matches_dump(void *priv, return devlink_dpipe_match_put(skb, &match); } -static void mlxsw_sp_erif_entry_clear(struct devlink_dpipe_entry *entry) -{ - unsigned int value_count, value_index; - struct devlink_dpipe_value *value; - - value = entry->action_values; - value_count = entry->action_values_count; - for (value_index = 0; value_index < value_count; value_index++) { - kfree(value[value_index].value); - kfree(value[value_index].mask); - } - - value = entry->match_values; - value_count = entry->match_values_count; - for (value_index = 0; value_index < value_count; value_index++) { - kfree(value[value_index].value); - kfree(value[value_index].mask); - } -} - static void mlxsw_sp_erif_match_action_prepare(struct devlink_dpipe_match *match, struct devlink_dpipe_action *action) @@ -270,12 +250,12 @@ start_again: goto start_again; rtnl_unlock(); - mlxsw_sp_erif_entry_clear(&entry); + devlink_dpipe_entry_clear(&entry); return 0; err_entry_append: err_entry_get: rtnl_unlock(); - mlxsw_sp_erif_entry_clear(&entry); + devlink_dpipe_entry_clear(&entry); return err; } diff --git a/include/net/devlink.h b/include/net/devlink.h index e96272b2cfec..047a0c54f652 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -323,6 +323,7 @@ int devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx); int devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx, struct devlink_dpipe_entry *entry); int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx); +void devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry); int devlink_dpipe_action_put(struct sk_buff *skb, struct devlink_dpipe_action *action); int devlink_dpipe_match_put(struct sk_buff *skb, @@ -448,6 +449,11 @@ devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx) return 0; } +static inline void +devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry) +{ +} + static inline int devlink_dpipe_action_put(struct sk_buff *skb, struct devlink_dpipe_action *action) diff --git a/net/core/devlink.c b/net/core/devlink.c index 87062ff36ab4..194708aa5f11 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1996,6 +1996,28 @@ int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx) } EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_close); +void devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry) + +{ + unsigned int value_count, value_index; + struct devlink_dpipe_value *value; + + value = entry->action_values; + value_count = entry->action_values_count; + for (value_index = 0; value_index < value_count; value_index++) { + kfree(value[value_index].value); + kfree(value[value_index].mask); + } + + value = entry->match_values; + value_count = entry->match_values_count; + for (value_index = 0; value_index < value_count; value_index++) { + kfree(value[value_index].value); + kfree(value[value_index].mask); + } +} +EXPORT_SYMBOL(devlink_dpipe_entry_clear); + static int devlink_dpipe_entries_fill(struct genl_info *info, enum devlink_command cmd, int flags, struct devlink_dpipe_table *table) -- cgit v1.2.3 From f17cc84d1c2f661cb29cb7efc58f9d654ef3c643 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:04 +0200 Subject: mlxsw: spectrum_router: Add helpers for neighbor access This is done as a preparation before introducing the ability to dump the host table via dpipe, and to count the table size. The mlxsw's neighbor representative struct stays private to the router module. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 36 ++++++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_router.h | 13 ++++++++ 2 files changed, 49 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index a0a9728af989..0b989e16a424 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -909,6 +909,42 @@ static const struct rhashtable_params mlxsw_sp_neigh_ht_params = { .key_len = sizeof(struct mlxsw_sp_neigh_key), }; +struct mlxsw_sp_neigh_entry * +mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif, + struct mlxsw_sp_neigh_entry *neigh_entry) +{ + if (!neigh_entry) { + if (list_empty(&rif->neigh_list)) + return NULL; + else + return list_first_entry(&rif->neigh_list, + typeof(*neigh_entry), + rif_list_node); + } + if (neigh_entry->rif_list_node.next == &rif->neigh_list) + return NULL; + return list_next_entry(neigh_entry, rif_list_node); +} + +int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry) +{ + return neigh_entry->key.n->tbl->family; +} + +unsigned char * +mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry) +{ + return neigh_entry->ha; +} + +u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry) +{ + struct neighbour *n; + + n = neigh_entry->key.n; + return ntohl(*((__be32 *) n->primary_key)); +} + static struct mlxsw_sp_neigh_entry * mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n, u16 rif) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index a3e8d2b25148..80ac032d64a2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -42,6 +42,8 @@ enum mlxsw_sp_rif_counter_dir { MLXSW_SP_RIF_COUNTER_EGRESS, }; +struct mlxsw_sp_neigh_entry; + struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp, u16 rif_index); u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif); @@ -56,5 +58,16 @@ void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp, int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *rif, enum mlxsw_sp_rif_counter_dir dir); +struct mlxsw_sp_neigh_entry * +mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif, + struct mlxsw_sp_neigh_entry *neigh_entry); +int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry); +unsigned char * +mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry); +u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry); + +#define mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) \ + for (neigh_entry = mlxsw_sp_rif_neigh_next(rif, NULL); neigh_entry; \ + neigh_entry = mlxsw_sp_rif_neigh_next(rif, neigh_entry)) #endif /* _MLXSW_ROUTER_H_*/ -- cgit v1.2.3 From 7e57ae9fc5fc7fa056bfb7fd60ac0d9df938b174 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:05 +0200 Subject: mlxsw: spectrum_dpipe: Fix label name Change label name for case of erif table init failure. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 1305df9c6bf9..6310c2c204c7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -324,10 +324,10 @@ int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) return err; err = mlxsw_sp_dpipe_erif_table_init(mlxsw_sp); if (err) - goto err_erif_register; + goto err_erif_table_init; return 0; -err_erif_register: +err_erif_table_init: devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core)); return err; } -- cgit v1.2.3 From 6aecb36bc01fc9c84f24c4155e8850b6a1962f03 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:06 +0200 Subject: mlxsw: spectrum_dpipe: Add IPv4 host table initial support Add IPv4 host table initial support. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 102 +++++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.h | 1 + 2 files changed, 103 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 6310c2c204c7..9c2b25a146e5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -74,6 +74,8 @@ static struct devlink_dpipe_header mlxsw_sp_dpipe_header_metadata = { static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = { &mlxsw_sp_dpipe_header_metadata, + &devlink_dpipe_header_ethernet, + &devlink_dpipe_header_ipv4, }; static struct devlink_dpipe_headers mlxsw_sp_dpipe_headers = { @@ -313,6 +315,99 @@ static void mlxsw_sp_dpipe_erif_table_fini(struct mlxsw_sp *mlxsw_sp) devlink_dpipe_table_unregister(devlink, MLXSW_SP_DPIPE_TABLE_NAME_ERIF); } +static int mlxsw_sp_dpipe_table_host_matches_dump(struct sk_buff *skb, int type) +{ + struct devlink_dpipe_match match = {0}; + int err; + + match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match.header = &mlxsw_sp_dpipe_header_metadata; + match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT; + + err = devlink_dpipe_match_put(skb, &match); + if (err) + return err; + + match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match.header = &devlink_dpipe_header_ipv4; + match.field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP; + + return devlink_dpipe_match_put(skb, &match); +} + +static int +mlxsw_sp_dpipe_table_host4_matches_dump(void *priv, struct sk_buff *skb) +{ + return mlxsw_sp_dpipe_table_host_matches_dump(skb, AF_INET); +} + +static int +mlxsw_sp_dpipe_table_host4_actions_dump(void *priv, struct sk_buff *skb) +{ + struct devlink_dpipe_action action = {0}; + + action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY; + action.header = &devlink_dpipe_header_ethernet; + action.field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC; + + return devlink_dpipe_action_put(skb, &action); +} + +static u64 +mlxsw_sp_dpipe_table_host_size_get(struct mlxsw_sp *mlxsw_sp, int type) +{ + u64 size = 0; + int i; + + rtnl_lock(); + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { + struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i); + struct mlxsw_sp_neigh_entry *neigh_entry; + + if (!rif) + continue; + mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { + if (mlxsw_sp_neigh_entry_type(neigh_entry) != type) + continue; + size++; + } + } + rtnl_unlock(); + + return size; +} + +static u64 mlxsw_sp_dpipe_table_host4_size_get(void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return mlxsw_sp_dpipe_table_host_size_get(mlxsw_sp, AF_INET); +} + +static struct devlink_dpipe_table_ops mlxsw_sp_host4_ops = { + .matches_dump = mlxsw_sp_dpipe_table_host4_matches_dump, + .actions_dump = mlxsw_sp_dpipe_table_host4_actions_dump, + .size_get = mlxsw_sp_dpipe_table_host4_size_get, +}; + +static int mlxsw_sp_dpipe_host4_table_init(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + return devlink_dpipe_table_register(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_HOST4, + &mlxsw_sp_host4_ops, + mlxsw_sp, false); +} + +static void mlxsw_sp_dpipe_host4_table_fini(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + devlink_dpipe_table_unregister(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_HOST4); +} + int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) { struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); @@ -325,8 +420,14 @@ int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) err = mlxsw_sp_dpipe_erif_table_init(mlxsw_sp); if (err) goto err_erif_table_init; + + err = mlxsw_sp_dpipe_host4_table_init(mlxsw_sp); + if (err) + goto err_host4_table_init; return 0; +err_host4_table_init: + mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp); err_erif_table_init: devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core)); return err; @@ -336,6 +437,7 @@ void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp) { struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp); mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp); devlink_dpipe_headers_unregister(devlink); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h index d2089298cba3..7785f9e26a18 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h @@ -39,5 +39,6 @@ int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp); #define MLXSW_SP_DPIPE_TABLE_NAME_ERIF "mlxsw_erif" +#define MLXSW_SP_DPIPE_TABLE_NAME_HOST4 "mlxsw_host4" #endif /* _MLXSW_PIPELINE_H_*/ -- cgit v1.2.3 From 6bba7e20da12022efb24b5ed2991c904fde9c8e1 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:07 +0200 Subject: mlxsw: reg: Make flow counter set type enum to be shared This is done as a preparation before introducing support for neighbor counters. The flow counter's type enum is used by many registers, yet, until now it was used only by mgpc and thus it was private. This patch updates the namespace for more generic usage. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 20 ++++++++++---------- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 7e8ba546c3a4..bcf55f6f94f3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5000,6 +5000,15 @@ enum mlxsw_reg_rauht_trap_id { */ MLXSW_ITEM32(reg, rauht, trap_id, 0x60, 0, 9); +enum mlxsw_reg_flow_counter_set_type { + /* No count */ + MLXSW_REG_FLOW_COUNTER_SET_TYPE_NO_COUNT = 0x00, + /* Count packets and bytes */ + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES = 0x03, + /* Count only packets */ + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS = 0x05, +}; + /* reg_rauht_counter_set_type * Counter set type for flow counters * Access: RW @@ -6041,15 +6050,6 @@ static inline void mlxsw_reg_mpsc_pack(char *payload, u8 local_port, bool e, MLXSW_REG_DEFINE(mgpc, MLXSW_REG_MGPC_ID, MLXSW_REG_MGPC_LEN); -enum mlxsw_reg_mgpc_counter_set_type { - /* No count */ - MLXSW_REG_MGPC_COUNTER_SET_TYPE_NO_COUT = 0x00, - /* Count packets and bytes */ - MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS_BYTES = 0x03, - /* Count only packets */ - MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS = 0x05, -}; - /* reg_mgpc_counter_set_type * Counter set type. * Access: OP @@ -6089,7 +6089,7 @@ MLXSW_ITEM64(reg, mgpc, packet_counter, 0x10, 0, 64); static inline void mlxsw_reg_mgpc_pack(char *payload, u32 counter_index, enum mlxsw_reg_mgpc_opcode opcode, - enum mlxsw_reg_mgpc_counter_set_type set_type) + enum mlxsw_reg_flow_counter_set_type set_type) { MLXSW_REG_ZERO(mgpc, payload); mlxsw_reg_mgpc_counter_index_set(payload, counter_index); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 6e641db16702..5332b8f0b3f7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -382,7 +382,7 @@ int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp, int err; mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_NOP, - MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS_BYTES); + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl); if (err) return err; @@ -397,7 +397,7 @@ static int mlxsw_sp_flow_counter_clear(struct mlxsw_sp *mlxsw_sp, char mgpc_pl[MLXSW_REG_MGPC_LEN]; mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_CLEAR, - MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS_BYTES); + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl); } -- cgit v1.2.3 From 7cfcbc7591e30aeebbfd3f7b0ff615b5ee4f1da7 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:08 +0200 Subject: mlxsw: spectrum_router: Add support for setting counters on neighbors Add support for setting counters on neighbors based on dpipe's host table counter status. This patch also adds the ability for getting the counter value, which will be used by the dpipe host table implementation in the next patches. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 8 ++++ drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 6 ++- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 56 ++++++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_router.h | 3 ++ 4 files changed, 71 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index bcf55f6f94f3..11e290c34aaf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5054,6 +5054,14 @@ static inline void mlxsw_reg_rauht_pack6(char *payload, mlxsw_reg_rauht_dip6_memcpy_to(payload, dip); } +static inline void mlxsw_reg_rauht_pack_counter(char *payload, + u64 counter_index) +{ + mlxsw_reg_rauht_counter_index_set(payload, counter_index); + mlxsw_reg_rauht_counter_set_type_set(payload, + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES); +} + /* RALEU - Router Algorithmic LPM ECMP Update Register * --------------------------------------------------- * The register enables updating the ECMP section in the action for multiple diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 5332b8f0b3f7..17fc98881642 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -386,8 +386,10 @@ int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp, err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl); if (err) return err; - *packets = mlxsw_reg_mgpc_packet_counter_get(mgpc_pl); - *bytes = mlxsw_reg_mgpc_byte_counter_get(mgpc_pl); + if (packets) + *packets = mlxsw_reg_mgpc_packet_counter_get(mgpc_pl); + if (bytes) + *bytes = mlxsw_reg_mgpc_byte_counter_get(mgpc_pl); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 0b989e16a424..abccd84235eb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -901,6 +901,8 @@ struct mlxsw_sp_neigh_entry { * this neigh entry */ struct list_head nexthop_neighs_list_node; + unsigned int counter_index; + bool counter_valid; }; static const struct rhashtable_params mlxsw_sp_neigh_ht_params = { @@ -945,6 +947,17 @@ u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry) return ntohl(*((__be32 *) n->primary_key)); } +int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + u64 *p_counter) +{ + if (!neigh_entry->counter_valid) + return -EINVAL; + + return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index, + p_counter, NULL); +} + static struct mlxsw_sp_neigh_entry * mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n, u16 rif) @@ -985,6 +998,41 @@ mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_neigh_ht_params); } +static bool +mlxsw_sp_neigh4_counter_should_alloc(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink; + + devlink = priv_to_devlink(mlxsw_sp->core); + return devlink_dpipe_table_counter_enabled(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_HOST4); +} + +static void +mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry) +{ + if (mlxsw_sp_neigh_entry_type(neigh_entry) != AF_INET || + !mlxsw_sp_neigh4_counter_should_alloc(mlxsw_sp)) + return; + + if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index)) + return; + + neigh_entry->counter_valid = true; +} + +static void +mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry) +{ + if (!neigh_entry->counter_valid) + return; + mlxsw_sp_flow_counter_free(mlxsw_sp, + neigh_entry->counter_index); + neigh_entry->counter_valid = false; +} + static struct mlxsw_sp_neigh_entry * mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) { @@ -1004,6 +1052,7 @@ mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) if (err) goto err_neigh_entry_insert; + mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry); list_add(&neigh_entry->rif_list_node, &rif->neigh_list); return neigh_entry; @@ -1018,6 +1067,7 @@ mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_neigh_entry *neigh_entry) { list_del(&neigh_entry->rif_list_node); + mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry); mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry); mlxsw_sp_neigh_entry_free(neigh_entry); } @@ -1323,6 +1373,9 @@ mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp, mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha, dip); + if (neigh_entry->counter_valid) + mlxsw_reg_rauht_pack_counter(rauht_pl, + neigh_entry->counter_index); mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); } @@ -1337,6 +1390,9 @@ mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp, mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha, dip); + if (neigh_entry->counter_valid) + mlxsw_reg_rauht_pack_counter(rauht_pl, + neigh_entry->counter_index); mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index 80ac032d64a2..aafae469be3a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -69,5 +69,8 @@ u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry); #define mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) \ for (neigh_entry = mlxsw_sp_rif_neigh_next(rif, NULL); neigh_entry; \ neigh_entry = mlxsw_sp_rif_neigh_next(rif, neigh_entry)) +int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + u64 *p_counter); #endif /* _MLXSW_ROUTER_H_*/ -- cgit v1.2.3 From a86f030915f247b70da051506a0e3d889a4002d8 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:09 +0200 Subject: mlxsw: spectrum_dpipe: Add support for IPv4 host table dump Add support for IPv4 host table dump. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 247 +++++++++++++++++++++ 1 file changed, 247 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 9c2b25a146e5..94d954bbee16 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -353,6 +353,252 @@ mlxsw_sp_dpipe_table_host4_actions_dump(void *priv, struct sk_buff *skb) return devlink_dpipe_action_put(skb, &action); } +enum mlxsw_sp_dpipe_table_host_match { + MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF, + MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP, + MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT, +}; + +static void +mlxsw_sp_dpipe_table_host_match_action_prepare(struct devlink_dpipe_match *matches, + struct devlink_dpipe_action *action, + int type) +{ + struct devlink_dpipe_match *match; + + match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF]; + match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match->header = &mlxsw_sp_dpipe_header_metadata; + match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT; + + match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; + match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match->header = &devlink_dpipe_header_ipv4; + match->field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP; + + action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY; + action->header = &devlink_dpipe_header_ethernet; + action->field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC; +} + +static int +mlxsw_sp_dpipe_table_host_entry_prepare(struct devlink_dpipe_entry *entry, + struct devlink_dpipe_value *match_values, + struct devlink_dpipe_match *matches, + struct devlink_dpipe_value *action_value, + struct devlink_dpipe_action *action, + int type) +{ + struct devlink_dpipe_value *match_value; + struct devlink_dpipe_match *match; + + entry->match_values = match_values; + entry->match_values_count = MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT; + + entry->action_values = action_value; + entry->action_values_count = 1; + + match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF]; + match_value = &match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF]; + + match_value->match = match; + match_value->value_size = sizeof(u32); + match_value->value = kmalloc(match_value->value_size, GFP_KERNEL); + if (!match_value->value) + return -ENOMEM; + + match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; + match_value = &match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; + + match_value->match = match; + match_value->value_size = sizeof(u32); + match_value->value = kmalloc(match_value->value_size, GFP_KERNEL); + if (!match_value->value) + return -ENOMEM; + + action_value->action = action; + action_value->value_size = sizeof(u64); + action_value->value = kmalloc(action_value->value_size, GFP_KERNEL); + if (!action_value->value) + return -ENOMEM; + + return 0; +} + +static void +__mlxsw_sp_dpipe_table_host4_entry_fill(struct devlink_dpipe_entry *entry, + struct mlxsw_sp_rif *rif, + unsigned char *ha, u32 dip) +{ + struct devlink_dpipe_value *value; + u32 *rif_value; + u32 *dip_value; + u8 *ha_value; + + /* Set Match RIF index */ + value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF]; + + rif_value = value->value; + *rif_value = mlxsw_sp_rif_index(rif); + value->mapping_value = mlxsw_sp_rif_dev_ifindex(rif); + value->mapping_valid = true; + + /* Set Match DIP */ + value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; + + dip_value = value->value; + *dip_value = dip; + + /* Set Action DMAC */ + value = entry->action_values; + ha_value = value->value; + ether_addr_copy(ha_value, ha); +} + +static void +mlxsw_sp_dpipe_table_host4_entry_fill(struct devlink_dpipe_entry *entry, + struct mlxsw_sp_neigh_entry *neigh_entry, + struct mlxsw_sp_rif *rif) +{ + unsigned char *ha; + u32 dip; + + ha = mlxsw_sp_neigh_entry_ha(neigh_entry); + dip = mlxsw_sp_neigh4_entry_dip(neigh_entry); + __mlxsw_sp_dpipe_table_host4_entry_fill(entry, rif, ha, dip); +} + +static void +mlxsw_sp_dpipe_table_host_entry_fill(struct mlxsw_sp *mlxsw_sp, + struct devlink_dpipe_entry *entry, + struct mlxsw_sp_neigh_entry *neigh_entry, + struct mlxsw_sp_rif *rif, + int type) +{ + int err; + + mlxsw_sp_dpipe_table_host4_entry_fill(entry, neigh_entry, rif); + err = mlxsw_sp_neigh_counter_get(mlxsw_sp, neigh_entry, + &entry->counter); + if (!err) + entry->counter_valid = true; +} + +static int +mlxsw_sp_dpipe_table_host_entries_get(struct mlxsw_sp *mlxsw_sp, + struct devlink_dpipe_entry *entry, + bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx, + int type) +{ + int rif_neigh_count = 0; + int rif_neigh_skip = 0; + int neigh_count = 0; + int rif_count; + int i, j; + int err; + + rtnl_lock(); + i = 0; + rif_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); +start_again: + err = devlink_dpipe_entry_ctx_prepare(dump_ctx); + if (err) + goto err_ctx_prepare; + j = 0; + rif_neigh_skip = rif_neigh_count; + for (; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { + struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i); + struct mlxsw_sp_neigh_entry *neigh_entry; + + if (!rif) + continue; + + rif_neigh_count = 0; + mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { + if (rif_neigh_count < rif_neigh_skip) + goto skip; + + mlxsw_sp_dpipe_table_host_entry_fill(mlxsw_sp, entry, + neigh_entry, rif, + type); + entry->index = neigh_count; + err = devlink_dpipe_entry_ctx_append(dump_ctx, entry); + if (err) { + if (err == -EMSGSIZE) { + if (!j) + goto err_entry_append; + else + goto out; + } + goto err_entry_append; + } + neigh_count++; + j++; +skip: + rif_neigh_count++; + } + rif_neigh_skip = 0; + } +out: + devlink_dpipe_entry_ctx_close(dump_ctx); + if (i != rif_count) + goto start_again; + + rtnl_unlock(); + return 0; + +err_ctx_prepare: +err_entry_append: + rtnl_unlock(); + return err; +} + +static int +mlxsw_sp_dpipe_table_host_entries_dump(struct mlxsw_sp *mlxsw_sp, + bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx, + int type) +{ + struct devlink_dpipe_value match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT]; + struct devlink_dpipe_match matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT]; + struct devlink_dpipe_value action_value; + struct devlink_dpipe_action action = {0}; + struct devlink_dpipe_entry entry = {0}; + int err; + + memset(matches, 0, MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT * + sizeof(matches[0])); + memset(match_values, 0, MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT * + sizeof(match_values[0])); + memset(&action_value, 0, sizeof(action_value)); + + mlxsw_sp_dpipe_table_host_match_action_prepare(matches, &action, type); + err = mlxsw_sp_dpipe_table_host_entry_prepare(&entry, match_values, + matches, &action_value, + &action, type); + if (err) + goto out; + + err = mlxsw_sp_dpipe_table_host_entries_get(mlxsw_sp, &entry, + counters_enabled, dump_ctx, + type); +out: + devlink_dpipe_entry_clear(&entry); + return err; +} + +static int +mlxsw_sp_dpipe_table_host4_entries_dump(void *priv, bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return mlxsw_sp_dpipe_table_host_entries_dump(mlxsw_sp, + counters_enabled, + dump_ctx, AF_INET); +} + static u64 mlxsw_sp_dpipe_table_host_size_get(struct mlxsw_sp *mlxsw_sp, int type) { @@ -387,6 +633,7 @@ static u64 mlxsw_sp_dpipe_table_host4_size_get(void *priv) static struct devlink_dpipe_table_ops mlxsw_sp_host4_ops = { .matches_dump = mlxsw_sp_dpipe_table_host4_matches_dump, .actions_dump = mlxsw_sp_dpipe_table_host4_actions_dump, + .entries_dump = mlxsw_sp_dpipe_table_host4_entries_dump, .size_get = mlxsw_sp_dpipe_table_host4_size_get, }; -- cgit v1.2.3 From a481d71323980da1da74bdfd2c7e01040ec66371 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:10 +0200 Subject: mlxsw: spectrum_dpipe: Add support for controlling neighbor counters Add support for controlling neighbor counters via dpipe. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 33 ++++++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 12 ++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_router.h | 4 +++ 3 files changed, 49 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 94d954bbee16..3c8599f14bff 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -599,6 +599,38 @@ mlxsw_sp_dpipe_table_host4_entries_dump(void *priv, bool counters_enabled, dump_ctx, AF_INET); } +static void +mlxsw_sp_dpipe_table_host_counters_update(struct mlxsw_sp *mlxsw_sp, + bool enable, int type) +{ + int i; + + rtnl_lock(); + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { + struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i); + struct mlxsw_sp_neigh_entry *neigh_entry; + + if (!rif) + continue; + mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { + if (mlxsw_sp_neigh_entry_type(neigh_entry) != type) + continue; + mlxsw_sp_neigh_entry_counter_update(mlxsw_sp, + neigh_entry, + enable); + } + } + rtnl_unlock(); +} + +static int mlxsw_sp_dpipe_table_host4_counters_update(void *priv, bool enable) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + mlxsw_sp_dpipe_table_host_counters_update(mlxsw_sp, enable, AF_INET); + return 0; +} + static u64 mlxsw_sp_dpipe_table_host_size_get(struct mlxsw_sp *mlxsw_sp, int type) { @@ -634,6 +666,7 @@ static struct devlink_dpipe_table_ops mlxsw_sp_host4_ops = { .matches_dump = mlxsw_sp_dpipe_table_host4_matches_dump, .actions_dump = mlxsw_sp_dpipe_table_host4_actions_dump, .entries_dump = mlxsw_sp_dpipe_table_host4_entries_dump, + .counters_set_update = mlxsw_sp_dpipe_table_host4_counters_update, .size_get = mlxsw_sp_dpipe_table_host4_size_get, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index abccd84235eb..377d85cff7fa 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1429,6 +1429,18 @@ mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp, } } +void +mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + bool adding) +{ + if (adding) + mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry); + else + mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry); + mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true); +} + struct mlxsw_sp_neigh_event_work { struct work_struct work; struct mlxsw_sp *mlxsw_sp; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index aafae469be3a..fb0f971c670c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -72,5 +72,9 @@ u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry); int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_neigh_entry *neigh_entry, u64 *p_counter); +void +mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + bool adding); #endif /* _MLXSW_ROUTER_H_*/ -- cgit v1.2.3 From 2886447dc5add28226451c163cd9ac1d1b3bf59a Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Thu, 24 Aug 2017 12:33:13 +0200 Subject: ixgbe: use return codes from ndo_xdp_xmit that are distinguishable For XDP_REDIRECT the use of return code -EINVAL is confusing, as it is used in three different cases. (1) When the index or ifindex lookup fails, and in the ixgbe driver (2) when link is down and (3) when XDP have not been enabled. The return code can be picked up by the tracepoint xdp:xdp_redirect for diagnosing why XDP_REDIRECT isn't working. Thus, there is a need different return codes to tell the issues apart. I'm considering using a specific err-code scheme for XDP_REDIRECT instead of using these errno codes. Signed-off-by: Jesper Dangaard Brouer Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 8d3224ad6434..d962368d08d0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9849,14 +9849,14 @@ static int ixgbe_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp) int err; if (unlikely(test_bit(__IXGBE_DOWN, &adapter->state))) - return -EINVAL; + return -ENETDOWN; /* During program transitions its possible adapter->xdp_prog is assigned * but ring has not been configured yet. In this case simply abort xmit. */ ring = adapter->xdp_prog ? adapter->xdp_ring[smp_processor_id()] : NULL; if (unlikely(!ring)) - return -EINVAL; + return -ENXIO; err = ixgbe_xmit_xdp_ring(adapter, xdp); if (err != IXGBE_XDP_TX) -- cgit v1.2.3 From 3f2c5fb2d81656e79cdfbe78a5790c35993ed1ff Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 23 Aug 2017 18:17:39 +0530 Subject: net/mlx4_core: make mlx4_profile const Make these const as they are only used in a copy operation. Signed-off-by: Bhumika Goyal Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 9ea2b0db6229..8404e165eca4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -121,7 +121,7 @@ static char mlx4_version[] = DRV_NAME ": Mellanox ConnectX core driver v" DRV_VERSION "\n"; -static struct mlx4_profile default_profile = { +static const struct mlx4_profile default_profile = { .num_qp = 1 << 18, .num_srq = 1 << 16, .rdmarc_per_qp = 1 << 4, @@ -131,7 +131,7 @@ static struct mlx4_profile default_profile = { .num_mtt = 1 << 20, /* It is really num mtt segements */ }; -static struct mlx4_profile low_mem_profile = { +static const struct mlx4_profile low_mem_profile = { .num_qp = 1 << 17, .num_srq = 1 << 6, .rdmarc_per_qp = 1 << 4, -- cgit v1.2.3 From 39a7e5892418514db114ea4842d7d88aee6a61b8 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 23 Aug 2017 18:22:01 +0530 Subject: net/mlx5e: make mlx5e_profile const Make this const as it is only passed as an argument to the function mlx5e_create_netdev and the corresponding argument is of type const. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 45c088c10ee1..45e03c427faf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -924,7 +924,7 @@ static int mlx5e_get_rep_max_num_channels(struct mlx5_core_dev *mdev) return MLX5E_PORT_REPRESENTOR_NCH; } -static struct mlx5e_profile mlx5e_rep_profile = { +static const struct mlx5e_profile mlx5e_rep_profile = { .init = mlx5e_init_rep, .init_rx = mlx5e_init_rep_rx, .cleanup_rx = mlx5e_cleanup_rep_rx, -- cgit v1.2.3 From 1e9d8e7ad3acd379d071e276f930f39bdebe3727 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 24 Aug 2017 17:47:11 -0700 Subject: tg3: Be drop monitor friendly tg3_tx() does the normal packet TX completion, tigon3_dma_hwbug_workaround() and tg3_tso_bug() both need to allocate a new SKB that is suitable to workaround HW bugs, and finally tg3_free_rings() is doing ring cleanup. Use dev_consume_skb_any() for these 3 locations to be SKB drop monitor friendly. Signed-off-by: Florian Fainelli Acked-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index d600c41fb1dc..af33dc15c55f 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -6587,7 +6587,7 @@ static void tg3_tx(struct tg3_napi *tnapi) pkts_compl++; bytes_compl += skb->len; - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); if (unlikely(tx_bug)) { tg3_tx_recover(tp); @@ -7829,7 +7829,7 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi, } } - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); *pskb = new_skb; return ret; } @@ -7882,7 +7882,7 @@ static int tg3_tso_bug(struct tg3 *tp, struct tg3_napi *tnapi, } while (segs); tg3_tso_bug_end: - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); return NETDEV_TX_OK; } @@ -8543,7 +8543,7 @@ static void tg3_free_rings(struct tg3 *tp) tg3_tx_skb_unmap(tnapi, i, skb_shinfo(skb)->nr_frags - 1); - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); } netdev_tx_reset_queue(netdev_get_tx_queue(tp->dev, j)); } -- cgit v1.2.3 From 43cee2d2465cab8289dc1d9206b90fcf975a604f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 24 Aug 2017 20:55:40 -0700 Subject: net: mv643xx_eth: Be drop monitor friendly txq_reclaim() does the normal transmit queue reclamation and rxq_deinit() does the RX ring cleanup, none of these are packet drops, so use dev_consume_skb() for both locations. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index fb2d533ae4ef..81c1fac00d33 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1121,7 +1121,7 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) struct sk_buff *skb = __skb_dequeue(&txq->tx_skb); if (!WARN_ON(!skb)) - dev_kfree_skb(skb); + dev_consume_skb_any(skb); } if (cmd_sts & ERROR_SUMMARY) { @@ -2024,7 +2024,7 @@ static void rxq_deinit(struct rx_queue *rxq) for (i = 0; i < rxq->rx_ring_size; i++) { if (rxq->rx_skb[i]) { - dev_kfree_skb(rxq->rx_skb[i]); + dev_consume_skb_any(rxq->rx_skb[i]); rxq->rx_desc_count--; } } -- cgit v1.2.3 From 256fbe111218ccc3de4aff1aab44c9b08cd06c92 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 24 Aug 2017 13:47:39 +0300 Subject: hinic: uninitialized variable in hinic_api_cmd_init() We never set the error code in this function. Fixes: eabf0fad81d5 ("net-next/hinic: Initialize api cmd resources") Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c index 8901801fe426..c40603a183df 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c @@ -941,6 +941,7 @@ int hinic_api_cmd_init(struct hinic_api_cmd_chain **chain, if (IS_ERR(chain[chain_type])) { dev_err(&pdev->dev, "Failed to create chain %d\n", chain_type); + err = PTR_ERR(chain[chain_type]); goto err_create_chain; } } -- cgit v1.2.3 From c6f71c418fcc46f59fc87ac93ce9336c12d7898b Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Thu, 24 Aug 2017 11:50:02 -0700 Subject: hv_netvsc: Fix rndis_filter_close error during netvsc_remove We now remove rndis filter before unregister_netdev(), which calls device close. It involves closing rndis filter already removed. This patch fixes this error. Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index c0c4c9195a3f..fac44c5c8d0d 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -119,12 +119,16 @@ static int netvsc_close(struct net_device *net) struct net_device *vf_netdev = rtnl_dereference(net_device_ctx->vf_netdev); struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); - int ret; + int ret = 0; u32 aread, i, msec = 10, retry = 0, retry_max = 20; struct vmbus_channel *chn; netif_tx_disable(net); + /* No need to close rndis filter if it is removed already */ + if (!nvdev) + goto out; + ret = rndis_filter_close(nvdev); if (ret != 0) { netdev_err(net, "unable to close device (ret %d).\n", ret); @@ -163,6 +167,7 @@ static int netvsc_close(struct net_device *net) ret = -ETIMEDOUT; } +out: if (vf_netdev) dev_close(vf_netdev); -- cgit v1.2.3 From 1e3a5fd5c0492d2cd37d86dce82ac7899136123f Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 23 Jun 2017 04:24:43 -0400 Subject: i40e/i40evf: adjust packet size to account for double VLANs Now that the kernel supports double VLAN tags, we should at least play nice. Adjust the max packet size to account for two VLAN tags, not just one. Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 3 +-- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 1 + drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 1 + drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index a7e5a76703e7..0cb571e337f6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9770,8 +9770,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) /* MTU range: 68 - 9706 */ netdev->min_mtu = ETH_MIN_MTU; - netdev->max_mtu = I40E_MAX_RXBUFFER - - (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + netdev->max_mtu = I40E_MAX_RXBUFFER - I40E_PACKET_HDR_PAD; return 0; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index b288d58313a6..a39892d2453d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -130,6 +130,7 @@ enum i40e_dyn_idx_t { * i.e. RXBUFFER_512 --> 1216 byte skb (size-2048 slab) */ #define I40E_RX_HDR_SIZE I40E_RXBUFFER_256 +#define I40E_PACKET_HDR_PAD (ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2)) #define i40e_rx_desc i40e_32byte_rx_desc #define I40E_RX_DMA_ATTR \ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 901282c87cf6..472f606629d4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -117,6 +117,7 @@ enum i40e_dyn_idx_t { * i.e. RXBUFFER_512 --> 1216 byte skb (size-2048 slab) */ #define I40E_RX_HDR_SIZE I40E_RXBUFFER_256 +#define I40E_PACKET_HDR_PAD (ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2)) #define i40e_rx_desc i40e_32byte_rx_desc #define I40E_RX_DMA_ATTR \ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 93536b9fc629..22919b444ddf 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2625,7 +2625,7 @@ static void i40evf_init_task(struct work_struct *work) /* MTU range: 68 - 9710 */ netdev->min_mtu = ETH_MIN_MTU; - netdev->max_mtu = I40E_MAX_RXBUFFER - (ETH_HLEN + ETH_FCS_LEN); + netdev->max_mtu = I40E_MAX_RXBUFFER - I40E_PACKET_HDR_PAD; if (!is_valid_ether_addr(adapter->hw.mac.addr)) { dev_info(&pdev->dev, "Invalid MAC address %pM, using random\n", -- cgit v1.2.3 From fe2647ab0c9970cfc2895f1671343c23fed27f44 Mon Sep 17 00:00:00 2001 From: Sudheer Mogilappagari Date: Fri, 23 Jun 2017 04:24:44 -0400 Subject: i40evf: prevent VF close returning before state transitions to DOWN Currently i40evf_close() can return before state transitions to __I40EVF_DOWN because of the latency involved in processing and receiving response from PF driver and scheduling of VF watchdog_task. Due to this inconsistency an immediate call to i40evf_open() fails because state is still DOWN_PENDING. When a VF interface is in up state and we try to add it as slave, The bonding driver calls dev_close() and dev_open() in short duration resulting in dev_open returning error. The ifenslave command needs to be run again for dev_open to succeed. This fix ensures that watchdog timer is scheduled immediately after admin queue operations are scheduled in i40evf_down(). In addition a wait condition is added at the end of i40evf_close so that function wont return when state is still DOWN_PENDING. The timeout value is chosen after some profiling and includes some buffer. Signed-off-by: Sudheer Mogilappagari Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 2 ++ drivers/net/ethernet/intel/i40evf/i40evf_main.c | 19 +++++++++++++++++++ drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c | 4 +++- 3 files changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 7901cc85cbe5..52cf38f47349 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -194,6 +195,7 @@ struct i40evf_adapter { struct work_struct adminq_task; struct delayed_work client_task; struct delayed_work init_task; + wait_queue_head_t down_waitqueue; struct i40e_q_vector *q_vectors; struct list_head vlan_filter_list; char misc_vector_name[IFNAMSIZ + 9]; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 22919b444ddf..21ab3ff5e9ec 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1143,6 +1143,7 @@ void i40evf_down(struct i40evf_adapter *adapter) } clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); + mod_timer_pending(&adapter->watchdog_timer, jiffies + 1); } /** @@ -1794,6 +1795,7 @@ static void i40evf_disable_vf(struct i40evf_adapter *adapter) clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); adapter->flags &= ~I40EVF_FLAG_RESET_PENDING; adapter->state = __I40EVF_DOWN; + wake_up(&adapter->down_waitqueue); dev_info(&adapter->pdev->dev, "Reset task did not complete, VF disabled\n"); } @@ -1939,6 +1941,7 @@ continue_reset: i40evf_irq_enable(adapter, true); } else { adapter->state = __I40EVF_DOWN; + wake_up(&adapter->down_waitqueue); } return; @@ -2238,6 +2241,7 @@ err_setup_tx: static int i40evf_close(struct net_device *netdev) { struct i40evf_adapter *adapter = netdev_priv(netdev); + int status; if (adapter->state <= __I40EVF_DOWN_PENDING) return 0; @@ -2255,7 +2259,18 @@ static int i40evf_close(struct net_device *netdev) * still active and can DMA into memory. Resources are cleared in * i40evf_virtchnl_completion() after we get confirmation from the PF * driver that the rings have been stopped. + * + * Also, we wait for state to transition to __I40EVF_DOWN before + * returning. State change occurs in i40evf_virtchnl_completion() after + * VF resources are released (which occurs after PF driver processes and + * responds to admin queue commands). */ + + status = wait_event_timeout(adapter->down_waitqueue, + adapter->state == __I40EVF_DOWN, + msecs_to_jiffies(200)); + if (!status) + netdev_warn(netdev, "Device resources not yet released\n"); return 0; } @@ -2683,6 +2698,7 @@ static void i40evf_init_task(struct work_struct *work) adapter->state = __I40EVF_DOWN; set_bit(__I40E_VSI_DOWN, adapter->vsi.state); i40evf_misc_irq_enable(adapter); + wake_up(&adapter->down_waitqueue); adapter->rss_key = kzalloc(adapter->rss_key_size, GFP_KERNEL); adapter->rss_lut = kzalloc(adapter->rss_lut_size, GFP_KERNEL); @@ -2844,6 +2860,9 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(5 * (pdev->devfn & 0x07))); + /* Setup the wait queue for indicating transition to down status */ + init_waitqueue_head(&adapter->down_waitqueue); + return 0; err_ioremap: diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index d2bb250a71af..6c403bf1bbb8 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -991,8 +991,10 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, case VIRTCHNL_OP_DISABLE_QUEUES: i40evf_free_all_tx_resources(adapter); i40evf_free_all_rx_resources(adapter); - if (adapter->state == __I40EVF_DOWN_PENDING) + if (adapter->state == __I40EVF_DOWN_PENDING) { adapter->state = __I40EVF_DOWN; + wake_up(&adapter->down_waitqueue); + } break; case VIRTCHNL_OP_VERSION: case VIRTCHNL_OP_CONFIG_IRQ_MAP: -- cgit v1.2.3 From 5a433199bfde1b5360bf592184a2b89ce4ee2eda Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Fri, 23 Jun 2017 04:24:45 -0400 Subject: i40e: Fix a bug with VMDq RSS queue allocation The X722 pf flag setup should happen before the VMDq RSS queue count is initialized for VMDq VSI to get the right number of queues for RSS in case of X722 devices. Signed-off-by: Anjali Singhai Jain Signed-off-by: Alice Michael Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 46 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0cb571e337f6..5df25df123d7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9000,6 +9000,29 @@ static int i40e_sw_init(struct i40e_pf *pf) pf->hw.func_caps.fd_filters_best_effort; } + if (pf->hw.mac.type == I40E_MAC_X722) { + pf->flags |= I40E_FLAG_RSS_AQ_CAPABLE + | I40E_FLAG_128_QP_RSS_CAPABLE + | I40E_FLAG_HW_ATR_EVICT_CAPABLE + | I40E_FLAG_OUTER_UDP_CSUM_CAPABLE + | I40E_FLAG_WB_ON_ITR_CAPABLE + | I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE + | I40E_FLAG_NO_PCI_LINK_CHECK + | I40E_FLAG_USE_SET_LLDP_MIB + | I40E_FLAG_GENEVE_OFFLOAD_CAPABLE + | I40E_FLAG_PTP_L4_CAPABLE + | I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE; + } else if ((pf->hw.aq.api_maj_ver > 1) || + ((pf->hw.aq.api_maj_ver == 1) && + (pf->hw.aq.api_min_ver > 4))) { + /* Supported in FW API version higher than 1.4 */ + pf->flags |= I40E_FLAG_GENEVE_OFFLOAD_CAPABLE; + } + + /* Enable HW ATR eviction if possible */ + if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) + pf->flags |= I40E_FLAG_HW_ATR_EVICT_ENABLED; + if ((pf->hw.mac.type == I40E_MAC_XL710) && (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) || (pf->hw.aq.fw_maj_ver < 4))) { @@ -9041,29 +9064,6 @@ static int i40e_sw_init(struct i40e_pf *pf) I40E_MAX_VF_COUNT); } #endif /* CONFIG_PCI_IOV */ - if (pf->hw.mac.type == I40E_MAC_X722) { - pf->flags |= I40E_FLAG_RSS_AQ_CAPABLE - | I40E_FLAG_128_QP_RSS_CAPABLE - | I40E_FLAG_HW_ATR_EVICT_CAPABLE - | I40E_FLAG_OUTER_UDP_CSUM_CAPABLE - | I40E_FLAG_WB_ON_ITR_CAPABLE - | I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE - | I40E_FLAG_NO_PCI_LINK_CHECK - | I40E_FLAG_USE_SET_LLDP_MIB - | I40E_FLAG_GENEVE_OFFLOAD_CAPABLE - | I40E_FLAG_PTP_L4_CAPABLE - | I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE; - } else if ((pf->hw.aq.api_maj_ver > 1) || - ((pf->hw.aq.api_maj_ver == 1) && - (pf->hw.aq.api_min_ver > 4))) { - /* Supported in FW API version higher than 1.4 */ - pf->flags |= I40E_FLAG_GENEVE_OFFLOAD_CAPABLE; - } - - /* Enable HW ATR eviction if possible */ - if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) - pf->flags |= I40E_FLAG_HW_ATR_EVICT_ENABLED; - pf->eeprom_version = 0xDEAD; pf->lan_veb = I40E_NO_VEB; pf->lan_vsi = I40E_NO_VSI; -- cgit v1.2.3 From d36e41dc78d31322f61952a558b817e15e60a855 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 23 Jun 2017 04:24:46 -0400 Subject: i40e: separate hw_features from runtime changing flags The number of flags found in pf->flags has grown quite large, and there are a lot of different types of flags. Most of the flags are simply hardware features which are enabled on some firmware or some MAC types. Other flags are dynamic run-time flags which enable or disable certain features of the driver. Separate these two types of flags into pf->hw_features and pf->flags. The hw_features list will contain a set of features which are enabled at init time. This will not contain toggles or otherwise dynamically changing features. These flags should not need atomic protections, as they will be set once during init and then be essentially read only. Everything else will remain in the flags variable. These flags may be modified at any time during run time. A future patch may wish to convert these flags into set_bit/clear_bit/test_bit or similar approach to ensure atomic correctness. The I40E_FLAG_MFP_ENABLED flag may be a good fit for hw_features but currently is used by ethtool in the private flags settings, and thus has been left as part of flags. Additionally, I40E_FLAG_DCB_CAPABLE may be a good fit for the hw_features but this patch has not tried to untangle it yet. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 43 +++++++------ drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 34 +++++----- drivers/net/ethernet/intel/i40e/i40e_main.c | 72 +++++++++++----------- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 6 +- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 2 +- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 8 +-- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 4 -- drivers/net/ethernet/intel/i40evf/i40evf.h | 2 - drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 9 files changed, 85 insertions(+), 88 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index d616f698e155..f07217b15ffd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -75,11 +75,11 @@ #define I40E_MIN_VSI_ALLOC 83 /* LAN, ATR, FCOE, 64 VF */ /* max 16 qps */ #define i40e_default_queues_per_vmdq(pf) \ - (((pf)->flags & I40E_FLAG_RSS_AQ_CAPABLE) ? 4 : 1) + (((pf)->hw_features & I40E_HW_RSS_AQ_CAPABLE) ? 4 : 1) #define I40E_DEFAULT_QUEUES_PER_VF 4 #define I40E_DEFAULT_QUEUES_PER_TC 1 /* should be a power of 2 */ #define i40e_pf_get_max_q_per_tc(pf) \ - (((pf)->flags & I40E_FLAG_128_QP_RSS_CAPABLE) ? 128 : 64) + (((pf)->hw_features & I40E_HW_128_QP_RSS_CAPABLE) ? 128 : 64) #define I40E_FDIR_RING 0 #define I40E_FDIR_RING_COUNT 32 #define I40E_MAX_AQ_BUF_SIZE 4096 @@ -401,6 +401,27 @@ struct i40e_pf { struct timer_list service_timer; struct work_struct service_task; + u64 hw_features; +#define I40E_HW_RSS_AQ_CAPABLE BIT_ULL(0) +#define I40E_HW_128_QP_RSS_CAPABLE BIT_ULL(1) +#define I40E_HW_ATR_EVICT_CAPABLE BIT_ULL(2) +#define I40E_HW_WB_ON_ITR_CAPABLE BIT_ULL(3) +#define I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(4) +#define I40E_HW_NO_PCI_LINK_CHECK BIT_ULL(5) +#define I40E_HW_100M_SGMII_CAPABLE BIT_ULL(6) +#define I40E_HW_NO_DCB_SUPPORT BIT_ULL(7) +#define I40E_HW_USE_SET_LLDP_MIB BIT_ULL(8) +#define I40E_HW_GENEVE_OFFLOAD_CAPABLE BIT_ULL(9) +#define I40E_HW_PTP_L4_CAPABLE BIT_ULL(10) +#define I40E_HW_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(11) +#define I40E_HW_MPLS_HDR_OFFLOAD_CAPABLE BIT_ULL(12) +#define I40E_HW_HAVE_CRT_RETIMER BIT_ULL(13) +#define I40E_HW_OUTER_UDP_CSUM_CAPABLE BIT_ULL(14) +#define I40E_HW_PHY_CONTROLS_LEDS BIT_ULL(15) +#define I40E_HW_STOP_FW_LLDP BIT_ULL(16) +#define I40E_HW_PORT_ID_VALID BIT_ULL(17) +#define I40E_HW_RESTART_AUTONEG BIT_ULL(18) + u64 flags; #define I40E_FLAG_RX_CSUM_ENABLED BIT_ULL(1) #define I40E_FLAG_MSI_ENABLED BIT_ULL(2) @@ -420,33 +441,15 @@ struct i40e_pf { #define I40E_FLAG_PTP BIT_ULL(25) #define I40E_FLAG_MFP_ENABLED BIT_ULL(26) #define I40E_FLAG_UDP_FILTER_SYNC BIT_ULL(27) -#define I40E_FLAG_PORT_ID_VALID BIT_ULL(28) #define I40E_FLAG_DCB_CAPABLE BIT_ULL(29) -#define I40E_FLAG_RSS_AQ_CAPABLE BIT_ULL(31) -#define I40E_FLAG_HW_ATR_EVICT_CAPABLE BIT_ULL(32) -#define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE BIT_ULL(33) -#define I40E_FLAG_128_QP_RSS_CAPABLE BIT_ULL(34) -#define I40E_FLAG_WB_ON_ITR_CAPABLE BIT_ULL(35) #define I40E_FLAG_VEB_STATS_ENABLED BIT_ULL(37) -#define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(38) #define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39) #define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) -#define I40E_FLAG_GENEVE_OFFLOAD_CAPABLE BIT_ULL(41) -#define I40E_FLAG_NO_PCI_LINK_CHECK BIT_ULL(42) -#define I40E_FLAG_100M_SGMII_CAPABLE BIT_ULL(43) -#define I40E_FLAG_RESTART_AUTONEG BIT_ULL(44) -#define I40E_FLAG_NO_DCB_SUPPORT BIT_ULL(45) -#define I40E_FLAG_USE_SET_LLDP_MIB BIT_ULL(46) -#define I40E_FLAG_STOP_FW_LLDP BIT_ULL(47) -#define I40E_FLAG_PHY_CONTROLS_LEDS BIT_ULL(48) #define I40E_FLAG_PF_MAC BIT_ULL(50) #define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(51) -#define I40E_FLAG_HAVE_CRT_RETIMER BIT_ULL(52) -#define I40E_FLAG_PTP_L4_CAPABLE BIT_ULL(53) #define I40E_FLAG_CLIENT_RESET BIT_ULL(54) #define I40E_FLAG_TEMP_LINK_POLLING BIT_ULL(55) #define I40E_FLAG_CLIENT_L2_CHANGE BIT_ULL(56) -#define I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(57) #define I40E_FLAG_LEGACY_RX BIT_ULL(58) struct i40e_client_instance *cinst; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 1d29152256fe..c76549e41705 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -271,7 +271,7 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, *advertising |= ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) *advertising |= ADVERTISED_1000baseT_Full; - if (pf->flags & I40E_FLAG_100M_SGMII_CAPABLE) { + if (pf->hw_features & I40E_HW_100M_SGMII_CAPABLE) { *supported |= SUPPORTED_100baseT_Full; *advertising |= ADVERTISED_100baseT_Full; } @@ -340,12 +340,12 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, *advertising |= ADVERTISED_20000baseKR2_Full; } if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR) { - if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) *supported |= SUPPORTED_10000baseKR_Full | SUPPORTED_Autoneg; *advertising |= ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) *advertising |= ADVERTISED_10000baseKR_Full; } if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) { @@ -356,12 +356,12 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, *advertising |= ADVERTISED_10000baseKX4_Full; } if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX) { - if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) *supported |= SUPPORTED_1000baseKX_Full | SUPPORTED_Autoneg; *advertising |= ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) *advertising |= ADVERTISED_1000baseKX_Full; } if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_KR || @@ -474,7 +474,7 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, SUPPORTED_1000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) advertising |= ADVERTISED_1000baseT_Full; - if (pf->flags & I40E_FLAG_100M_SGMII_CAPABLE) { + if (pf->hw_features & I40E_HW_100M_SGMII_CAPABLE) { supported |= SUPPORTED_100baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) @@ -1765,7 +1765,7 @@ static int i40e_get_ts_info(struct net_device *dev, BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ); - if (pf->flags & I40E_FLAG_PTP_L4_CAPABLE) + if (pf->hw_features & I40E_HW_PTP_L4_CAPABLE) info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) | @@ -2005,7 +2005,7 @@ static int i40e_set_phys_id(struct net_device *netdev, switch (state) { case ETHTOOL_ID_ACTIVE: - if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) { + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) { pf->led_status = i40e_led_get(hw); } else { i40e_aq_set_phy_debug(hw, I40E_PHY_DEBUG_ALL, NULL); @@ -2015,19 +2015,19 @@ static int i40e_set_phys_id(struct net_device *netdev, } return blink_freq; case ETHTOOL_ID_ON: - if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) i40e_led_set(hw, 0xf, false); else ret = i40e_led_set_phy(hw, true, pf->led_status, 0); break; case ETHTOOL_ID_OFF: - if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) i40e_led_set(hw, 0x0, false); else ret = i40e_led_set_phy(hw, false, pf->led_status, 0); break; case ETHTOOL_ID_INACTIVE: - if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) { + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) { i40e_led_set(hw, pf->led_status, false); } else { ret = i40e_led_set_phy(hw, false, pf->led_status, @@ -2727,22 +2727,22 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) switch (nfc->flow_type) { case TCP_V4_FLOW: flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK); break; case TCP_V6_FLOW: flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV6_TCP; - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK); - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK); break; case UDP_V4_FLOW: flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP; - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP); @@ -2751,7 +2751,7 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) break; case UDP_V6_FLOW: flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV6_UDP; - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP); @@ -4122,7 +4122,7 @@ flags_complete: } /* Only allow ATR evict on hardware that is capable of handling it */ - if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) + if (!(pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE)) pf->flags &= ~I40E_FLAG_HW_ATR_EVICT_ENABLED; if (changed_flags & I40E_FLAG_TRUE_PROMISC_SUPPORT) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5df25df123d7..d7248e5c5f01 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5350,7 +5350,7 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf) int err = 0; /* Do not enable DCB for SW1 and SW2 images even if the FW is capable */ - if (pf->flags & I40E_FLAG_NO_DCB_SUPPORT) + if (pf->hw_features & I40E_HW_NO_DCB_SUPPORT) goto out; /* Get the initial DCB configuration */ @@ -7332,7 +7332,7 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) wr32(hw, I40E_REG_MSS, val); } - if (pf->flags & I40E_FLAG_RESTART_AUTONEG) { + if (pf->hw_features & I40E_HW_RESTART_AUTONEG) { msleep(75); ret = i40e_aq_set_link_restart_an(&pf->hw, true, NULL); if (ret) @@ -7970,7 +7970,7 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi) ring->count = vsi->num_desc; ring->size = 0; ring->dcb_tc = 0; - if (vsi->back->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) + if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) ring->flags = I40E_TXR_FLAGS_WB_ON_ITR; ring->tx_itr_setting = pf->tx_itr_default; vsi->tx_rings[i] = ring++; @@ -7987,7 +7987,7 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi) ring->count = vsi->num_desc; ring->size = 0; ring->dcb_tc = 0; - if (vsi->back->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) + if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) ring->flags = I40E_TXR_FLAGS_WB_ON_ITR; set_ring_xdp(ring); ring->tx_itr_setting = pf->tx_itr_default; @@ -8523,7 +8523,7 @@ static int i40e_vsi_config_rss(struct i40e_vsi *vsi) u8 *lut; int ret; - if (!(pf->flags & I40E_FLAG_RSS_AQ_CAPABLE)) + if (!(pf->hw_features & I40E_HW_RSS_AQ_CAPABLE)) return 0; if (!vsi->rss_size) @@ -8653,7 +8653,7 @@ int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) { struct i40e_pf *pf = vsi->back; - if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) + if (pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) return i40e_config_rss_aq(vsi, seed, lut, lut_size); else return i40e_config_rss_reg(vsi, seed, lut, lut_size); @@ -8672,7 +8672,7 @@ int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) { struct i40e_pf *pf = vsi->back; - if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) + if (pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) return i40e_get_rss_aq(vsi, seed, lut, lut_size); else return i40e_get_rss_reg(vsi, seed, lut, lut_size); @@ -9001,47 +9001,47 @@ static int i40e_sw_init(struct i40e_pf *pf) } if (pf->hw.mac.type == I40E_MAC_X722) { - pf->flags |= I40E_FLAG_RSS_AQ_CAPABLE - | I40E_FLAG_128_QP_RSS_CAPABLE - | I40E_FLAG_HW_ATR_EVICT_CAPABLE - | I40E_FLAG_OUTER_UDP_CSUM_CAPABLE - | I40E_FLAG_WB_ON_ITR_CAPABLE - | I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE - | I40E_FLAG_NO_PCI_LINK_CHECK - | I40E_FLAG_USE_SET_LLDP_MIB - | I40E_FLAG_GENEVE_OFFLOAD_CAPABLE - | I40E_FLAG_PTP_L4_CAPABLE - | I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE; + pf->hw_features |= (I40E_HW_RSS_AQ_CAPABLE | + I40E_HW_128_QP_RSS_CAPABLE | + I40E_HW_ATR_EVICT_CAPABLE | + I40E_HW_WB_ON_ITR_CAPABLE | + I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE | + I40E_HW_NO_PCI_LINK_CHECK | + I40E_HW_USE_SET_LLDP_MIB | + I40E_HW_GENEVE_OFFLOAD_CAPABLE | + I40E_HW_PTP_L4_CAPABLE | + I40E_HW_WOL_MC_MAGIC_PKT_WAKE | + I40E_HW_OUTER_UDP_CSUM_CAPABLE); } else if ((pf->hw.aq.api_maj_ver > 1) || ((pf->hw.aq.api_maj_ver == 1) && (pf->hw.aq.api_min_ver > 4))) { /* Supported in FW API version higher than 1.4 */ - pf->flags |= I40E_FLAG_GENEVE_OFFLOAD_CAPABLE; + pf->hw_features |= I40E_HW_GENEVE_OFFLOAD_CAPABLE; } /* Enable HW ATR eviction if possible */ - if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) + if (pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE) pf->flags |= I40E_FLAG_HW_ATR_EVICT_ENABLED; if ((pf->hw.mac.type == I40E_MAC_XL710) && (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) || (pf->hw.aq.fw_maj_ver < 4))) { - pf->flags |= I40E_FLAG_RESTART_AUTONEG; + pf->hw_features |= I40E_HW_RESTART_AUTONEG; /* No DCB support for FW < v4.33 */ - pf->flags |= I40E_FLAG_NO_DCB_SUPPORT; + pf->hw_features |= I40E_HW_NO_DCB_SUPPORT; } /* Disable FW LLDP if FW < v4.3 */ if ((pf->hw.mac.type == I40E_MAC_XL710) && (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 3)) || (pf->hw.aq.fw_maj_ver < 4))) - pf->flags |= I40E_FLAG_STOP_FW_LLDP; + pf->hw_features |= I40E_HW_STOP_FW_LLDP; /* Use the FW Set LLDP MIB API if FW > v4.40 */ if ((pf->hw.mac.type == I40E_MAC_XL710) && (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver >= 40)) || (pf->hw.aq.fw_maj_ver >= 5))) - pf->flags |= I40E_FLAG_USE_SET_LLDP_MIB; + pf->hw_features |= I40E_HW_USE_SET_LLDP_MIB; if (pf->hw.func_caps.vmdq) { pf->num_vmdq_vsis = I40E_DEFAULT_NUM_VMDQ_VSI; @@ -9244,7 +9244,7 @@ static void i40e_udp_tunnel_add(struct net_device *netdev, pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_VXLAN; break; case UDP_TUNNEL_TYPE_GENEVE: - if (!(pf->flags & I40E_FLAG_GENEVE_OFFLOAD_CAPABLE)) + if (!(pf->hw_features & I40E_HW_GENEVE_OFFLOAD_CAPABLE)) return; pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_NGE; break; @@ -9311,7 +9311,7 @@ static int i40e_get_phys_port_id(struct net_device *netdev, struct i40e_pf *pf = np->vsi->back; struct i40e_hw *hw = &pf->hw; - if (!(pf->flags & I40E_FLAG_PORT_ID_VALID)) + if (!(pf->hw_features & I40E_HW_PORT_ID_VALID)) return -EOPNOTSUPP; ppid->id_len = min_t(int, sizeof(hw->mac.port_addr), sizeof(ppid->id)); @@ -9689,7 +9689,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) NETIF_F_RXCSUM | 0; - if (!(pf->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE)) + if (!(pf->hw_features & I40E_HW_OUTER_UDP_CSUM_CAPABLE)) netdev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM; @@ -10447,7 +10447,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, break; } - if ((pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) && + if ((pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) && (vsi->type == I40E_VSI_VMDQ2)) { ret = i40e_vsi_config_rss(vsi); } @@ -11456,7 +11456,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * Ignore error return codes because if it was already disabled via * hardware settings this will fail */ - if (pf->flags & I40E_FLAG_STOP_FW_LLDP) { + if (pf->hw_features & I40E_HW_STOP_FW_LLDP) { dev_info(&pdev->dev, "Stopping firmware LLDP agent.\n"); i40e_aq_stop_lldp(hw, true, NULL); } @@ -11473,7 +11473,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ether_addr_copy(hw->mac.perm_addr, hw->mac.addr); i40e_get_port_mac_addr(hw, hw->mac.port_addr); if (is_valid_ether_addr(hw->mac.port_addr)) - pf->flags |= I40E_FLAG_PORT_ID_VALID; + pf->hw_features |= I40E_HW_PORT_ID_VALID; pci_set_drvdata(pdev, pf); pci_save_state(pdev); @@ -11589,7 +11589,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) wr32(hw, I40E_REG_MSS, val); } - if (pf->flags & I40E_FLAG_RESTART_AUTONEG) { + if (pf->hw_features & I40E_HW_RESTART_AUTONEG) { msleep(75); err = i40e_aq_set_link_restart_an(&pf->hw, true, NULL); if (err) @@ -11676,7 +11676,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * and will report PCI Gen 1 x 1 by default so don't bother * checking them. */ - if (!(pf->flags & I40E_FLAG_NO_PCI_LINK_CHECK)) { + if (!(pf->hw_features & I40E_HW_NO_PCI_LINK_CHECK)) { char speed[PCI_SPEED_SIZE] = "Unknown"; char width[PCI_WIDTH_SIZE] = "Unknown"; @@ -11747,9 +11747,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if ((pf->hw.device_id == I40E_DEV_ID_10G_BASE_T) || (pf->hw.device_id == I40E_DEV_ID_10G_BASE_T4)) - pf->flags |= I40E_FLAG_PHY_CONTROLS_LEDS; + pf->hw_features |= I40E_HW_PHY_CONTROLS_LEDS; if (pf->hw.device_id == I40E_DEV_ID_SFP_I_X722) - pf->flags |= I40E_FLAG_HAVE_CRT_RETIMER; + pf->hw_features |= I40E_HW_HAVE_CRT_RETIMER; /* print a string summarizing features */ i40e_print_features(pf); @@ -12061,7 +12061,7 @@ static void i40e_shutdown(struct pci_dev *pdev) */ i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false); - if (pf->wol_en && (pf->flags & I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE)) + if (pf->wol_en && (pf->hw_features & I40E_HW_WOL_MC_MAGIC_PKT_WAKE)) i40e_enable_mc_magic_wake(pf); i40e_prep_for_reset(pf, false); @@ -12093,7 +12093,7 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) set_bit(__I40E_SUSPENDED, pf->state); set_bit(__I40E_DOWN, pf->state); - if (pf->wol_en && (pf->flags & I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE)) + if (pf->wol_en && (pf->hw_features & I40E_HW_WOL_MC_MAGIC_PKT_WAKE)) i40e_enable_mc_magic_wake(pf); i40e_prep_for_reset(pf, false); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 0129ed3b78ec..d8456c381c99 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -569,7 +569,7 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: - if (!(pf->flags & I40E_FLAG_PTP_L4_CAPABLE)) + if (!(pf->hw_features & I40E_HW_PTP_L4_CAPABLE)) return -ERANGE; pf->ptp_rx = true; tsyntype = I40E_PRTTSYN_CTL1_V1MESSTYPE0_MASK | @@ -583,7 +583,7 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: - if (!(pf->flags & I40E_FLAG_PTP_L4_CAPABLE)) + if (!(pf->hw_features & I40E_HW_PTP_L4_CAPABLE)) return -ERANGE; /* fall through */ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: @@ -592,7 +592,7 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, pf->ptp_rx = true; tsyntype = I40E_PRTTSYN_CTL1_V2MESSTYPE0_MASK | I40E_PRTTSYN_CTL1_TSYNTYPE_V2; - if (pf->flags & I40E_FLAG_PTP_L4_CAPABLE) { + if (pf->hw_features & I40E_HW_PTP_L4_CAPABLE) { tsyntype |= I40E_PRTTSYN_CTL1_UDP_ENA_MASK; config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; } else { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index a39892d2453d..f0a0eabc2666 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -112,7 +112,7 @@ enum i40e_dyn_idx_t { BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP)) #define i40e_pf_get_default_rss_hena(pf) \ - (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ + (((pf)->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ I40E_DEFAULT_RSS_HENA_EXPANDED : I40E_DEFAULT_RSS_HENA) /* Supported Rx Buffer Sizes (a multiple of 128) */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 979110d59f67..3ef67dc094fc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1542,14 +1542,14 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PF) { vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF; } else { - if ((pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) && + if ((pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) && (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_AQ)) vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_AQ; else vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_REG; } - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) { + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) { if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2) vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2; @@ -1558,7 +1558,7 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP) vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP; - if ((pf->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE) && + if ((pf->hw_features & I40E_HW_OUTER_UDP_CSUM_CAPABLE) && (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)) vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM; @@ -1573,7 +1573,7 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING; } - if (pf->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) { + if (pf->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) { if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_WB_ON_ITR; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 472f606629d4..489684002e94 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -98,10 +98,6 @@ enum i40e_dyn_idx_t { BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \ BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP)) -#define i40e_pf_get_default_rss_hena(pf) \ - (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ - I40E_DEFAULT_RSS_HENA_EXPANDED : I40E_DEFAULT_RSS_HENA) - /* Supported Rx Buffer Sizes (a multiple of 128) */ #define I40E_RXBUFFER_256 256 #define I40E_RXBUFFER_1536 1536 /* 128B aligned standard Ethernet frame */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 52cf38f47349..7f905368fc93 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -238,8 +238,6 @@ struct i40evf_adapter { /* duplicates for common code */ #define I40E_FLAG_DCB_ENABLED 0 #define I40E_FLAG_RX_CSUM_ENABLED I40EVF_FLAG_RX_CSUM_ENABLED -#define I40E_FLAG_WB_ON_ITR_CAPABLE I40EVF_FLAG_WB_ON_ITR_CAPABLE -#define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE #define I40E_FLAG_LEGACY_RX I40EVF_FLAG_LEGACY_RX /* flags for admin queue service task */ u32 aq_required; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 21ab3ff5e9ec..8603911cc550 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1242,7 +1242,7 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter) tx_ring->dev = &adapter->pdev->dev; tx_ring->count = adapter->tx_desc_count; tx_ring->tx_itr_setting = (I40E_ITR_DYNAMIC | I40E_ITR_TX_DEF); - if (adapter->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) + if (adapter->flags & I40EVF_FLAG_WB_ON_ITR_CAPABLE) tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR; rx_ring = &adapter->rx_rings[i]; -- cgit v1.2.3 From 28921a0c2f5d4d051eff3f1db0588dea7cb8fe49 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 23 Jun 2017 04:24:47 -0400 Subject: i40e: remove workaround for Open Firmware MAC address Since commit b499ffb0a22c ("i40e: Look up MAC address in Open Firmware or IDPROM"), we've had support for obtaining the MAC address form Open Firmware or IDPROM. This code relied on sending the Open Firmware address directly to the device firmware instead of relying on our MAC/VLAN filter list. Thus, a work around was introduced in commit b1b15df59232 ("i40e: Explicitly write platform-specific mac address after PF reset") We refactored the Open Firmware address enablement code in the ill-named commit 41c4c2b50d52 ("i40e: allow look-up of MAC address from Open Firmware or IDPROM") Since this refactor, we no longer even set I40E_FLAG_PF_MAC. Further, we don't need this work around, because we actually store the MAC address as part of the MAC/VLAN filter hash. Thus, we will restore the address correctly upon reset. The refactor above failed to revert the workaround, so do that now. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 - drivers/net/ethernet/intel/i40e/i40e_main.c | 60 ----------------------------- 2 files changed, 61 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index f07217b15ffd..d0c1bf5441d8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -445,7 +445,6 @@ struct i40e_pf { #define I40E_FLAG_VEB_STATS_ENABLED BIT_ULL(37) #define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39) #define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) -#define I40E_FLAG_PF_MAC BIT_ULL(50) #define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(51) #define I40E_FLAG_CLIENT_RESET BIT_ULL(54) #define I40E_FLAG_TEMP_LINK_POLLING BIT_ULL(55) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index d7248e5c5f01..4d1eb0c19028 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2713,44 +2713,6 @@ static int i40e_vlan_rx_kill_vid(struct net_device *netdev, return 0; } -/** - * i40e_macaddr_init - explicitly write the mac address filters - * - * @vsi: pointer to the vsi - * @macaddr: the MAC address - * - * This is needed when the macaddr has been obtained by other - * means than the default, e.g., from Open Firmware or IDPROM. - * Returns 0 on success, negative on failure - **/ -static int i40e_macaddr_init(struct i40e_vsi *vsi, u8 *macaddr) -{ - int ret; - struct i40e_aqc_add_macvlan_element_data element; - - ret = i40e_aq_mac_address_write(&vsi->back->hw, - I40E_AQC_WRITE_TYPE_LAA_WOL, - macaddr, NULL); - if (ret) { - dev_info(&vsi->back->pdev->dev, - "Addr change for VSI failed: %d\n", ret); - return -EADDRNOTAVAIL; - } - - memset(&element, 0, sizeof(element)); - ether_addr_copy(element.mac_addr, macaddr); - element.flags = cpu_to_le16(I40E_AQC_MACVLAN_ADD_PERFECT_MATCH); - ret = i40e_aq_add_macvlan(&vsi->back->hw, vsi->seid, &element, 1, NULL); - if (ret) { - dev_info(&vsi->back->pdev->dev, - "add filter failed err %s aq_err %s\n", - i40e_stat_str(&vsi->back->hw, ret), - i40e_aq_str(&vsi->back->hw, - vsi->back->hw.aq.asq_last_status)); - } - return ret; -} - /** * i40e_restore_vlan - Reinstate vlans when vsi/netdev comes back up * @vsi: the vsi being brought back up @@ -3203,19 +3165,8 @@ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi) **/ static void i40e_set_vsi_rx_mode(struct i40e_vsi *vsi) { - struct i40e_pf *pf = vsi->back; - int err; - if (vsi->netdev) i40e_set_rx_mode(vsi->netdev); - - if (!!(pf->flags & I40E_FLAG_PF_MAC)) { - err = i40e_macaddr_init(vsi, pf->hw.mac.addr); - if (err) { - dev_warn(&pf->pdev->dev, - "could not set up macaddr; err %d\n", err); - } - } } /** @@ -10400,17 +10351,6 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, switch (vsi->type) { /* setup the netdev if needed */ case I40E_VSI_MAIN: - /* Apply relevant filters if a platform-specific mac - * address was selected. - */ - if (!!(pf->flags & I40E_FLAG_PF_MAC)) { - ret = i40e_macaddr_init(vsi, pf->hw.mac.addr); - if (ret) { - dev_warn(&pf->pdev->dev, - "could not set up macaddr; err %d\n", - ret); - } - } case I40E_VSI_VMDQ2: ret = i40e_config_netdev(vsi); if (ret) -- cgit v1.2.3 From 10a955ff62e56fe13dae1f29aabc04bc589eaf46 Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Fri, 23 Jun 2017 04:24:48 -0400 Subject: i40e: Detect ATR HW Evict NVM issue and disable the feature This patch fixes a problem with the HW ATR eviction feature where the NVM setting was incorrect. This patch detects the issue on X720 adapters and disables the feature if the NVM setting is incorrect. Without this patch, HW ATR Evict feature does not work on broken NVMs and is not detected either. If the HW ATR Evict feature is disabled the SW Eviction feature will take effect. Signed-off-by: Anjali Singhai Jain Signed-off-by: Alice Michael Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4d1eb0c19028..6a59d9367a2a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8963,6 +8963,14 @@ static int i40e_sw_init(struct i40e_pf *pf) I40E_HW_PTP_L4_CAPABLE | I40E_HW_WOL_MC_MAGIC_PKT_WAKE | I40E_HW_OUTER_UDP_CSUM_CAPABLE); + +#define I40E_FDEVICT_PCTYPE_DEFAULT 0xc03 + if (rd32(&pf->hw, I40E_GLQF_FDEVICTENA(1)) != + I40E_FDEVICT_PCTYPE_DEFAULT) { + dev_warn(&pf->pdev->dev, + "FD EVICT PCTYPES are not right, disable FD HW EVICT\n"); + pf->hw_features &= ~I40E_HW_ATR_EVICT_CAPABLE; + } } else if ((pf->hw.aq.api_maj_ver > 1) || ((pf->hw.aq.api_maj_ver == 1) && (pf->hw.aq.api_min_ver > 4))) { -- cgit v1.2.3 From 841c950d67c6facde32a8644ced20c04aebb7dd8 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 23 Jun 2017 04:24:50 -0400 Subject: i40e/i40evf: use cmpxchg64 when updating private flags in ethtool When a user gives an invalid command to change a private flag which is not supported, either because it is read-only, or the device is not capable of the feature, we simply ignore the request. A naive solution would simply be to report error codes when one of the flags was not supported. However, this causes problems because it makes the operation not atomic. If a user requests multiple private flags together at once we could end up changing one before failing at the second flag. We can do a bit better if we instead update a temporary copy of the flags variable in the loop, and then copy it into place after. If we aren't careful this has the pitfall of potentially silently overwriting any changes caused by other threads. Avoid this by using cmpxchg64 which will compare and swap the flags variable only if it currently matched the old value. We'll report -EAGAIN in the (hopefully rare!) case where the cmpxchg64 fails. This ensures that we can properly report when flags are not supported in an atomic fashion without the risk of overwriting other threads changes. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 57 +++++++++++++++------- drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 41 ++++++++++++---- 2 files changed, 70 insertions(+), 28 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index c76549e41705..a868c8d4fec9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -4069,23 +4069,26 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - u64 changed_flags; + u64 orig_flags, new_flags, changed_flags; u32 i, j; - changed_flags = pf->flags; + orig_flags = READ_ONCE(pf->flags); + new_flags = orig_flags; for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { const struct i40e_priv_flags *priv_flags; priv_flags = &i40e_gstrings_priv_flags[i]; - if (priv_flags->read_only) - continue; - if (flags & BIT(i)) - pf->flags |= priv_flags->flag; + new_flags |= priv_flags->flag; else - pf->flags &= ~(priv_flags->flag); + new_flags &= ~(priv_flags->flag); + + /* If this is a read-only flag, it can't be changed */ + if (priv_flags->read_only && + ((orig_flags ^ new_flags) & ~BIT(i))) + return -EOPNOTSUPP; } if (pf->hw.pf_id != 0) @@ -4096,18 +4099,40 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) priv_flags = &i40e_gl_gstrings_priv_flags[j]; - if (priv_flags->read_only) - continue; - if (flags & BIT(i + j)) - pf->flags |= priv_flags->flag; + new_flags |= priv_flags->flag; else - pf->flags &= ~(priv_flags->flag); + new_flags &= ~(priv_flags->flag); + + /* If this is a read-only flag, it can't be changed */ + if (priv_flags->read_only && + ((orig_flags ^ new_flags) & ~BIT(i))) + return -EOPNOTSUPP; } flags_complete: - /* check for flags that changed */ - changed_flags ^= pf->flags; + /* Before we finalize any flag changes, we need to perform some + * checks to ensure that the changes are supported and safe. + */ + + /* ATR eviction is not supported on all devices */ + if ((new_flags & I40E_FLAG_HW_ATR_EVICT_ENABLED) && + !(pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE)) + return -EOPNOTSUPP; + + /* Compare and exchange the new flags into place. If we failed, that + * is if cmpxchg64 returns anything but the old value, this means that + * something else has modified the flags variable since we copied it + * originally. We'll just punt with an error and log something in the + * message buffer. + */ + if (cmpxchg64(&pf->flags, orig_flags, new_flags) != orig_flags) { + dev_warn(&pf->pdev->dev, + "Unable to update pf->flags as it was modified by another thread...\n"); + return -EAGAIN; + } + + changed_flags = orig_flags ^ new_flags; /* Process any additional changes needed as a result of flag changes. * The changed_flags value reflects the list of bits that were @@ -4121,10 +4146,6 @@ flags_complete: set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); } - /* Only allow ATR evict on hardware that is capable of handling it */ - if (!(pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE)) - pf->flags &= ~I40E_FLAG_HW_ATR_EVICT_ENABLED; - if (changed_flags & I40E_FLAG_TRUE_PROMISC_SUPPORT) { u16 sw_flags = 0, valid_flags = 0; int ret; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index 76fd89c1dbb2..65874d6b3ab9 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -258,29 +258,50 @@ static u32 i40evf_get_priv_flags(struct net_device *netdev) static int i40evf_set_priv_flags(struct net_device *netdev, u32 flags) { struct i40evf_adapter *adapter = netdev_priv(netdev); - u64 changed_flags; + u32 orig_flags, new_flags, changed_flags; u32 i; - changed_flags = adapter->flags; + orig_flags = READ_ONCE(adapter->flags); + new_flags = orig_flags; for (i = 0; i < I40EVF_PRIV_FLAGS_STR_LEN; i++) { const struct i40evf_priv_flags *priv_flags; priv_flags = &i40evf_gstrings_priv_flags[i]; - if (priv_flags->read_only) - continue; - if (flags & BIT(i)) - adapter->flags |= priv_flags->flag; + new_flags |= priv_flags->flag; else - adapter->flags &= ~(priv_flags->flag); + new_flags &= ~(priv_flags->flag); + + if (priv_flags->read_only && + ((orig_flags ^ new_flags) & ~BIT(i))) + return -EOPNOTSUPP; + } + + /* Before we finalize any flag changes, any checks which we need to + * perform to determine if the new flags will be supported should go + * here... + */ + + /* Compare and exchange the new flags into place. If we failed, that + * is if cmpxchg returns anything but the old value, this means + * something else must have modified the flags variable since we + * copied it. We'll just punt with an error and log something in the + * message buffer. + */ + if (cmpxchg(&adapter->flags, orig_flags, new_flags) != orig_flags) { + dev_warn(&adapter->pdev->dev, + "Unable to update adapter->flags as it was modified by another thread...\n"); + return -EAGAIN; } - /* check for flags that changed */ - changed_flags ^= adapter->flags; + changed_flags = orig_flags ^ new_flags; - /* Process any additional changes needed as a result of flag changes. */ + /* Process any additional changes needed as a result of flag changes. + * The changed_flags value reflects the list of bits that were changed + * in the code above. + */ /* issue a reset to force legacy-rx change to take effect */ if (changed_flags & I40EVF_FLAG_LEGACY_RX) { -- cgit v1.2.3 From fcf6cfc8a695bd82d123166aeedab9fa12720b57 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 23 Jun 2017 04:24:51 -0400 Subject: i40e: move check for avoiding VID=0 filters into i40e_vsi_add_vlan In i40e_vsi_add_vlan we treat attempting to add VID=0 as an error, because it does not do what the caller might expect. We already special case VID=0 in i40e_vlan_rx_add_vid so that we avoid this error when adding the VLAN. This special casing is necessary so that we do not add the VLAN=0 filter since we don't want to stop receiving untagged traffic. Unfortunately, not all callers of i40e_vsi_add_vlan are aware of this, including when we add VLANs from a VF device. Rather than special casing every single caller of i40e_vsi_add_vlan, lets just move this check internally. This makes the code simpler because the caller does not need to be aware of how VLAN=0 is special, and we don't forget to add this check in new places. This fixes a harmless error message displaying when adding a VLAN from within a VF. The message was meaningless but there is no reason to confuse end users and system administrators, and this is now avoided. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 6a59d9367a2a..1b3b681a8b1d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2595,9 +2595,20 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, u16 vid) { int err; - if (!vid || vsi->info.pvid) + if (vsi->info.pvid) return -EINVAL; + /* The network stack will attempt to add VID=0, with the intention to + * receive priority tagged packets with a VLAN of 0. Our HW receives + * these packets by default when configured to receive untagged + * packets, so we don't need to add a filter for this case. + * Additionally, HW interprets adding a VID=0 filter as meaning to + * receive *only* tagged traffic and stops receiving untagged traffic. + * Thus, we do not want to actually add a filter for VID=0 + */ + if (!vid) + return 0; + /* Locked once because all functions invoked below iterates list*/ spin_lock_bh(&vsi->mac_filter_hash_lock); err = i40e_add_vlan_all_mac(vsi, vid); @@ -2674,15 +2685,7 @@ static int i40e_vlan_rx_add_vid(struct net_device *netdev, if (vid >= VLAN_N_VID) return -EINVAL; - /* If the network stack called us with vid = 0 then - * it is asking to receive priority tagged packets with - * vlan id 0. Our HW receives them by default when configured - * to receive untagged packets so there is no need to add an - * extra filter for vlan 0 tagged packets. - */ - if (vid) - ret = i40e_vsi_add_vlan(vsi, vid); - + ret = i40e_vsi_add_vlan(vsi, vid); if (!ret) set_bit(vid, vsi->active_vlans); -- cgit v1.2.3 From fbb113f773482496d601e0bd934e680b35876016 Mon Sep 17 00:00:00 2001 From: Stefan Assmann Date: Thu, 29 Jun 2017 15:12:24 +0200 Subject: i40e/i40evf: rename vf_offload_flags to vf_cap_flags in struct virtchnl_vf_resource The current name of vf_offload_flags indicates that the bitmap is limited to offload related features. Make this more generic by renaming it to vf_cap_flags, which allows for other capabilities besides offloading to be added. Signed-off-by: Stefan Assmann Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 22 +++++++++++----------- drivers/net/ethernet/intel/i40evf/i40e_common.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf.h | 10 +++++----- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 12 ++++++------ include/linux/avf/virtchnl.h | 4 ++-- 5 files changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 3ef67dc094fc..057c77be96e4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1528,39 +1528,39 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) VIRTCHNL_VF_OFFLOAD_RSS_REG | VIRTCHNL_VF_OFFLOAD_VLAN; - vfres->vf_offload_flags = VIRTCHNL_VF_OFFLOAD_L2; + vfres->vf_cap_flags = VIRTCHNL_VF_OFFLOAD_L2; vsi = pf->vsi[vf->lan_vsi_idx]; if (!vsi->info.pvid) - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_VLAN; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_VLAN; if (i40e_vf_client_capable(pf, vf->vf_id) && (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_IWARP)) { - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_IWARP; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_IWARP; set_bit(I40E_VF_STATE_IWARPENA, &vf->vf_states); } if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PF) { - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF; } else { if ((pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) && (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_AQ)) - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_AQ; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_AQ; else - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_REG; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_REG; } if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) { if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2) - vfres->vf_offload_flags |= + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2; } if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP) - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP; if ((pf->hw_features & I40E_HW_OUTER_UDP_CSUM_CAPABLE) && (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)) - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM; if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_POLLING) { if (pf->flags & I40E_FLAG_MFP_ENABLED) { @@ -1570,12 +1570,12 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) aq_ret = I40E_ERR_PARAM; goto err; } - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING; } if (pf->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) { if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) - vfres->vf_offload_flags |= + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_WB_ON_ITR; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index 1dd1938f594f..d69c2e44cd1a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -1104,7 +1104,7 @@ void i40e_vf_parse_hw_config(struct i40e_hw *hw, hw->dev_caps.num_rx_qp = msg->num_queue_pairs; hw->dev_caps.num_tx_qp = msg->num_queue_pairs; hw->dev_caps.num_msix_vectors_vf = msg->max_vectors; - hw->dev_caps.dcb = msg->vf_offload_flags & + hw->dev_caps.dcb = msg->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_L2; hw->dev_caps.fcoe = 0; for (i = 0; i < msg->num_vsis; i++) { diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 7f905368fc93..d310544c6c6e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -277,19 +277,19 @@ struct i40evf_adapter { enum virtchnl_link_speed link_speed; enum virtchnl_ops current_op; #define CLIENT_ALLOWED(_a) ((_a)->vf_res ? \ - (_a)->vf_res->vf_offload_flags & \ + (_a)->vf_res->vf_cap_flags & \ VIRTCHNL_VF_OFFLOAD_IWARP : \ 0) #define CLIENT_ENABLED(_a) ((_a)->cinst) /* RSS by the PF should be preferred over RSS via other methods. */ -#define RSS_PF(_a) ((_a)->vf_res->vf_offload_flags & \ +#define RSS_PF(_a) ((_a)->vf_res->vf_cap_flags & \ VIRTCHNL_VF_OFFLOAD_RSS_PF) -#define RSS_AQ(_a) ((_a)->vf_res->vf_offload_flags & \ +#define RSS_AQ(_a) ((_a)->vf_res->vf_cap_flags & \ VIRTCHNL_VF_OFFLOAD_RSS_AQ) -#define RSS_REG(_a) (!((_a)->vf_res->vf_offload_flags & \ +#define RSS_REG(_a) (!((_a)->vf_res->vf_cap_flags & \ (VIRTCHNL_VF_OFFLOAD_RSS_AQ | \ VIRTCHNL_VF_OFFLOAD_RSS_PF))) -#define VLAN_ALLOWED(_a) ((_a)->vf_res->vf_offload_flags & \ +#define VLAN_ALLOWED(_a) ((_a)->vf_res->vf_cap_flags & \ VIRTCHNL_VF_OFFLOAD_VLAN) struct virtchnl_vf_resource *vf_res; /* incl. all VSIs */ struct virtchnl_vsi_resource *vsi_res; /* our LAN VSI */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 8603911cc550..4a36c2ee3837 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1418,7 +1418,7 @@ static int i40evf_init_rss(struct i40evf_adapter *adapter) if (!RSS_PF(adapter)) { /* Enable PCTYPES for RSS, TCP/UDP with IPv4/IPv6 */ - if (adapter->vf_res->vf_offload_flags & + if (adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2) adapter->hena = I40E_DEFAULT_RSS_HENA_EXPANDED; else @@ -2371,7 +2371,7 @@ static netdev_features_t i40evf_fix_features(struct net_device *netdev, struct i40evf_adapter *adapter = netdev_priv(netdev); features &= ~I40EVF_VLAN_FEATURES; - if (adapter->vf_res->vf_offload_flags & VIRTCHNL_VF_OFFLOAD_VLAN) + if (adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN) features |= I40EVF_VLAN_FEATURES; return features; } @@ -2458,7 +2458,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) /* advertise to stack only if offloads for encapsulated packets is * supported */ - if (vfres->vf_offload_flags & VIRTCHNL_VF_OFFLOAD_ENCAP) { + if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ENCAP) { hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | NETIF_F_GSO_GRE_CSUM | @@ -2468,7 +2468,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) NETIF_F_GSO_PARTIAL | 0; - if (!(vfres->vf_offload_flags & + if (!(vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)) netdev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; @@ -2496,7 +2496,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) adapter->vsi.work_limit = I40E_DEFAULT_IRQ_WORK; vsi->netdev = adapter->netdev; vsi->qs_handle = adapter->vsi_res->qset_handle; - if (vfres->vf_offload_flags & VIRTCHNL_VF_OFFLOAD_RSS_PF) { + if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_RSS_PF) { adapter->rss_key_size = vfres->rss_key_size; adapter->rss_lut_size = vfres->rss_lut_size; } else { @@ -2664,7 +2664,7 @@ static void i40evf_init_task(struct work_struct *work) if (err) goto err_sw_init; i40evf_map_rings_to_vectors(adapter); - if (adapter->vf_res->vf_offload_flags & + if (adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE; diff --git a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h index c893b9520a67..becfca2ae94e 100644 --- a/include/linux/avf/virtchnl.h +++ b/include/linux/avf/virtchnl.h @@ -223,7 +223,7 @@ struct virtchnl_vsi_resource { VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_vsi_resource); -/* VF offload flags +/* VF capability flags * VIRTCHNL_VF_OFFLOAD_L2 flag is inclusive of base mode L2 offloads including * TX/RX Checksum offloading and TSO for non-tunnelled packets. */ @@ -251,7 +251,7 @@ struct virtchnl_vf_resource { u16 max_vectors; u16 max_mtu; - u32 vf_offload_flags; + u32 vf_cap_flags; u32 rss_key_size; u32 rss_lut_size; -- cgit v1.2.3 From f0db78928783f0a4cce4940e8c03c2e9a760e629 Mon Sep 17 00:00:00 2001 From: Alan Brady Date: Wed, 12 Jul 2017 05:46:04 -0400 Subject: i40evf: use netdev variable in reset task If we're going to bother initializing a variable to reference it we might as well use it. Signed-off-by: Alan Brady Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 4a36c2ee3837..4dccb67e9268 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1879,7 +1879,7 @@ static void i40evf_reset_task(struct work_struct *work) } continue_reset: - if (netif_running(adapter->netdev)) { + if (netif_running(netdev)) { netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); adapter->link_up = false; @@ -1947,7 +1947,7 @@ continue_reset: return; reset_err: dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n"); - i40evf_close(adapter->netdev); + i40evf_close(netdev); } /** -- cgit v1.2.3 From 7e4d01e7d3f7d4f7b0a768a1028cb26ea06c8694 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 12 Jul 2017 05:46:05 -0400 Subject: i40e: use cpumask_copy instead of direct assignment According to the header file cpumask.h, we shouldn't be directly copying a cpumask_t, since its a bitmap and might not be copied correctly. Lets use the provided cpumask_copy() function instead. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1b3b681a8b1d..b0ccd3c2eec6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3449,7 +3449,7 @@ static void i40e_irq_affinity_notify(struct irq_affinity_notify *notify, struct i40e_q_vector *q_vector = container_of(notify, struct i40e_q_vector, affinity_notify); - q_vector->affinity_mask = *mask; + cpumask_copy(&q_vector->affinity_mask, mask); } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 4dccb67e9268..0d87191b6bac 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -520,7 +520,7 @@ static void i40evf_irq_affinity_notify(struct irq_affinity_notify *notify, struct i40e_q_vector *q_vector = container_of(notify, struct i40e_q_vector, affinity_notify); - q_vector->affinity_mask = *mask; + cpumask_copy(&q_vector->affinity_mask, mask); } /** -- cgit v1.2.3 From 06b2decd924891b6c7570a91f91e11a5a8fed421 Mon Sep 17 00:00:00 2001 From: Alan Brady Date: Wed, 12 Jul 2017 05:46:06 -0400 Subject: i40e: prevent changing ITR if adaptive-rx/tx enabled Currently the driver allows the user to change (or even disable) interrupt moderation if adaptive-rx/tx is enabled when this should not be the case. Adaptive RX/TX will not respect the user's ITR settings so allowing the user to change it is weird. This bug would also allow the user to disable interrupt moderation with adaptive-rx/tx enabled which doesn't make much sense either. This patch makes it such that if adaptive-rx/tx is enabled, the user cannot make any manual adjustments to interrupt moderation. It also makes it so that if ITR is disabled but adaptive-rx/tx is then enabled, ITR will be re-enabled. Signed-off-by: Alan Brady Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 65 +++++++++++++++++--------- 1 file changed, 43 insertions(+), 22 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index a868c8d4fec9..05e89864f781 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2194,14 +2194,29 @@ static int __i40e_set_coalesce(struct net_device *netdev, int queue) { struct i40e_netdev_priv *np = netdev_priv(netdev); + u16 intrl_reg, cur_rx_itr, cur_tx_itr; struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - u16 intrl_reg; int i; if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq) vsi->work_limit = ec->tx_max_coalesced_frames_irq; + if (queue < 0) { + cur_rx_itr = vsi->rx_rings[0]->rx_itr_setting; + cur_tx_itr = vsi->tx_rings[0]->tx_itr_setting; + } else if (queue < vsi->num_queue_pairs) { + cur_rx_itr = vsi->rx_rings[queue]->rx_itr_setting; + cur_tx_itr = vsi->tx_rings[queue]->tx_itr_setting; + } else { + netif_info(pf, drv, netdev, "Invalid queue value, queue range is 0 - %d\n", + vsi->num_queue_pairs - 1); + return -EINVAL; + } + + cur_tx_itr &= ~I40E_ITR_DYNAMIC; + cur_rx_itr &= ~I40E_ITR_DYNAMIC; + /* tx_coalesce_usecs_high is ignored, use rx-usecs-high instead */ if (ec->tx_coalesce_usecs_high != vsi->int_rate_limit) { netif_info(pf, drv, netdev, "tx-usecs-high is not used, please program rx-usecs-high\n"); @@ -2214,15 +2229,34 @@ static int __i40e_set_coalesce(struct net_device *netdev, return -EINVAL; } - if (ec->rx_coalesce_usecs == 0) { - if (ec->use_adaptive_rx_coalesce) - netif_info(pf, drv, netdev, "rx-usecs=0, need to disable adaptive-rx for a complete disable\n"); - } else if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) || - (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1))) { - netif_info(pf, drv, netdev, "Invalid value, rx-usecs range is 0-8160\n"); - return -EINVAL; + if (ec->rx_coalesce_usecs != cur_rx_itr && + ec->use_adaptive_rx_coalesce) { + netif_info(pf, drv, netdev, "RX interrupt moderation cannot be changed if adaptive-rx is enabled.\n"); + return -EINVAL; + } + + if (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1)) { + netif_info(pf, drv, netdev, "Invalid value, rx-usecs range is 0-8160\n"); + return -EINVAL; } + if (ec->tx_coalesce_usecs != cur_tx_itr && + ec->use_adaptive_tx_coalesce) { + netif_info(pf, drv, netdev, "TX interrupt moderation cannot be changed if adaptive-tx is enabled.\n"); + return -EINVAL; + } + + if (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1)) { + netif_info(pf, drv, netdev, "Invalid value, tx-usecs range is 0-8160\n"); + return -EINVAL; + } + + if (ec->use_adaptive_rx_coalesce && !cur_rx_itr) + ec->rx_coalesce_usecs = I40E_MIN_ITR << 1; + + if (ec->use_adaptive_tx_coalesce && !cur_tx_itr) + ec->tx_coalesce_usecs = I40E_MIN_ITR << 1; + intrl_reg = i40e_intrl_usec_to_reg(ec->rx_coalesce_usecs_high); vsi->int_rate_limit = INTRL_REG_TO_USEC(intrl_reg); if (vsi->int_rate_limit != ec->rx_coalesce_usecs_high) { @@ -2230,27 +2264,14 @@ static int __i40e_set_coalesce(struct net_device *netdev, vsi->int_rate_limit); } - if (ec->tx_coalesce_usecs == 0) { - if (ec->use_adaptive_tx_coalesce) - netif_info(pf, drv, netdev, "tx-usecs=0, need to disable adaptive-tx for a complete disable\n"); - } else if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) || - (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1))) { - netif_info(pf, drv, netdev, "Invalid value, tx-usecs range is 0-8160\n"); - return -EINVAL; - } - /* rx and tx usecs has per queue value. If user doesn't specify the queue, * apply to all queues. */ if (queue < 0) { for (i = 0; i < vsi->num_queue_pairs; i++) i40e_set_itr_per_queue(vsi, ec, i); - } else if (queue < vsi->num_queue_pairs) { - i40e_set_itr_per_queue(vsi, ec, queue); } else { - netif_info(pf, drv, netdev, "Invalid queue value, queue range is 0 - %d\n", - vsi->num_queue_pairs - 1); - return -EINVAL; + i40e_set_itr_per_queue(vsi, ec, queue); } return 0; -- cgit v1.2.3 From 2bf01935ec5362aee6ff9ffc2476043af321aa42 Mon Sep 17 00:00:00 2001 From: Sudheer Mogilappagari Date: Wed, 12 Jul 2017 05:46:07 -0400 Subject: i40e: synchronize nvmupdate command and adminq subtask During NVM update, state machine gets into unrecoverable state because i40e_clean_adminq_subtask can get scheduled after the admin queue command but before other state variables are updated. This causes incorrect input to i40e_nvmupd_check_wait_event and state transitions don't happen. This issue existed before but surfaced after commit 373149fc99a0 ("i40e: Decrease the scope of rtnl lock") This fix adds locking around admin queue command and update of state variables so that adminq_subtask will have accurate information whenever it gets scheduled. Signed-off-by: Sudheer Mogilappagari Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_nvm.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 6fdecd70dcbc..2cf7db2dc7cd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -753,6 +753,11 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; } + /* Acquire lock to prevent race condition where adminq_task + * can execute after i40e_nvmupd_nvm_read/write but before state + * variables (nvm_wait_opcode, nvm_release_on_done) are updated + */ + mutex_lock(&hw->aq.arq_mutex); switch (hw->nvmupd_state) { case I40E_NVMUPD_STATE_INIT: status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); @@ -788,6 +793,7 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, *perrno = -ESRCH; break; } + mutex_unlock(&hw->aq.arq_mutex); return status; } -- cgit v1.2.3 From 7d8697afae5c61a9495fff54a1c5d053dea4505f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 25 Aug 2017 11:24:28 +0300 Subject: hinic: skb_pad() frees on error The skb_pad() function frees the skb on error, so this code has a double free. Fixes: 00e57a6d4ad3 ("net-next/hinic: Add Tx operation") Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_tx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index 5bf6a32faa46..abe3e38cd342 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -192,7 +192,7 @@ netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if (skb->len < MIN_SKB_LEN) { if (skb_pad(skb, MIN_SKB_LEN - skb->len)) { netdev_err(netdev, "Failed to pad skb\n"); - goto skb_error; + goto update_error_stats; } skb->len = MIN_SKB_LEN; @@ -237,6 +237,7 @@ flush_skbs: skb_error: dev_kfree_skb_any(skb); +update_error_stats: u64_stats_update_begin(&txq->txq_stats.syncp); txq->txq_stats.tx_dropped++; u64_stats_update_end(&txq->txq_stats.syncp); -- cgit v1.2.3 From 25528d90f541fa23cb7b862a7e9dc0b9c44d9adc Mon Sep 17 00:00:00 2001 From: Pablo Cascón Date: Thu, 24 Aug 2017 21:31:49 -0700 Subject: nfp: add basic SR-IOV ndo functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add basic ndo_set/get_vf to support SR-IOV. VF to egress phy static mapping by now. Use vfcfg ABI version 2 to write the info to the FW and collect the return value from the mailbox. Signed-off-by: Pablo Cascón Signed-off-by: Jimmy Kizito Signed-off-by: Rami Tomer Signed-off-by: Simon Horman Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/Makefile | 1 + drivers/net/ethernet/netronome/nfp/nfp_main.h | 4 + .../net/ethernet/netronome/nfp/nfp_net_common.c | 6 + drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h | 1 + drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 20 +- drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c | 243 +++++++++++++++++++++ drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h | 86 ++++++++ drivers/net/ethernet/netronome/nfp/nic/main.c | 12 + 8 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c create mode 100644 drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile index b8e1358868bd..96e579a15cbe 100644 --- a/drivers/net/ethernet/netronome/nfp/Makefile +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -23,6 +23,7 @@ nfp-objs := \ nfp_net_ethtool.o \ nfp_net_main.o \ nfp_net_repr.o \ + nfp_net_sriov.o \ nfp_netvf_main.o \ nfp_port.o \ bpf/main.o \ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index 6922410806db..be0ee59f2eb9 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -73,6 +73,8 @@ struct nfp_rtsym_table; * @mac_stats_mem: Pointer to mapped MAC stats area * @vf_cfg_bar: Pointer to the CPP area for the VF configuration BAR * @vf_cfg_mem: Pointer to mapped VF configuration area + * @vfcfg_tbl2_area: Pointer to the CPP area for the VF config table + * @vfcfg_tbl2: Pointer to mapped VF config table * @irq_entries: Array of MSI-X entries for all vNICs * @limit_vfs: Number of VFs supported by firmware (~0 for PCI limit) * @num_vfs: Number of SR-IOV VFs enabled @@ -107,6 +109,8 @@ struct nfp_pf { u8 __iomem *mac_stats_mem; struct nfp_cpp_area *vf_cfg_bar; u8 __iomem *vf_cfg_mem; + struct nfp_cpp_area *vfcfg_tbl2_area; + u8 __iomem *vfcfg_tbl2; struct msix_entry *irq_entries; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 732f1d315fba..ecbec28cfa76 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -71,6 +71,7 @@ #include "nfp_app.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" +#include "nfp_net_sriov.h" #include "nfp_port.h" /** @@ -3421,6 +3422,11 @@ const struct net_device_ops nfp_net_netdev_ops = { .ndo_get_stats64 = nfp_net_stat64, .ndo_vlan_rx_add_vid = nfp_net_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = nfp_net_vlan_rx_kill_vid, + .ndo_set_vf_mac = nfp_app_set_vf_mac, + .ndo_set_vf_vlan = nfp_app_set_vf_vlan, + .ndo_set_vf_spoofchk = nfp_app_set_vf_spoofchk, + .ndo_get_vf_config = nfp_app_get_vf_config, + .ndo_set_vf_link_state = nfp_app_set_vf_link_state, .ndo_setup_tc = nfp_port_setup_tc, .ndo_tx_timeout = nfp_net_tx_timeout, .ndo_set_rx_mode = nfp_net_set_rx_mode, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index e5e94e0746ec..b0a452ba9039 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -164,6 +164,7 @@ #define NFP_NET_CFG_UPDATE_BPF (0x1 << 10) /* BPF program load */ #define NFP_NET_CFG_UPDATE_MACADDR (0x1 << 11) /* MAC address change */ #define NFP_NET_CFG_UPDATE_MBOX (0x1 << 12) /* Mailbox update */ +#define NFP_NET_CFG_UPDATE_VF (0x1 << 13) /* VF settings change */ #define NFP_NET_CFG_UPDATE_ERR (0x1 << 31) /* A error occurred */ #define NFP_NET_CFG_TXRS_ENABLE 0x0008 #define NFP_NET_CFG_RXRS_ENABLE 0x0010 diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index acdad6f20251..2da083fd5e13 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -57,6 +57,7 @@ #include "nfpcore/nfp6000_pcie.h" #include "nfp_app.h" #include "nfp_net_ctrl.h" +#include "nfp_net_sriov.h" #include "nfp_net.h" #include "nfp_main.h" #include "nfp_port.h" @@ -489,6 +490,8 @@ static void nfp_net_pf_app_stop(struct nfp_pf *pf) static void nfp_net_pci_unmap_mem(struct nfp_pf *pf) { + if (pf->vfcfg_tbl2_area) + nfp_cpp_area_release_free(pf->vfcfg_tbl2_area); if (pf->vf_cfg_bar) nfp_cpp_area_release_free(pf->vf_cfg_bar); if (pf->mac_stats_bar) @@ -535,17 +538,32 @@ static int nfp_net_pci_map_mem(struct nfp_pf *pf) pf->vf_cfg_mem = NULL; } + min_size = NFP_NET_VF_CFG_SZ * pf->limit_vfs + NFP_NET_VF_CFG_MB_SZ; + pf->vfcfg_tbl2 = nfp_net_pf_map_rtsym(pf, "net.vfcfg_tbl2", + "_pf%d_net_vf_cfg2", + min_size, &pf->vfcfg_tbl2_area); + if (IS_ERR(pf->vfcfg_tbl2)) { + if (PTR_ERR(pf->vfcfg_tbl2) != -ENOENT) { + err = PTR_ERR(pf->vfcfg_tbl2); + goto err_unmap_vf_cfg; + } + pf->vfcfg_tbl2 = NULL; + } + mem = nfp_cpp_map_area(pf->cpp, "net.qc", 0, 0, NFP_PCIE_QUEUE(0), NFP_QCP_QUEUE_AREA_SZ, &pf->qc_area); if (IS_ERR(mem)) { nfp_err(pf->cpp, "Failed to map Queue Controller area.\n"); err = PTR_ERR(mem); - goto err_unmap_vf_cfg; + goto err_unmap_vfcfg_tbl2; } return 0; +err_unmap_vfcfg_tbl2: + if (pf->vfcfg_tbl2_area) + nfp_cpp_area_release_free(pf->vfcfg_tbl2_area); err_unmap_vf_cfg: if (pf->vf_cfg_bar) nfp_cpp_area_release_free(pf->vf_cfg_bar); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c new file mode 100644 index 000000000000..e6d2e06b050c --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "nfpcore/nfp_cpp.h" +#include "nfp_app.h" +#include "nfp_main.h" +#include "nfp_net_ctrl.h" +#include "nfp_net.h" +#include "nfp_net_sriov.h" + +static int +nfp_net_sriov_check(struct nfp_app *app, int vf, u16 cap, const char *msg) +{ + u16 cap_vf; + + if (!app || !app->pf->vfcfg_tbl2) + return -EOPNOTSUPP; + + cap_vf = readw(app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_CAP); + if ((cap_vf & cap) != cap) { + nfp_warn(app->pf->cpp, "ndo_set_vf_%s not supported\n", msg); + return -EOPNOTSUPP; + } + + if (vf < 0 || vf >= app->pf->num_vfs) { + nfp_warn(app->pf->cpp, "invalid VF id %d\n", vf); + return -EINVAL; + } + + return 0; +} + +static int +nfp_net_sriov_update(struct nfp_app *app, int vf, u16 update, const char *msg) +{ + struct nfp_net *nn; + int ret; + + /* Write update info to mailbox in VF config symbol */ + writeb(vf, app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_VF_NUM); + writew(update, app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_UPD); + + nn = list_first_entry(&app->pf->vnics, struct nfp_net, vnic_list); + /* Signal VF reconfiguration */ + ret = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_VF); + if (ret) + return ret; + + ret = readw(app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_RET); + if (ret) + nfp_warn(app->pf->cpp, + "FW refused VF %s update with errno: %d\n", msg, ret); + return -ret; +} + +int nfp_app_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + int err; + + err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_MAC, "mac"); + if (err) + return err; + + if (is_multicast_ether_addr(mac)) { + nfp_warn(app->pf->cpp, + "invalid Ethernet address %pM for VF id %d\n", + mac, vf); + return -EINVAL; + } + + /* Write MAC to VF entry in VF config symbol */ + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; + writel(get_unaligned_be32(mac), app->pf->vfcfg_tbl2 + vf_offset); + writew(get_unaligned_be16(mac + 4), + app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_MAC_LO); + + return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_MAC, "MAC"); +} + +int nfp_app_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, + __be16 vlan_proto) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + u16 vlan_tci; + int err; + + err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_VLAN, "vlan"); + if (err) + return err; + + if (vlan_proto != htons(ETH_P_8021Q)) + return -EOPNOTSUPP; + + if (vlan > 4095 || qos > 7) { + nfp_warn(app->pf->cpp, + "invalid vlan id or qos for VF id %d\n", vf); + return -EINVAL; + } + + /* Write VLAN tag to VF entry in VF config symbol */ + vlan_tci = FIELD_PREP(NFP_NET_VF_CFG_VLAN_VID, vlan) | + FIELD_PREP(NFP_NET_VF_CFG_VLAN_QOS, qos); + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; + writew(vlan_tci, app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_VLAN); + + return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_VLAN, + "vlan"); +} + +int nfp_app_set_vf_spoofchk(struct net_device *netdev, int vf, bool enable) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + u8 vf_ctrl; + int err; + + err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_SPOOF, + "spoofchk"); + if (err) + return err; + + /* Write spoof check control bit to VF entry in VF config symbol */ + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ + + NFP_NET_VF_CFG_CTRL; + vf_ctrl = readb(app->pf->vfcfg_tbl2 + vf_offset); + vf_ctrl &= ~NFP_NET_VF_CFG_CTRL_SPOOF; + vf_ctrl |= FIELD_PREP(NFP_NET_VF_CFG_CTRL_SPOOF, enable); + writeb(vf_ctrl, app->pf->vfcfg_tbl2 + vf_offset); + + return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_SPOOF, + "spoofchk"); +} + +int nfp_app_set_vf_link_state(struct net_device *netdev, int vf, + int link_state) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + u8 vf_ctrl; + int err; + + err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_LINK_STATE, + "link_state"); + if (err) + return err; + + switch (link_state) { + case IFLA_VF_LINK_STATE_AUTO: + case IFLA_VF_LINK_STATE_ENABLE: + case IFLA_VF_LINK_STATE_DISABLE: + break; + default: + return -EINVAL; + } + + /* Write link state to VF entry in VF config symbol */ + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ + + NFP_NET_VF_CFG_CTRL; + vf_ctrl = readb(app->pf->vfcfg_tbl2 + vf_offset); + vf_ctrl &= ~NFP_NET_VF_CFG_CTRL_LINK_STATE; + vf_ctrl |= FIELD_PREP(NFP_NET_VF_CFG_CTRL_LINK_STATE, link_state); + writeb(vf_ctrl, app->pf->vfcfg_tbl2 + vf_offset); + + return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_LINK_STATE, + "link state"); +} + +int nfp_app_get_vf_config(struct net_device *netdev, int vf, + struct ifla_vf_info *ivi) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + u16 vlan_tci; + u32 mac_hi; + u16 mac_lo; + u8 flags; + int err; + + err = nfp_net_sriov_check(app, vf, 0, ""); + if (err) + return err; + + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; + + mac_hi = readl(app->pf->vfcfg_tbl2 + vf_offset); + mac_lo = readw(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_MAC_LO); + + flags = readb(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_CTRL); + vlan_tci = readw(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_VLAN); + + memset(ivi, 0, sizeof(*ivi)); + ivi->vf = vf; + + put_unaligned_be32(mac_hi, &ivi->mac[0]); + put_unaligned_be16(mac_lo, &ivi->mac[4]); + + ivi->vlan = FIELD_GET(NFP_NET_VF_CFG_VLAN_VID, vlan_tci); + ivi->qos = FIELD_GET(NFP_NET_VF_CFG_VLAN_QOS, vlan_tci); + + ivi->spoofchk = FIELD_GET(NFP_NET_VF_CFG_CTRL_SPOOF, flags); + ivi->linkstate = FIELD_GET(NFP_NET_VF_CFG_CTRL_LINK_STATE, flags); + + return 0; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h new file mode 100644 index 000000000000..e9df9d1eab8e --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _NFP_NET_SRIOV_H_ +#define _NFP_NET_SRIOV_H_ + +/** + * SRIOV VF configuration. + * The configuration memory begins with a mailbox region for communication with + * the firmware followed by individual VF entries. + */ +#define NFP_NET_VF_CFG_SZ 16 +#define NFP_NET_VF_CFG_MB_SZ 16 + +/* VF config mailbox */ +#define NFP_NET_VF_CFG_MB 0x0 +#define NFP_NET_VF_CFG_MB_CAP 0x0 +#define NFP_NET_VF_CFG_MB_CAP_MAC (0x1 << 0) +#define NFP_NET_VF_CFG_MB_CAP_VLAN (0x1 << 1) +#define NFP_NET_VF_CFG_MB_CAP_SPOOF (0x1 << 2) +#define NFP_NET_VF_CFG_MB_CAP_LINK_STATE (0x1 << 3) +#define NFP_NET_VF_CFG_MB_RET 0x2 +#define NFP_NET_VF_CFG_MB_UPD 0x4 +#define NFP_NET_VF_CFG_MB_UPD_MAC (0x1 << 0) +#define NFP_NET_VF_CFG_MB_UPD_VLAN (0x1 << 1) +#define NFP_NET_VF_CFG_MB_UPD_SPOOF (0x1 << 2) +#define NFP_NET_VF_CFG_MB_UPD_LINK_STATE (0x1 << 3) +#define NFP_NET_VF_CFG_MB_VF_NUM 0x7 + +/* VF config entry + * MAC_LO is set that the MAC address can be read in a single 6 byte read + * by the NFP + */ +#define NFP_NET_VF_CFG_MAC 0x0 +#define NFP_NET_VF_CFG_MAC_HI 0x0 +#define NFP_NET_VF_CFG_MAC_LO 0x6 +#define NFP_NET_VF_CFG_CTRL 0x4 +#define NFP_NET_VF_CFG_CTRL_SPOOF 0x4 +#define NFP_NET_VF_CFG_CTRL_LINK_STATE 0x3 +#define NFP_NET_VF_CFG_LS_MODE_AUTO 0 +#define NFP_NET_VF_CFG_LS_MODE_ENABLE 1 +#define NFP_NET_VF_CFG_LS_MODE_DISABLE 2 +#define NFP_NET_VF_CFG_VLAN 0x8 +#define NFP_NET_VF_CFG_VLAN_QOS 0xe000 +#define NFP_NET_VF_CFG_VLAN_VID 0x0fff + +int nfp_app_set_vf_mac(struct net_device *netdev, int vf, u8 *mac); +int nfp_app_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, + __be16 vlan_proto); +int nfp_app_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); +int nfp_app_set_vf_link_state(struct net_device *netdev, int vf, + int link_state); +int nfp_app_get_vf_config(struct net_device *netdev, int vf, + struct ifla_vf_info *ivi); + +#endif /* _NFP_NET_SRIOV_H_ */ diff --git a/drivers/net/ethernet/netronome/nfp/nic/main.c b/drivers/net/ethernet/netronome/nfp/nic/main.c index 520684242b7d..8287a85d22c1 100644 --- a/drivers/net/ethernet/netronome/nfp/nic/main.c +++ b/drivers/net/ethernet/netronome/nfp/nic/main.c @@ -49,10 +49,22 @@ static int nfp_nic_init(struct nfp_app *app) return 0; } +static int nfp_nic_sriov_enable(struct nfp_app *app, int num_vfs) +{ + return 0; +} + +static void nfp_nic_sriov_disable(struct nfp_app *app) +{ +} + const struct nfp_app_type app_nic = { .id = NFP_APP_CORE_NIC, .name = "nic", .init = nfp_nic_init, .vnic_init = nfp_app_nic_vnic_init, + + .sriov_enable = nfp_nic_sriov_enable, + .sriov_disable = nfp_nic_sriov_disable, }; -- cgit v1.2.3 From 6abd224b2554cbbb31db62a8b4de7506df553f38 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Thu, 24 Aug 2017 21:31:50 -0700 Subject: nfp: add basic SR-IOV ndo functions to representors Add basic ndo_set/get_vf to support SR-IOV on all types of port representors. Signed-off-by: Simon Horman Signed-off-by: Dirk van der Merwe Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_repr.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 0f9878d1bf40..d540a9dc77b3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -43,6 +43,7 @@ #include "nfp_main.h" #include "nfp_net_ctrl.h" #include "nfp_net_repr.h" +#include "nfp_net_sriov.h" #include "nfp_port.h" static void @@ -247,6 +248,11 @@ const struct net_device_ops nfp_repr_netdev_ops = { .ndo_get_offload_stats = nfp_repr_get_offload_stats, .ndo_get_phys_port_name = nfp_port_get_phys_port_name, .ndo_setup_tc = nfp_port_setup_tc, + .ndo_set_vf_mac = nfp_app_set_vf_mac, + .ndo_set_vf_vlan = nfp_app_set_vf_vlan, + .ndo_set_vf_spoofchk = nfp_app_set_vf_spoofchk, + .ndo_get_vf_config = nfp_app_get_vf_config, + .ndo_set_vf_link_state = nfp_app_set_vf_link_state, }; static void nfp_repr_clean(struct nfp_repr *repr) -- cgit v1.2.3 From ec15ecdee5eb9e33a565e1e8eaef39fd4de565cb Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Fri, 25 Aug 2017 15:24:46 +0200 Subject: net: mvpp2: fix the packet size configuration for 10G The MVPP22_XLG_CTRL1_FRAMESIZELIMIT define is used as an offset, but is defined as BIT(0). Updated its name to contains "OFFS" as in offset and fix its value using the offset value, 0. Reported-by: Stefan Chulski Signed-off-by: Antoine Tenart Fixes: 76eb1b1de5b6 ("net: mvpp2: set maximum packet size for 10G ports") Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 7fa251bf91ae..fea9ae5b70ba 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -367,7 +367,7 @@ #define MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN BIT(7) #define MVPP22_XLG_CTRL0_MIB_CNT_DIS BIT(14) #define MVPP22_XLG_CTRL1_REG 0x104 -#define MVPP22_XLG_CTRL1_FRAMESIZELIMIT BIT(0) +#define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS 0 #define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK 0x1fff #define MVPP22_XLG_CTRL3_REG 0x11c #define MVPP22_XLG_CTRL3_MACMODESELECT_MASK (7 << 13) @@ -4669,7 +4669,7 @@ static inline void mvpp2_xlg_max_rx_size_set(struct mvpp2_port *port) val = readl(port->base + MVPP22_XLG_CTRL1_REG); val &= ~MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK; val |= ((port->pkt_size - MVPP2_MH_SIZE) / 2) << - MVPP22_XLG_CTRL1_FRAMESIZELIMIT; + MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS; writel(val, port->base + MVPP22_XLG_CTRL1_REG); } -- cgit v1.2.3 From 167d52edc4991e81012ef571643d0307aa2bb916 Mon Sep 17 00:00:00 2001 From: Sudheer Mogilappagari Date: Sun, 27 Aug 2017 15:07:47 -0700 Subject: i40e: Update state variable for adminq subtask During NVM update, state machine gets into unrecoverable state because i40e_clean_adminq_subtask can get scheduled after the admin queue command but before other state variables are updated. This causes incorrect input to i40e_nvmupd_check_wait_event and state transitions don't happen. This fix updates the state variables so that adminq_subtask will have accurate information whenever it gets scheduled. Signed-off-by: Sudheer Mogilappagari Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_nvm.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 2cf7db2dc7cd..96afef98a08f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -755,7 +755,11 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, /* Acquire lock to prevent race condition where adminq_task * can execute after i40e_nvmupd_nvm_read/write but before state - * variables (nvm_wait_opcode, nvm_release_on_done) are updated + * variables (nvm_wait_opcode, nvm_release_on_done) are updated. + * + * During NVMUpdate, it is observed that lock could be held for + * ~5ms for most commands. However lock is held for ~60ms for + * NVMUPD_CSUM_LCB command. */ mutex_lock(&hw->aq.arq_mutex); switch (hw->nvmupd_state) { @@ -778,7 +782,8 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, */ if (cmd->offset == 0xffff) { i40e_nvmupd_check_wait_event(hw, hw->nvm_wait_opcode); - return 0; + status = 0; + goto exit; } status = I40E_ERR_NOT_READY; @@ -793,6 +798,7 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, *perrno = -ESRCH; break; } +exit: mutex_unlock(&hw->aq.arq_mutex); return status; } -- cgit v1.2.3 From ed601f660131be6bb9a8a109b0f2bf031786100f Mon Sep 17 00:00:00 2001 From: Mariusz Stachura Date: Wed, 12 Jul 2017 05:46:08 -0400 Subject: i40e: Store the requested FEC information Store information about FEC modes, that were requested. It will be used in printing link status information function and this way there is no need to call admin queue there. Signed-off-by: Mariusz Stachura Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 4 ++++ drivers/net/ethernet/intel/i40e/i40e_type.h | 1 + drivers/net/ethernet/intel/i40evf/i40e_type.h | 1 + 3 files changed, 6 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 8e082a946411..5c36a18a31be 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -2529,6 +2529,10 @@ i40e_status i40e_update_link_info(struct i40e_hw *hw) if (status) return status; + hw->phy.link_info.req_fec_info = + abilities.fec_cfg_curr_mod_ext_info & + (I40E_AQ_REQUEST_FEC_KR | I40E_AQ_REQUEST_FEC_RS); + memcpy(hw->phy.link_info.module_type, &abilities.module_type, sizeof(hw->phy.link_info.module_type)); } diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 3a18ed13edc4..fd4bbdd88b57 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -185,6 +185,7 @@ struct i40e_link_status { enum i40e_aq_link_speed link_speed; u8 link_info; u8 an_info; + u8 req_fec_info; u8 fec_info; u8 ext_info; u8 loopback; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index bde7f24af1c6..2ea919d9cdcf 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -159,6 +159,7 @@ struct i40e_link_status { enum i40e_aq_link_speed link_speed; u8 link_info; u8 an_info; + u8 req_fec_info; u8 fec_info; u8 ext_info; u8 loopback; -- cgit v1.2.3 From b5d5504aa1e961fc1f87ee7b092bf5ce1a7bf0de Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 12 Jul 2017 05:46:09 -0400 Subject: i40e: prevent snprintf format specifier truncation Increase the size of the prefix buffer so that it can hold enough characters for every possible input. Although 20 is enough for all expected inputs, it is possible for the values to be larger than expected, resulting in a possibly truncated string. Additionally, lets use sizeof(prefix) in order to ensure we use the correct size if we need to change the array length in the future. New versions of GCC starting at 7 now include warnings to prevent truncation unless you handle the return code. At most 27 bytes can be written here, so lets just increase the buffer size even if for all expected hw->bus.* values we only needed 20. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 4 ++-- drivers/net/ethernet/intel/i40evf/i40e_common.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 5c36a18a31be..111426ba5fbc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -328,9 +328,9 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc, len = buf_len; /* write the full 16-byte chunks */ if (hw->debug_mask & mask) { - char prefix[20]; + char prefix[27]; - snprintf(prefix, 20, + snprintf(prefix, sizeof(prefix), "i40e %02x:%02x.%x: \t0x", hw->bus.bus_id, hw->bus.device, diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index d69c2e44cd1a..8d3a2bfe186a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -333,9 +333,9 @@ void i40evf_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc, len = buf_len; /* write the full 16-byte chunks */ if (hw->debug_mask & mask) { - char prefix[20]; + char prefix[27]; - snprintf(prefix, 20, + snprintf(prefix, sizeof(prefix), "i40evf %02x:%02x.%x: \t0x", hw->bus.bus_id, hw->bus.device, -- cgit v1.2.3 From e53b382f3a207690fc0411a3b39fbd21d7470cfc Mon Sep 17 00:00:00 2001 From: Akeem G Abodunrin Date: Wed, 12 Jul 2017 05:46:10 -0400 Subject: i40e: Use correct flag to enable egress traffic for unicast promisc Albeit, we usually set true promiscuous mode for both multicast and unicast at the same time - however, it is possible to set it individually, so using allmulti flag which is only for allmulticast might caused unwanted behavior in mirroring egress traffic promiscuous for unicast in VF. Signed-off-by: Akeem G Abodunrin Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 057c77be96e4..27d87bef4ba3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1758,7 +1758,7 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, } } else { aq_ret = i40e_aq_set_vsi_unicast_promiscuous(hw, vsi->seid, - allmulti, NULL, + alluni, NULL, true); aq_err = pf->hw.aq.asq_last_status; if (aq_ret) { -- cgit v1.2.3 From 696ac80aa11fb80e641068123412cd397b460a0b Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 12 Jul 2017 05:46:11 -0400 Subject: i40evf: fix possible snprintf truncation of q_vector->name The q_vector names are based on the interface name with a driver prefix, the type of q_vector setup, and the queue number. We previously set the size of this variable to IFNAMSIZ + 9, which is incorrect, because we actually include a minimum of 14 characters extra beyond the interface name size. New versions of GCC since 7 include a new warning that detects this possible truncation and complains. We can fix this by increasing the size in case our interface name is too large to avoid truncation. We don't need to go beyond 14 because the compiler is smart enough to realize our values can never exceed size of 1. We do go up to 15 here because possible future changes may increase the number of queues beyond one digit. While we are here, also change some variables to be unsigned (since they are never negative) and stop using an extra unnecessary %s format specifier. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 21 +++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index d310544c6c6e..e5293d35fb6a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -121,7 +121,7 @@ struct i40e_q_vector { #define ITR_COUNTDOWN_START 100 u8 itr_countdown; /* when 0 or 1 update ITR */ int v_idx; /* vector index in list */ - char name[IFNAMSIZ + 9]; + char name[IFNAMSIZ + 15]; bool arm_wb_state; cpumask_t affinity_mask; struct irq_affinity_notify affinity_notify; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 0d87191b6bac..258e8e27068b 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -543,9 +543,9 @@ static void i40evf_irq_affinity_release(struct kref *ref) {} static int i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) { - int vector, err, q_vectors; - int rx_int_idx = 0, tx_int_idx = 0; - int irq_num; + unsigned int vector, q_vectors; + unsigned int rx_int_idx = 0, tx_int_idx = 0; + int irq_num, err; i40evf_irq_disable(adapter); /* Decrement for Other and TCP Timer vectors */ @@ -556,18 +556,15 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) irq_num = adapter->msix_entries[vector + NONQ_VECS].vector; if (q_vector->tx.ring && q_vector->rx.ring) { - snprintf(q_vector->name, sizeof(q_vector->name) - 1, - "i40evf-%s-%s-%d", basename, - "TxRx", rx_int_idx++); + snprintf(q_vector->name, sizeof(q_vector->name), + "i40evf-%s-TxRx-%d", basename, rx_int_idx++); tx_int_idx++; } else if (q_vector->rx.ring) { - snprintf(q_vector->name, sizeof(q_vector->name) - 1, - "i40evf-%s-%s-%d", basename, - "rx", rx_int_idx++); + snprintf(q_vector->name, sizeof(q_vector->name), + "i40evf-%s-rx-%d", basename, rx_int_idx++); } else if (q_vector->tx.ring) { - snprintf(q_vector->name, sizeof(q_vector->name) - 1, - "i40evf-%s-%s-%d", basename, - "tx", tx_int_idx++); + snprintf(q_vector->name, sizeof(q_vector->name), + "i40evf-%s-tx-%d", basename, tx_int_idx++); } else { /* skip this unused q_vector */ continue; -- cgit v1.2.3 From 8c9eb350aa7b66ab06f3e378dab3c7875a0bf83a Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 12 Jul 2017 05:46:12 -0400 Subject: i40e: force VMDQ device name truncation In new versions of GCC since 7.x a new warning exists which warns when a string is truncated before all of the format can be completed. When we setup VMDQ netdev names we are copying a pre-existing interface name which could be up to 15 characters in length. Since we also add 4 bytes, v, the literal %, the d and a \0 null, we would overrun the available size unless snprintf truncated for us. The snprintf call will of course truncate on the end, so lets instead modify the code to force truncation of the copied netdev name by 4 characters, to create enough space for the 4 bytes we're adding. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b0ccd3c2eec6..3a6a752c6c58 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9690,8 +9690,13 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) i40e_add_mac_filter(vsi, mac_addr); spin_unlock_bh(&vsi->mac_filter_hash_lock); } else { - /* relate the VSI_VMDQ name to the VSI_MAIN name */ - snprintf(netdev->name, IFNAMSIZ, "%sv%%d", + /* Relate the VSI_VMDQ name to the VSI_MAIN name. Note that we + * are still limited by IFNAMSIZ, but we're adding 'v%d\0' to + * the end, which is 4 bytes long, so force truncation of the + * original name by IFNAMSIZ - 4 + */ + snprintf(netdev->name, IFNAMSIZ, "%.*sv%%d", + IFNAMSIZ - 4, pf->vsi[pf->lan_vsi]->netdev->name); random_ether_addr(mac_addr); -- cgit v1.2.3 From 8774370d268f2f43d8487d230e0d4fa1647759b3 Mon Sep 17 00:00:00 2001 From: Mariusz Stachura Date: Mon, 17 Jul 2017 22:09:45 -0700 Subject: i40e/i40evf: support for VF VLAN tag stripping control This patch gives VF capability to control VLAN tag stripping via ethtool. As rx-vlan-offload was fixed before, now the VF is able to change it using "ethtool --offload rxvlan on/off" settings. Signed-off-by: Mariusz Stachura Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 60 ++++++++++++++++++++++ drivers/net/ethernet/intel/i40evf/i40evf.h | 4 ++ drivers/net/ethernet/intel/i40evf/i40evf_main.c | 33 ++++++++++++ .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 40 +++++++++++++++ include/linux/avf/virtchnl.h | 5 ++ 5 files changed, 142 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 27d87bef4ba3..4d1e670f490e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2529,6 +2529,60 @@ err: return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_SET_RSS_HENA, aq_ret); } +/** + * i40e_vc_enable_vlan_stripping + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * @msglen: msg length + * + * Enable vlan header stripping for the VF + **/ +static int i40e_vc_enable_vlan_stripping(struct i40e_vf *vf, u8 *msg, + u16 msglen) +{ + struct i40e_vsi *vsi = vf->pf->vsi[vf->lan_vsi_idx]; + i40e_status aq_ret = 0; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + i40e_vlan_stripping_enable(vsi); + + /* send the response to the VF */ +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING, + aq_ret); +} + +/** + * i40e_vc_disable_vlan_stripping + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * @msglen: msg length + * + * Disable vlan header stripping for the VF + **/ +static int i40e_vc_disable_vlan_stripping(struct i40e_vf *vf, u8 *msg, + u16 msglen) +{ + struct i40e_vsi *vsi = vf->pf->vsi[vf->lan_vsi_idx]; + i40e_status aq_ret = 0; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + i40e_vlan_stripping_disable(vsi); + + /* send the response to the VF */ +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING, + aq_ret); +} + /** * i40e_vc_process_vf_msg * @pf: pointer to the PF structure @@ -2648,6 +2702,12 @@ int i40e_vc_process_vf_msg(struct i40e_pf *pf, s16 vf_id, u32 v_opcode, case VIRTCHNL_OP_SET_RSS_HENA: ret = i40e_vc_set_rss_hena(vf, msg, msglen); break; + case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: + ret = i40e_vc_enable_vlan_stripping(vf, msg, msglen); + break; + case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING: + ret = i40e_vc_disable_vlan_stripping(vf, msg, msglen); + break; case VIRTCHNL_OP_UNKNOWN: default: diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index e5293d35fb6a..82f69031e5cd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -261,6 +261,8 @@ struct i40evf_adapter { #define I40EVF_FLAG_AQ_RELEASE_PROMISC BIT(16) #define I40EVF_FLAG_AQ_REQUEST_ALLMULTI BIT(17) #define I40EVF_FLAG_AQ_RELEASE_ALLMULTI BIT(18) +#define I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING BIT(19) +#define I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING BIT(20) /* OS defined structs */ struct net_device *netdev; @@ -358,6 +360,8 @@ void i40evf_get_hena(struct i40evf_adapter *adapter); void i40evf_set_hena(struct i40evf_adapter *adapter); void i40evf_set_rss_key(struct i40evf_adapter *adapter); void i40evf_set_rss_lut(struct i40evf_adapter *adapter); +void i40evf_enable_vlan_stripping(struct i40evf_adapter *adapter); +void i40evf_disable_vlan_stripping(struct i40evf_adapter *adapter); void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, enum virtchnl_ops v_opcode, i40e_status v_retval, u8 *msg, u16 msglen); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 258e8e27068b..9ee277e87f10 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1676,6 +1676,16 @@ static void i40evf_watchdog_task(struct work_struct *work) goto watchdog_done; } + if (adapter->aq_required & I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING) { + i40evf_enable_vlan_stripping(adapter); + goto watchdog_done; + } + + if (adapter->aq_required & I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING) { + i40evf_disable_vlan_stripping(adapter); + goto watchdog_done; + } + if (adapter->aq_required & I40EVF_FLAG_AQ_CONFIGURE_QUEUES) { i40evf_configure_queues(adapter); goto watchdog_done; @@ -2293,6 +2303,28 @@ static int i40evf_change_mtu(struct net_device *netdev, int new_mtu) return 0; } +/** + * i40e_set_features - set the netdev feature flags + * @netdev: ptr to the netdev being adjusted + * @features: the feature set that the stack is suggesting + * Note: expects to be called while under rtnl_lock() + **/ +static int i40evf_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct i40evf_adapter *adapter = netdev_priv(netdev); + + if (!VLAN_ALLOWED(adapter)) + return -EINVAL; + + if (features & NETIF_F_HW_VLAN_CTAG_RX) + adapter->aq_required |= I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING; + else + adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING; + + return 0; +} + /** * i40evf_features_check - Validate encapsulated packet conforms to limits * @skb: skb buff @@ -2386,6 +2418,7 @@ static const struct net_device_ops i40evf_netdev_ops = { .ndo_vlan_rx_kill_vid = i40evf_vlan_rx_kill_vid, .ndo_features_check = i40evf_features_check, .ndo_fix_features = i40evf_fix_features, + .ndo_set_features = i40evf_set_features, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = i40evf_netpoll, #endif diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 6c403bf1bbb8..85876f4fb1fb 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -820,6 +820,46 @@ void i40evf_set_rss_lut(struct i40evf_adapter *adapter) kfree(vrl); } +/** + * i40evf_enable_vlan_stripping + * @adapter: adapter structure + * + * Request VLAN header stripping to be enabled + **/ +void i40evf_enable_vlan_stripping(struct i40evf_adapter *adapter) +{ + if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) { + /* bail because we already have a command pending */ + dev_err(&adapter->pdev->dev, "Cannot enable stripping, command %d pending\n", + adapter->current_op); + return; + } + adapter->current_op = VIRTCHNL_OP_ENABLE_VLAN_STRIPPING; + adapter->aq_required &= ~I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING; + i40evf_send_pf_msg(adapter, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING, + NULL, 0); +} + +/** + * i40evf_disable_vlan_stripping + * @adapter: adapter structure + * + * Request VLAN header stripping to be disabled + **/ +void i40evf_disable_vlan_stripping(struct i40evf_adapter *adapter) +{ + if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) { + /* bail because we already have a command pending */ + dev_err(&adapter->pdev->dev, "Cannot disable stripping, command %d pending\n", + adapter->current_op); + return; + } + adapter->current_op = VIRTCHNL_OP_DISABLE_VLAN_STRIPPING; + adapter->aq_required &= ~I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING; + i40evf_send_pf_msg(adapter, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING, + NULL, 0); +} + /** * i40evf_print_link_message - print link up or down * @adapter: adapter structure diff --git a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h index becfca2ae94e..2b038442c352 100644 --- a/include/linux/avf/virtchnl.h +++ b/include/linux/avf/virtchnl.h @@ -133,6 +133,8 @@ enum virtchnl_ops { VIRTCHNL_OP_CONFIG_RSS_LUT = 24, VIRTCHNL_OP_GET_RSS_HENA_CAPS = 25, VIRTCHNL_OP_SET_RSS_HENA = 26, + VIRTCHNL_OP_ENABLE_VLAN_STRIPPING = 27, + VIRTCHNL_OP_DISABLE_VLAN_STRIPPING = 28, }; /* This macro is used to generate a compilation error if a structure @@ -686,6 +688,9 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode, case VIRTCHNL_OP_SET_RSS_HENA: valid_len = sizeof(struct virtchnl_rss_hena); break; + case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: + case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING: + break; /* These are always errors coming from the VF. */ case VIRTCHNL_OP_EVENT: case VIRTCHNL_OP_UNKNOWN: -- cgit v1.2.3 From 68e49702a1216bbf098ebfff954eeb8f6fd96415 Mon Sep 17 00:00:00 2001 From: Mariusz Stachura Date: Wed, 12 Jul 2017 05:46:14 -0400 Subject: i40e: 25G FEC status improvements This patch improves the system log message. The log message will be expanded to include the FEC mode the FW requested before link was established. Signed-off-by: Mariusz Stachura Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 3a6a752c6c58..5a06cd23b9e6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5354,6 +5354,7 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) char *speed = "Unknown"; char *fc = "Unknown"; char *fec = ""; + char *req_fec = ""; char *an = ""; new_speed = vsi->back->hw.phy.link_info.link_speed; @@ -5415,6 +5416,7 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) } if (vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_25GB) { + req_fec = ", Requested FEC: None"; fec = ", FEC: None"; an = ", Autoneg: False"; @@ -5427,10 +5429,22 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) else if (vsi->back->hw.phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_RS_ENA) fec = ", FEC: CL108 RS-FEC"; + + /* 'CL108 RS-FEC' should be displayed when RS is requested, or + * both RS and FC are requested + */ + if (vsi->back->hw.phy.link_info.req_fec_info & + (I40E_AQ_REQUEST_FEC_KR | I40E_AQ_REQUEST_FEC_RS)) { + if (vsi->back->hw.phy.link_info.req_fec_info & + I40E_AQ_REQUEST_FEC_RS) + req_fec = ", Requested FEC: CL108 RS-FEC"; + else + req_fec = ", Requested FEC: CL74 FC-FEC/BASE-R"; + } } - netdev_info(vsi->netdev, "NIC Link is Up, %sbps Full Duplex%s%s, Flow Control: %s\n", - speed, fec, an, fc); + netdev_info(vsi->netdev, "NIC Link is Up, %sbps Full Duplex%s%s%s, Flow Control: %s\n", + speed, req_fec, fec, an, fc); } /** -- cgit v1.2.3 From 19279235bea221798e3307a8bec2c02559cab0c5 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Fri, 14 Jul 2017 09:10:07 -0400 Subject: i40e: Fix for unused value issue found by static analysis This patch fixes an issue where an error return value is set, but without an immediate exit, the value can be overwritten by the following code execution. The condition at this point is not fatal, so remove the error assignment and comment the intent for future code maintainers Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5a06cd23b9e6..0962b85ef6f3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9884,13 +9884,15 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) */ ret = i40e_vsi_config_tc(vsi, enabled_tc); if (ret) { + /* Single TC condition is not fatal, + * message and continue + */ dev_info(&pf->pdev->dev, "failed to configure TCs for main VSI tc_map 0x%08x, err %s aq_err %s\n", enabled_tc, i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - ret = -ENOENT; } } break; -- cgit v1.2.3 From ba4460d45a6ec04e29e55e6c97edc0e842c18999 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 14 Jul 2017 09:10:08 -0400 Subject: i40e: remove workaround for resetting XPS Since commit 3ffa037d7f78 ("i40e: Set XPS bit mask to zero in DCB mode") we've tried to reset the XPS settings by building a custom empty CPU mask. This workaround is not necessary because we're not really removing the XPS setting, but simply setting it so that no CPU is valid. Second, we shorten the code further by using zalloc_cpumask_var instead of a separate call to bitmap_zero(). Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0962b85ef6f3..7366e7c7f399 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2874,22 +2874,15 @@ static void i40e_vsi_free_rx_resources(struct i40e_vsi *vsi) static void i40e_config_xps_tx_ring(struct i40e_ring *ring) { struct i40e_vsi *vsi = ring->vsi; - cpumask_var_t mask; if (!ring->q_vector || !ring->netdev) return; - /* Single TC mode enable XPS */ - if (vsi->tc_config.numtc <= 1) { - if (!test_and_set_bit(__I40E_TX_XPS_INIT_DONE, &ring->state)) - netif_set_xps_queue(ring->netdev, - &ring->q_vector->affinity_mask, - ring->queue_index); - } else if (alloc_cpumask_var(&mask, GFP_KERNEL)) { - /* Disable XPS to allow selection based on TC */ - bitmap_zero(cpumask_bits(mask), nr_cpumask_bits); - netif_set_xps_queue(ring->netdev, mask, ring->queue_index); - free_cpumask_var(mask); + if ((vsi->tc_config.numtc <= 1) && + !test_and_set_bit(__I40E_TX_XPS_INIT_DONE, &ring->state)) { + netif_set_xps_queue(ring->netdev, + &ring->q_vector->affinity_mask, + ring->queue_index); } /* schedule our worker thread which will take care of -- cgit v1.2.3 From 9254c0e34e4253c41fdcd4670b754506ce20d3eb Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 14 Jul 2017 09:10:09 -0400 Subject: i40e: move enabling icr0 into i40e_update_enable_itr If we don't have MSI-X enabled, we handle interrupts on all icr0. This is a special case, so let's move the conditional into i40e_update_enable_itr() in order to make i40e_napi_poll easier to read about. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 8a969d8f0790..5c1edcce9459 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2243,6 +2243,12 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, int idx = q_vector->v_idx; int rx_itr_setting, tx_itr_setting; + /* If we don't have MSIX, then we only need to re-enable icr0 */ + if (!(vsi->back->flags & I40E_FLAG_MSIX_ENABLED)) { + i40e_irq_dynamic_enable_icr0(vsi->back, false); + return; + } + vector = (q_vector->v_idx + vsi->base_vector); /* avoid dynamic calculation if in countdown mode OR if @@ -2396,8 +2402,6 @@ tx_only: */ if (!clean_complete) i40e_force_wb(vsi, q_vector); - else if (!(vsi->back->flags & I40E_FLAG_MSIX_ENABLED)) - i40e_irq_dynamic_enable_icr0(vsi->back, false); else i40e_update_enable_itr(vsi, q_vector); -- cgit v1.2.3 From 759dc4a7e605e0dc21708b0a6e0816ed0ac82641 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 14 Jul 2017 09:10:10 -0400 Subject: i40e: initialize our affinity_mask based on cpu_possible_mask On older kernels a call to irq_set_affinity_hint does not guarantee that the IRQ affinity will be set. If nothing else on the system sets the IRQ affinity this can result in a bug in the i40e_napi_poll() routine where we notice that our interrupt fired on the "wrong" CPU according to our internal affinity_mask variable. This results in a bug where we continuously tell NAPI to stop polling to move the interrupt to a new CPU, but the CPU never changes because our affinity mask does not match the actual mask setup for the IRQ. The root problem is a mismatched affinity mask value. So lets initialize the value to cpu_possible_mask instead. This ensures that prior to the first time we get an IRQ affinity notification we'll have the mask set to include every possible CPU. We use cpu_possible_mask instead of cpu_online_mask since the former is almost certainly never going to change, while the later might change after we've made a copy. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 12 +++++++----- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 7 +++++-- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7366e7c7f399..6498da8806cb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2881,7 +2881,7 @@ static void i40e_config_xps_tx_ring(struct i40e_ring *ring) if ((vsi->tc_config.numtc <= 1) && !test_and_set_bit(__I40E_TX_XPS_INIT_DONE, &ring->state)) { netif_set_xps_queue(ring->netdev, - &ring->q_vector->affinity_mask, + get_cpu_mask(ring->q_vector->v_idx), ring->queue_index); } @@ -3506,8 +3506,10 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) q_vector->affinity_notify.notify = i40e_irq_affinity_notify; q_vector->affinity_notify.release = i40e_irq_affinity_release; irq_set_affinity_notifier(irq_num, &q_vector->affinity_notify); - /* assign the mask for this irq */ - irq_set_affinity_hint(irq_num, &q_vector->affinity_mask); + /* get_cpu_mask returns a static constant mask with + * a permanent lifetime so it's ok to use here. + */ + irq_set_affinity_hint(irq_num, get_cpu_mask(q_vector->v_idx)); } vsi->irqs_ready = true; @@ -4289,7 +4291,7 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi) /* clear the affinity notifier in the IRQ descriptor */ irq_set_affinity_notifier(irq_num, NULL); - /* clear the affinity_mask in the IRQ descriptor */ + /* remove our suggested affinity mask for this IRQ */ irq_set_affinity_hint(irq_num, NULL); synchronize_irq(irq_num); free_irq(irq_num, vsi->q_vectors[i]); @@ -8235,7 +8237,7 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx, int cpu) q_vector->vsi = vsi; q_vector->v_idx = v_idx; - cpumask_set_cpu(cpu, &q_vector->affinity_mask); + cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask); if (vsi->netdev) netif_napi_add(vsi->netdev, &q_vector->napi, diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 9ee277e87f10..1825d956bb00 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -584,8 +584,10 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) q_vector->affinity_notify.release = i40evf_irq_affinity_release; irq_set_affinity_notifier(irq_num, &q_vector->affinity_notify); - /* assign the mask for this irq */ - irq_set_affinity_hint(irq_num, &q_vector->affinity_mask); + /* get_cpu_mask returns a static constant mask with + * a permanent lifetime so it's ok to use here. + */ + irq_set_affinity_hint(irq_num, get_cpu_mask(q_vector->v_idx)); } return 0; @@ -1456,6 +1458,7 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter) q_vector->adapter = adapter; q_vector->vsi = &adapter->vsi; q_vector->v_idx = q_idx; + cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask); netif_napi_add(adapter->netdev, &q_vector->napi, i40evf_napi_poll, NAPI_POLL_WEIGHT); } -- cgit v1.2.3 From 6d9777298b54bf1212fcaa6ee6679a430ceca452 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 14 Jul 2017 09:10:11 -0400 Subject: i40e: invert logic for checking incorrect cpu vs irq affinity In commit 96db776a3682 ("i40e/vf: fix interrupt affinity bug") we added some code to force exit of polling in case we did not have the correct CPU. This is important since it was possible for the IRQ affinity to be changed while the CPU is pegged at 100%. This can result in the polling routine being stuck on the wrong CPU until traffic finally stops. Unfortunately, the implementation, "if the CPU is correct, exit as normal, otherwise, fall-through to the end-polling exit" is incredibly confusing to reason about. In this case, the normal flow looks like the exception, while the exception actually occurs far away from the if statement and comment. We recently discovered and fixed a bug in this code because we were incorrectly initializing the affinity mask. Re-write the code so that the exceptional case is handled at the check, rather than having the logic be spread through the regular exit flow. This does end up with minor code duplication, but the resulting code is much easier to reason about. The new logic is identical, but inverted. If we are running on a CPU not in our affinity mask, we'll exit polling. However, the code flow is much easier to understand. Note that we don't actually have to check for MSI-X, because in the MSI case we'll only have one q_vector, but its default affinity mask should be correct as it includes all CPUs when it's initialized. Further, we could at some point add code to setup the notifier for the non-MSI-X case and enable this workaround for that case too, if desired, though there isn't much gain since its unlikely to be the common case. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 31 +++++++++++++-------------- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 30 +++++++++++++------------- 2 files changed, 30 insertions(+), 31 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 5c1edcce9459..3999afea518b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2369,7 +2369,6 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { - const cpumask_t *aff_mask = &q_vector->affinity_mask; int cpu_id = smp_processor_id(); /* It is possible that the interrupt affinity has changed but, @@ -2379,15 +2378,22 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) * continue to poll, otherwise we must stop polling so the * interrupt can move to the correct cpu. */ - if (likely(cpumask_test_cpu(cpu_id, aff_mask) || - !(vsi->back->flags & I40E_FLAG_MSIX_ENABLED))) { + if (!cpumask_test_cpu(cpu_id, &q_vector->affinity_mask)) { + /* Tell napi that we are done polling */ + napi_complete_done(napi, work_done); + + /* Force an interrupt */ + i40e_force_wb(vsi, q_vector); + + /* Return budget-1 so that polling stops */ + return budget - 1; + } tx_only: - if (arm_wb) { - q_vector->tx.ring[0].tx_stats.tx_force_wb++; - i40e_enable_wb_on_itr(vsi, q_vector); - } - return budget; + if (arm_wb) { + q_vector->tx.ring[0].tx_stats.tx_force_wb++; + i40e_enable_wb_on_itr(vsi, q_vector); } + return budget; } if (vsi->back->flags & I40E_TXR_FLAGS_WB_ON_ITR) @@ -2396,14 +2402,7 @@ tx_only: /* Work is done so exit the polling mode and re-enable the interrupt */ napi_complete_done(napi, work_done); - /* If we're prematurely stopping polling to fix the interrupt - * affinity we want to make sure polling starts back up so we - * issue a call to i40e_force_wb which triggers a SW interrupt. - */ - if (!clean_complete) - i40e_force_wb(vsi, q_vector); - else - i40e_update_enable_itr(vsi, q_vector); + i40e_update_enable_itr(vsi, q_vector); return min(work_done, budget - 1); } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index d91676ccf125..f15e341ada9e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1575,7 +1575,6 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { - const cpumask_t *aff_mask = &q_vector->affinity_mask; int cpu_id = smp_processor_id(); /* It is possible that the interrupt affinity has changed but, @@ -1585,14 +1584,22 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) * continue to poll, otherwise we must stop polling so the * interrupt can move to the correct cpu. */ - if (likely(cpumask_test_cpu(cpu_id, aff_mask))) { + if (!cpumask_test_cpu(cpu_id, &q_vector->affinity_mask)) { + /* Tell napi that we are done polling */ + napi_complete_done(napi, work_done); + + /* Force an interrupt */ + i40evf_force_wb(vsi, q_vector); + + /* Return budget-1 so that polling stops */ + return budget - 1; + } tx_only: - if (arm_wb) { - q_vector->tx.ring[0].tx_stats.tx_force_wb++; - i40e_enable_wb_on_itr(vsi, q_vector); - } - return budget; + if (arm_wb) { + q_vector->tx.ring[0].tx_stats.tx_force_wb++; + i40e_enable_wb_on_itr(vsi, q_vector); } + return budget; } if (vsi->back->flags & I40E_TXR_FLAGS_WB_ON_ITR) @@ -1601,14 +1608,7 @@ tx_only: /* Work is done so exit the polling mode and re-enable the interrupt */ napi_complete_done(napi, work_done); - /* If we're prematurely stopping polling to fix the interrupt - * affinity we want to make sure polling starts back up so we - * issue a call to i40evf_force_wb which triggers a SW interrupt. - */ - if (!clean_complete) - i40evf_force_wb(vsi, q_vector); - else - i40e_update_enable_itr(vsi, q_vector); + i40e_update_enable_itr(vsi, q_vector); return min(work_done, budget - 1); } -- cgit v1.2.3 From 0a2c7722be1705edca34458bd9de2f97188f9636 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 14 Jul 2017 09:10:12 -0400 Subject: i40e/i40evf: remove ULTRA latency mode Since commit c56625d59726 ("i40e/i40evf: change dynamic interrupt thresholds") a new higher latency ITR setting called I40E_ULTRA_LATENCY was added with a cryptic comment about how it was meant for adjusting Rx more aggressively when streaming small packets. This mode was attempting to calculate packets per second and then kick in when we have a huge number of small packets. Unfortunately, the ULTRA setting was kicking in for workloads it wasn't intended for including single-thread UDP_STREAM workloads. This wasn't caught for a variety of reasons. First, the ip_defrag routines were improved somewhat which makes the UDP_STREAM test still reasonable at 10GbE, even when dropped down to 8k interrupts a second. Additionally, some other obvious workloads appear to work fine, such as TCP_STREAM. The number 40k doesn't make sense for a number of reasons. First, we absolutely can do more than 40k packets per second. Second, we calculate the value inline in an integer, which sometimes can overflow resulting in using incorrect values. If we fix this overflow it makes it even more likely that we'll enter ULTRA mode which is the opposite of what we want. The ULTRA mode was added originally as a way to reduce CPU utilization during a small packet workload where we weren't keeping up anyways. It should never have been kicking in during these other workloads. Given the issues outlined above, let's remove the ULTRA latency mode. If necessary, a better solution to the CPU utilization issue for small packet workloads will be added in a future patch. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 17 ----------------- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 1 - drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 17 ----------------- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 1 - 4 files changed, 36 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 3999afea518b..f00f233092e9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -959,7 +959,6 @@ void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) { enum i40e_latency_range new_latency_range = rc->latency_range; - struct i40e_q_vector *qv = rc->ring->q_vector; u32 new_itr = rc->itr; int bytes_per_int; int usecs; @@ -971,7 +970,6 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) * 0-10MB/s lowest (50000 ints/s) * 10-20MB/s low (20000 ints/s) * 20-1249MB/s bulk (18000 ints/s) - * > 40000 Rx packets per second (8000 ints/s) * * The math works out because the divisor is in 10^(-6) which * turns the bytes/us input value into MB/s values, but @@ -994,24 +992,12 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) new_latency_range = I40E_LOWEST_LATENCY; break; case I40E_BULK_LATENCY: - case I40E_ULTRA_LATENCY: default: if (bytes_per_int <= 20) new_latency_range = I40E_LOW_LATENCY; break; } - /* this is to adjust RX more aggressively when streaming small - * packets. The value of 40000 was picked as it is just beyond - * what the hardware can receive per second if in low latency - * mode. - */ -#define RX_ULTRA_PACKET_RATE 40000 - - if ((((rc->total_packets * 1000000) / usecs) > RX_ULTRA_PACKET_RATE) && - (&qv->rx == rc)) - new_latency_range = I40E_ULTRA_LATENCY; - rc->latency_range = new_latency_range; switch (new_latency_range) { @@ -1024,9 +1010,6 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) case I40E_BULK_LATENCY: new_itr = I40E_ITR_18K; break; - case I40E_ULTRA_LATENCY: - new_itr = I40E_ITR_8K; - break; default: break; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index f0a0eabc2666..e6456e8a899c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -454,7 +454,6 @@ enum i40e_latency_range { I40E_LOWEST_LATENCY = 0, I40E_LOW_LATENCY = 1, I40E_BULK_LATENCY = 2, - I40E_ULTRA_LATENCY = 3, }; struct i40e_ring_container { diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index f15e341ada9e..2f7d9f4a6746 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -357,7 +357,6 @@ void i40evf_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) { enum i40e_latency_range new_latency_range = rc->latency_range; - struct i40e_q_vector *qv = rc->ring->q_vector; u32 new_itr = rc->itr; int bytes_per_int; int usecs; @@ -369,7 +368,6 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) * 0-10MB/s lowest (50000 ints/s) * 10-20MB/s low (20000 ints/s) * 20-1249MB/s bulk (18000 ints/s) - * > 40000 Rx packets per second (8000 ints/s) * * The math works out because the divisor is in 10^(-6) which * turns the bytes/us input value into MB/s values, but @@ -392,24 +390,12 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) new_latency_range = I40E_LOWEST_LATENCY; break; case I40E_BULK_LATENCY: - case I40E_ULTRA_LATENCY: default: if (bytes_per_int <= 20) new_latency_range = I40E_LOW_LATENCY; break; } - /* this is to adjust RX more aggressively when streaming small - * packets. The value of 40000 was picked as it is just beyond - * what the hardware can receive per second if in low latency - * mode. - */ -#define RX_ULTRA_PACKET_RATE 40000 - - if ((((rc->total_packets * 1000000) / usecs) > RX_ULTRA_PACKET_RATE) && - (&qv->rx == rc)) - new_latency_range = I40E_ULTRA_LATENCY; - rc->latency_range = new_latency_range; switch (new_latency_range) { @@ -422,9 +408,6 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) case I40E_BULK_LATENCY: new_itr = I40E_ITR_18K; break; - case I40E_ULTRA_LATENCY: - new_itr = I40E_ITR_8K; - break; default: break; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 489684002e94..fb506c9f5a6e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -425,7 +425,6 @@ enum i40e_latency_range { I40E_LOWEST_LATENCY = 0, I40E_LOW_LATENCY = 1, I40E_BULK_LATENCY = 2, - I40E_ULTRA_LATENCY = 3, }; struct i40e_ring_container { -- cgit v1.2.3 From 742c9875759c1858c3312442a78a80f3e93d82c4 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 14 Jul 2017 09:10:13 -0400 Subject: i40e/i40evf: avoid dynamic ITR updates when polling or low packet rate The dynamic ITR algorithm depends on a calculation of usecs which assumes that the interrupts have been firing constantly at the interrupt throttle rate. This is not guaranteed because we could have a low packet rate, or have been polling in software. We'll estimate whether this is the case by using jiffies to determine if we've been too long. If the time difference of jiffies is larger we are guaranteed to have an incorrect calculation. If the time difference of jiffies is smaller we might have been polling some but the difference shouldn't affect the calculation too much. This ensures that we don't get stuck in BULK latency during certain rare situations where we receive bursts of packets that force us into NAPI polling. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 22 +++++++++++++++++----- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 1 + drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 22 +++++++++++++++++----- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 1 + 4 files changed, 36 insertions(+), 10 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index f00f233092e9..1519dfb851d0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -961,11 +961,25 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) enum i40e_latency_range new_latency_range = rc->latency_range; u32 new_itr = rc->itr; int bytes_per_int; - int usecs; + unsigned int usecs, estimated_usecs; if (rc->total_packets == 0 || !rc->itr) return false; + usecs = (rc->itr << 1) * ITR_COUNTDOWN_START; + bytes_per_int = rc->total_bytes / usecs; + + /* The calculations in this algorithm depend on interrupts actually + * firing at the ITR rate. This may not happen if the packet rate is + * really low, or if we've been napi polling. Check to make sure + * that's not the case before we continue. + */ + estimated_usecs = jiffies_to_usecs(jiffies - rc->last_itr_update); + if (estimated_usecs > usecs) { + new_latency_range = I40E_LOW_LATENCY; + goto reset_latency; + } + /* simple throttlerate management * 0-10MB/s lowest (50000 ints/s) * 10-20MB/s low (20000 ints/s) @@ -977,9 +991,6 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) * are in 2 usec increments in the ITR registers, and make sure * to use the smoothed values that the countdown timer gives us. */ - usecs = (rc->itr << 1) * ITR_COUNTDOWN_START; - bytes_per_int = rc->total_bytes / usecs; - switch (new_latency_range) { case I40E_LOWEST_LATENCY: if (bytes_per_int > 10) @@ -998,6 +1009,7 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) break; } +reset_latency: rc->latency_range = new_latency_range; switch (new_latency_range) { @@ -1016,12 +1028,12 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) rc->total_bytes = 0; rc->total_packets = 0; + rc->last_itr_update = jiffies; if (new_itr != rc->itr) { rc->itr = new_itr; return true; } - return false; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index e6456e8a899c..2f848bc5e391 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -461,6 +461,7 @@ struct i40e_ring_container { struct i40e_ring *ring; unsigned int total_bytes; /* total bytes processed this int */ unsigned int total_packets; /* total packets processed this int */ + unsigned long last_itr_update; /* jiffies of last ITR update */ u16 count; enum i40e_latency_range latency_range; u16 itr; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 2f7d9f4a6746..c32c62462c84 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -359,11 +359,25 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) enum i40e_latency_range new_latency_range = rc->latency_range; u32 new_itr = rc->itr; int bytes_per_int; - int usecs; + unsigned int usecs, estimated_usecs; if (rc->total_packets == 0 || !rc->itr) return false; + usecs = (rc->itr << 1) * ITR_COUNTDOWN_START; + bytes_per_int = rc->total_bytes / usecs; + + /* The calculations in this algorithm depend on interrupts actually + * firing at the ITR rate. This may not happen if the packet rate is + * really low, or if we've been napi polling. Check to make sure + * that's not the case before we continue. + */ + estimated_usecs = jiffies_to_usecs(jiffies - rc->last_itr_update); + if (estimated_usecs > usecs) { + new_latency_range = I40E_LOW_LATENCY; + goto reset_latency; + } + /* simple throttlerate management * 0-10MB/s lowest (50000 ints/s) * 10-20MB/s low (20000 ints/s) @@ -375,9 +389,6 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) * are in 2 usec increments in the ITR registers, and make sure * to use the smoothed values that the countdown timer gives us. */ - usecs = (rc->itr << 1) * ITR_COUNTDOWN_START; - bytes_per_int = rc->total_bytes / usecs; - switch (new_latency_range) { case I40E_LOWEST_LATENCY: if (bytes_per_int > 10) @@ -396,6 +407,7 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) break; } +reset_latency: rc->latency_range = new_latency_range; switch (new_latency_range) { @@ -414,12 +426,12 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) rc->total_bytes = 0; rc->total_packets = 0; + rc->last_itr_update = jiffies; if (new_itr != rc->itr) { rc->itr = new_itr; return true; } - return false; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index fb506c9f5a6e..0d9f98bc07bd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -432,6 +432,7 @@ struct i40e_ring_container { struct i40e_ring *ring; unsigned int total_bytes; /* total bytes processed this int */ unsigned int total_packets; /* total packets processed this int */ + unsigned long last_itr_update; /* jiffies of last ITR update */ u16 count; enum i40e_latency_range latency_range; u16 itr; -- cgit v1.2.3 From cc8737a5fe9051b7fa052b08c57ddb9f539c389a Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Fri, 25 Aug 2017 13:10:43 -0400 Subject: xen-netback: update ubuf_info initialization to anonymous union The xen driver initializes struct ubuf_info fields using designated initializers. I recently moved these fields inside a nested anonymous struct inside an anonymous union. I had missed this use case. This breaks compilation of xen-netback with older compilers. >From kbuild bot with gcc-4.4.7: drivers/net//xen-netback/interface.c: In function 'xenvif_init_queue': >> drivers/net//xen-netback/interface.c:554: error: unknown field 'ctx' specified in initializer >> drivers/net//xen-netback/interface.c:554: warning: missing braces around initializer drivers/net//xen-netback/interface.c:554: warning: (near initialization for '(anonymous).') >> drivers/net//xen-netback/interface.c:554: warning: initialization makes integer from pointer without a cast >> drivers/net//xen-netback/interface.c:555: error: unknown field 'desc' specified in initializer Add double braces around the designated initializers to match their nested position in the struct. After this, compilation succeeds again. Fixes: 4ab6c99d99bb ("sock: MSG_ZEROCOPY notification coalescing") Reported-by: kbuild bot Signed-off-by: Willem de Bruijn Acked-by: Wei Liu Signed-off-by: David S. Miller --- drivers/net/xen-netback/interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index e322a862ddfe..ee8ed9da00ad 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -551,8 +551,8 @@ int xenvif_init_queue(struct xenvif_queue *queue) for (i = 0; i < MAX_PENDING_REQS; i++) { queue->pending_tx_info[i].callback_struct = (struct ubuf_info) { .callback = xenvif_zerocopy_callback, - .ctx = NULL, - .desc = i }; + { { .ctx = NULL, + .desc = i } } }; queue->grant_tx_handle[i] = NETBACK_INVALID_HANDLE; } -- cgit v1.2.3 From 10bfec0a2b4b9d3b81e09b43266ebe370bb17760 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 26 Aug 2017 08:35:38 +0200 Subject: mlxsw: spectrum: compile-in dpipe support only if devlink is enabled Makes no sense to have dpipe compiled in when devlink is not enabled, because the devlink dpipe registation is noop function. So don't compile it in. This also fixes missing extern structs errors. Reported-by: kbuild test robot Fixes: a86f030915f2 ("mlxsw: spectrum_dpipe: Add support for IPv4 host table dump") Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Makefile | 3 ++- drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index 62fc42f396bb..f9956f3bc45c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -16,8 +16,9 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \ spectrum_switchdev.o spectrum_router.o \ spectrum_kvdl.o spectrum_acl_tcam.o \ spectrum_acl.o spectrum_flower.o \ - spectrum_cnt.o spectrum_dpipe.o \ + spectrum_cnt.o \ spectrum_fid.o mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o +mlxsw_spectrum-$(CONFIG_NET_DEVLINK) += spectrum_dpipe.o obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o mlxsw_minimal-objs := minimal.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h index 7785f9e26a18..c56a3d92a2bb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h @@ -35,9 +35,24 @@ #ifndef _MLXSW_PIPELINE_H_ #define _MLXSW_PIPELINE_H_ +#if IS_ENABLED(CONFIG_NET_DEVLINK) + int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp); +#else + +static inline int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) +{ + return 0; +} + +static inline void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp) +{ +} + +#endif + #define MLXSW_SP_DPIPE_TABLE_NAME_ERIF "mlxsw_erif" #define MLXSW_SP_DPIPE_TABLE_NAME_HOST4 "mlxsw_host4" -- cgit v1.2.3 From 18fed7e15d8fd38747819ee8e66c8771e0281226 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 26 Aug 2017 08:35:39 +0200 Subject: mlxsw: spectrum_dpipe: Fix host table dump During the neighbor traversal the neighbors from different families should be ignored. Fixes: c58035a74aba ("mlxsw: spectrum_dpipe: Add support for IPv4 host table dump") Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 3c8599f14bff..3ea13148ed05 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -516,6 +516,9 @@ start_again: rif_neigh_count = 0; mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { + if (mlxsw_sp_neigh_entry_type(neigh_entry) != type) + continue; + if (rif_neigh_count < rif_neigh_skip) goto skip; -- cgit v1.2.3 From 0df49584ed6f0e99f3e16d6b10acef32fe21ce0d Mon Sep 17 00:00:00 2001 From: Himanshu Jha Date: Sun, 27 Aug 2017 01:47:47 +0530 Subject: net: ethernet: broadcom: Remove null check before kfree Kfree on NULL pointer is a no-op and therefore checking is redundant. Signed-off-by: Himanshu Jha Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/sb1250-mac.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index 16a0f192daec..ecdef42f0ae6 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -1367,15 +1367,11 @@ static int sbmac_initctx(struct sbmac_softc *s) static void sbdma_uninitctx(struct sbmacdma *d) { - if (d->sbdma_dscrtable_unaligned) { - kfree(d->sbdma_dscrtable_unaligned); - d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = NULL; - } + kfree(d->sbdma_dscrtable_unaligned); + d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = NULL; - if (d->sbdma_ctxtable) { - kfree(d->sbdma_ctxtable); - d->sbdma_ctxtable = NULL; - } + kfree(d->sbdma_ctxtable); + d->sbdma_ctxtable = NULL; } -- cgit v1.2.3 From ca58ce576662eab2dc71f6af120dd07d8e11a925 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Sun, 27 Aug 2017 16:13:37 +0300 Subject: fsl/fman: move struct fman to header file Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/fman.c | 80 +------------------------ drivers/net/ethernet/freescale/fman/fman.h | 75 +++++++++++++++++++++++ drivers/net/ethernet/freescale/fman/fman_port.c | 8 +-- 3 files changed, 82 insertions(+), 81 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index e714b8fa55eb..8179cc19843f 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -32,9 +32,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include "fman.h" -#include "fman_muram.h" - #include #include #include @@ -46,6 +43,9 @@ #include #include +#include "fman.h" +#include "fman_muram.h" + /* General defines */ #define FMAN_LIODN_TBL 64 /* size of LIODN table */ #define MAX_NUM_OF_MACS 10 @@ -564,80 +564,6 @@ struct fman_cfg { u32 qmi_def_tnums_thresh; }; -/* Structure that holds information received from device tree */ -struct fman_dts_params { - void __iomem *base_addr; /* FMan virtual address */ - struct resource *res; /* FMan memory resource */ - u8 id; /* FMan ID */ - - int err_irq; /* FMan Error IRQ */ - - u16 clk_freq; /* FMan clock freq (In Mhz) */ - - u32 qman_channel_base; /* QMan channels base */ - u32 num_of_qman_channels; /* Number of QMan channels */ - - struct resource muram_res; /* MURAM resource */ -}; - -/** fman_exceptions_cb - * fman - Pointer to FMan - * exception - The exception. - * - * Exceptions user callback routine, will be called upon an exception - * passing the exception identification. - * - * Return: irq status - */ -typedef irqreturn_t (fman_exceptions_cb)(struct fman *fman, - enum fman_exceptions exception); - -/** fman_bus_error_cb - * fman - Pointer to FMan - * port_id - Port id - * addr - Address that caused the error - * tnum - Owner of error - * liodn - Logical IO device number - * - * Bus error user callback routine, will be called upon bus error, - * passing parameters describing the errors and the owner. - * - * Return: IRQ status - */ -typedef irqreturn_t (fman_bus_error_cb)(struct fman *fman, u8 port_id, - u64 addr, u8 tnum, u16 liodn); - -struct fman { - struct device *dev; - void __iomem *base_addr; - struct fman_intr_src intr_mng[FMAN_EV_CNT]; - - struct fman_fpm_regs __iomem *fpm_regs; - struct fman_bmi_regs __iomem *bmi_regs; - struct fman_qmi_regs __iomem *qmi_regs; - struct fman_dma_regs __iomem *dma_regs; - struct fman_hwp_regs __iomem *hwp_regs; - fman_exceptions_cb *exception_cb; - fman_bus_error_cb *bus_error_cb; - /* Spinlock for FMan use */ - spinlock_t spinlock; - struct fman_state_struct *state; - - struct fman_cfg *cfg; - struct muram_info *muram; - /* cam section in muram */ - unsigned long cam_offset; - size_t cam_size; - /* Fifo in MURAM */ - unsigned long fifo_offset; - size_t fifo_size; - - u32 liodn_base[64]; - u32 liodn_offset[64]; - - struct fman_dts_params dts_params; -}; - static irqreturn_t fman_exceptions(struct fman *fman, enum fman_exceptions exception) { diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h index f53e1473dbcc..1015dac9bd57 100644 --- a/drivers/net/ethernet/freescale/fman/fman.h +++ b/drivers/net/ethernet/freescale/fman/fman.h @@ -34,6 +34,8 @@ #define __FM_H #include +#include +#include /* FM Frame descriptor macros */ /* Frame queue Context Override */ @@ -274,6 +276,79 @@ struct fman_intr_src { void *src_handle; }; +/** fman_exceptions_cb + * fman - Pointer to FMan + * exception - The exception. + * + * Exceptions user callback routine, will be called upon an exception + * passing the exception identification. + * + * Return: irq status + */ +typedef irqreturn_t (fman_exceptions_cb)(struct fman *fman, + enum fman_exceptions exception); +/** fman_bus_error_cb + * fman - Pointer to FMan + * port_id - Port id + * addr - Address that caused the error + * tnum - Owner of error + * liodn - Logical IO device number + * + * Bus error user callback routine, will be called upon bus error, + * passing parameters describing the errors and the owner. + * + * Return: IRQ status + */ +typedef irqreturn_t (fman_bus_error_cb)(struct fman *fman, u8 port_id, + u64 addr, u8 tnum, u16 liodn); + +/* Structure that holds information received from device tree */ +struct fman_dts_params { + void __iomem *base_addr; /* FMan virtual address */ + struct resource *res; /* FMan memory resource */ + u8 id; /* FMan ID */ + + int err_irq; /* FMan Error IRQ */ + + u16 clk_freq; /* FMan clock freq (In Mhz) */ + + u32 qman_channel_base; /* QMan channels base */ + u32 num_of_qman_channels; /* Number of QMan channels */ + + struct resource muram_res; /* MURAM resource */ +}; + +struct fman { + struct device *dev; + void __iomem *base_addr; + struct fman_intr_src intr_mng[FMAN_EV_CNT]; + + struct fman_fpm_regs __iomem *fpm_regs; + struct fman_bmi_regs __iomem *bmi_regs; + struct fman_qmi_regs __iomem *qmi_regs; + struct fman_dma_regs __iomem *dma_regs; + struct fman_hwp_regs __iomem *hwp_regs; + fman_exceptions_cb *exception_cb; + fman_bus_error_cb *bus_error_cb; + /* Spinlock for FMan use */ + spinlock_t spinlock; + struct fman_state_struct *state; + + struct fman_cfg *cfg; + struct muram_info *muram; + /* cam section in muram */ + unsigned long cam_offset; + size_t cam_size; + /* Fifo in MURAM */ + unsigned long fifo_offset; + size_t fifo_size; + + u32 liodn_base[64]; + u32 liodn_offset[64]; + + struct fman_dts_params dts_params; +}; + /* Structure for port-FM communication during fman_port_init. */ struct fman_port_init_params { u8 port_id; /* port Id */ diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c index 49bfa11f2d20..d51e6aa2b356 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.c +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -32,10 +32,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include "fman_port.h" -#include "fman.h" -#include "fman_sp.h" - #include #include #include @@ -45,6 +41,10 @@ #include #include +#include "fman.h" +#include "fman_port.h" +#include "fman_sp.h" + /* Queue ID */ #define DFLT_FQ_ID 0x00FFFFFF -- cgit v1.2.3 From 7472f4f281d0f7c9cf66494b70a64c5ea5ca67da Mon Sep 17 00:00:00 2001 From: Iordache Florinel-R70177 Date: Sun, 27 Aug 2017 16:13:38 +0300 Subject: fsl/fman: enable FMan Keygen Add support for the FMan Keygen with a hardcoded scheme to spread incoming traffic on a FQ range based on source and destination IPs and ports. Signed-off-by: Iordache Florinel Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/Makefile | 2 +- drivers/net/ethernet/freescale/fman/fman.c | 8 + drivers/net/ethernet/freescale/fman/fman.h | 2 + drivers/net/ethernet/freescale/fman/fman_keygen.c | 783 ++++++++++++++++++++++ drivers/net/ethernet/freescale/fman/fman_keygen.h | 46 ++ drivers/net/ethernet/freescale/fman/fman_port.c | 40 +- drivers/net/ethernet/freescale/fman/fman_port.h | 5 + 7 files changed, 884 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/freescale/fman/fman_keygen.c create mode 100644 drivers/net/ethernet/freescale/fman/fman_keygen.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index 60491779e49f..2c38119b172c 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_FSL_FMAN) += fsl_fman.o obj-$(CONFIG_FSL_FMAN) += fsl_fman_port.o obj-$(CONFIG_FSL_FMAN) += fsl_mac.o -fsl_fman-objs := fman_muram.o fman.o fman_sp.o +fsl_fman-objs := fman_muram.o fman.o fman_sp.o fman_keygen.o fsl_fman_port-objs := fman_port.o fsl_mac-objs:= mac.o fman_dtsec.o fman_memac.o fman_tgec.o diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index 8179cc19843f..f420dac2f484 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -45,6 +45,7 @@ #include "fman.h" #include "fman_muram.h" +#include "fman_keygen.h" /* General defines */ #define FMAN_LIODN_TBL 64 /* size of LIODN table */ @@ -56,6 +57,7 @@ /* Modules registers offsets */ #define BMI_OFFSET 0x00080000 #define QMI_OFFSET 0x00080400 +#define KG_OFFSET 0x000C1000 #define DMA_OFFSET 0x000C2000 #define FPM_OFFSET 0x000C3000 #define IMEM_OFFSET 0x000C4000 @@ -1737,6 +1739,7 @@ static int fman_config(struct fman *fman) fman->qmi_regs = base_addr + QMI_OFFSET; fman->dma_regs = base_addr + DMA_OFFSET; fman->hwp_regs = base_addr + HWP_OFFSET; + fman->kg_regs = base_addr + KG_OFFSET; fman->base_addr = base_addr; spin_lock_init(&fman->spinlock); @@ -2009,6 +2012,11 @@ static int fman_init(struct fman *fman) /* Init HW Parser */ hwp_init(fman->hwp_regs); + /* Init KeyGen */ + fman->keygen = keygen_init(fman->kg_regs); + if (!fman->keygen) + return -EINVAL; + err = enable(fman, cfg); if (err != 0) return err; diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h index 1015dac9bd57..bfa02e0014ae 100644 --- a/drivers/net/ethernet/freescale/fman/fman.h +++ b/drivers/net/ethernet/freescale/fman/fman.h @@ -328,6 +328,7 @@ struct fman { struct fman_qmi_regs __iomem *qmi_regs; struct fman_dma_regs __iomem *dma_regs; struct fman_hwp_regs __iomem *hwp_regs; + struct fman_kg_regs __iomem *kg_regs; fman_exceptions_cb *exception_cb; fman_bus_error_cb *bus_error_cb; /* Spinlock for FMan use */ @@ -336,6 +337,7 @@ struct fman { struct fman_cfg *cfg; struct muram_info *muram; + struct fman_keygen *keygen; /* cam section in muram */ unsigned long cam_offset; size_t cam_size; diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.c b/drivers/net/ethernet/freescale/fman/fman_keygen.c new file mode 100644 index 000000000000..f54da3c684d0 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.c @@ -0,0 +1,783 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NXP nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY NXP ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NXP BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +#include "fman_keygen.h" + +/* Maximum number of HW Ports */ +#define FMAN_MAX_NUM_OF_HW_PORTS 64 + +/* Maximum number of KeyGen Schemes */ +#define FM_KG_MAX_NUM_OF_SCHEMES 32 + +/* Number of generic KeyGen Generic Extract Command Registers */ +#define FM_KG_NUM_OF_GENERIC_REGS 8 + +/* Dummy port ID */ +#define DUMMY_PORT_ID 0 + +/* Select Scheme Value Register */ +#define KG_SCH_DEF_USE_KGSE_DV_0 2 +#define KG_SCH_DEF_USE_KGSE_DV_1 3 + +/* Registers Shifting values */ +#define FM_KG_KGAR_NUM_SHIFT 16 +#define KG_SCH_DEF_L4_PORT_SHIFT 8 +#define KG_SCH_DEF_IP_ADDR_SHIFT 18 +#define KG_SCH_HASH_CONFIG_SHIFT_SHIFT 24 + +/* KeyGen Registers bit field masks: */ + +/* Enable bit field mask for KeyGen General Configuration Register */ +#define FM_KG_KGGCR_EN 0x80000000 + +/* KeyGen Global Registers bit field masks */ +#define FM_KG_KGAR_GO 0x80000000 +#define FM_KG_KGAR_READ 0x40000000 +#define FM_KG_KGAR_WRITE 0x00000000 +#define FM_KG_KGAR_SEL_SCHEME_ENTRY 0x00000000 +#define FM_KG_KGAR_SCM_WSEL_UPDATE_CNT 0x00008000 + +#define FM_KG_KGAR_ERR 0x20000000 +#define FM_KG_KGAR_SEL_CLS_PLAN_ENTRY 0x01000000 +#define FM_KG_KGAR_SEL_PORT_ENTRY 0x02000000 +#define FM_KG_KGAR_SEL_PORT_WSEL_SP 0x00008000 +#define FM_KG_KGAR_SEL_PORT_WSEL_CPP 0x00004000 + +/* Error events exceptions */ +#define FM_EX_KG_DOUBLE_ECC 0x80000000 +#define FM_EX_KG_KEYSIZE_OVERFLOW 0x40000000 + +/* Scheme Registers bit field masks */ +#define KG_SCH_MODE_EN 0x80000000 +#define KG_SCH_VSP_NO_KSP_EN 0x80000000 +#define KG_SCH_HASH_CONFIG_SYM 0x40000000 + +/* Known Protocol field codes */ +#define KG_SCH_KN_PORT_ID 0x80000000 +#define KG_SCH_KN_MACDST 0x40000000 +#define KG_SCH_KN_MACSRC 0x20000000 +#define KG_SCH_KN_TCI1 0x10000000 +#define KG_SCH_KN_TCI2 0x08000000 +#define KG_SCH_KN_ETYPE 0x04000000 +#define KG_SCH_KN_PPPSID 0x02000000 +#define KG_SCH_KN_PPPID 0x01000000 +#define KG_SCH_KN_MPLS1 0x00800000 +#define KG_SCH_KN_MPLS2 0x00400000 +#define KG_SCH_KN_MPLS_LAST 0x00200000 +#define KG_SCH_KN_IPSRC1 0x00100000 +#define KG_SCH_KN_IPDST1 0x00080000 +#define KG_SCH_KN_PTYPE1 0x00040000 +#define KG_SCH_KN_IPTOS_TC1 0x00020000 +#define KG_SCH_KN_IPV6FL1 0x00010000 +#define KG_SCH_KN_IPSRC2 0x00008000 +#define KG_SCH_KN_IPDST2 0x00004000 +#define KG_SCH_KN_PTYPE2 0x00002000 +#define KG_SCH_KN_IPTOS_TC2 0x00001000 +#define KG_SCH_KN_IPV6FL2 0x00000800 +#define KG_SCH_KN_GREPTYPE 0x00000400 +#define KG_SCH_KN_IPSEC_SPI 0x00000200 +#define KG_SCH_KN_IPSEC_NH 0x00000100 +#define KG_SCH_KN_IPPID 0x00000080 +#define KG_SCH_KN_L4PSRC 0x00000004 +#define KG_SCH_KN_L4PDST 0x00000002 +#define KG_SCH_KN_TFLG 0x00000001 + +/* NIA values */ +#define NIA_ENG_BMI 0x00500000 +#define NIA_BMI_AC_ENQ_FRAME 0x00000002 +#define ENQUEUE_KG_DFLT_NIA (NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME) + +/* Hard-coded configuration: + * These values are used as hard-coded values for KeyGen configuration + * and they replace user selections for this hard-coded version + */ + +/* Hash distribution shift */ +#define DEFAULT_HASH_DIST_FQID_SHIFT 0 + +/* Hash shift */ +#define DEFAULT_HASH_SHIFT 0 + +/* Symmetric hash usage: + * Warning: + * - the value for symmetric hash usage must be in accordance with hash + * key defined below + * - according to tests performed, spreading is not working if symmetric + * hash is set on true + * So ultimately symmetric hash functionality should be always disabled: + */ +#define DEFAULT_SYMMETRIC_HASH false + +/* Hash Key extraction fields: */ +#define DEFAULT_HASH_KEY_EXTRACT_FIELDS \ + (KG_SCH_KN_IPSRC1 | KG_SCH_KN_IPDST1 | \ + KG_SCH_KN_L4PSRC | KG_SCH_KN_L4PDST) + +/* Default values to be used as hash key in case IPv4 or L4 (TCP, UDP) + * don't exist in the frame + */ +/* Default IPv4 address */ +#define DEFAULT_HASH_KEY_IPv4_ADDR 0x0A0A0A0A +/* Default L4 port */ +#define DEFAULT_HASH_KEY_L4_PORT 0x0B0B0B0B + +/* KeyGen Memory Mapped Registers: */ + +/* Scheme Configuration RAM Registers */ +struct fman_kg_scheme_regs { + u32 kgse_mode; /* 0x100: MODE */ + u32 kgse_ekfc; /* 0x104: Extract Known Fields Command */ + u32 kgse_ekdv; /* 0x108: Extract Known Default Value */ + u32 kgse_bmch; /* 0x10C: Bit Mask Command High */ + u32 kgse_bmcl; /* 0x110: Bit Mask Command Low */ + u32 kgse_fqb; /* 0x114: Frame Queue Base */ + u32 kgse_hc; /* 0x118: Hash Command */ + u32 kgse_ppc; /* 0x11C: Policer Profile Command */ + u32 kgse_gec[FM_KG_NUM_OF_GENERIC_REGS]; + /* 0x120: Generic Extract Command */ + u32 kgse_spc; + /* 0x140: KeyGen Scheme Entry Statistic Packet Counter */ + u32 kgse_dv0; /* 0x144: KeyGen Scheme Entry Default Value 0 */ + u32 kgse_dv1; /* 0x148: KeyGen Scheme Entry Default Value 1 */ + u32 kgse_ccbs; + /* 0x14C: KeyGen Scheme Entry Coarse Classification Bit*/ + u32 kgse_mv; /* 0x150: KeyGen Scheme Entry Match vector */ + u32 kgse_om; /* 0x154: KeyGen Scheme Entry Operation Mode bits */ + u32 kgse_vsp; + /* 0x158: KeyGen Scheme Entry Virtual Storage Profile */ +}; + +/* Port Partition Configuration Registers */ +struct fman_kg_pe_regs { + u32 fmkg_pe_sp; /* 0x100: KeyGen Port entry Scheme Partition */ + u32 fmkg_pe_cpp; + /* 0x104: KeyGen Port Entry Classification Plan Partition */ +}; + +/* General Configuration and Status Registers + * Global Statistic Counters + * KeyGen Global Registers + */ +struct fman_kg_regs { + u32 fmkg_gcr; /* 0x000: KeyGen General Configuration Register */ + u32 res004; /* 0x004: Reserved */ + u32 res008; /* 0x008: Reserved */ + u32 fmkg_eer; /* 0x00C: KeyGen Error Event Register */ + u32 fmkg_eeer; /* 0x010: KeyGen Error Event Enable Register */ + u32 res014; /* 0x014: Reserved */ + u32 res018; /* 0x018: Reserved */ + u32 fmkg_seer; /* 0x01C: KeyGen Scheme Error Event Register */ + u32 fmkg_seeer; /* 0x020: KeyGen Scheme Error Event Enable Register */ + u32 fmkg_gsr; /* 0x024: KeyGen Global Status Register */ + u32 fmkg_tpc; /* 0x028: Total Packet Counter Register */ + u32 fmkg_serc; /* 0x02C: Soft Error Capture Register */ + u32 res030[4]; /* 0x030: Reserved */ + u32 fmkg_fdor; /* 0x034: Frame Data Offset Register */ + u32 fmkg_gdv0r; /* 0x038: Global Default Value Register 0 */ + u32 fmkg_gdv1r; /* 0x03C: Global Default Value Register 1 */ + u32 res04c[6]; /* 0x040: Reserved */ + u32 fmkg_feer; /* 0x044: Force Error Event Register */ + u32 res068[38]; /* 0x048: Reserved */ + union { + u32 fmkg_indirect[63]; /* 0x100: Indirect Access Registers */ + struct fman_kg_scheme_regs fmkg_sch; /* Scheme Registers */ + struct fman_kg_pe_regs fmkg_pe; /* Port Partition Registers */ + }; + u32 fmkg_ar; /* 0x1FC: KeyGen Action Register */ +}; + +/* KeyGen Scheme data */ +struct keygen_scheme { + bool used; /* Specifies if this scheme is used */ + u8 hw_port_id; + /* Hardware port ID + * schemes sharing between multiple ports is not + * currently supported + * so we have only one port id bound to a scheme + */ + u32 base_fqid; + /* Base FQID: + * Must be between 1 and 2^24-1 + * If hash is used and an even distribution is + * expected according to hash_fqid_count, + * base_fqid must be aligned to hash_fqid_count + */ + u32 hash_fqid_count; + /* FQ range for hash distribution: + * Must be a power of 2 + * Represents the range of queues for spreading + */ + bool use_hashing; /* Usage of Hashing and spreading over FQ */ + bool symmetric_hash; /* Symmetric Hash option usage */ + u8 hashShift; + /* Hash result right shift. + * Select the 24 bits out of the 64 hash result. + * 0 means using the 24 LSB's, otherwise + * use the 24 LSB's after shifting right + */ + u32 match_vector; /* Match Vector */ +}; + +/* KeyGen driver data */ +struct fman_keygen { + struct keygen_scheme schemes[FM_KG_MAX_NUM_OF_SCHEMES]; + /* Array of schemes */ + struct fman_kg_regs __iomem *keygen_regs; /* KeyGen registers */ +}; + +/* keygen_write_ar_wait + * + * Write Action Register with specified value, wait for GO bit field to be + * idle and then read the error + * + * regs: KeyGen registers + * fmkg_ar: Action Register value + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_write_ar_wait(struct fman_kg_regs __iomem *regs, u32 fmkg_ar) +{ + iowrite32be(fmkg_ar, ®s->fmkg_ar); + + /* Wait for GO bit field to be idle */ + while (fmkg_ar & FM_KG_KGAR_GO) + fmkg_ar = ioread32be(®s->fmkg_ar); + + if (fmkg_ar & FM_KG_KGAR_ERR) + return -EINVAL; + + return 0; +} + +/* build_ar_scheme + * + * Build Action Register value for scheme settings + * + * scheme_id: Scheme ID + * update_counter: update scheme counter + * write: true for action to write the scheme or false for read action + * + * Return: AR value + */ +static u32 build_ar_scheme(u8 scheme_id, bool update_counter, bool write) +{ + u32 rw = (u32)(write ? FM_KG_KGAR_WRITE : FM_KG_KGAR_READ); + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_SCHEME_ENTRY | + DUMMY_PORT_ID | + ((u32)scheme_id << FM_KG_KGAR_NUM_SHIFT) | + (update_counter ? FM_KG_KGAR_SCM_WSEL_UPDATE_CNT : 0)); +} + +/* build_ar_bind_scheme + * + * Build Action Register value for port binding to schemes + * + * hwport_id: HW Port ID + * write: true for action to write the bind or false for read action + * + * Return: AR value + */ +static u32 build_ar_bind_scheme(u8 hwport_id, bool write) +{ + u32 rw = write ? (u32)FM_KG_KGAR_WRITE : (u32)FM_KG_KGAR_READ; + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_PORT_ENTRY | + hwport_id | + FM_KG_KGAR_SEL_PORT_WSEL_SP); +} + +/* keygen_write_sp + * + * Write Scheme Partition Register with specified value + * + * regs: KeyGen Registers + * sp: Scheme Partition register value + * add: true to add a scheme partition or false to clear + * + * Return: none + */ +static void keygen_write_sp(struct fman_kg_regs __iomem *regs, u32 sp, bool add) +{ + u32 tmp; + + tmp = ioread32be(®s->fmkg_pe.fmkg_pe_sp); + + if (add) + tmp |= sp; + else + tmp &= ~sp; + + iowrite32be(tmp, ®s->fmkg_pe.fmkg_pe_sp); +} + +/* build_ar_bind_cls_plan + * + * Build Action Register value for Classification Plan + * + * hwport_id: HW Port ID + * write: true for action to write the CP or false for read action + * + * Return: AR value + */ +static u32 build_ar_bind_cls_plan(u8 hwport_id, bool write) +{ + u32 rw = write ? (u32)FM_KG_KGAR_WRITE : (u32)FM_KG_KGAR_READ; + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_PORT_ENTRY | + hwport_id | + FM_KG_KGAR_SEL_PORT_WSEL_CPP); +} + +/* keygen_write_cpp + * + * Write Classification Plan Partition Register with specified value + * + * regs: KeyGen Registers + * cpp: CPP register value + * + * Return: none + */ +static void keygen_write_cpp(struct fman_kg_regs __iomem *regs, u32 cpp) +{ + iowrite32be(cpp, ®s->fmkg_pe.fmkg_pe_cpp); +} + +/* keygen_write_scheme + * + * Write all Schemes Registers with specified values + * + * regs: KeyGen Registers + * scheme_id: Scheme ID + * scheme_regs: Scheme registers values desired to be written + * update_counter: update scheme counter + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_write_scheme(struct fman_kg_regs __iomem *regs, u8 scheme_id, + struct fman_kg_scheme_regs *scheme_regs, + bool update_counter) +{ + u32 ar_reg; + int err, i; + + /* Write indirect scheme registers */ + iowrite32be(scheme_regs->kgse_mode, ®s->fmkg_sch.kgse_mode); + iowrite32be(scheme_regs->kgse_ekfc, ®s->fmkg_sch.kgse_ekfc); + iowrite32be(scheme_regs->kgse_ekdv, ®s->fmkg_sch.kgse_ekdv); + iowrite32be(scheme_regs->kgse_bmch, ®s->fmkg_sch.kgse_bmch); + iowrite32be(scheme_regs->kgse_bmcl, ®s->fmkg_sch.kgse_bmcl); + iowrite32be(scheme_regs->kgse_fqb, ®s->fmkg_sch.kgse_fqb); + iowrite32be(scheme_regs->kgse_hc, ®s->fmkg_sch.kgse_hc); + iowrite32be(scheme_regs->kgse_ppc, ®s->fmkg_sch.kgse_ppc); + iowrite32be(scheme_regs->kgse_spc, ®s->fmkg_sch.kgse_spc); + iowrite32be(scheme_regs->kgse_dv0, ®s->fmkg_sch.kgse_dv0); + iowrite32be(scheme_regs->kgse_dv1, ®s->fmkg_sch.kgse_dv1); + iowrite32be(scheme_regs->kgse_ccbs, ®s->fmkg_sch.kgse_ccbs); + iowrite32be(scheme_regs->kgse_mv, ®s->fmkg_sch.kgse_mv); + iowrite32be(scheme_regs->kgse_om, ®s->fmkg_sch.kgse_om); + iowrite32be(scheme_regs->kgse_vsp, ®s->fmkg_sch.kgse_vsp); + + for (i = 0 ; i < FM_KG_NUM_OF_GENERIC_REGS ; i++) + iowrite32be(scheme_regs->kgse_gec[i], + ®s->fmkg_sch.kgse_gec[i]); + + /* Write AR (Action register) */ + ar_reg = build_ar_scheme(scheme_id, update_counter, true); + err = keygen_write_ar_wait(regs, ar_reg); + if (err != 0) { + pr_err("Writing Action Register failed\n"); + return err; + } + + return err; +} + +/* get_free_scheme_id + * + * Find the first free scheme available to be used + * + * keygen: KeyGen handle + * scheme_id: pointer to scheme id + * + * Return: 0 on success, -EINVAL when the are no available free schemes + */ +static int get_free_scheme_id(struct fman_keygen *keygen, u8 *scheme_id) +{ + u8 i; + + for (i = 0; i < FM_KG_MAX_NUM_OF_SCHEMES; i++) + if (!keygen->schemes[i].used) { + *scheme_id = i; + return 0; + } + + return -EINVAL; +} + +/* get_scheme + * + * Provides the scheme for specified ID + * + * keygen: KeyGen handle + * scheme_id: Scheme ID + * + * Return: handle to required scheme + */ +static struct keygen_scheme *get_scheme(struct fman_keygen *keygen, + u8 scheme_id) +{ + if (scheme_id >= FM_KG_MAX_NUM_OF_SCHEMES) + return NULL; + return &keygen->schemes[scheme_id]; +} + +/* keygen_bind_port_to_schemes + * + * Bind the port to schemes + * + * keygen: KeyGen handle + * scheme_id: id of the scheme to bind to + * bind: true to bind the port or false to unbind it + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_bind_port_to_schemes(struct fman_keygen *keygen, + u8 scheme_id, + bool bind) +{ + struct fman_kg_regs __iomem *keygen_regs = keygen->keygen_regs; + struct keygen_scheme *scheme; + u32 ar_reg; + u32 schemes_vector = 0; + int err; + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (!scheme->used) { + pr_err("Cannot bind port to an invalid scheme\n"); + return -EINVAL; + } + + schemes_vector |= 1 << (31 - scheme_id); + + ar_reg = build_ar_bind_scheme(scheme->hw_port_id, false); + err = keygen_write_ar_wait(keygen_regs, ar_reg); + if (err != 0) { + pr_err("Reading Action Register failed\n"); + return err; + } + + keygen_write_sp(keygen_regs, schemes_vector, bind); + + ar_reg = build_ar_bind_scheme(scheme->hw_port_id, true); + err = keygen_write_ar_wait(keygen_regs, ar_reg); + if (err != 0) { + pr_err("Writing Action Register failed\n"); + return err; + } + + return 0; +} + +/* keygen_scheme_setup + * + * Setup the scheme according to required configuration + * + * keygen: KeyGen handle + * scheme_id: scheme ID + * enable: true to enable scheme or false to disable it + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_scheme_setup(struct fman_keygen *keygen, u8 scheme_id, + bool enable) +{ + struct fman_kg_regs __iomem *keygen_regs = keygen->keygen_regs; + struct fman_kg_scheme_regs scheme_regs; + struct keygen_scheme *scheme; + u32 tmp_reg; + int err; + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (enable && scheme->used) { + pr_err("The requested Scheme is already used\n"); + return -EINVAL; + } + + /* Clear scheme registers */ + memset(&scheme_regs, 0, sizeof(struct fman_kg_scheme_regs)); + + /* Setup all scheme registers: */ + tmp_reg = 0; + + if (enable) { + /* Enable Scheme */ + tmp_reg |= KG_SCH_MODE_EN; + /* Enqueue frame NIA */ + tmp_reg |= ENQUEUE_KG_DFLT_NIA; + } + + scheme_regs.kgse_mode = tmp_reg; + + scheme_regs.kgse_mv = scheme->match_vector; + + /* Scheme don't override StorageProfile: + * valid only for DPAA_VERSION >= 11 + */ + scheme_regs.kgse_vsp = KG_SCH_VSP_NO_KSP_EN; + + /* Configure Hard-Coded Rx Hashing: */ + + if (scheme->use_hashing) { + /* configure kgse_ekfc */ + scheme_regs.kgse_ekfc = DEFAULT_HASH_KEY_EXTRACT_FIELDS; + + /* configure kgse_ekdv */ + tmp_reg = 0; + tmp_reg |= (KG_SCH_DEF_USE_KGSE_DV_0 << + KG_SCH_DEF_IP_ADDR_SHIFT); + tmp_reg |= (KG_SCH_DEF_USE_KGSE_DV_1 << + KG_SCH_DEF_L4_PORT_SHIFT); + scheme_regs.kgse_ekdv = tmp_reg; + + /* configure kgse_dv0 */ + scheme_regs.kgse_dv0 = DEFAULT_HASH_KEY_IPv4_ADDR; + /* configure kgse_dv1 */ + scheme_regs.kgse_dv1 = DEFAULT_HASH_KEY_L4_PORT; + + /* configure kgse_hc */ + tmp_reg = 0; + tmp_reg |= ((scheme->hash_fqid_count - 1) << + DEFAULT_HASH_DIST_FQID_SHIFT); + tmp_reg |= scheme->hashShift << KG_SCH_HASH_CONFIG_SHIFT_SHIFT; + + if (scheme->symmetric_hash) { + /* Normally extraction key should be verified if + * complies with symmetric hash + * But because extraction is hard-coded, we are sure + * the key is symmetric + */ + tmp_reg |= KG_SCH_HASH_CONFIG_SYM; + } + scheme_regs.kgse_hc = tmp_reg; + } else { + scheme_regs.kgse_ekfc = 0; + scheme_regs.kgse_hc = 0; + scheme_regs.kgse_ekdv = 0; + scheme_regs.kgse_dv0 = 0; + scheme_regs.kgse_dv1 = 0; + } + + /* configure kgse_fqb: Scheme FQID base */ + tmp_reg = 0; + tmp_reg |= scheme->base_fqid; + scheme_regs.kgse_fqb = tmp_reg; + + /* features not used by hard-coded configuration */ + scheme_regs.kgse_bmch = 0; + scheme_regs.kgse_bmcl = 0; + scheme_regs.kgse_spc = 0; + + /* Write scheme registers */ + err = keygen_write_scheme(keygen_regs, scheme_id, &scheme_regs, true); + if (err != 0) { + pr_err("Writing scheme registers failed\n"); + return err; + } + + /* Update used field for Scheme */ + scheme->used = enable; + + return 0; +} + +/* keygen_init + * + * KeyGen initialization: + * Initializes and enables KeyGen, allocate driver memory, setup registers, + * clear port bindings, invalidate all schemes + * + * keygen_regs: KeyGen registers base address + * + * Return: Handle to KeyGen driver + */ +struct fman_keygen *keygen_init(struct fman_kg_regs __iomem *keygen_regs) +{ + struct fman_keygen *keygen; + u32 ar; + int i; + + /* Allocate memory for KeyGen driver */ + keygen = kzalloc(sizeof(*keygen), GFP_KERNEL); + if (!keygen) + return NULL; + + keygen->keygen_regs = keygen_regs; + + /* KeyGen initialization (for Master partition): + * Setup KeyGen registers + */ + iowrite32be(ENQUEUE_KG_DFLT_NIA, &keygen_regs->fmkg_gcr); + + iowrite32be(FM_EX_KG_DOUBLE_ECC | FM_EX_KG_KEYSIZE_OVERFLOW, + &keygen_regs->fmkg_eer); + + iowrite32be(0, &keygen_regs->fmkg_fdor); + iowrite32be(0, &keygen_regs->fmkg_gdv0r); + iowrite32be(0, &keygen_regs->fmkg_gdv1r); + + /* Clear binding between ports to schemes and classification plans + * so that all ports are not bound to any scheme/classification plan + */ + for (i = 0; i < FMAN_MAX_NUM_OF_HW_PORTS; i++) { + /* Clear all pe sp schemes registers */ + keygen_write_sp(keygen_regs, 0xffffffff, false); + ar = build_ar_bind_scheme(i, true); + keygen_write_ar_wait(keygen_regs, ar); + + /* Clear all pe cpp classification plans registers */ + keygen_write_cpp(keygen_regs, 0); + ar = build_ar_bind_cls_plan(i, true); + keygen_write_ar_wait(keygen_regs, ar); + } + + /* Enable all scheme interrupts */ + iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seer); + iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seeer); + + /* Enable KyeGen */ + iowrite32be(ioread32be(&keygen_regs->fmkg_gcr) | FM_KG_KGGCR_EN, + &keygen_regs->fmkg_gcr); + + return keygen; +} +EXPORT_SYMBOL(keygen_init); + +/* keygen_port_hashing_init + * + * Initializes a port for Rx Hashing with specified configuration parameters + * + * keygen: KeyGen handle + * hw_port_id: HW Port ID + * hash_base_fqid: Hashing Base FQID used for spreading + * hash_size: Hashing size + * + * Return: Zero for success or error code in case of failure + */ +int keygen_port_hashing_init(struct fman_keygen *keygen, u8 hw_port_id, + u32 hash_base_fqid, u32 hash_size) +{ + struct keygen_scheme *scheme; + u8 scheme_id; + int err; + + /* Validate Scheme configuration parameters */ + if (hash_base_fqid == 0 || (hash_base_fqid & ~0x00FFFFFF)) { + pr_err("Base FQID must be between 1 and 2^24-1\n"); + return -EINVAL; + } + if (hash_size == 0 || (hash_size & (hash_size - 1)) != 0) { + pr_err("Hash size must be power of two\n"); + return -EINVAL; + } + + /* Find a free scheme */ + err = get_free_scheme_id(keygen, &scheme_id); + if (err) { + pr_err("The maximum number of available Schemes has been exceeded\n"); + return -EINVAL; + } + + /* Create and configure Hard-Coded Scheme: */ + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (scheme->used) { + pr_err("The requested Scheme is already used\n"); + return -EINVAL; + } + + /* Clear all scheme fields because the scheme may have been + * previously used + */ + memset(scheme, 0, sizeof(struct keygen_scheme)); + + /* Setup scheme: */ + scheme->hw_port_id = hw_port_id; + scheme->use_hashing = true; + scheme->base_fqid = hash_base_fqid; + scheme->hash_fqid_count = hash_size; + scheme->symmetric_hash = DEFAULT_SYMMETRIC_HASH; + scheme->hashShift = DEFAULT_HASH_SHIFT; + + /* All Schemes in hard-coded configuration + * are Indirect Schemes + */ + scheme->match_vector = 0; + + err = keygen_scheme_setup(keygen, scheme_id, true); + if (err != 0) { + pr_err("Scheme setup failed\n"); + return err; + } + + /* Bind Rx port to Scheme */ + err = keygen_bind_port_to_schemes(keygen, scheme_id, true); + if (err != 0) { + pr_err("Binding port to schemes failed\n"); + return err; + } + + return 0; +} +EXPORT_SYMBOL(keygen_port_hashing_init); diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.h b/drivers/net/ethernet/freescale/fman/fman_keygen.h new file mode 100644 index 000000000000..c4640de3f4cb --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.h @@ -0,0 +1,46 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NXP nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY NXP ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NXP BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __KEYGEN_H +#define __KEYGEN_H + +#include + +struct fman_keygen; +struct fman_kg_regs; + +struct fman_keygen *keygen_init(struct fman_kg_regs __iomem *keygen_regs); + +int keygen_port_hashing_init(struct fman_keygen *keygen, u8 hw_port_id, + u32 hash_base_fqid, u32 hash_size); + +#endif /* __KEYGEN_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c index d51e6aa2b356..f571b3c37f27 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.c +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -44,6 +44,7 @@ #include "fman.h" #include "fman_port.h" #include "fman_sp.h" +#include "fman_keygen.h" /* Queue ID */ #define DFLT_FQ_ID 0x00FFFFFF @@ -184,6 +185,7 @@ #define NIA_ENG_QMI_ENQ 0x00540000 #define NIA_ENG_QMI_DEQ 0x00580000 #define NIA_ENG_HWP 0x00440000 +#define NIA_ENG_HWK 0x00480000 #define NIA_BMI_AC_ENQ_FRAME 0x00000002 #define NIA_BMI_AC_TX_RELEASE 0x000002C0 #define NIA_BMI_AC_RELEASE 0x000000C0 @@ -394,6 +396,8 @@ struct fman_port_bpools { struct fman_port_cfg { u32 dflt_fqid; u32 err_fqid; + u32 pcd_base_fqid; + u32 pcd_fqs_count; u8 deq_sp; bool deq_high_priority; enum fman_port_deq_type deq_type; @@ -1271,6 +1275,10 @@ static void set_rx_dflt_cfg(struct fman_port *port, port_params->specific_params.rx_params.err_fqid; port->cfg->dflt_fqid = port_params->specific_params.rx_params.dflt_fqid; + port->cfg->pcd_base_fqid = + port_params->specific_params.rx_params.pcd_base_fqid; + port->cfg->pcd_fqs_count = + port_params->specific_params.rx_params.pcd_fqs_count; } static void set_tx_dflt_cfg(struct fman_port *port, @@ -1397,6 +1405,24 @@ err_port_cfg: } EXPORT_SYMBOL(fman_port_config); +/** + * fman_port_use_kg_hash + * port: A pointer to a FM Port module. + * Sets the HW KeyGen or the BMI as HW Parser next engine, enabling + * or bypassing the KeyGen hashing of Rx traffic + */ +void fman_port_use_kg_hash(struct fman_port *port, bool enable) +{ + if (enable) + /* After the Parser frames go to KeyGen */ + iowrite32be(NIA_ENG_HWK, &port->bmi_regs->rx.fmbm_rfpne); + else + /* After the Parser frames go to BMI */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME, + &port->bmi_regs->rx.fmbm_rfpne); +} +EXPORT_SYMBOL(fman_port_use_kg_hash); + /** * fman_port_init * port: A pointer to a FM Port module. @@ -1407,9 +1433,10 @@ EXPORT_SYMBOL(fman_port_config); */ int fman_port_init(struct fman_port *port) { + struct fman_port_init_params params; + struct fman_keygen *keygen; struct fman_port_cfg *cfg; int err; - struct fman_port_init_params params; if (is_init_done(port->cfg)) return -EINVAL; @@ -1472,6 +1499,17 @@ int fman_port_init(struct fman_port *port) if (err) return err; + if (port->cfg->pcd_fqs_count) { + keygen = port->dts_params.fman->keygen; + err = keygen_port_hashing_init(keygen, port->port_id, + port->cfg->pcd_base_fqid, + port->cfg->pcd_fqs_count); + if (err) + return err; + + fman_port_use_kg_hash(port, true); + } + kfree(port->cfg); port->cfg = NULL; diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h index 8ba901737048..5a99611fa1dc 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.h +++ b/drivers/net/ethernet/freescale/fman/fman_port.h @@ -100,6 +100,9 @@ struct fman_port; struct fman_port_rx_params { u32 err_fqid; /* Error Queue Id. */ u32 dflt_fqid; /* Default Queue Id. */ + u32 pcd_base_fqid; /* PCD base Queue Id. */ + u32 pcd_fqs_count; /* Number of PCD FQs. */ + /* Which external buffer pools are used * (up to FMAN_PORT_MAX_EXT_POOLS_NUM), and their sizes. */ @@ -134,6 +137,8 @@ struct fman_port_params { int fman_port_config(struct fman_port *port, struct fman_port_params *params); +void fman_port_use_kg_hash(struct fman_port *port, bool enable); + int fman_port_init(struct fman_port *port); int fman_port_cfg_buf_prefix_content(struct fman_port *port, -- cgit v1.2.3 From 3150b7c20b862896b1ac9467440a2febe1d11bc0 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Sun, 27 Aug 2017 16:13:39 +0300 Subject: dpaa_eth: use multiple Rx frame queues Add a block of 128 Rx frame queues per port. The FMan hardware will send traffic on one of these queues based on the FMan port Parse Classify Distribute setup. The hash computed by the FMan Keygen block will select the Rx FQ. Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 50 +++++++++++++++++++--- drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 1 + .../net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c | 3 ++ 3 files changed, 47 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index c7fa285378b5..6d89e74ded5c 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -158,7 +158,7 @@ MODULE_PARM_DESC(tx_timeout, "The Tx timeout in ms"); #define DPAA_RX_PRIV_DATA_SIZE (u16)(DPAA_TX_PRIV_DATA_SIZE + \ dpaa_rx_extra_headroom) -#define DPAA_ETH_RX_QUEUES 128 +#define DPAA_ETH_PCD_RXQ_NUM 128 #define DPAA_ENQUEUE_RETRIES 100000 @@ -169,6 +169,7 @@ struct fm_port_fqs { struct dpaa_fq *tx_errq; struct dpaa_fq *rx_defq; struct dpaa_fq *rx_errq; + struct dpaa_fq *rx_pcdq; }; /* All the dpa bps in use at any moment */ @@ -628,6 +629,7 @@ static inline void dpaa_assign_wq(struct dpaa_fq *fq, int idx) fq->wq = 5; break; case FQ_TYPE_RX_DEFAULT: + case FQ_TYPE_RX_PCD: fq->wq = 6; break; case FQ_TYPE_TX: @@ -688,6 +690,7 @@ static int dpaa_alloc_all_fqs(struct device *dev, struct list_head *list, struct fm_port_fqs *port_fqs) { struct dpaa_fq *dpaa_fq; + u32 fq_base, fq_base_aligned, i; dpaa_fq = dpaa_fq_alloc(dev, 0, 1, list, FQ_TYPE_RX_ERROR); if (!dpaa_fq) @@ -701,6 +704,26 @@ static int dpaa_alloc_all_fqs(struct device *dev, struct list_head *list, port_fqs->rx_defq = &dpaa_fq[0]; + /* the PCD FQIDs range needs to be aligned for correct operation */ + if (qman_alloc_fqid_range(&fq_base, 2 * DPAA_ETH_PCD_RXQ_NUM)) + goto fq_alloc_failed; + + fq_base_aligned = ALIGN(fq_base, DPAA_ETH_PCD_RXQ_NUM); + + for (i = fq_base; i < fq_base_aligned; i++) + qman_release_fqid(i); + + for (i = fq_base_aligned + DPAA_ETH_PCD_RXQ_NUM; + i < (fq_base + 2 * DPAA_ETH_PCD_RXQ_NUM); i++) + qman_release_fqid(i); + + dpaa_fq = dpaa_fq_alloc(dev, fq_base_aligned, DPAA_ETH_PCD_RXQ_NUM, + list, FQ_TYPE_RX_PCD); + if (!dpaa_fq) + goto fq_alloc_failed; + + port_fqs->rx_pcdq = &dpaa_fq[0]; + if (!dpaa_fq_alloc(dev, 0, DPAA_ETH_TXQ_NUM, list, FQ_TYPE_TX_CONF_MQ)) goto fq_alloc_failed; @@ -870,13 +893,14 @@ static void dpaa_fq_setup(struct dpaa_priv *priv, const struct dpaa_fq_cbs *fq_cbs, struct fman_port *tx_port) { - int egress_cnt = 0, conf_cnt = 0, num_portals = 0, cpu; + int egress_cnt = 0, conf_cnt = 0, num_portals = 0, portal_cnt = 0, cpu; const cpumask_t *affine_cpus = qman_affine_cpus(); - u16 portals[NR_CPUS]; + u16 channels[NR_CPUS]; struct dpaa_fq *fq; for_each_cpu(cpu, affine_cpus) - portals[num_portals++] = qman_affine_channel(cpu); + channels[num_portals++] = qman_affine_channel(cpu); + if (num_portals == 0) dev_err(priv->net_dev->dev.parent, "No Qman software (affine) channels found"); @@ -890,6 +914,12 @@ static void dpaa_fq_setup(struct dpaa_priv *priv, case FQ_TYPE_RX_ERROR: dpaa_setup_ingress(priv, fq, &fq_cbs->rx_errq); break; + case FQ_TYPE_RX_PCD: + if (!num_portals) + continue; + dpaa_setup_ingress(priv, fq, &fq_cbs->rx_defq); + fq->channel = channels[portal_cnt++ % num_portals]; + break; case FQ_TYPE_TX: dpaa_setup_egress(priv, fq, tx_port, &fq_cbs->egress_ern); @@ -1039,7 +1069,8 @@ static int dpaa_fq_init(struct dpaa_fq *dpaa_fq, bool td_enable) /* Put all the ingress queues in our "ingress CGR". */ if (priv->use_ingress_cgr && (dpaa_fq->fq_type == FQ_TYPE_RX_DEFAULT || - dpaa_fq->fq_type == FQ_TYPE_RX_ERROR)) { + dpaa_fq->fq_type == FQ_TYPE_RX_ERROR || + dpaa_fq->fq_type == FQ_TYPE_RX_PCD)) { initfq.we_mask |= cpu_to_be16(QM_INITFQ_WE_CGID); initfq.fqd.fq_ctrl |= cpu_to_be16(QM_FQCTRL_CGE); initfq.fqd.cgid = (u8)priv->ingress_cgr.cgrid; @@ -1170,7 +1201,7 @@ static int dpaa_eth_init_tx_port(struct fman_port *port, struct dpaa_fq *errq, static int dpaa_eth_init_rx_port(struct fman_port *port, struct dpaa_bp **bps, size_t count, struct dpaa_fq *errq, - struct dpaa_fq *defq, + struct dpaa_fq *defq, struct dpaa_fq *pcdq, struct dpaa_buffer_layout *buf_layout) { struct fman_buffer_prefix_content buf_prefix_content; @@ -1190,6 +1221,10 @@ static int dpaa_eth_init_rx_port(struct fman_port *port, struct dpaa_bp **bps, rx_p = ¶ms.specific_params.rx_params; rx_p->err_fqid = errq->fqid; rx_p->dflt_fqid = defq->fqid; + if (pcdq) { + rx_p->pcd_base_fqid = pcdq->fqid; + rx_p->pcd_fqs_count = DPAA_ETH_PCD_RXQ_NUM; + } count = min(ARRAY_SIZE(rx_p->ext_buf_pools.ext_buf_pool), count); rx_p->ext_buf_pools.num_of_pools_used = (u8)count; @@ -1234,7 +1269,8 @@ static int dpaa_eth_init_ports(struct mac_device *mac_dev, return err; err = dpaa_eth_init_rx_port(rxport, bps, count, port_fqs->rx_errq, - port_fqs->rx_defq, &buf_layout[RX]); + port_fqs->rx_defq, port_fqs->rx_pcdq, + &buf_layout[RX]); return err; } diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h index 9941a7866ebe..496a12c50ada 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h @@ -52,6 +52,7 @@ enum dpaa_fq_type { FQ_TYPE_RX_DEFAULT = 1, /* Rx Default FQs */ FQ_TYPE_RX_ERROR, /* Rx Error FQs */ + FQ_TYPE_RX_PCD, /* Rx Parse Classify Distribute FQs */ FQ_TYPE_TX, /* "Real" Tx FQs */ FQ_TYPE_TX_CONFIRM, /* Tx default Conf FQ (actually an Rx FQ) */ FQ_TYPE_TX_CONF_MQ, /* Tx conf FQs (one for each Tx FQ) */ diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c index ec75d1c6fa89..0d9b185e317f 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c @@ -71,6 +71,9 @@ static ssize_t dpaa_eth_show_fqids(struct device *dev, case FQ_TYPE_RX_ERROR: str = "Rx error"; break; + case FQ_TYPE_RX_PCD: + str = "Rx PCD"; + break; case FQ_TYPE_TX_CONFIRM: str = "Tx default confirmation"; break; -- cgit v1.2.3 From bcf0994b23fed62cfd55e922e74b0dd43958e6fd Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Sun, 27 Aug 2017 16:13:40 +0300 Subject: dpaa_eth: enable Rx hashing control Allow ethtool control of the Rx flow hashing. By default RSS is enabled, this allows to turn it off by bypassing the FMan Keygen block and sending all traffic on the default Rx frame queue. Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c | 113 +++++++++++++++++++++ 1 file changed, 113 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c index aad825088357..965f6520d58a 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c @@ -399,6 +399,117 @@ static void dpaa_get_strings(struct net_device *net_dev, u32 stringset, memcpy(strings, dpaa_stats_global, size); } +static int dpaa_get_hash_opts(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + cmd->data = 0; + + switch (cmd->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + case UDP_V4_FLOW: + case UDP_V6_FLOW: + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + /* Fall through */ + case IPV4_FLOW: + case IPV6_FLOW: + case SCTP_V4_FLOW: + case SCTP_V6_FLOW: + case AH_ESP_V4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V4_FLOW: + case AH_V6_FLOW: + case ESP_V4_FLOW: + case ESP_V6_FLOW: + cmd->data |= RXH_IP_SRC | RXH_IP_DST; + break; + default: + cmd->data = 0; + break; + } + + return 0; +} + +static int dpaa_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, + u32 *unused) +{ + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_GRXFH: + ret = dpaa_get_hash_opts(dev, cmd); + break; + default: + break; + } + + return ret; +} + +static void dpaa_set_hash(struct net_device *net_dev, bool enable) +{ + struct mac_device *mac_dev; + struct fman_port *rxport; + struct dpaa_priv *priv; + + priv = netdev_priv(net_dev); + mac_dev = priv->mac_dev; + rxport = mac_dev->port[0]; + + fman_port_use_kg_hash(rxport, enable); +} + +static int dpaa_set_hash_opts(struct net_device *dev, + struct ethtool_rxnfc *nfc) +{ + int ret = -EINVAL; + + /* we support hashing on IPv4/v6 src/dest IP and L4 src/dest port */ + if (nfc->data & + ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) + return -EINVAL; + + switch (nfc->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + case UDP_V4_FLOW: + case UDP_V6_FLOW: + case IPV4_FLOW: + case IPV6_FLOW: + case SCTP_V4_FLOW: + case SCTP_V6_FLOW: + case AH_ESP_V4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V4_FLOW: + case AH_V6_FLOW: + case ESP_V4_FLOW: + case ESP_V6_FLOW: + dpaa_set_hash(dev, !!nfc->data); + ret = 0; + break; + default: + break; + } + + return ret; +} + +static int dpaa_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_SRXFH: + ret = dpaa_set_hash_opts(dev, cmd); + break; + default: + break; + } + + return ret; +} + const struct ethtool_ops dpaa_ethtool_ops = { .get_drvinfo = dpaa_get_drvinfo, .get_msglevel = dpaa_get_msglevel, @@ -412,4 +523,6 @@ const struct ethtool_ops dpaa_ethtool_ops = { .get_strings = dpaa_get_strings, .get_link_ksettings = dpaa_get_link_ksettings, .set_link_ksettings = dpaa_set_link_ksettings, + .get_rxnfc = dpaa_get_rxnfc, + .set_rxnfc = dpaa_set_rxnfc, }; -- cgit v1.2.3 From 056057e288e70755a423ddfd2e45b97cd147a411 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Sun, 27 Aug 2017 16:13:41 +0300 Subject: dpaa_eth: add NETIF_F_RXHASH Set the skb hash when then FMan Keygen hash result is available. Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 23 +++++++++++++++++++--- drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 1 + drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c | 9 +++++++-- drivers/net/ethernet/freescale/fman/fman_port.c | 11 +++++++++++ drivers/net/ethernet/freescale/fman/fman_port.h | 2 ++ 5 files changed, 41 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 6d89e74ded5c..73ca8d761e18 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -236,7 +236,7 @@ static int dpaa_netdev_init(struct net_device *net_dev, net_dev->max_mtu = dpaa_get_max_mtu(); net_dev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_LLTX); + NETIF_F_LLTX | NETIF_F_RXHASH); net_dev->hw_features |= NETIF_F_SG | NETIF_F_HIGHDMA; /* The kernels enables GSO automatically, if we declare NETIF_F_SG. @@ -2237,12 +2237,13 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, dma_addr_t addr = qm_fd_addr(fd); enum qm_fd_format fd_format; struct net_device *net_dev; - u32 fd_status; + u32 fd_status, hash_offset; struct dpaa_bp *dpaa_bp; struct dpaa_priv *priv; unsigned int skb_len; struct sk_buff *skb; int *count_ptr; + void *vaddr; fd_status = be32_to_cpu(fd->status); fd_format = qm_fd_get_format(fd); @@ -2288,7 +2289,8 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, dma_unmap_single(dpaa_bp->dev, addr, dpaa_bp->size, DMA_FROM_DEVICE); /* prefetch the first 64 bytes of the frame or the SGT start */ - prefetch(phys_to_virt(addr) + qm_fd_get_offset(fd)); + vaddr = phys_to_virt(addr); + prefetch(vaddr + qm_fd_get_offset(fd)); fd_format = qm_fd_get_format(fd); /* The only FD types that we may receive are contig and S/G */ @@ -2309,6 +2311,18 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, skb->protocol = eth_type_trans(skb, net_dev); + if (net_dev->features & NETIF_F_RXHASH && priv->keygen_in_use && + !fman_port_get_hash_result_offset(priv->mac_dev->port[RX], + &hash_offset)) { + enum pkt_hash_types type; + + /* if L4 exists, it was used in the hash generation */ + type = be32_to_cpu(fd->status) & FM_FD_STAT_L4CV ? + PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3; + skb_set_hash(skb, be32_to_cpu(*(u32 *)(vaddr + hash_offset)), + type); + } + skb_len = skb->len; if (unlikely(netif_receive_skb(skb) == NET_RX_DROP)) @@ -2774,6 +2788,9 @@ static int dpaa_eth_probe(struct platform_device *pdev) if (err) goto init_ports_failed; + /* Rx traffic distribution based on keygen hashing defaults to on */ + priv->keygen_in_use = true; + priv->percpu_priv = devm_alloc_percpu(dev, *priv->percpu_priv); if (!priv->percpu_priv) { dev_err(dev, "devm_alloc_percpu() failed\n"); diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h index 496a12c50ada..bd9422082f83 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h @@ -159,6 +159,7 @@ struct dpaa_priv { struct list_head dpaa_fq_list; u8 num_tc; + bool keygen_in_use; u32 msg_enable; /* net_device message level */ struct { diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c index 965f6520d58a..faea674094b9 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c @@ -402,6 +402,8 @@ static void dpaa_get_strings(struct net_device *net_dev, u32 stringset, static int dpaa_get_hash_opts(struct net_device *dev, struct ethtool_rxnfc *cmd) { + struct dpaa_priv *priv = netdev_priv(dev); + cmd->data = 0; switch (cmd->flow_type) { @@ -409,7 +411,8 @@ static int dpaa_get_hash_opts(struct net_device *dev, case TCP_V6_FLOW: case UDP_V4_FLOW: case UDP_V6_FLOW: - cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + if (priv->keygen_in_use) + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; /* Fall through */ case IPV4_FLOW: case IPV6_FLOW: @@ -421,7 +424,8 @@ static int dpaa_get_hash_opts(struct net_device *dev, case AH_V6_FLOW: case ESP_V4_FLOW: case ESP_V6_FLOW: - cmd->data |= RXH_IP_SRC | RXH_IP_DST; + if (priv->keygen_in_use) + cmd->data |= RXH_IP_SRC | RXH_IP_DST; break; default: cmd->data = 0; @@ -458,6 +462,7 @@ static void dpaa_set_hash(struct net_device *net_dev, bool enable) rxport = mac_dev->port[0]; fman_port_use_kg_hash(rxport, enable); + priv->keygen_in_use = enable; } static int dpaa_set_hash_opts(struct net_device *dev, diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c index f571b3c37f27..1789b206be58 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.c +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -1720,6 +1720,17 @@ u32 fman_port_get_qman_channel_id(struct fman_port *port) } EXPORT_SYMBOL(fman_port_get_qman_channel_id); +int fman_port_get_hash_result_offset(struct fman_port *port, u32 *offset) +{ + if (port->buffer_offsets.hash_result_offset == ILLEGAL_BASE) + return -EINVAL; + + *offset = port->buffer_offsets.hash_result_offset; + + return 0; +} +EXPORT_SYMBOL(fman_port_get_hash_result_offset); + static int fman_port_probe(struct platform_device *of_dev) { struct fman_port *port; diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h index 5a99611fa1dc..e86ca6a34e4e 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.h +++ b/drivers/net/ethernet/freescale/fman/fman_port.h @@ -151,6 +151,8 @@ int fman_port_enable(struct fman_port *port); u32 fman_port_get_qman_channel_id(struct fman_port *port); +int fman_port_get_hash_result_offset(struct fman_port *port, u32 *offset); + struct fman_port *fman_port_bind(struct device *dev); #endif /* __FMAN_PORT_H */ -- cgit v1.2.3 From 52600dcc9e3ac4c023edd48984f3005ab8191e71 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Sun, 27 Aug 2017 16:13:43 +0300 Subject: dpaa_eth: check allocation result Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 73ca8d761e18..42258060f142 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -2561,6 +2561,9 @@ static struct dpaa_bp *dpaa_bp_alloc(struct device *dev) dpaa_bp->bpid = FSL_DPAA_BPID_INV; dpaa_bp->percpu_count = devm_alloc_percpu(dev, *dpaa_bp->percpu_count); + if (!dpaa_bp->percpu_count) + return ERR_PTR(-ENOMEM); + dpaa_bp->config_count = FSL_DPAA_ETH_MAX_BUF_COUNT; dpaa_bp->seed_cb = dpaa_bp_seed; -- cgit v1.2.3 From 6c391ff758eb9f41e11c6143fd5bdb3b3324bf9f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 27 Aug 2017 17:03:32 +0200 Subject: irda: move drivers/net/irda to drivers/staging/irda/drivers Move the irda drivers from drivers/net/irda/ to drivers/staging/irda/drivers as they will be deleted in a future kernel release. Signed-off-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- drivers/net/Makefile | 1 - drivers/net/irda/Kconfig | 398 ---- drivers/net/irda/Makefile | 42 - drivers/net/irda/act200l-sir.c | 250 -- drivers/net/irda/actisys-sir.c | 245 -- drivers/net/irda/ali-ircc.c | 2218 ------------------ drivers/net/irda/ali-ircc.h | 227 -- drivers/net/irda/au1k_ir.c | 989 -------- drivers/net/irda/bfin_sir.c | 817 ------- drivers/net/irda/bfin_sir.h | 93 - drivers/net/irda/donauboe.c | 1732 -------------- drivers/net/irda/donauboe.h | 362 --- drivers/net/irda/esi-sir.c | 157 -- drivers/net/irda/girbil-sir.c | 252 -- drivers/net/irda/irda-usb.c | 1914 ---------------- drivers/net/irda/irda-usb.h | 174 -- drivers/net/irda/irtty-sir.c | 570 ----- drivers/net/irda/irtty-sir.h | 34 - drivers/net/irda/kingsun-sir.c | 634 ------ drivers/net/irda/ks959-sir.c | 912 -------- drivers/net/irda/ksdazzle-sir.c | 813 ------- drivers/net/irda/litelink-sir.c | 199 -- drivers/net/irda/ma600-sir.c | 253 --- drivers/net/irda/mcp2120-sir.c | 224 -- drivers/net/irda/mcs7780.c | 987 -------- drivers/net/irda/mcs7780.h | 165 -- drivers/net/irda/nsc-ircc.c | 2410 -------------------- drivers/net/irda/nsc-ircc.h | 281 --- drivers/net/irda/old_belkin-sir.c | 146 -- drivers/net/irda/pxaficp_ir.c | 1076 --------- drivers/net/irda/sa1100_ir.c | 1150 ---------- drivers/net/irda/sh_sir.c | 810 ------- drivers/net/irda/sir-dev.h | 191 -- drivers/net/irda/sir_dev.c | 987 -------- drivers/net/irda/sir_dongle.c | 133 -- drivers/net/irda/smsc-ircc2.c | 3026 ------------------------- drivers/net/irda/smsc-ircc2.h | 191 -- drivers/net/irda/smsc-sio.h | 100 - drivers/net/irda/stir4200.c | 1134 --------- drivers/net/irda/tekram-sir.c | 225 -- drivers/net/irda/toim3232-sir.c | 358 --- drivers/net/irda/via-ircc.c | 1593 ------------- drivers/net/irda/via-ircc.h | 846 ------- drivers/net/irda/vlsi_ir.c | 1872 --------------- drivers/net/irda/vlsi_ir.h | 757 ------- drivers/net/irda/w83977af.h | 53 - drivers/net/irda/w83977af_ir.c | 1285 ----------- drivers/net/irda/w83977af_ir.h | 198 -- drivers/staging/Makefile | 1 + drivers/staging/irda/drivers/Kconfig | 398 ++++ drivers/staging/irda/drivers/Makefile | 42 + drivers/staging/irda/drivers/act200l-sir.c | 250 ++ drivers/staging/irda/drivers/actisys-sir.c | 245 ++ drivers/staging/irda/drivers/ali-ircc.c | 2218 ++++++++++++++++++ drivers/staging/irda/drivers/ali-ircc.h | 227 ++ drivers/staging/irda/drivers/au1k_ir.c | 989 ++++++++ drivers/staging/irda/drivers/bfin_sir.c | 817 +++++++ drivers/staging/irda/drivers/bfin_sir.h | 93 + drivers/staging/irda/drivers/donauboe.c | 1732 ++++++++++++++ drivers/staging/irda/drivers/donauboe.h | 362 +++ drivers/staging/irda/drivers/esi-sir.c | 157 ++ drivers/staging/irda/drivers/girbil-sir.c | 252 ++ drivers/staging/irda/drivers/irda-usb.c | 1914 ++++++++++++++++ drivers/staging/irda/drivers/irda-usb.h | 174 ++ drivers/staging/irda/drivers/irtty-sir.c | 570 +++++ drivers/staging/irda/drivers/irtty-sir.h | 34 + drivers/staging/irda/drivers/kingsun-sir.c | 634 ++++++ drivers/staging/irda/drivers/ks959-sir.c | 912 ++++++++ drivers/staging/irda/drivers/ksdazzle-sir.c | 813 +++++++ drivers/staging/irda/drivers/litelink-sir.c | 199 ++ drivers/staging/irda/drivers/ma600-sir.c | 253 +++ drivers/staging/irda/drivers/mcp2120-sir.c | 224 ++ drivers/staging/irda/drivers/mcs7780.c | 987 ++++++++ drivers/staging/irda/drivers/mcs7780.h | 165 ++ drivers/staging/irda/drivers/nsc-ircc.c | 2410 ++++++++++++++++++++ drivers/staging/irda/drivers/nsc-ircc.h | 281 +++ drivers/staging/irda/drivers/old_belkin-sir.c | 146 ++ drivers/staging/irda/drivers/pxaficp_ir.c | 1076 +++++++++ drivers/staging/irda/drivers/sa1100_ir.c | 1150 ++++++++++ drivers/staging/irda/drivers/sh_sir.c | 810 +++++++ drivers/staging/irda/drivers/sir-dev.h | 191 ++ drivers/staging/irda/drivers/sir_dev.c | 987 ++++++++ drivers/staging/irda/drivers/sir_dongle.c | 133 ++ drivers/staging/irda/drivers/smsc-ircc2.c | 3026 +++++++++++++++++++++++++ drivers/staging/irda/drivers/smsc-ircc2.h | 191 ++ drivers/staging/irda/drivers/smsc-sio.h | 100 + drivers/staging/irda/drivers/stir4200.c | 1134 +++++++++ drivers/staging/irda/drivers/tekram-sir.c | 225 ++ drivers/staging/irda/drivers/toim3232-sir.c | 358 +++ drivers/staging/irda/drivers/via-ircc.c | 1593 +++++++++++++ drivers/staging/irda/drivers/via-ircc.h | 846 +++++++ drivers/staging/irda/drivers/vlsi_ir.c | 1872 +++++++++++++++ drivers/staging/irda/drivers/vlsi_ir.h | 757 +++++++ drivers/staging/irda/drivers/w83977af.h | 53 + drivers/staging/irda/drivers/w83977af_ir.c | 1285 +++++++++++ drivers/staging/irda/drivers/w83977af_ir.h | 198 ++ drivers/staging/irda/net/Kconfig | 2 +- 97 files changed, 33485 insertions(+), 33485 deletions(-) delete mode 100644 drivers/net/irda/Kconfig delete mode 100644 drivers/net/irda/Makefile delete mode 100644 drivers/net/irda/act200l-sir.c delete mode 100644 drivers/net/irda/actisys-sir.c delete mode 100644 drivers/net/irda/ali-ircc.c delete mode 100644 drivers/net/irda/ali-ircc.h delete mode 100644 drivers/net/irda/au1k_ir.c delete mode 100644 drivers/net/irda/bfin_sir.c delete mode 100644 drivers/net/irda/bfin_sir.h delete mode 100644 drivers/net/irda/donauboe.c delete mode 100644 drivers/net/irda/donauboe.h delete mode 100644 drivers/net/irda/esi-sir.c delete mode 100644 drivers/net/irda/girbil-sir.c delete mode 100644 drivers/net/irda/irda-usb.c delete mode 100644 drivers/net/irda/irda-usb.h delete mode 100644 drivers/net/irda/irtty-sir.c delete mode 100644 drivers/net/irda/irtty-sir.h delete mode 100644 drivers/net/irda/kingsun-sir.c delete mode 100644 drivers/net/irda/ks959-sir.c delete mode 100644 drivers/net/irda/ksdazzle-sir.c delete mode 100644 drivers/net/irda/litelink-sir.c delete mode 100644 drivers/net/irda/ma600-sir.c delete mode 100644 drivers/net/irda/mcp2120-sir.c delete mode 100644 drivers/net/irda/mcs7780.c delete mode 100644 drivers/net/irda/mcs7780.h delete mode 100644 drivers/net/irda/nsc-ircc.c delete mode 100644 drivers/net/irda/nsc-ircc.h delete mode 100644 drivers/net/irda/old_belkin-sir.c delete mode 100644 drivers/net/irda/pxaficp_ir.c delete mode 100644 drivers/net/irda/sa1100_ir.c delete mode 100644 drivers/net/irda/sh_sir.c delete mode 100644 drivers/net/irda/sir-dev.h delete mode 100644 drivers/net/irda/sir_dev.c delete mode 100644 drivers/net/irda/sir_dongle.c delete mode 100644 drivers/net/irda/smsc-ircc2.c delete mode 100644 drivers/net/irda/smsc-ircc2.h delete mode 100644 drivers/net/irda/smsc-sio.h delete mode 100644 drivers/net/irda/stir4200.c delete mode 100644 drivers/net/irda/tekram-sir.c delete mode 100644 drivers/net/irda/toim3232-sir.c delete mode 100644 drivers/net/irda/via-ircc.c delete mode 100644 drivers/net/irda/via-ircc.h delete mode 100644 drivers/net/irda/vlsi_ir.c delete mode 100644 drivers/net/irda/vlsi_ir.h delete mode 100644 drivers/net/irda/w83977af.h delete mode 100644 drivers/net/irda/w83977af_ir.c delete mode 100644 drivers/net/irda/w83977af_ir.h create mode 100644 drivers/staging/irda/drivers/Kconfig create mode 100644 drivers/staging/irda/drivers/Makefile create mode 100644 drivers/staging/irda/drivers/act200l-sir.c create mode 100644 drivers/staging/irda/drivers/actisys-sir.c create mode 100644 drivers/staging/irda/drivers/ali-ircc.c create mode 100644 drivers/staging/irda/drivers/ali-ircc.h create mode 100644 drivers/staging/irda/drivers/au1k_ir.c create mode 100644 drivers/staging/irda/drivers/bfin_sir.c create mode 100644 drivers/staging/irda/drivers/bfin_sir.h create mode 100644 drivers/staging/irda/drivers/donauboe.c create mode 100644 drivers/staging/irda/drivers/donauboe.h create mode 100644 drivers/staging/irda/drivers/esi-sir.c create mode 100644 drivers/staging/irda/drivers/girbil-sir.c create mode 100644 drivers/staging/irda/drivers/irda-usb.c create mode 100644 drivers/staging/irda/drivers/irda-usb.h create mode 100644 drivers/staging/irda/drivers/irtty-sir.c create mode 100644 drivers/staging/irda/drivers/irtty-sir.h create mode 100644 drivers/staging/irda/drivers/kingsun-sir.c create mode 100644 drivers/staging/irda/drivers/ks959-sir.c create mode 100644 drivers/staging/irda/drivers/ksdazzle-sir.c create mode 100644 drivers/staging/irda/drivers/litelink-sir.c create mode 100644 drivers/staging/irda/drivers/ma600-sir.c create mode 100644 drivers/staging/irda/drivers/mcp2120-sir.c create mode 100644 drivers/staging/irda/drivers/mcs7780.c create mode 100644 drivers/staging/irda/drivers/mcs7780.h create mode 100644 drivers/staging/irda/drivers/nsc-ircc.c create mode 100644 drivers/staging/irda/drivers/nsc-ircc.h create mode 100644 drivers/staging/irda/drivers/old_belkin-sir.c create mode 100644 drivers/staging/irda/drivers/pxaficp_ir.c create mode 100644 drivers/staging/irda/drivers/sa1100_ir.c create mode 100644 drivers/staging/irda/drivers/sh_sir.c create mode 100644 drivers/staging/irda/drivers/sir-dev.h create mode 100644 drivers/staging/irda/drivers/sir_dev.c create mode 100644 drivers/staging/irda/drivers/sir_dongle.c create mode 100644 drivers/staging/irda/drivers/smsc-ircc2.c create mode 100644 drivers/staging/irda/drivers/smsc-ircc2.h create mode 100644 drivers/staging/irda/drivers/smsc-sio.h create mode 100644 drivers/staging/irda/drivers/stir4200.c create mode 100644 drivers/staging/irda/drivers/tekram-sir.c create mode 100644 drivers/staging/irda/drivers/toim3232-sir.c create mode 100644 drivers/staging/irda/drivers/via-ircc.c create mode 100644 drivers/staging/irda/drivers/via-ircc.h create mode 100644 drivers/staging/irda/drivers/vlsi_ir.c create mode 100644 drivers/staging/irda/drivers/vlsi_ir.h create mode 100644 drivers/staging/irda/drivers/w83977af.h create mode 100644 drivers/staging/irda/drivers/w83977af_ir.c create mode 100644 drivers/staging/irda/drivers/w83977af_ir.h (limited to 'drivers/net') diff --git a/drivers/net/Makefile b/drivers/net/Makefile index b2f6556d8848..8dff900085d6 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -45,7 +45,6 @@ obj-$(CONFIG_ETHERNET) += ethernet/ obj-$(CONFIG_FDDI) += fddi/ obj-$(CONFIG_HIPPI) += hippi/ obj-$(CONFIG_HAMRADIO) += hamradio/ -obj-$(CONFIG_IRDA) += irda/ obj-$(CONFIG_PLIP) += plip/ obj-$(CONFIG_PPP) += ppp/ obj-$(CONFIG_PPP_ASYNC) += ppp/ diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig deleted file mode 100644 index e070e1222733..000000000000 --- a/drivers/net/irda/Kconfig +++ /dev/null @@ -1,398 +0,0 @@ -menu "Infrared-port device drivers" - depends on IRDA!=n - -comment "SIR device drivers" - -config IRTTY_SIR - tristate "IrTTY (uses Linux serial driver)" - depends on IRDA && TTY - help - Say Y here if you want to build support for the IrTTY line - discipline. To compile it as a module, choose M here: the module - will be called irtty-sir. IrTTY makes it possible to use Linux's - own serial driver for all IrDA ports that are 16550 compatible. - Most IrDA chips are 16550 compatible so you should probably say Y - to this option. Using IrTTY will however limit the speed of the - connection to 115200 bps (IrDA SIR mode). - - If unsure, say Y. - -config BFIN_SIR - tristate "Blackfin SIR on UART" - depends on BLACKFIN && IRDA - default n - help - Say Y here if your want to enable SIR function on Blackfin UART - devices. - - To activate this driver you can start irattach like: - "irattach irda0 -s" - - Saying M, it will be built as a module named bfin_sir. - - Note that you need to turn off one of the serial drivers for SIR - to use that UART. - -config BFIN_SIR0 - bool "Blackfin SIR on UART0" - depends on BFIN_SIR && !SERIAL_BFIN_UART0 - -config BFIN_SIR1 - bool "Blackfin SIR on UART1" - depends on BFIN_SIR && !SERIAL_BFIN_UART1 && (!BF531 && !BF532 && !BF533 && !BF561) - -config BFIN_SIR2 - bool "Blackfin SIR on UART2" - depends on BFIN_SIR && !SERIAL_BFIN_UART2 && (BF54x || BF538 || BF539) - -config BFIN_SIR3 - bool "Blackfin SIR on UART3" - depends on BFIN_SIR && !SERIAL_BFIN_UART3 && (BF54x) - -choice - prompt "SIR Mode" - depends on BFIN_SIR - default SIR_BFIN_DMA - -config SIR_BFIN_DMA - bool "DMA mode" - depends on !DMA_UNCACHED_NONE - -config SIR_BFIN_PIO - bool "PIO mode" -endchoice - -config SH_SIR - tristate "SuperH SIR on UART" - depends on IRDA && SUPERH && \ - (CPU_SUBTYPE_SH7722 || CPU_SUBTYPE_SH7723 || \ - CPU_SUBTYPE_SH7724) - default n - help - Say Y here if your want to enable SIR function on SuperH UART - devices. - -comment "Dongle support" - -config DONGLE - bool "Serial dongle support" - depends on IRTTY_SIR - help - Say Y here if you have an infrared device that connects to your - computer's serial port. These devices are called dongles. Then say Y - or M to the driver for your particular dongle below. - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about serial dongles. - -config ESI_DONGLE - tristate "ESI JetEye PC dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Extended Systems - JetEye PC dongle. To compile it as a module, choose M here. The ESI - dongle attaches to the normal 9-pin serial port connector, and can - currently only be used by IrTTY. To activate support for ESI - dongles you will have to start irattach like this: - "irattach -d esi". - -config ACTISYS_DONGLE - tristate "ACTiSYS IR-220L and IR220L+ dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the ACTiSYS IR-220L and - IR220L+ dongles. To compile it as a module, choose M here. The - ACTiSYS dongles attaches to the normal 9-pin serial port connector, - and can currently only be used by IrTTY. To activate support for - ACTiSYS dongles you will have to start irattach like this: - "irattach -d actisys" or "irattach -d actisys+". - -config TEKRAM_DONGLE - tristate "Tekram IrMate 210B dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Tekram IrMate 210B - dongle. To compile it as a module, choose M here. The Tekram dongle - attaches to the normal 9-pin serial port connector, and can - currently only be used by IrTTY. To activate support for Tekram - dongles you will have to start irattach like this: - "irattach -d tekram". - -config TOIM3232_DONGLE - tristate "TOIM3232 IrDa dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Vishay/Temic - TOIM3232 and TOIM4232 based dongles. - To compile it as a module, choose M here. - -config LITELINK_DONGLE - tristate "Parallax LiteLink dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Parallax Litelink - dongle. To compile it as a module, choose M here. The Parallax - dongle attaches to the normal 9-pin serial port connector, and can - currently only be used by IrTTY. To activate support for Parallax - dongles you will have to start irattach like this: - "irattach -d litelink". - -config MA600_DONGLE - tristate "Mobile Action MA600 dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Mobile Action MA600 - dongle. To compile it as a module, choose M here. The MA600 dongle - attaches to the normal 9-pin serial port connector, and can - currently only be used by IrTTY. The driver should also support - the MA620 USB version of the dongle, if the integrated USB-to-RS232 - converter is supported by usbserial. To activate support for - MA600 dongle you will have to start irattach like this: - "irattach -d ma600". - -config GIRBIL_DONGLE - tristate "Greenwich GIrBIL dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Greenwich GIrBIL - dongle. If you want to compile it as a module, choose M here. - The Greenwich dongle attaches to the normal 9-pin serial port - connector, and can currently only be used by IrTTY. To activate - support for Greenwich dongles you will have to start irattach - like this: "irattach -d girbil". - -config MCP2120_DONGLE - tristate "Microchip MCP2120" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Microchip MCP2120 - dongle. If you want to compile it as a module, choose M here. - The MCP2120 dongle attaches to the normal 9-pin serial port - connector, and can currently only be used by IrTTY. To activate - support for MCP2120 dongles you will have to start irattach - like this: "irattach -d mcp2120". - - You must build this dongle yourself. For more information see: - - -config OLD_BELKIN_DONGLE - tristate "Old Belkin dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Adaptec Airport 1000 - and 2000 dongles. If you want to compile it as a module, choose - M here. Some information is contained in the comments - at the top of . - -config ACT200L_DONGLE - tristate "ACTiSYS IR-200L dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the ACTiSYS IR-200L - dongle. If you want to compile it as a module, choose M here. - The ACTiSYS IR-200L dongle attaches to the normal 9-pin serial - port connector, and can currently only be used by IrTTY. - To activate support for ACTiSYS IR-200L dongle you will have to - start irattach like this: "irattach -d act200l". - -config KINGSUN_DONGLE - tristate "KingSun/DonShine DS-620 IrDA-USB dongle" - depends on IRDA && USB - help - Say Y or M here if you want to build support for the KingSun/DonShine - DS-620 IrDA-USB bridge device driver. - - This USB bridge does not conform to the IrDA-USB device class - specification, and therefore needs its own specific driver. This - dongle supports SIR speed only (9600 bps). - - To compile it as a module, choose M here: the module will be called - kingsun-sir. - -config KSDAZZLE_DONGLE - tristate "KingSun Dazzle IrDA-USB dongle" - depends on IRDA && USB - help - Say Y or M here if you want to build support for the KingSun Dazzle - IrDA-USB bridge device driver. - - This USB bridge does not conform to the IrDA-USB device class - specification, and therefore needs its own specific driver. This - dongle supports SIR speeds only (9600 through 115200 bps). - - To compile it as a module, choose M here: the module will be called - ksdazzle-sir. - -config KS959_DONGLE - tristate "KingSun KS-959 IrDA-USB dongle" - depends on IRDA && USB - help - Say Y or M here if you want to build support for the KingSun KS-959 - IrDA-USB bridge device driver. - - This USB bridge does not conform to the IrDA-USB device class - specification, and therefore needs its own specific driver. This - dongle supports SIR speeds only (9600 through 57600 bps). - - To compile it as a module, choose M here: the module will be called - ks959-sir. - -comment "FIR device drivers" - -config USB_IRDA - tristate "IrDA USB dongles" - depends on IRDA && USB - select FW_LOADER - ---help--- - Say Y here if you want to build support for the USB IrDA FIR Dongle - device driver. To compile it as a module, choose M here: the module - will be called irda-usb. IrDA-USB support the various IrDA USB - dongles available and most of their peculiarities. Those dongles - plug in the USB port of your computer, are plug and play, and - support SIR and FIR (4Mbps) speeds. On the other hand, those - dongles tend to be less efficient than a FIR chipset. - - Please note that the driver is still experimental. And of course, - you will need both USB and IrDA support in your kernel... - -config SIGMATEL_FIR - tristate "SigmaTel STIr4200 bridge" - depends on IRDA && USB - select CRC32 - ---help--- - Say Y here if you want to build support for the SigmaTel STIr4200 - USB IrDA FIR bridge device driver. - - USB bridge based on the SigmaTel STIr4200 don't conform to the - IrDA-USB device class specification, and therefore need their - own specific driver. Those dongles support SIR and FIR (4Mbps) - speeds. - - To compile it as a module, choose M here: the module will be called - stir4200. - -config NSC_FIR - tristate "NSC PC87108/PC87338" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build support for the NSC PC87108 and - PC87338 IrDA chipsets. This driver supports SIR, - MIR and FIR (4Mbps) speeds. - - To compile it as a module, choose M here: the module will be called - nsc-ircc. - -config WINBOND_FIR - tristate "Winbond W83977AF (IR)" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build IrDA support for the Winbond - W83977AF super-io chipset. This driver should be used for the IrDA - chipset in the Corel NetWinder. The driver supports SIR, MIR and - FIR (4Mbps) speeds. - - To compile it as a module, choose M here: the module will be called - w83977af_ir. - -config TOSHIBA_FIR - tristate "Toshiba Type-O IR Port" - depends on IRDA && PCI && !64BIT && VIRT_TO_BUS - help - Say Y here if you want to build support for the Toshiba Type-O IR - and Donau oboe chipsets. These chipsets are used by the Toshiba - Libretto 100/110CT, Tecra 8100, Portege 7020 and many more laptops. - To compile it as a module, choose M here: the module will be called - donauboe. - -config AU1000_FIR - tristate "Alchemy IrDA SIR/FIR" - depends on IRDA && MIPS_ALCHEMY - help - Say Y/M here to build support the IrDA peripheral on the - Alchemy Au1000 and Au1100 SoCs. - Say M to build a module; it will be called au1k_ir.ko - -config SMC_IRCC_FIR - tristate "SMSC IrCC" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build support for the SMC Infrared - Communications Controller. It is used in a wide variety of - laptops (Fujitsu, Sony, Compaq and some Toshiba). - To compile it as a module, choose M here: the module will be called - smsc-ircc2.o. - -config ALI_FIR - tristate "ALi M5123 FIR" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build support for the ALi M5123 FIR - Controller. The ALi M5123 FIR Controller is embedded in ALi M1543C, - M1535, M1535D, M1535+, M1535D South Bridge. This driver supports - SIR, MIR and FIR (4Mbps) speeds. - - To compile it as a module, choose M here: the module will be called - ali-ircc. - -config VLSI_FIR - tristate "VLSI 82C147 SIR/MIR/FIR" - depends on IRDA && PCI - help - Say Y here if you want to build support for the VLSI 82C147 - PCI-IrDA Controller. This controller is used by the HP OmniBook 800 - and 5500 notebooks. The driver provides support for SIR, MIR and - FIR (4Mbps) speeds. - - To compile it as a module, choose M here: the module will be called - vlsi_ir. - -config SA1100_FIR - tristate "SA1100 Internal IR" - depends on ARCH_SA1100 && IRDA && DMA_SA11X0 - -config VIA_FIR - tristate "VIA VT8231/VT1211 SIR/MIR/FIR" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build support for the VIA VT8231 - and VIA VT1211 IrDA controllers, found on the motherboards using - those VIA chipsets. To use this controller, you will need - to plug a specific 5 pins FIR IrDA dongle in the specific - motherboard connector. The driver provides support for SIR, MIR - and FIR (4Mbps) speeds. - - You will need to specify the 'dongle_id' module parameter to - indicate the FIR dongle attached to the controller. - - To compile it as a module, choose M here: the module will be called - via-ircc. - -config PXA_FICP - tristate "Intel PXA2xx Internal FICP" - depends on ARCH_PXA && IRDA - help - Say Y or M here if you want to build support for the PXA2xx - built-in IRDA interface which can support both SIR and FIR. - This driver relies on platform specific helper routines so - available capabilities may vary from one PXA2xx target to - another. - -config MCS_FIR - tristate "MosChip MCS7780 IrDA-USB dongle" - depends on IRDA && USB - select CRC32 - help - Say Y or M here if you want to build support for the MosChip - MCS7780 IrDA-USB bridge device driver. - - USB bridge based on the MosChip MCS7780 don't conform to the - IrDA-USB device class specification, and therefore need their - own specific driver. Those dongles support SIR and FIR (4Mbps) - speeds. - - To compile it as a module, choose M here: the module will be called - mcs7780. - -endmenu - diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile deleted file mode 100644 index 4c344433dae5..000000000000 --- a/drivers/net/irda/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -# -# Makefile for the Linux IrDA infrared port device drivers. -# -# 9 Aug 2000, Christoph Hellwig -# Rewritten to use lists instead of if-statements. -# - -# FIR drivers -obj-$(CONFIG_USB_IRDA) += irda-usb.o -obj-$(CONFIG_SIGMATEL_FIR) += stir4200.o -obj-$(CONFIG_NSC_FIR) += nsc-ircc.o -obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o -obj-$(CONFIG_SA1100_FIR) += sa1100_ir.o -obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o -obj-$(CONFIG_SMC_IRCC_FIR) += smsc-ircc2.o -obj-$(CONFIG_ALI_FIR) += ali-ircc.o -obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o -obj-$(CONFIG_VIA_FIR) += via-ircc.o -obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o -obj-$(CONFIG_MCS_FIR) += mcs7780.o -obj-$(CONFIG_AU1000_FIR) += au1k_ir.o -# SIR drivers -obj-$(CONFIG_IRTTY_SIR) += irtty-sir.o sir-dev.o -obj-$(CONFIG_BFIN_SIR) += bfin_sir.o -obj-$(CONFIG_SH_SIR) += sh_sir.o -# dongle drivers for SIR drivers -obj-$(CONFIG_ESI_DONGLE) += esi-sir.o -obj-$(CONFIG_TEKRAM_DONGLE) += tekram-sir.o -obj-$(CONFIG_ACTISYS_DONGLE) += actisys-sir.o -obj-$(CONFIG_LITELINK_DONGLE) += litelink-sir.o -obj-$(CONFIG_GIRBIL_DONGLE) += girbil-sir.o -obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin-sir.o -obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o -obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o -obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o -obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o -obj-$(CONFIG_KINGSUN_DONGLE) += kingsun-sir.o -obj-$(CONFIG_KSDAZZLE_DONGLE) += ksdazzle-sir.o -obj-$(CONFIG_KS959_DONGLE) += ks959-sir.o - -# The SIR helper module -sir-dev-objs := sir_dev.o sir_dongle.o diff --git a/drivers/net/irda/act200l-sir.c b/drivers/net/irda/act200l-sir.c deleted file mode 100644 index e8917511e1aa..000000000000 --- a/drivers/net/irda/act200l-sir.c +++ /dev/null @@ -1,250 +0,0 @@ -/********************************************************************* - * - * Filename: act200l.c - * Version: 0.8 - * Description: Implementation for the ACTiSYS ACT-IR200L dongle - * Status: Experimental. - * Author: SHIMIZU Takuya - * Created at: Fri Aug 3 17:35:42 2001 - * Modified at: Fri Aug 17 10:22:40 2001 - * Modified by: SHIMIZU Takuya - * - * Copyright (c) 2001 SHIMIZU Takuya, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int act200l_reset(struct sir_dev *dev); -static int act200l_open(struct sir_dev *dev); -static int act200l_close(struct sir_dev *dev); -static int act200l_change_speed(struct sir_dev *dev, unsigned speed); - -/* Regsiter 0: Control register #1 */ -#define ACT200L_REG0 0x00 -#define ACT200L_TXEN 0x01 /* Enable transmitter */ -#define ACT200L_RXEN 0x02 /* Enable receiver */ - -/* Register 1: Control register #2 */ -#define ACT200L_REG1 0x10 -#define ACT200L_LODB 0x01 /* Load new baud rate count value */ -#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */ - -/* Register 4: Output Power register */ -#define ACT200L_REG4 0x40 -#define ACT200L_OP0 0x01 /* Enable LED1C output */ -#define ACT200L_OP1 0x02 /* Enable LED2C output */ -#define ACT200L_BLKR 0x04 - -/* Register 5: Receive Mode register */ -#define ACT200L_REG5 0x50 -#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */ - -/* Register 6: Receive Sensitivity register #1 */ -#define ACT200L_REG6 0x60 -#define ACT200L_RS0 0x01 /* receive threshold bit 0 */ -#define ACT200L_RS1 0x02 /* receive threshold bit 1 */ - -/* Register 7: Receive Sensitivity register #2 */ -#define ACT200L_REG7 0x70 -#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */ - -/* Register 8,9: Baud Rate Dvider register #1,#2 */ -#define ACT200L_REG8 0x80 -#define ACT200L_REG9 0x90 - -#define ACT200L_2400 0x5f -#define ACT200L_9600 0x17 -#define ACT200L_19200 0x0b -#define ACT200L_38400 0x05 -#define ACT200L_57600 0x03 -#define ACT200L_115200 0x01 - -/* Register 13: Control register #3 */ -#define ACT200L_REG13 0xd0 -#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */ - -/* Register 15: Status register */ -#define ACT200L_REG15 0xf0 - -/* Register 21: Control register #4 */ -#define ACT200L_REG21 0x50 -#define ACT200L_EXCK 0x02 /* Disable clock output driver */ -#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */ - -static struct dongle_driver act200l = { - .owner = THIS_MODULE, - .driver_name = "ACTiSYS ACT-IR200L", - .type = IRDA_ACT200L_DONGLE, - .open = act200l_open, - .close = act200l_close, - .reset = act200l_reset, - .set_speed = act200l_change_speed, -}; - -static int __init act200l_sir_init(void) -{ - return irda_register_dongle(&act200l); -} - -static void __exit act200l_sir_cleanup(void) -{ - irda_unregister_dongle(&act200l); -} - -static int act200l_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power on the dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Set the speeds we can accept */ - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - qos->min_turn_time.bits = 0x03; - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int act200l_close(struct sir_dev *dev) -{ - /* Power off the dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function act200l_change_speed (dev, speed) - * - * Set the speed for the ACTiSYS ACT-IR200L type dongle. - * - */ -static int act200l_change_speed(struct sir_dev *dev, unsigned speed) -{ - u8 control[3]; - int ret = 0; - - /* Clear DTR and set RTS to enter command mode */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - switch (speed) { - default: - ret = -EINVAL; - /* fall through */ - case 9600: - control[0] = ACT200L_REG8 | (ACT200L_9600 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_9600 >> 4) & 0x0f); - break; - case 19200: - control[0] = ACT200L_REG8 | (ACT200L_19200 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_19200 >> 4) & 0x0f); - break; - case 38400: - control[0] = ACT200L_REG8 | (ACT200L_38400 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_38400 >> 4) & 0x0f); - break; - case 57600: - control[0] = ACT200L_REG8 | (ACT200L_57600 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_57600 >> 4) & 0x0f); - break; - case 115200: - control[0] = ACT200L_REG8 | (ACT200L_115200 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f); - break; - } - control[2] = ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE; - - /* Write control bytes */ - sirdev_raw_write(dev, control, 3); - msleep(5); - - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - dev->speed = speed; - return ret; -} - -/* - * Function act200l_reset (driver) - * - * Reset the ACTiSYS ACT-IR200L type dongle. - */ - -#define ACT200L_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1) -#define ACT200L_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2) - -static int act200l_reset(struct sir_dev *dev) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - static const u8 control[9] = { - ACT200L_REG15, - ACT200L_REG13 | ACT200L_SHDW, - ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL, - ACT200L_REG13, - ACT200L_REG7 | ACT200L_ENPOS, - ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1, - ACT200L_REG5 | ACT200L_RWIDL, - ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR, - ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN - }; - int ret = 0; - - switch (state) { - case SIRDEV_STATE_DONGLE_RESET: - /* Reset the dongle : set RTS low for 25 ms */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - state = ACT200L_STATE_WAIT1_RESET; - delay = 50; - break; - - case ACT200L_STATE_WAIT1_RESET: - /* Clear DTR and set RTS to enter command mode */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - udelay(25); /* better wait for some short while */ - - /* Write control bytes */ - sirdev_raw_write(dev, control, sizeof(control)); - state = ACT200L_STATE_WAIT2_RESET; - delay = 15; - break; - - case ACT200L_STATE_WAIT2_RESET: - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - dev->speed = 9600; - break; - default: - net_err_ratelimited("%s(), unknown state %d\n", - __func__, state); - ret = -1; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -MODULE_AUTHOR("SHIMIZU Takuya "); -MODULE_DESCRIPTION("ACTiSYS ACT-IR200L dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-10"); /* IRDA_ACT200L_DONGLE */ - -module_init(act200l_sir_init); -module_exit(act200l_sir_cleanup); diff --git a/drivers/net/irda/actisys-sir.c b/drivers/net/irda/actisys-sir.c deleted file mode 100644 index e224b8b99517..000000000000 --- a/drivers/net/irda/actisys-sir.c +++ /dev/null @@ -1,245 +0,0 @@ -/********************************************************************* - * - * Filename: actisys.c - * Version: 1.1 - * Description: Implementation for the ACTiSYS IR-220L and IR-220L+ - * dongles - * Status: Beta. - * Authors: Dag Brattli (initially) - * Jean Tourrilhes (new version) - * Martin Diehl (new version for sir_dev) - * Created at: Wed Oct 21 20:02:35 1998 - * Modified at: Sun Oct 27 22:02:13 2002 - * Modified by: Martin Diehl - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 1999 Jean Tourrilhes - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -/* - * Changelog - * - * 0.8 -> 0.9999 - Jean - * o New initialisation procedure : much safer and correct - * o New procedure the change speed : much faster and simpler - * o Other cleanups & comments - * Thanks to Lichen Wang @ Actisys for his excellent help... - * - * 1.0 -> 1.1 - Martin Diehl - * modified for new sir infrastructure - */ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -/* - * Define the timing of the pulses we send to the dongle (to reset it, and - * to toggle speeds). Basically, the limit here is the propagation speed of - * the signals through the serial port, the dongle being much faster. Any - * serial port support 115 kb/s, so we are sure that pulses 8.5 us wide can - * go through cleanly . If you are on the wild side, you can try to lower - * this value (Actisys recommended me 2 us, and 0 us work for me on a P233!) - */ -#define MIN_DELAY 10 /* 10 us to be on the conservative side */ - -static int actisys_open(struct sir_dev *); -static int actisys_close(struct sir_dev *); -static int actisys_change_speed(struct sir_dev *, unsigned); -static int actisys_reset(struct sir_dev *); - -/* These are the baudrates supported, in the order available */ -/* Note : the 220L doesn't support 38400, but we will fix that below */ -static unsigned baud_rates[] = { 9600, 19200, 57600, 115200, 38400 }; - -#define MAX_SPEEDS ARRAY_SIZE(baud_rates) - -static struct dongle_driver act220l = { - .owner = THIS_MODULE, - .driver_name = "Actisys ACT-220L", - .type = IRDA_ACTISYS_DONGLE, - .open = actisys_open, - .close = actisys_close, - .reset = actisys_reset, - .set_speed = actisys_change_speed, -}; - -static struct dongle_driver act220l_plus = { - .owner = THIS_MODULE, - .driver_name = "Actisys ACT-220L+", - .type = IRDA_ACTISYS_PLUS_DONGLE, - .open = actisys_open, - .close = actisys_close, - .reset = actisys_reset, - .set_speed = actisys_change_speed, -}; - -static int __init actisys_sir_init(void) -{ - int ret; - - /* First, register an Actisys 220L dongle */ - ret = irda_register_dongle(&act220l); - if (ret < 0) - return ret; - - /* Now, register an Actisys 220L+ dongle */ - ret = irda_register_dongle(&act220l_plus); - if (ret < 0) { - irda_unregister_dongle(&act220l); - return ret; - } - return 0; -} - -static void __exit actisys_sir_cleanup(void) -{ - /* We have to remove both dongles */ - irda_unregister_dongle(&act220l_plus); - irda_unregister_dongle(&act220l); -} - -static int actisys_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Set the speeds we can accept */ - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - - /* Remove support for 38400 if this is not a 220L+ dongle */ - if (dev->dongle_drv->type == IRDA_ACTISYS_DONGLE) - qos->baud_rate.bits &= ~IR_38400; - - qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int actisys_close(struct sir_dev *dev) -{ - /* Power off the dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function actisys_change_speed (task) - * - * Change speed of the ACTiSYS IR-220L and IR-220L+ type IrDA dongles. - * To cycle through the available baud rates, pulse RTS low for a few us. - * - * First, we reset the dongle to always start from a known state. - * Then, we cycle through the speeds by pulsing RTS low and then up. - * The dongle allow us to pulse quite fast, se we can set speed in one go, - * which is must faster ( < 100 us) and less complex than what is found - * in some other dongle drivers... - * Note that even if the new speed is the same as the current speed, - * we reassert the speed. This make sure that things are all right, - * and it's fast anyway... - * By the way, this function will work for both type of dongles, - * because the additional speed is at the end of the sequence... - */ -static int actisys_change_speed(struct sir_dev *dev, unsigned speed) -{ - int ret = 0; - int i = 0; - - pr_debug("%s(), speed=%d (was %d)\n", __func__, speed, dev->speed); - - /* dongle was already resetted from irda_request state machine, - * we are in known state (dongle default) - */ - - /* - * Now, we can set the speed requested. Send RTS pulses until we - * reach the target speed - */ - for (i = 0; i < MAX_SPEEDS; i++) { - if (speed == baud_rates[i]) { - dev->speed = speed; - break; - } - /* Set RTS low for 10 us */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - udelay(MIN_DELAY); - - /* Set RTS high for 10 us */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - udelay(MIN_DELAY); - } - - /* Check if life is sweet... */ - if (i >= MAX_SPEEDS) { - actisys_reset(dev); - ret = -EINVAL; /* This should not happen */ - } - - /* Basta lavoro, on se casse d'ici... */ - return ret; -} - -/* - * Function actisys_reset (task) - * - * Reset the Actisys type dongle. Warning, this function must only be - * called with a process context! - * - * We need to do two things in this function : - * o first make sure that the dongle is in a state where it can operate - * o second put the dongle in a know state - * - * The dongle is powered of the RTS and DTR lines. In the dongle, there - * is a big capacitor to accommodate the current spikes. This capacitor - * takes a least 50 ms to be charged. In theory, the Bios set those lines - * up, so by the time we arrive here we should be set. It doesn't hurt - * to be on the conservative side, so we will wait... - * - * Then, we set the speed to 9600 b/s to get in a known state (see in - * change_speed for details). It is needed because the IrDA stack - * has tried to set the speed immediately after our first return, - * so before we can be sure the dongle is up and running. - */ - -static int actisys_reset(struct sir_dev *dev) -{ - /* Reset the dongle : set DTR low for 10 us */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - udelay(MIN_DELAY); - - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - dev->speed = 9600; /* That's the default */ - - return 0; -} - -MODULE_AUTHOR("Dag Brattli - Jean Tourrilhes "); -MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */ -MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */ - -module_init(actisys_sir_init); -module_exit(actisys_sir_cleanup); diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c deleted file mode 100644 index 35f198d83701..000000000000 --- a/drivers/net/irda/ali-ircc.c +++ /dev/null @@ -1,2218 +0,0 @@ -/********************************************************************* - * - * Filename: ali-ircc.h - * Version: 0.5 - * Description: Driver for the ALI M1535D and M1543C FIR Controller - * Status: Experimental. - * Author: Benjamin Kong - * Created at: 2000/10/16 03:46PM - * Modified at: 2001/1/3 02:55PM - * Modified by: Benjamin Kong - * Modified at: 2003/11/6 and support for ALi south-bridge chipsets M1563 - * Modified by: Clear Zhang - * - * Copyright (c) 2000 Benjamin Kong - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "ali-ircc.h" - -#define CHIP_IO_EXTENT 8 -#define BROKEN_DONGLE_ID - -#define ALI_IRCC_DRIVER_NAME "ali-ircc" - -/* Power Management */ -static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state); -static int ali_ircc_resume(struct platform_device *dev); - -static struct platform_driver ali_ircc_driver = { - .suspend = ali_ircc_suspend, - .resume = ali_ircc_resume, - .driver = { - .name = ALI_IRCC_DRIVER_NAME, - }, -}; - -/* Module parameters */ -static int qos_mtt_bits = 0x07; /* 1 ms or more */ - -/* Use BIOS settions by default, but user may supply module parameters */ -static unsigned int io[] = { ~0, ~0, ~0, ~0 }; -static unsigned int irq[] = { 0, 0, 0, 0 }; -static unsigned int dma[] = { 0, 0, 0, 0 }; - -static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info); -static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info); -static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info); - -/* These are the currently known ALi south-bridge chipsets, the only one difference - * is that M1543C doesn't support HP HDSL-3600 - */ -static ali_chip_t chips[] = -{ - { "M1543", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x43, ali_ircc_probe_53, ali_ircc_init_43 }, - { "M1535", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x53, ali_ircc_probe_53, ali_ircc_init_53 }, - { "M1563", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x63, ali_ircc_probe_53, ali_ircc_init_53 }, - { NULL } -}; - -/* Max 4 instances for now */ -static struct ali_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; - -/* Dongle Types */ -static char *dongle_types[] = { - "TFDS6000", - "HP HSDL-3600", - "HP HSDL-1100", - "No dongle connected", -}; - -/* Some prototypes */ -static int ali_ircc_open(int i, chipio_t *info); - -static int ali_ircc_close(struct ali_ircc_cb *self); - -static int ali_ircc_setup(chipio_t *info); -static int ali_ircc_is_receiving(struct ali_ircc_cb *self); -static int ali_ircc_net_open(struct net_device *dev); -static int ali_ircc_net_close(struct net_device *dev); -static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud); - -/* SIR function */ -static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self); -static void ali_ircc_sir_receive(struct ali_ircc_cb *self); -static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self); -static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len); -static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed); - -/* FIR function */ -static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, - struct net_device *dev); -static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 speed); -static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self); -static int ali_ircc_dma_receive(struct ali_ircc_cb *self); -static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self); -static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self); -static void ali_ircc_dma_xmit(struct ali_ircc_cb *self); - -/* My Function */ -static int ali_ircc_read_dongle_id (int i, chipio_t *info); -static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed); - -/* ALi chip function */ -static void SIR2FIR(int iobase); -static void FIR2SIR(int iobase); -static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable); - -/* - * Function ali_ircc_init () - * - * Initialize chip. Find out whay kinds of chips we are dealing with - * and their configuration registers address - */ -static int __init ali_ircc_init(void) -{ - ali_chip_t *chip; - chipio_t info; - int ret; - int cfg, cfg_base; - int reg, revision; - int i = 0; - - ret = platform_driver_register(&ali_ircc_driver); - if (ret) { - net_err_ratelimited("%s, Can't register driver!\n", - ALI_IRCC_DRIVER_NAME); - return ret; - } - - ret = -ENODEV; - - /* Probe for all the ALi chipsets we know about */ - for (chip= chips; chip->name; chip++, i++) - { - pr_debug("%s(), Probing for %s ...\n", __func__, chip->name); - - /* Try all config registers for this chip */ - for (cfg=0; cfg<2; cfg++) - { - cfg_base = chip->cfg[cfg]; - if (!cfg_base) - continue; - - memset(&info, 0, sizeof(chipio_t)); - info.cfg_base = cfg_base; - info.fir_base = io[i]; - info.dma = dma[i]; - info.irq = irq[i]; - - - /* Enter Configuration */ - outb(chip->entr1, cfg_base); - outb(chip->entr2, cfg_base); - - /* Select Logical Device 5 Registers (UART2) */ - outb(0x07, cfg_base); - outb(0x05, cfg_base+1); - - /* Read Chip Identification Register */ - outb(chip->cid_index, cfg_base); - reg = inb(cfg_base+1); - - if (reg == chip->cid_value) - { - pr_debug("%s(), Chip found at 0x%03x\n", - __func__, cfg_base); - - outb(0x1F, cfg_base); - revision = inb(cfg_base+1); - pr_debug("%s(), Found %s chip, revision=%d\n", - __func__, chip->name, revision); - - /* - * If the user supplies the base address, then - * we init the chip, if not we probe the values - * set by the BIOS - */ - if (io[i] < 2000) - { - chip->init(chip, &info); - } - else - { - chip->probe(chip, &info); - } - - if (ali_ircc_open(i, &info) == 0) - ret = 0; - i++; - } - else - { - pr_debug("%s(), No %s chip at 0x%03x\n", - __func__, chip->name, cfg_base); - } - /* Exit configuration */ - outb(0xbb, cfg_base); - } - } - - if (ret) - platform_driver_unregister(&ali_ircc_driver); - - return ret; -} - -/* - * Function ali_ircc_cleanup () - * - * Close all configured chips - * - */ -static void __exit ali_ircc_cleanup(void) -{ - int i; - - for (i=0; i < ARRAY_SIZE(dev_self); i++) { - if (dev_self[i]) - ali_ircc_close(dev_self[i]); - } - - platform_driver_unregister(&ali_ircc_driver); - -} - -static const struct net_device_ops ali_ircc_sir_ops = { - .ndo_open = ali_ircc_net_open, - .ndo_stop = ali_ircc_net_close, - .ndo_start_xmit = ali_ircc_sir_hard_xmit, - .ndo_do_ioctl = ali_ircc_net_ioctl, -}; - -static const struct net_device_ops ali_ircc_fir_ops = { - .ndo_open = ali_ircc_net_open, - .ndo_stop = ali_ircc_net_close, - .ndo_start_xmit = ali_ircc_fir_hard_xmit, - .ndo_do_ioctl = ali_ircc_net_ioctl, -}; - -/* - * Function ali_ircc_open (int i, chipio_t *inf) - * - * Open driver instance - * - */ -static int ali_ircc_open(int i, chipio_t *info) -{ - struct net_device *dev; - struct ali_ircc_cb *self; - int dongle_id; - int err; - - if (i >= ARRAY_SIZE(dev_self)) { - net_err_ratelimited("%s(), maximum number of supported chips reached!\n", - __func__); - return -ENOMEM; - } - - /* Set FIR FIFO and DMA Threshold */ - if ((ali_ircc_setup(info)) == -1) - return -1; - - dev = alloc_irdadev(sizeof(*self)); - if (dev == NULL) { - net_err_ratelimited("%s(), can't allocate memory for control block!\n", - __func__); - return -ENOMEM; - } - - self = netdev_priv(dev); - self->netdev = dev; - spin_lock_init(&self->lock); - - /* Need to store self somewhere */ - dev_self[i] = self; - self->index = i; - - /* Initialize IO */ - self->io.cfg_base = info->cfg_base; /* In ali_ircc_probe_53 assign */ - self->io.fir_base = info->fir_base; /* info->sir_base = info->fir_base */ - self->io.sir_base = info->sir_base; /* ALi SIR and FIR use the same address */ - self->io.irq = info->irq; - self->io.fir_ext = CHIP_IO_EXTENT; - self->io.dma = info->dma; - self->io.fifo_size = 16; /* SIR: 16, FIR: 32 Benjamin 2000/11/1 */ - - /* Reserve the ioports that we need */ - if (!request_region(self->io.fir_base, self->io.fir_ext, - ALI_IRCC_DRIVER_NAME)) { - net_warn_ratelimited("%s(), can't get iobase of 0x%03x\n", - __func__, self->io.fir_base); - err = -ENODEV; - goto err_out1; - } - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* The only value we must override it the baudrate */ - self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| - IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); // benjamin 2000/11/8 05:27PM - - self->qos.min_turn_time.bits = qos_mtt_bits; - - irda_qos_bits_to_value(&self->qos); - - /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ - self->rx_buff.truesize = 14384; - self->tx_buff.truesize = 14384; - - /* Allocate memory if needed */ - self->rx_buff.head = - dma_zalloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (self->rx_buff.head == NULL) { - err = -ENOMEM; - goto err_out2; - } - - self->tx_buff.head = - dma_zalloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (self->tx_buff.head == NULL) { - err = -ENOMEM; - goto err_out3; - } - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Tx queue info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Override the network functions we need to use */ - dev->netdev_ops = &ali_ircc_sir_ops; - - err = register_netdev(dev); - if (err) { - net_err_ratelimited("%s(), register_netdev() failed!\n", - __func__); - goto err_out4; - } - net_info_ratelimited("IrDA: Registered device %s\n", dev->name); - - /* Check dongle id */ - dongle_id = ali_ircc_read_dongle_id(i, info); - net_info_ratelimited("%s(), %s, Found dongle: %s\n", - __func__, ALI_IRCC_DRIVER_NAME, - dongle_types[dongle_id]); - - self->io.dongle_id = dongle_id; - - - return 0; - - err_out4: - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - err_out3: - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - err_out2: - release_region(self->io.fir_base, self->io.fir_ext); - err_out1: - dev_self[i] = NULL; - free_netdev(dev); - return err; -} - - -/* - * Function ali_ircc_close (self) - * - * Close driver instance - * - */ -static int __exit ali_ircc_close(struct ali_ircc_cb *self) -{ - int iobase; - - IRDA_ASSERT(self != NULL, return -1;); - - iobase = self->io.fir_base; - - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Release the PORT that this driver is using */ - pr_debug("%s(), Releasing Region %03x\n", __func__, self->io.fir_base); - release_region(self->io.fir_base, self->io.fir_ext); - - if (self->tx_buff.head) - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - - if (self->rx_buff.head) - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - dev_self[self->index] = NULL; - free_netdev(self->netdev); - - - return 0; -} - -/* - * Function ali_ircc_init_43 (chip, info) - * - * Initialize the ALi M1543 chip. - */ -static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info) -{ - /* All controller information like I/O address, DMA channel, IRQ - * are set by BIOS - */ - - return 0; -} - -/* - * Function ali_ircc_init_53 (chip, info) - * - * Initialize the ALi M1535 chip. - */ -static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info) -{ - /* All controller information like I/O address, DMA channel, IRQ - * are set by BIOS - */ - - return 0; -} - -/* - * Function ali_ircc_probe_53 (chip, info) - * - * Probes for the ALi M1535D or M1535 - */ -static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int hi, low, reg; - - - /* Enter Configuration */ - outb(chip->entr1, cfg_base); - outb(chip->entr2, cfg_base); - - /* Select Logical Device 5 Registers (UART2) */ - outb(0x07, cfg_base); - outb(0x05, cfg_base+1); - - /* Read address control register */ - outb(0x60, cfg_base); - hi = inb(cfg_base+1); - outb(0x61, cfg_base); - low = inb(cfg_base+1); - info->fir_base = (hi<<8) + low; - - info->sir_base = info->fir_base; - - pr_debug("%s(), probing fir_base=0x%03x\n", __func__, info->fir_base); - - /* Read IRQ control register */ - outb(0x70, cfg_base); - reg = inb(cfg_base+1); - info->irq = reg & 0x0f; - pr_debug("%s(), probing irq=%d\n", __func__, info->irq); - - /* Read DMA channel */ - outb(0x74, cfg_base); - reg = inb(cfg_base+1); - info->dma = reg & 0x07; - - if(info->dma == 0x04) - net_warn_ratelimited("%s(), No DMA channel assigned !\n", - __func__); - else - pr_debug("%s(), probing dma=%d\n", __func__, info->dma); - - /* Read Enabled Status */ - outb(0x30, cfg_base); - reg = inb(cfg_base+1); - info->enabled = (reg & 0x80) && (reg & 0x01); - pr_debug("%s(), probing enabled=%d\n", __func__, info->enabled); - - /* Read Power Status */ - outb(0x22, cfg_base); - reg = inb(cfg_base+1); - info->suspended = (reg & 0x20); - pr_debug("%s(), probing suspended=%d\n", __func__, info->suspended); - - /* Exit configuration */ - outb(0xbb, cfg_base); - - - return 0; -} - -/* - * Function ali_ircc_setup (info) - * - * Set FIR FIFO and DMA Threshold - * Returns non-negative on success. - * - */ -static int ali_ircc_setup(chipio_t *info) -{ - unsigned char tmp; - int version; - int iobase = info->fir_base; - - - /* Locking comments : - * Most operations here need to be protected. We are called before - * the device instance is created in ali_ircc_open(), therefore - * nobody can bother us - Jean II */ - - /* Switch to FIR space */ - SIR2FIR(iobase); - - /* Master Reset */ - outb(0x40, iobase+FIR_MCR); // benjamin 2000/11/30 11:45AM - - /* Read FIR ID Version Register */ - switch_bank(iobase, BANK3); - version = inb(iobase+FIR_ID_VR); - - /* Should be 0x00 in the M1535/M1535D */ - if(version != 0x00) - { - net_err_ratelimited("%s, Wrong chip version %02x\n", - ALI_IRCC_DRIVER_NAME, version); - return -1; - } - - /* Set FIR FIFO Threshold Register */ - switch_bank(iobase, BANK1); - outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); - - /* Set FIR DMA Threshold Register */ - outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); - - /* CRC enable */ - switch_bank(iobase, BANK2); - outb(inb(iobase+FIR_IRDA_CR) | IRDA_CR_CRC, iobase+FIR_IRDA_CR); - - /* NDIS driver set TX Length here BANK2 Alias 3, Alias4*/ - - /* Switch to Bank 0 */ - switch_bank(iobase, BANK0); - - tmp = inb(iobase+FIR_LCR_B); - tmp &=~0x20; // disable SIP - tmp |= 0x80; // these two steps make RX mode - tmp &= 0xbf; - outb(tmp, iobase+FIR_LCR_B); - - /* Disable Interrupt */ - outb(0x00, iobase+FIR_IER); - - - /* Switch to SIR space */ - FIR2SIR(iobase); - - net_info_ratelimited("%s, driver loaded (Benjamin Kong)\n", - ALI_IRCC_DRIVER_NAME); - - /* Enable receive interrupts */ - // outb(UART_IER_RDI, iobase+UART_IER); //benjamin 2000/11/23 01:25PM - // Turn on the interrupts in ali_ircc_net_open - - - return 0; -} - -/* - * Function ali_ircc_read_dongle_id (int index, info) - * - * Try to read dongle identification. This procedure needs to be executed - * once after power-on/reset. It also needs to be used whenever you suspect - * that the user may have plugged/unplugged the IrDA Dongle. - */ -static int ali_ircc_read_dongle_id (int i, chipio_t *info) -{ - int dongle_id, reg; - int cfg_base = info->cfg_base; - - - /* Enter Configuration */ - outb(chips[i].entr1, cfg_base); - outb(chips[i].entr2, cfg_base); - - /* Select Logical Device 5 Registers (UART2) */ - outb(0x07, cfg_base); - outb(0x05, cfg_base+1); - - /* Read Dongle ID */ - outb(0xf0, cfg_base); - reg = inb(cfg_base+1); - dongle_id = ((reg>>6)&0x02) | ((reg>>5)&0x01); - pr_debug("%s(), probing dongle_id=%d, dongle_types=%s\n", - __func__, dongle_id, dongle_types[dongle_id]); - - /* Exit configuration */ - outb(0xbb, cfg_base); - - - return dongle_id; -} - -/* - * Function ali_ircc_interrupt (irq, dev_id, regs) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t ali_ircc_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct ali_ircc_cb *self; - int ret; - - - self = netdev_priv(dev); - - spin_lock(&self->lock); - - /* Dispatch interrupt handler for the current speed */ - if (self->io.speed > 115200) - ret = ali_ircc_fir_interrupt(self); - else - ret = ali_ircc_sir_interrupt(self); - - spin_unlock(&self->lock); - - return ret; -} -/* - * Function ali_ircc_fir_interrupt(irq, struct ali_ircc_cb *self) - * - * Handle MIR/FIR interrupt - * - */ -static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self) -{ - __u8 eir, OldMessageCount; - int iobase, tmp; - - - iobase = self->io.fir_base; - - switch_bank(iobase, BANK0); - self->InterruptID = inb(iobase+FIR_IIR); - self->BusStatus = inb(iobase+FIR_BSR); - - OldMessageCount = (self->LineStatus + 1) & 0x07; - self->LineStatus = inb(iobase+FIR_LSR); - //self->ier = inb(iobase+FIR_IER); 2000/12/1 04:32PM - eir = self->InterruptID & self->ier; /* Mask out the interesting ones */ - - pr_debug("%s(), self->InterruptID = %x\n", __func__, self->InterruptID); - pr_debug("%s(), self->LineStatus = %x\n", __func__, self->LineStatus); - pr_debug("%s(), self->ier = %x\n", __func__, self->ier); - pr_debug("%s(), eir = %x\n", __func__, eir); - - /* Disable interrupts */ - SetCOMInterrupts(self, FALSE); - - /* Tx or Rx Interrupt */ - - if (eir & IIR_EOM) - { - if (self->io.direction == IO_XMIT) /* TX */ - { - pr_debug("%s(), ******* IIR_EOM (Tx) *******\n", - __func__); - - if(ali_ircc_dma_xmit_complete(self)) - { - if (irda_device_txqueue_empty(self->netdev)) - { - /* Prepare for receive */ - ali_ircc_dma_receive(self); - self->ier = IER_EOM; - } - } - else - { - self->ier = IER_EOM; - } - - } - else /* RX */ - { - pr_debug("%s(), ******* IIR_EOM (Rx) *******\n", - __func__); - - if(OldMessageCount > ((self->LineStatus+1) & 0x07)) - { - self->rcvFramesOverflow = TRUE; - pr_debug("%s(), ******* self->rcvFramesOverflow = TRUE ********\n", - __func__); - } - - if (ali_ircc_dma_receive_complete(self)) - { - pr_debug("%s(), ******* receive complete ********\n", - __func__); - - self->ier = IER_EOM; - } - else - { - pr_debug("%s(), ******* Not receive complete ********\n", - __func__); - - self->ier = IER_EOM | IER_TIMER; - } - - } - } - /* Timer Interrupt */ - else if (eir & IIR_TIMER) - { - if(OldMessageCount > ((self->LineStatus+1) & 0x07)) - { - self->rcvFramesOverflow = TRUE; - pr_debug("%s(), ******* self->rcvFramesOverflow = TRUE *******\n", - __func__); - } - /* Disable Timer */ - switch_bank(iobase, BANK1); - tmp = inb(iobase+FIR_CR); - outb( tmp& ~CR_TIMER_EN, iobase+FIR_CR); - - /* Check if this is a Tx timer interrupt */ - if (self->io.direction == IO_XMIT) - { - ali_ircc_dma_xmit(self); - - /* Interrupt on EOM */ - self->ier = IER_EOM; - - } - else /* Rx */ - { - if(ali_ircc_dma_receive_complete(self)) - { - self->ier = IER_EOM; - } - else - { - self->ier = IER_EOM | IER_TIMER; - } - } - } - - /* Restore Interrupt */ - SetCOMInterrupts(self, TRUE); - - return IRQ_RETVAL(eir); -} - -/* - * Function ali_ircc_sir_interrupt (irq, self, eir) - * - * Handle SIR interrupt - * - */ -static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self) -{ - int iobase; - int iir, lsr; - - - iobase = self->io.sir_base; - - iir = inb(iobase+UART_IIR) & UART_IIR_ID; - if (iir) { - /* Clear interrupt */ - lsr = inb(iobase+UART_LSR); - - pr_debug("%s(), iir=%02x, lsr=%02x, iobase=%#x\n", - __func__, iir, lsr, iobase); - - switch (iir) - { - case UART_IIR_RLSI: - pr_debug("%s(), RLSI\n", __func__); - break; - case UART_IIR_RDI: - /* Receive interrupt */ - ali_ircc_sir_receive(self); - break; - case UART_IIR_THRI: - if (lsr & UART_LSR_THRE) - { - /* Transmitter ready for data */ - ali_ircc_sir_write_wakeup(self); - } - break; - default: - pr_debug("%s(), unhandled IIR=%#x\n", - __func__, iir); - break; - } - - } - - - return IRQ_RETVAL(iir); -} - - -/* - * Function ali_ircc_sir_receive (self) - * - * Receive one frame from the infrared port - * - */ -static void ali_ircc_sir_receive(struct ali_ircc_cb *self) -{ - int boguscount = 0; - int iobase; - - IRDA_ASSERT(self != NULL, return;); - - iobase = self->io.sir_base; - - /* - * Receive all characters in Rx FIFO, unwrap and unstuff them. - * async_unwrap_char will deliver all found frames - */ - do { - async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, - inb(iobase+UART_RX)); - - /* Make sure we don't stay here too long */ - if (boguscount++ > 32) { - pr_debug("%s(), breaking!\n", __func__); - break; - } - } while (inb(iobase+UART_LSR) & UART_LSR_DR); - -} - -/* - * Function ali_ircc_sir_write_wakeup (tty) - * - * Called by the driver when there's room for more data. If we have - * more packets to send, we send them here. - * - */ -static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self) -{ - int actual = 0; - int iobase; - - IRDA_ASSERT(self != NULL, return;); - - - iobase = self->io.sir_base; - - /* Finished with frame? */ - if (self->tx_buff.len > 0) - { - /* Write data left in transmit buffer */ - actual = ali_ircc_sir_write(iobase, self->io.fifo_size, - self->tx_buff.data, self->tx_buff.len); - self->tx_buff.data += actual; - self->tx_buff.len -= actual; - } - else - { - if (self->new_speed) - { - /* We must wait until all data are gone */ - while(!(inb(iobase+UART_LSR) & UART_LSR_TEMT)) - pr_debug("%s(), UART_LSR_THRE\n", __func__); - - pr_debug("%s(), Changing speed! self->new_speed = %d\n", - __func__, self->new_speed); - ali_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - - // benjamin 2000/11/10 06:32PM - if (self->io.speed > 115200) - { - pr_debug("%s(), ali_ircc_change_speed from UART_LSR_TEMT\n", - __func__); - - self->ier = IER_EOM; - // SetCOMInterrupts(self, TRUE); - return; - } - } - else - { - netif_wake_queue(self->netdev); - } - - self->netdev->stats.tx_packets++; - - /* Turn on receive interrupts */ - outb(UART_IER_RDI, iobase+UART_IER); - } - -} - -static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud) -{ - struct net_device *dev = self->netdev; - int iobase; - - - pr_debug("%s(), setting speed = %d\n", __func__, baud); - - /* This function *must* be called with irq off and spin-lock. - * - Jean II */ - - iobase = self->io.fir_base; - - SetCOMInterrupts(self, FALSE); // 2000/11/24 11:43AM - - /* Go to MIR, FIR Speed */ - if (baud > 115200) - { - - - ali_ircc_fir_change_speed(self, baud); - - /* Install FIR xmit handler*/ - dev->netdev_ops = &ali_ircc_fir_ops; - - /* Enable Interuupt */ - self->ier = IER_EOM; // benjamin 2000/11/20 07:24PM - - /* Be ready for incoming frames */ - ali_ircc_dma_receive(self); // benajmin 2000/11/8 07:46PM not complete - } - /* Go to SIR Speed */ - else - { - ali_ircc_sir_change_speed(self, baud); - - /* Install SIR xmit handler*/ - dev->netdev_ops = &ali_ircc_sir_ops; - } - - - SetCOMInterrupts(self, TRUE); // 2000/11/24 11:43AM - - netif_wake_queue(self->netdev); - -} - -static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud) -{ - - int iobase; - struct ali_ircc_cb *self = priv; - struct net_device *dev; - - - IRDA_ASSERT(self != NULL, return;); - - dev = self->netdev; - iobase = self->io.fir_base; - - pr_debug("%s(), self->io.speed = %d, change to speed = %d\n", - __func__, self->io.speed, baud); - - /* Come from SIR speed */ - if(self->io.speed <=115200) - { - SIR2FIR(iobase); - } - - /* Update accounting for new speed */ - self->io.speed = baud; - - // Set Dongle Speed mode - ali_ircc_change_dongle_speed(self, baud); - -} - -/* - * Function ali_sir_change_speed (self, speed) - * - * Set speed of IrDA port to specified baudrate - * - */ -static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) -{ - struct ali_ircc_cb *self = priv; - int iobase; - int fcr; /* FIFO control reg */ - int lcr; /* Line control reg */ - int divisor; - - - pr_debug("%s(), Setting speed to: %d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return;); - - iobase = self->io.sir_base; - - /* Come from MIR or FIR speed */ - if(self->io.speed >115200) - { - // Set Dongle Speed mode first - ali_ircc_change_dongle_speed(self, speed); - - FIR2SIR(iobase); - } - - // Clear Line and Auxiluary status registers 2000/11/24 11:47AM - - inb(iobase+UART_LSR); - inb(iobase+UART_SCR); - - /* Update accounting for new speed */ - self->io.speed = speed; - - divisor = 115200/speed; - - fcr = UART_FCR_ENABLE_FIFO; - - /* - * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and - * almost 1,7 ms at 19200 bps. At speeds above that we can just forget - * about this timeout since it will always be fast enough. - */ - if (self->io.speed < 38400) - fcr |= UART_FCR_TRIGGER_1; - else - fcr |= UART_FCR_TRIGGER_14; - - /* IrDA ports use 8N1 */ - lcr = UART_LCR_WLEN8; - - outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */ - outb(divisor & 0xff, iobase+UART_DLL); /* Set speed */ - outb(divisor >> 8, iobase+UART_DLM); - outb(lcr, iobase+UART_LCR); /* Set 8N1 */ - outb(fcr, iobase+UART_FCR); /* Enable FIFO's */ - - /* without this, the connection will be broken after come back from FIR speed, - but with this, the SIR connection is harder to established */ - outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR); -} - -static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed) -{ - - struct ali_ircc_cb *self = priv; - int iobase,dongle_id; - int tmp = 0; - - - iobase = self->io.fir_base; /* or iobase = self->io.sir_base; */ - dongle_id = self->io.dongle_id; - - /* We are already locked, no need to do it again */ - - pr_debug("%s(), Set Speed for %s , Speed = %d\n", - __func__, dongle_types[dongle_id], speed); - - switch_bank(iobase, BANK2); - tmp = inb(iobase+FIR_IRDA_CR); - - /* IBM type dongle */ - if(dongle_id == 0) - { - if(speed == 4000000) - { - // __ __ - // SD/MODE __| |__ __ - // __ __ - // IRTX __ __| |__ - // T1 T2 T3 T4 T5 - - tmp &= ~IRDA_CR_HDLC; // HDLC=0 - tmp |= IRDA_CR_CRC; // CRC=1 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - - // T1 -> SD/MODE:0 IRTX:0 - tmp &= ~0x09; - tmp |= 0x02; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T2 -> SD/MODE:1 IRTX:0 - tmp &= ~0x01; - tmp |= 0x0a; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T3 -> SD/MODE:1 IRTX:1 - tmp |= 0x0b; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T4 -> SD/MODE:0 IRTX:1 - tmp &= ~0x08; - tmp |= 0x03; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T5 -> SD/MODE:0 IRTX:0 - tmp &= ~0x09; - tmp |= 0x02; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // reset -> Normal TX output Signal - outb(tmp & ~0x02, iobase+FIR_IRDA_CR); - } - else /* speed <=1152000 */ - { - // __ - // SD/MODE __| |__ - // - // IRTX ________ - // T1 T2 T3 - - /* MIR 115200, 57600 */ - if (speed==1152000) - { - tmp |= 0xA0; //HDLC=1, 1.152Mbps=1 - } - else - { - tmp &=~0x80; //HDLC 0.576Mbps - tmp |= 0x20; //HDLC=1, - } - - tmp |= IRDA_CR_CRC; // CRC=1 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - - /* MIR 115200, 57600 */ - - //switch_bank(iobase, BANK2); - // T1 -> SD/MODE:0 IRTX:0 - tmp &= ~0x09; - tmp |= 0x02; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T2 -> SD/MODE:1 IRTX:0 - tmp &= ~0x01; - tmp |= 0x0a; - outb(tmp, iobase+FIR_IRDA_CR); - - // T3 -> SD/MODE:0 IRTX:0 - tmp &= ~0x09; - tmp |= 0x02; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // reset -> Normal TX output Signal - outb(tmp & ~0x02, iobase+FIR_IRDA_CR); - } - } - else if (dongle_id == 1) /* HP HDSL-3600 */ - { - switch(speed) - { - case 4000000: - tmp &= ~IRDA_CR_HDLC; // HDLC=0 - break; - - case 1152000: - tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 - break; - - case 576000: - tmp &=~0x80; // HDLC 0.576Mbps - tmp |= 0x20; // HDLC=1, - break; - } - - tmp |= IRDA_CR_CRC; // CRC=1 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - } - else /* HP HDSL-1100 */ - { - if(speed <= 115200) /* SIR */ - { - - tmp &= ~IRDA_CR_FIR_SIN; // HP sin select = 0 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - } - else /* MIR FIR */ - { - - switch(speed) - { - case 4000000: - tmp &= ~IRDA_CR_HDLC; // HDLC=0 - break; - - case 1152000: - tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 - break; - - case 576000: - tmp &=~0x80; // HDLC 0.576Mbps - tmp |= 0x20; // HDLC=1, - break; - } - - tmp |= IRDA_CR_CRC; // CRC=1 - tmp |= IRDA_CR_FIR_SIN; // HP sin select = 1 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - } - } - - switch_bank(iobase, BANK0); - -} - -/* - * Function ali_ircc_sir_write (driver) - * - * Fill Tx FIFO with transmit data - * - */ -static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len) -{ - int actual = 0; - - - /* Tx FIFO should be empty! */ - if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { - pr_debug("%s(), failed, fifo not empty!\n", __func__); - return 0; - } - - /* Fill FIFO with current frame */ - while ((fifo_size-- > 0) && (actual < len)) { - /* Transmit next byte */ - outb(buf[actual], iobase+UART_TX); - - actual++; - } - - return actual; -} - -/* - * Function ali_ircc_net_open (dev) - * - * Start the device - * - */ -static int ali_ircc_net_open(struct net_device *dev) -{ - struct ali_ircc_cb *self; - int iobase; - char hwname[32]; - - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return 0;); - - iobase = self->io.fir_base; - - /* Request IRQ and install Interrupt Handler */ - if (request_irq(self->io.irq, ali_ircc_interrupt, 0, dev->name, dev)) - { - net_warn_ratelimited("%s, unable to allocate irq=%d\n", - ALI_IRCC_DRIVER_NAME, self->io.irq); - return -EAGAIN; - } - - /* - * Always allocate the DMA channel after the IRQ, and clean up on - * failure. - */ - if (request_dma(self->io.dma, dev->name)) { - net_warn_ratelimited("%s, unable to allocate dma=%d\n", - ALI_IRCC_DRIVER_NAME, self->io.dma); - free_irq(self->io.irq, dev); - return -EAGAIN; - } - - /* Turn on interrups */ - outb(UART_IER_RDI , iobase+UART_IER); - - /* Ready to play! */ - netif_start_queue(dev); //benjamin by irport - - /* Give self a hardware name */ - sprintf(hwname, "ALI-FIR @ 0x%03x", self->io.fir_base); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open(dev, &self->qos, hwname); - - - return 0; -} - -/* - * Function ali_ircc_net_close (dev) - * - * Stop the device - * - */ -static int ali_ircc_net_close(struct net_device *dev) -{ - - struct ali_ircc_cb *self; - //int iobase; - - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - disable_dma(self->io.dma); - - /* Disable interrupts */ - SetCOMInterrupts(self, FALSE); - - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - - - return 0; -} - -/* - * Function ali_ircc_fir_hard_xmit (skb, dev) - * - * Transmit the frame - * - */ -static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct ali_ircc_cb *self; - unsigned long flags; - int iobase; - __u32 speed; - int mtt, diff; - - - self = netdev_priv(dev); - iobase = self->io.fir_base; - - netif_stop_queue(dev); - - /* Make sure tests *& speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Note : you should make sure that speed changes are not going - * to corrupt any outgoing frame. Look at nsc-ircc for the gory - * details - Jean II */ - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame */ - if (!skb->len) { - ali_ircc_change_speed(self, speed); - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - - /* Register and copy this frame to DMA memory */ - self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; - self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; - self->tx_fifo.tail += skb->len; - - dev->stats.tx_bytes += skb->len; - - skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, - skb->len); - self->tx_fifo.len++; - self->tx_fifo.free++; - - /* Start transmit only if there is currently no transmit going on */ - if (self->tx_fifo.len == 1) - { - /* Check if we must wait the min turn time or not */ - mtt = irda_get_mtt(skb); - - if (mtt) - { - /* Check how much time we have used already */ - diff = ktime_us_delta(ktime_get(), self->stamp); - /* self->stamp is set from ali_ircc_dma_receive_complete() */ - - pr_debug("%s(), ******* diff = %d *******\n", - __func__, diff); - - /* Check if the mtt is larger than the time we have - * already used by all the protocol processing - */ - if (mtt > diff) - { - mtt -= diff; - - /* - * Use timer if delay larger than 1000 us, and - * use udelay for smaller values which should - * be acceptable - */ - if (mtt > 500) - { - /* Adjust for timer resolution */ - mtt = (mtt+250) / 500; /* 4 discard, 5 get advanced, Let's round off */ - - pr_debug("%s(), ************** mtt = %d ***********\n", - __func__, mtt); - - /* Setup timer */ - if (mtt == 1) /* 500 us */ - { - switch_bank(iobase, BANK1); - outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); - } - else if (mtt == 2) /* 1 ms */ - { - switch_bank(iobase, BANK1); - outb(TIMER_IIR_1ms, iobase+FIR_TIMER_IIR); - } - else /* > 2ms -> 4ms */ - { - switch_bank(iobase, BANK1); - outb(TIMER_IIR_2ms, iobase+FIR_TIMER_IIR); - } - - - /* Start timer */ - outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); - self->io.direction = IO_XMIT; - - /* Enable timer interrupt */ - self->ier = IER_TIMER; - SetCOMInterrupts(self, TRUE); - - /* Timer will take care of the rest */ - goto out; - } - else - udelay(mtt); - } // if (if (mtt > diff) - }// if (mtt) - - /* Enable EOM interrupt */ - self->ier = IER_EOM; - SetCOMInterrupts(self, TRUE); - - /* Transmit frame */ - ali_ircc_dma_xmit(self); - } // if (self->tx_fifo.len == 1) - - out: - - /* Not busy transmitting anymore if window is not full */ - if (self->tx_fifo.free < MAX_TX_WINDOW) - netif_wake_queue(self->netdev); - - /* Restore bank register */ - switch_bank(iobase, BANK0); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - - -static void ali_ircc_dma_xmit(struct ali_ircc_cb *self) -{ - int iobase, tmp; - unsigned char FIFO_OPTI, Hi, Lo; - - - - iobase = self->io.fir_base; - - /* FIFO threshold , this method comes from NDIS5 code */ - - if(self->tx_fifo.queue[self->tx_fifo.ptr].len < TX_FIFO_Threshold) - FIFO_OPTI = self->tx_fifo.queue[self->tx_fifo.ptr].len-1; - else - FIFO_OPTI = TX_FIFO_Threshold; - - /* Disable DMA */ - switch_bank(iobase, BANK1); - outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); - - self->io.direction = IO_XMIT; - - irda_setup_dma(self->io.dma, - ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - - self->tx_buff.head) + self->tx_buff_dma, - self->tx_fifo.queue[self->tx_fifo.ptr].len, - DMA_TX_MODE); - - /* Reset Tx FIFO */ - switch_bank(iobase, BANK0); - outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); - - /* Set Tx FIFO threshold */ - if (self->fifo_opti_buf!=FIFO_OPTI) - { - switch_bank(iobase, BANK1); - outb(FIFO_OPTI, iobase+FIR_FIFO_TR) ; - self->fifo_opti_buf=FIFO_OPTI; - } - - /* Set Tx DMA threshold */ - switch_bank(iobase, BANK1); - outb(TX_DMA_Threshold, iobase+FIR_DMA_TR); - - /* Set max Tx frame size */ - Hi = (self->tx_fifo.queue[self->tx_fifo.ptr].len >> 8) & 0x0f; - Lo = self->tx_fifo.queue[self->tx_fifo.ptr].len & 0xff; - switch_bank(iobase, BANK2); - outb(Hi, iobase+FIR_TX_DSR_HI); - outb(Lo, iobase+FIR_TX_DSR_LO); - - /* Disable SIP , Disable Brick Wall (we don't support in TX mode), Change to TX mode */ - switch_bank(iobase, BANK0); - tmp = inb(iobase+FIR_LCR_B); - tmp &= ~0x20; // Disable SIP - outb(((unsigned char)(tmp & 0x3f) | LCR_B_TX_MODE) & ~LCR_B_BW, iobase+FIR_LCR_B); - pr_debug("%s(), *** Change to TX mode: FIR_LCR_B = 0x%x ***\n", - __func__, inb(iobase + FIR_LCR_B)); - - outb(0, iobase+FIR_LSR); - - /* Enable DMA and Burst Mode */ - switch_bank(iobase, BANK1); - outb(inb(iobase+FIR_CR) | CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); - - switch_bank(iobase, BANK0); - -} - -static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self) -{ - int iobase; - int ret = TRUE; - - - iobase = self->io.fir_base; - - /* Disable DMA */ - switch_bank(iobase, BANK1); - outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); - - /* Check for underrun! */ - switch_bank(iobase, BANK0); - if((inb(iobase+FIR_LSR) & LSR_FRAME_ABORT) == LSR_FRAME_ABORT) - - { - net_err_ratelimited("%s(), ********* LSR_FRAME_ABORT *********\n", - __func__); - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - } - else - { - self->netdev->stats.tx_packets++; - } - - /* Check if we need to change the speed */ - if (self->new_speed) - { - ali_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - /* Finished with this frame, so prepare for next */ - self->tx_fifo.ptr++; - self->tx_fifo.len--; - - /* Any frames to be sent back-to-back? */ - if (self->tx_fifo.len) - { - ali_ircc_dma_xmit(self); - - /* Not finished yet! */ - ret = FALSE; - } - else - { /* Reset Tx FIFO info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - } - - /* Make sure we have room for more frames */ - if (self->tx_fifo.free < MAX_TX_WINDOW) { - /* Not busy transmitting anymore */ - /* Tell the network layer, that we can accept more frames */ - netif_wake_queue(self->netdev); - } - - switch_bank(iobase, BANK0); - - return ret; -} - -/* - * Function ali_ircc_dma_receive (self) - * - * Get ready for receiving a frame. The device will initiate a DMA - * if it starts to receive a frame. - * - */ -static int ali_ircc_dma_receive(struct ali_ircc_cb *self) -{ - int iobase, tmp; - - - iobase = self->io.fir_base; - - /* Reset Tx FIFO info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Disable DMA */ - switch_bank(iobase, BANK1); - outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); - - /* Reset Message Count */ - switch_bank(iobase, BANK0); - outb(0x07, iobase+FIR_LSR); - - self->rcvFramesOverflow = FALSE; - - self->LineStatus = inb(iobase+FIR_LSR) ; - - /* Reset Rx FIFO info */ - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Rx FIFO */ - // switch_bank(iobase, BANK0); - outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); - - self->st_fifo.len = self->st_fifo.pending_bytes = 0; - self->st_fifo.tail = self->st_fifo.head = 0; - - irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, - DMA_RX_MODE); - - /* Set Receive Mode,Brick Wall */ - //switch_bank(iobase, BANK0); - tmp = inb(iobase+FIR_LCR_B); - outb((unsigned char)(tmp &0x3f) | LCR_B_RX_MODE | LCR_B_BW , iobase + FIR_LCR_B); // 2000/12/1 05:16PM - pr_debug("%s(), *** Change To RX mode: FIR_LCR_B = 0x%x ***\n", - __func__, inb(iobase + FIR_LCR_B)); - - /* Set Rx Threshold */ - switch_bank(iobase, BANK1); - outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); - outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); - - /* Enable DMA and Burst Mode */ - // switch_bank(iobase, BANK1); - outb(CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); - - switch_bank(iobase, BANK0); - return 0; -} - -static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) -{ - struct st_fifo *st_fifo; - struct sk_buff *skb; - __u8 status, MessageCount; - int len, i, iobase, val; - - st_fifo = &self->st_fifo; - iobase = self->io.fir_base; - - switch_bank(iobase, BANK0); - MessageCount = inb(iobase+ FIR_LSR)&0x07; - - if (MessageCount > 0) - pr_debug("%s(), Message count = %d\n", __func__, MessageCount); - - for (i=0; i<=MessageCount; i++) - { - /* Bank 0 */ - switch_bank(iobase, BANK0); - status = inb(iobase+FIR_LSR); - - switch_bank(iobase, BANK2); - len = inb(iobase+FIR_RX_DSR_HI) & 0x0f; - len = len << 8; - len |= inb(iobase+FIR_RX_DSR_LO); - - pr_debug("%s(), RX Length = 0x%.2x,\n", __func__ , len); - pr_debug("%s(), RX Status = 0x%.2x,\n", __func__ , status); - - if (st_fifo->tail >= MAX_RX_WINDOW) { - pr_debug("%s(), window is full!\n", __func__); - continue; - } - - st_fifo->entries[st_fifo->tail].status = status; - st_fifo->entries[st_fifo->tail].len = len; - st_fifo->pending_bytes += len; - st_fifo->tail++; - st_fifo->len++; - } - - for (i=0; i<=MessageCount; i++) - { - /* Get first entry */ - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->pending_bytes -= len; - st_fifo->head++; - st_fifo->len--; - - /* Check for errors */ - if ((status & 0xd8) || self->rcvFramesOverflow || (len==0)) - { - pr_debug("%s(), ************* RX Errors ************\n", - __func__); - - /* Skip frame */ - self->netdev->stats.rx_errors++; - - self->rx_buff.data += len; - - if (status & LSR_FIFO_UR) - { - self->netdev->stats.rx_frame_errors++; - pr_debug("%s(), ************* FIFO Errors ************\n", - __func__); - } - if (status & LSR_FRAME_ERROR) - { - self->netdev->stats.rx_frame_errors++; - pr_debug("%s(), ************* FRAME Errors ************\n", - __func__); - } - - if (status & LSR_CRC_ERROR) - { - self->netdev->stats.rx_crc_errors++; - pr_debug("%s(), ************* CRC Errors ************\n", - __func__); - } - - if(self->rcvFramesOverflow) - { - self->netdev->stats.rx_frame_errors++; - pr_debug("%s(), ************* Overran DMA buffer ************\n", - __func__); - } - if(len == 0) - { - self->netdev->stats.rx_frame_errors++; - pr_debug("%s(), ********** Receive Frame Size = 0 *********\n", - __func__); - } - } - else - { - - if (st_fifo->pending_bytes < 32) - { - switch_bank(iobase, BANK0); - val = inb(iobase+FIR_BSR); - if ((val& BSR_FIFO_NOT_EMPTY)== 0x80) - { - pr_debug("%s(), ************* BSR_FIFO_NOT_EMPTY ************\n", - __func__); - - /* Put this entry back in fifo */ - st_fifo->head--; - st_fifo->len++; - st_fifo->pending_bytes += len; - st_fifo->entries[st_fifo->head].status = status; - st_fifo->entries[st_fifo->head].len = len; - - /* - * DMA not finished yet, so try again - * later, set timer value, resolution - * 500 us - */ - - switch_bank(iobase, BANK1); - outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); // 2001/1/2 05:07PM - - /* Enable Timer */ - outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); - - return FALSE; /* I'll be back! */ - } - } - - /* - * Remember the time we received this frame, so we can - * reduce the min turn time a bit since we will know - * how much time we have used for protocol processing - */ - self->stamp = ktime_get(); - - skb = dev_alloc_skb(len+1); - if (skb == NULL) - { - self->netdev->stats.rx_dropped++; - - return FALSE; - } - - /* Make sure IP header gets aligned */ - skb_reserve(skb, 1); - - /* Copy frame without CRC, CRC is removed by hardware*/ - skb_put(skb, len); - skb_copy_to_linear_data(skb, self->rx_buff.data, len); - - /* Move to next frame */ - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - } - } - - switch_bank(iobase, BANK0); - - return TRUE; -} - - - -/* - * Function ali_ircc_sir_hard_xmit (skb, dev) - * - * Transmit the frame! - * - */ -static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct ali_ircc_cb *self; - unsigned long flags; - int iobase; - __u32 speed; - - - IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - - iobase = self->io.sir_base; - - netif_stop_queue(dev); - - /* Make sure tests *& speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Note : you should make sure that speed changes are not going - * to corrupt any outgoing frame. Look at nsc-ircc for the gory - * details - Jean II */ - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame */ - if (!skb->len) { - ali_ircc_change_speed(self, speed); - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - - /* Init tx buffer */ - self->tx_buff.data = self->tx_buff.head; - - /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - self->netdev->stats.tx_bytes += self->tx_buff.len; - - /* Turn on transmit finished interrupt. Will fire immediately! */ - outb(UART_IER_THRI, iobase+UART_IER); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - - dev_kfree_skb(skb); - - - return NETDEV_TX_OK; -} - - -/* - * Function ali_ircc_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct ali_ircc_cb *self; - unsigned long flags; - int ret = 0; - - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__ , dev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - pr_debug("%s(), SIOCSBANDWIDTH\n", __func__); - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) - return -EPERM; - - spin_lock_irqsave(&self->lock, flags); - ali_ircc_change_speed(self, irq->ifr_baudrate); - spin_unlock_irqrestore(&self->lock, flags); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - pr_debug("%s(), SIOCSMEDIABUSY\n", __func__); - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - pr_debug("%s(), SIOCGRECEIVING\n", __func__); - /* This is protected */ - irq->ifr_receiving = ali_ircc_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } - - - return ret; -} - -/* - * Function ali_ircc_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int ali_ircc_is_receiving(struct ali_ircc_cb *self) -{ - unsigned long flags; - int status = FALSE; - int iobase; - - - IRDA_ASSERT(self != NULL, return FALSE;); - - spin_lock_irqsave(&self->lock, flags); - - if (self->io.speed > 115200) - { - iobase = self->io.fir_base; - - switch_bank(iobase, BANK1); - if((inb(iobase+FIR_FIFO_FR) & 0x3f) != 0) - { - /* We are receiving something */ - pr_debug("%s(), We are receiving something\n", - __func__); - status = TRUE; - } - switch_bank(iobase, BANK0); - } - else - { - status = (self->rx_buff.state != OUTSIDE_FRAME); - } - - spin_unlock_irqrestore(&self->lock, flags); - - - return status; -} - -static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state) -{ - struct ali_ircc_cb *self = platform_get_drvdata(dev); - - net_info_ratelimited("%s, Suspending\n", ALI_IRCC_DRIVER_NAME); - - if (self->io.suspended) - return 0; - - ali_ircc_net_close(self->netdev); - - self->io.suspended = 1; - - return 0; -} - -static int ali_ircc_resume(struct platform_device *dev) -{ - struct ali_ircc_cb *self = platform_get_drvdata(dev); - - if (!self->io.suspended) - return 0; - - ali_ircc_net_open(self->netdev); - - net_info_ratelimited("%s, Waking up\n", ALI_IRCC_DRIVER_NAME); - - self->io.suspended = 0; - - return 0; -} - -/* ALi Chip Function */ - -static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable) -{ - - unsigned char newMask; - - int iobase = self->io.fir_base; /* or sir_base */ - - pr_debug("%s(), -------- Start -------- ( Enable = %d )\n", - __func__, enable); - - /* Enable the interrupt which we wish to */ - if (enable){ - if (self->io.direction == IO_XMIT) - { - if (self->io.speed > 115200) /* FIR, MIR */ - { - newMask = self->ier; - } - else /* SIR */ - { - newMask = UART_IER_THRI | UART_IER_RDI; - } - } - else { - if (self->io.speed > 115200) /* FIR, MIR */ - { - newMask = self->ier; - } - else /* SIR */ - { - newMask = UART_IER_RDI; - } - } - } - else /* Disable all the interrupts */ - { - newMask = 0x00; - - } - - //SIR and FIR has different registers - if (self->io.speed > 115200) - { - switch_bank(iobase, BANK0); - outb(newMask, iobase+FIR_IER); - } - else - outb(newMask, iobase+UART_IER); - -} - -static void SIR2FIR(int iobase) -{ - //unsigned char tmp; - - - /* Already protected (change_speed() or setup()), no need to lock. - * Jean II */ - - outb(0x28, iobase+UART_MCR); - outb(0x68, iobase+UART_MCR); - outb(0x88, iobase+UART_MCR); - - outb(0x60, iobase+FIR_MCR); /* Master Reset */ - outb(0x20, iobase+FIR_MCR); /* Master Interrupt Enable */ - - //tmp = inb(iobase+FIR_LCR_B); /* SIP enable */ - //tmp |= 0x20; - //outb(tmp, iobase+FIR_LCR_B); - -} - -static void FIR2SIR(int iobase) -{ - unsigned char val; - - - /* Already protected (change_speed() or setup()), no need to lock. - * Jean II */ - - outb(0x20, iobase+FIR_MCR); /* IRQ to low */ - outb(0x00, iobase+UART_IER); - - outb(0xA0, iobase+FIR_MCR); /* Don't set master reset */ - outb(0x00, iobase+UART_FCR); - outb(0x07, iobase+UART_FCR); - - val = inb(iobase+UART_RX); - val = inb(iobase+UART_LSR); - val = inb(iobase+UART_MSR); - -} - -MODULE_AUTHOR("Benjamin Kong "); -MODULE_DESCRIPTION("ALi FIR Controller Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" ALI_IRCC_DRIVER_NAME); - - -module_param_hw_array(io, int, ioport, NULL, 0); -MODULE_PARM_DESC(io, "Base I/O addresses"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "IRQ lines"); -module_param_hw_array(dma, int, dma, NULL, 0); -MODULE_PARM_DESC(dma, "DMA channels"); - -module_init(ali_ircc_init); -module_exit(ali_ircc_cleanup); diff --git a/drivers/net/irda/ali-ircc.h b/drivers/net/irda/ali-ircc.h deleted file mode 100644 index c2d9747a5108..000000000000 --- a/drivers/net/irda/ali-ircc.h +++ /dev/null @@ -1,227 +0,0 @@ -/********************************************************************* - * - * Filename: ali-ircc.h - * Version: 0.5 - * Description: Driver for the ALI M1535D and M1543C FIR Controller - * Status: Experimental. - * Author: Benjamin Kong - * Created at: 2000/10/16 03:46PM - * Modified at: 2001/1/3 02:56PM - * Modified by: Benjamin Kong - * - * Copyright (c) 2000 Benjamin Kong - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#ifndef ALI_IRCC_H -#define ALI_IRCC_H - -#include - -#include -#include -#include -#include - -/* SIR Register */ -/* Usr definition of linux/serial_reg.h */ - -/* FIR Register */ -#define BANK0 0x20 -#define BANK1 0x21 -#define BANK2 0x22 -#define BANK3 0x23 - -#define FIR_MCR 0x07 /* Master Control Register */ - -/* Bank 0 */ -#define FIR_DR 0x00 /* Alias 0, FIR Data Register (R/W) */ -#define FIR_IER 0x01 /* Alias 1, FIR Interrupt Enable Register (R/W) */ -#define FIR_IIR 0x02 /* Alias 2, FIR Interrupt Identification Register (Read only) */ -#define FIR_LCR_A 0x03 /* Alias 3, FIR Line Control Register A (R/W) */ -#define FIR_LCR_B 0x04 /* Alias 4, FIR Line Control Register B (R/W) */ -#define FIR_LSR 0x05 /* Alias 5, FIR Line Status Register (R/W) */ -#define FIR_BSR 0x06 /* Alias 6, FIR Bus Status Register (Read only) */ - - - /* Alias 1 */ - #define IER_FIFO 0x10 /* FIR FIFO Interrupt Enable */ - #define IER_TIMER 0x20 /* Timer Interrupt Enable */ - #define IER_EOM 0x40 /* End of Message Interrupt Enable */ - #define IER_ACT 0x80 /* Active Frame Interrupt Enable */ - - /* Alias 2 */ - #define IIR_FIFO 0x10 /* FIR FIFO Interrupt */ - #define IIR_TIMER 0x20 /* Timer Interrupt */ - #define IIR_EOM 0x40 /* End of Message Interrupt */ - #define IIR_ACT 0x80 /* Active Frame Interrupt */ - - /* Alias 3 */ - #define LCR_A_FIFO_RESET 0x80 /* FIFO Reset */ - - /* Alias 4 */ - #define LCR_B_BW 0x10 /* Brick Wall */ - #define LCR_B_SIP 0x20 /* SIP Enable */ - #define LCR_B_TX_MODE 0x40 /* Transmit Mode */ - #define LCR_B_RX_MODE 0x80 /* Receive Mode */ - - /* Alias 5 */ - #define LSR_FIR_LSA 0x00 /* FIR Line Status Address */ - #define LSR_FRAME_ABORT 0x08 /* Frame Abort */ - #define LSR_CRC_ERROR 0x10 /* CRC Error */ - #define LSR_SIZE_ERROR 0x20 /* Size Error */ - #define LSR_FRAME_ERROR 0x40 /* Frame Error */ - #define LSR_FIFO_UR 0x80 /* FIFO Underrun */ - #define LSR_FIFO_OR 0x80 /* FIFO Overrun */ - - /* Alias 6 */ - #define BSR_FIFO_NOT_EMPTY 0x80 /* FIFO Not Empty */ - -/* Bank 1 */ -#define FIR_CR 0x00 /* Alias 0, FIR Configuration Register (R/W) */ -#define FIR_FIFO_TR 0x01 /* Alias 1, FIR FIFO Threshold Register (R/W) */ -#define FIR_DMA_TR 0x02 /* Alias 2, FIR DMA Threshold Register (R/W) */ -#define FIR_TIMER_IIR 0x03 /* Alias 3, FIR Timer interrupt interval register (W/O) */ -#define FIR_FIFO_FR 0x03 /* Alias 3, FIR FIFO Flag register (R/O) */ -#define FIR_FIFO_RAR 0x04 /* Alias 4, FIR FIFO Read Address register (R/O) */ -#define FIR_FIFO_WAR 0x05 /* Alias 5, FIR FIFO Write Address register (R/O) */ -#define FIR_TR 0x06 /* Alias 6, Test REgister (W/O) */ - - /* Alias 0 */ - #define CR_DMA_EN 0x01 /* DMA Enable */ - #define CR_DMA_BURST 0x02 /* DMA Burst Mode */ - #define CR_TIMER_EN 0x08 /* Timer Enable */ - - /* Alias 3 */ - #define TIMER_IIR_500 0x00 /* 500 us */ - #define TIMER_IIR_1ms 0x01 /* 1 ms */ - #define TIMER_IIR_2ms 0x02 /* 2 ms */ - #define TIMER_IIR_4ms 0x03 /* 4 ms */ - -/* Bank 2 */ -#define FIR_IRDA_CR 0x00 /* Alias 0, IrDA Control Register (R/W) */ -#define FIR_BOF_CR 0x01 /* Alias 1, BOF Count Register (R/W) */ -#define FIR_BW_CR 0x02 /* Alias 2, Brick Wall Count Register (R/W) */ -#define FIR_TX_DSR_HI 0x03 /* Alias 3, TX Data Size Register (high) (R/W) */ -#define FIR_TX_DSR_LO 0x04 /* Alias 4, TX Data Size Register (low) (R/W) */ -#define FIR_RX_DSR_HI 0x05 /* Alias 5, RX Data Size Register (high) (R/W) */ -#define FIR_RX_DSR_LO 0x06 /* Alias 6, RX Data Size Register (low) (R/W) */ - - /* Alias 0 */ - #define IRDA_CR_HDLC1152 0x80 /* 1.152Mbps HDLC Select */ - #define IRDA_CR_CRC 0X40 /* CRC Select. */ - #define IRDA_CR_HDLC 0x20 /* HDLC select. */ - #define IRDA_CR_HP_MODE 0x10 /* HP mode (read only) */ - #define IRDA_CR_SD_ST 0x08 /* SD/MODE State. */ - #define IRDA_CR_FIR_SIN 0x04 /* FIR SIN Select. */ - #define IRDA_CR_ITTX_0 0x02 /* SOUT State. IRTX force to 0 */ - #define IRDA_CR_ITTX_1 0x03 /* SOUT State. IRTX force to 1 */ - -/* Bank 3 */ -#define FIR_ID_VR 0x00 /* Alias 0, FIR ID Version Register (R/O) */ -#define FIR_MODULE_CR 0x01 /* Alias 1, FIR Module Control Register (R/W) */ -#define FIR_IO_BASE_HI 0x02 /* Alias 2, FIR Higher I/O Base Address Register (R/O) */ -#define FIR_IO_BASE_LO 0x03 /* Alias 3, FIR Lower I/O Base Address Register (R/O) */ -#define FIR_IRQ_CR 0x04 /* Alias 4, FIR IRQ Channel Register (R/O) */ -#define FIR_DMA_CR 0x05 /* Alias 5, FIR DMA Channel Register (R/O) */ - -struct ali_chip { - char *name; - int cfg[2]; - unsigned char entr1; - unsigned char entr2; - unsigned char cid_index; - unsigned char cid_value; - int (*probe)(struct ali_chip *chip, chipio_t *info); - int (*init)(struct ali_chip *chip, chipio_t *info); -}; -typedef struct ali_chip ali_chip_t; - - -/* DMA modes needed */ -#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ -#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ - -#define MAX_TX_WINDOW 7 -#define MAX_RX_WINDOW 7 - -#define TX_FIFO_Threshold 8 -#define RX_FIFO_Threshold 1 -#define TX_DMA_Threshold 1 -#define RX_DMA_Threshold 1 - -/* For storing entries in the status FIFO */ - -struct st_fifo_entry { - int status; - int len; -}; - -struct st_fifo { - struct st_fifo_entry entries[MAX_RX_WINDOW]; - int pending_bytes; - int head; - int tail; - int len; -}; - -struct frame_cb { - void *start; /* Start of frame in DMA mem */ - int len; /* Length of frame in DMA mem */ -}; - -struct tx_fifo { - struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */ - int ptr; /* Currently being sent */ - int len; /* Length of queue */ - int free; /* Next free slot */ - void *tail; /* Next free start in DMA mem */ -}; - -/* Private data for each instance */ -struct ali_ircc_cb { - - struct st_fifo st_fifo; /* Info about received frames */ - struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ - - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; /* QoS capabilities for this device */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - __u8 ier; /* Interrupt enable register */ - - __u8 InterruptID; /* Interrupt ID */ - __u8 BusStatus; /* Bus Status */ - __u8 LineStatus; /* Line Status */ - - unsigned char rcvFramesOverflow; - - ktime_t stamp; - - spinlock_t lock; /* For serializing operations */ - - __u32 new_speed; - int index; /* Instance index */ - - unsigned char fifo_opti_buf; -}; - -static inline void switch_bank(int iobase, int bank) -{ - outb(bank, iobase+FIR_MCR); -} - -#endif /* ALI_IRCC_H */ diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c deleted file mode 100644 index be4ea6aa57a9..000000000000 --- a/drivers/net/irda/au1k_ir.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * Alchemy Semi Au1000 IrDA driver - * - * Copyright 2001 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * ppopov@mvista.com or source@mvista.com - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* registers */ -#define IR_RING_PTR_STATUS 0x00 -#define IR_RING_BASE_ADDR_H 0x04 -#define IR_RING_BASE_ADDR_L 0x08 -#define IR_RING_SIZE 0x0C -#define IR_RING_PROMPT 0x10 -#define IR_RING_ADDR_CMPR 0x14 -#define IR_INT_CLEAR 0x18 -#define IR_CONFIG_1 0x20 -#define IR_SIR_FLAGS 0x24 -#define IR_STATUS 0x28 -#define IR_READ_PHY_CONFIG 0x2C -#define IR_WRITE_PHY_CONFIG 0x30 -#define IR_MAX_PKT_LEN 0x34 -#define IR_RX_BYTE_CNT 0x38 -#define IR_CONFIG_2 0x3C -#define IR_ENABLE 0x40 - -/* Config1 */ -#define IR_RX_INVERT_LED (1 << 0) -#define IR_TX_INVERT_LED (1 << 1) -#define IR_ST (1 << 2) -#define IR_SF (1 << 3) -#define IR_SIR (1 << 4) -#define IR_MIR (1 << 5) -#define IR_FIR (1 << 6) -#define IR_16CRC (1 << 7) -#define IR_TD (1 << 8) -#define IR_RX_ALL (1 << 9) -#define IR_DMA_ENABLE (1 << 10) -#define IR_RX_ENABLE (1 << 11) -#define IR_TX_ENABLE (1 << 12) -#define IR_LOOPBACK (1 << 14) -#define IR_SIR_MODE (IR_SIR | IR_DMA_ENABLE | \ - IR_RX_ALL | IR_RX_ENABLE | IR_SF | \ - IR_16CRC) - -/* ir_status */ -#define IR_RX_STATUS (1 << 9) -#define IR_TX_STATUS (1 << 10) -#define IR_PHYEN (1 << 15) - -/* ir_write_phy_config */ -#define IR_BR(x) (((x) & 0x3f) << 10) /* baud rate */ -#define IR_PW(x) (((x) & 0x1f) << 5) /* pulse width */ -#define IR_P(x) ((x) & 0x1f) /* preamble bits */ - -/* Config2 */ -#define IR_MODE_INV (1 << 0) -#define IR_ONE_PIN (1 << 1) -#define IR_PHYCLK_40MHZ (0 << 2) -#define IR_PHYCLK_48MHZ (1 << 2) -#define IR_PHYCLK_56MHZ (2 << 2) -#define IR_PHYCLK_64MHZ (3 << 2) -#define IR_DP (1 << 4) -#define IR_DA (1 << 5) -#define IR_FLT_HIGH (0 << 6) -#define IR_FLT_MEDHI (1 << 6) -#define IR_FLT_MEDLO (2 << 6) -#define IR_FLT_LO (3 << 6) -#define IR_IEN (1 << 8) - -/* ir_enable */ -#define IR_HC (1 << 3) /* divide SBUS clock by 2 */ -#define IR_CE (1 << 2) /* clock enable */ -#define IR_C (1 << 1) /* coherency bit */ -#define IR_BE (1 << 0) /* set in big endian mode */ - -#define NUM_IR_DESC 64 -#define RING_SIZE_4 0x0 -#define RING_SIZE_16 0x3 -#define RING_SIZE_64 0xF -#define MAX_NUM_IR_DESC 64 -#define MAX_BUF_SIZE 2048 - -/* Ring descriptor flags */ -#define AU_OWN (1 << 7) /* tx,rx */ -#define IR_DIS_CRC (1 << 6) /* tx */ -#define IR_BAD_CRC (1 << 5) /* tx */ -#define IR_NEED_PULSE (1 << 4) /* tx */ -#define IR_FORCE_UNDER (1 << 3) /* tx */ -#define IR_DISABLE_TX (1 << 2) /* tx */ -#define IR_HW_UNDER (1 << 0) /* tx */ -#define IR_TX_ERROR (IR_DIS_CRC | IR_BAD_CRC | IR_HW_UNDER) - -#define IR_PHY_ERROR (1 << 6) /* rx */ -#define IR_CRC_ERROR (1 << 5) /* rx */ -#define IR_MAX_LEN (1 << 4) /* rx */ -#define IR_FIFO_OVER (1 << 3) /* rx */ -#define IR_SIR_ERROR (1 << 2) /* rx */ -#define IR_RX_ERROR (IR_PHY_ERROR | IR_CRC_ERROR | \ - IR_MAX_LEN | IR_FIFO_OVER | IR_SIR_ERROR) - -struct db_dest { - struct db_dest *pnext; - volatile u32 *vaddr; - dma_addr_t dma_addr; -}; - -struct ring_dest { - u8 count_0; /* 7:0 */ - u8 count_1; /* 12:8 */ - u8 reserved; - u8 flags; - u8 addr_0; /* 7:0 */ - u8 addr_1; /* 15:8 */ - u8 addr_2; /* 23:16 */ - u8 addr_3; /* 31:24 */ -}; - -/* Private data for each instance */ -struct au1k_private { - void __iomem *iobase; - int irq_rx, irq_tx; - - struct db_dest *pDBfree; - struct db_dest db[2 * NUM_IR_DESC]; - volatile struct ring_dest *rx_ring[NUM_IR_DESC]; - volatile struct ring_dest *tx_ring[NUM_IR_DESC]; - struct db_dest *rx_db_inuse[NUM_IR_DESC]; - struct db_dest *tx_db_inuse[NUM_IR_DESC]; - u32 rx_head; - u32 tx_head; - u32 tx_tail; - u32 tx_full; - - iobuff_t rx_buff; - - struct net_device *netdev; - struct qos_info qos; - struct irlap_cb *irlap; - - u8 open; - u32 speed; - u32 newspeed; - - struct resource *ioarea; - struct au1k_irda_platform_data *platdata; - struct clk *irda_clk; -}; - -static int qos_mtt_bits = 0x07; /* 1 ms or more */ - -static void au1k_irda_plat_set_phy_mode(struct au1k_private *p, int mode) -{ - if (p->platdata && p->platdata->set_phy_mode) - p->platdata->set_phy_mode(mode); -} - -static inline unsigned long irda_read(struct au1k_private *p, - unsigned long ofs) -{ - /* - * IrDA peripheral bug. You have to read the register - * twice to get the right value. - */ - (void)__raw_readl(p->iobase + ofs); - return __raw_readl(p->iobase + ofs); -} - -static inline void irda_write(struct au1k_private *p, unsigned long ofs, - unsigned long val) -{ - __raw_writel(val, p->iobase + ofs); - wmb(); -} - -/* - * Buffer allocation/deallocation routines. The buffer descriptor returned - * has the virtual and dma address of a buffer suitable for - * both, receive and transmit operations. - */ -static struct db_dest *GetFreeDB(struct au1k_private *aup) -{ - struct db_dest *db; - db = aup->pDBfree; - - if (db) - aup->pDBfree = db->pnext; - return db; -} - -/* - DMA memory allocation, derived from pci_alloc_consistent. - However, the Au1000 data cache is coherent (when programmed - so), therefore we return KSEG0 address, not KSEG1. -*/ -static void *dma_alloc(size_t size, dma_addr_t *dma_handle) -{ - void *ret; - int gfp = GFP_ATOMIC | GFP_DMA; - - ret = (void *)__get_free_pages(gfp, get_order(size)); - - if (ret != NULL) { - memset(ret, 0, size); - *dma_handle = virt_to_bus(ret); - ret = (void *)KSEG0ADDR(ret); - } - return ret; -} - -static void dma_free(void *vaddr, size_t size) -{ - vaddr = (void *)KSEG0ADDR(vaddr); - free_pages((unsigned long) vaddr, get_order(size)); -} - - -static void setup_hw_rings(struct au1k_private *aup, u32 rx_base, u32 tx_base) -{ - int i; - for (i = 0; i < NUM_IR_DESC; i++) { - aup->rx_ring[i] = (volatile struct ring_dest *) - (rx_base + sizeof(struct ring_dest) * i); - } - for (i = 0; i < NUM_IR_DESC; i++) { - aup->tx_ring[i] = (volatile struct ring_dest *) - (tx_base + sizeof(struct ring_dest) * i); - } -} - -static int au1k_irda_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL); - if (io->head != NULL) { - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - } - return io->head ? 0 : -ENOMEM; -} - -/* - * Set the IrDA communications speed. - */ -static int au1k_irda_set_speed(struct net_device *dev, int speed) -{ - struct au1k_private *aup = netdev_priv(dev); - volatile struct ring_dest *ptxd; - unsigned long control; - int ret = 0, timeout = 10, i; - - if (speed == aup->speed) - return ret; - - /* disable PHY first */ - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF); - irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN); - - /* disable RX/TX */ - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) & ~(IR_RX_ENABLE | IR_TX_ENABLE)); - msleep(20); - while (irda_read(aup, IR_STATUS) & (IR_RX_STATUS | IR_TX_STATUS)) { - msleep(20); - if (!timeout--) { - printk(KERN_ERR "%s: rx/tx disable timeout\n", - dev->name); - break; - } - } - - /* disable DMA */ - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) & ~IR_DMA_ENABLE); - msleep(20); - - /* After we disable tx/rx. the index pointers go back to zero. */ - aup->tx_head = aup->tx_tail = aup->rx_head = 0; - for (i = 0; i < NUM_IR_DESC; i++) { - ptxd = aup->tx_ring[i]; - ptxd->flags = 0; - ptxd->count_0 = 0; - ptxd->count_1 = 0; - } - - for (i = 0; i < NUM_IR_DESC; i++) { - ptxd = aup->rx_ring[i]; - ptxd->count_0 = 0; - ptxd->count_1 = 0; - ptxd->flags = AU_OWN; - } - - if (speed == 4000000) - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_FIR); - else - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR); - - switch (speed) { - case 9600: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(11) | IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 19200: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(5) | IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 38400: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(2) | IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 57600: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(1) | IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 115200: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 4000000: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_P(15)); - irda_write(aup, IR_CONFIG_1, IR_FIR | IR_DMA_ENABLE | - IR_RX_ENABLE); - break; - default: - printk(KERN_ERR "%s unsupported speed %x\n", dev->name, speed); - ret = -EINVAL; - break; - } - - aup->speed = speed; - irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) | IR_PHYEN); - - control = irda_read(aup, IR_STATUS); - irda_write(aup, IR_RING_PROMPT, 0); - - if (control & (1 << 14)) { - printk(KERN_ERR "%s: configuration error\n", dev->name); - } else { - if (control & (1 << 11)) - printk(KERN_DEBUG "%s Valid SIR config\n", dev->name); - if (control & (1 << 12)) - printk(KERN_DEBUG "%s Valid MIR config\n", dev->name); - if (control & (1 << 13)) - printk(KERN_DEBUG "%s Valid FIR config\n", dev->name); - if (control & (1 << 10)) - printk(KERN_DEBUG "%s TX enabled\n", dev->name); - if (control & (1 << 9)) - printk(KERN_DEBUG "%s RX enabled\n", dev->name); - } - - return ret; -} - -static void update_rx_stats(struct net_device *dev, u32 status, u32 count) -{ - struct net_device_stats *ps = &dev->stats; - - ps->rx_packets++; - - if (status & IR_RX_ERROR) { - ps->rx_errors++; - if (status & (IR_PHY_ERROR | IR_FIFO_OVER)) - ps->rx_missed_errors++; - if (status & IR_MAX_LEN) - ps->rx_length_errors++; - if (status & IR_CRC_ERROR) - ps->rx_crc_errors++; - } else - ps->rx_bytes += count; -} - -static void update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len) -{ - struct net_device_stats *ps = &dev->stats; - - ps->tx_packets++; - ps->tx_bytes += pkt_len; - - if (status & IR_TX_ERROR) { - ps->tx_errors++; - ps->tx_aborted_errors++; - } -} - -static void au1k_tx_ack(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - volatile struct ring_dest *ptxd; - - ptxd = aup->tx_ring[aup->tx_tail]; - while (!(ptxd->flags & AU_OWN) && (aup->tx_tail != aup->tx_head)) { - update_tx_stats(dev, ptxd->flags, - (ptxd->count_1 << 8) | ptxd->count_0); - ptxd->count_0 = 0; - ptxd->count_1 = 0; - wmb(); - aup->tx_tail = (aup->tx_tail + 1) & (NUM_IR_DESC - 1); - ptxd = aup->tx_ring[aup->tx_tail]; - - if (aup->tx_full) { - aup->tx_full = 0; - netif_wake_queue(dev); - } - } - - if (aup->tx_tail == aup->tx_head) { - if (aup->newspeed) { - au1k_irda_set_speed(dev, aup->newspeed); - aup->newspeed = 0; - } else { - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) & ~IR_TX_ENABLE); - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) | IR_RX_ENABLE); - irda_write(aup, IR_RING_PROMPT, 0); - } - } -} - -static int au1k_irda_rx(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - volatile struct ring_dest *prxd; - struct sk_buff *skb; - struct db_dest *pDB; - u32 flags, count; - - prxd = aup->rx_ring[aup->rx_head]; - flags = prxd->flags; - - while (!(flags & AU_OWN)) { - pDB = aup->rx_db_inuse[aup->rx_head]; - count = (prxd->count_1 << 8) | prxd->count_0; - if (!(flags & IR_RX_ERROR)) { - /* good frame */ - update_rx_stats(dev, flags, count); - skb = alloc_skb(count + 1, GFP_ATOMIC); - if (skb == NULL) { - dev->stats.rx_dropped++; - continue; - } - skb_reserve(skb, 1); - if (aup->speed == 4000000) - skb_put(skb, count); - else - skb_put(skb, count - 2); - skb_copy_to_linear_data(skb, (void *)pDB->vaddr, - count - 2); - skb->dev = dev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - prxd->count_0 = 0; - prxd->count_1 = 0; - } - prxd->flags |= AU_OWN; - aup->rx_head = (aup->rx_head + 1) & (NUM_IR_DESC - 1); - irda_write(aup, IR_RING_PROMPT, 0); - - /* next descriptor */ - prxd = aup->rx_ring[aup->rx_head]; - flags = prxd->flags; - - } - return 0; -} - -static irqreturn_t au1k_irda_interrupt(int dummy, void *dev_id) -{ - struct net_device *dev = dev_id; - struct au1k_private *aup = netdev_priv(dev); - - irda_write(aup, IR_INT_CLEAR, 0); /* ack irda interrupts */ - - au1k_irda_rx(dev); - au1k_tx_ack(dev); - - return IRQ_HANDLED; -} - -static int au1k_init(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - u32 enable, ring_address, phyck; - struct clk *c; - int i; - - c = clk_get(NULL, "irda_clk"); - if (IS_ERR(c)) - return PTR_ERR(c); - i = clk_prepare_enable(c); - if (i) { - clk_put(c); - return i; - } - - switch (clk_get_rate(c)) { - case 40000000: - phyck = IR_PHYCLK_40MHZ; - break; - case 48000000: - phyck = IR_PHYCLK_48MHZ; - break; - case 56000000: - phyck = IR_PHYCLK_56MHZ; - break; - case 64000000: - phyck = IR_PHYCLK_64MHZ; - break; - default: - clk_disable_unprepare(c); - clk_put(c); - return -EINVAL; - } - aup->irda_clk = c; - - enable = IR_HC | IR_CE | IR_C; -#ifndef CONFIG_CPU_LITTLE_ENDIAN - enable |= IR_BE; -#endif - aup->tx_head = 0; - aup->tx_tail = 0; - aup->rx_head = 0; - - for (i = 0; i < NUM_IR_DESC; i++) - aup->rx_ring[i]->flags = AU_OWN; - - irda_write(aup, IR_ENABLE, enable); - msleep(20); - - /* disable PHY */ - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF); - irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN); - msleep(20); - - irda_write(aup, IR_MAX_PKT_LEN, MAX_BUF_SIZE); - - ring_address = (u32)virt_to_phys((void *)aup->rx_ring[0]); - irda_write(aup, IR_RING_BASE_ADDR_H, ring_address >> 26); - irda_write(aup, IR_RING_BASE_ADDR_L, (ring_address >> 10) & 0xffff); - - irda_write(aup, IR_RING_SIZE, - (RING_SIZE_64 << 8) | (RING_SIZE_64 << 12)); - - irda_write(aup, IR_CONFIG_2, phyck | IR_ONE_PIN); - irda_write(aup, IR_RING_ADDR_CMPR, 0); - - au1k_irda_set_speed(dev, 9600); - return 0; -} - -static int au1k_irda_start(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - char hwname[32]; - int retval; - - retval = au1k_init(dev); - if (retval) { - printk(KERN_ERR "%s: error in au1k_init\n", dev->name); - return retval; - } - - retval = request_irq(aup->irq_tx, &au1k_irda_interrupt, 0, - dev->name, dev); - if (retval) { - printk(KERN_ERR "%s: unable to get IRQ %d\n", - dev->name, dev->irq); - return retval; - } - retval = request_irq(aup->irq_rx, &au1k_irda_interrupt, 0, - dev->name, dev); - if (retval) { - free_irq(aup->irq_tx, dev); - printk(KERN_ERR "%s: unable to get IRQ %d\n", - dev->name, dev->irq); - return retval; - } - - /* Give self a hardware name */ - sprintf(hwname, "Au1000 SIR/FIR"); - aup->irlap = irlap_open(dev, &aup->qos, hwname); - netif_start_queue(dev); - - /* int enable */ - irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) | IR_IEN); - - /* power up */ - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR); - - return 0; -} - -static int au1k_irda_stop(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF); - - /* disable interrupts */ - irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) & ~IR_IEN); - irda_write(aup, IR_CONFIG_1, 0); - irda_write(aup, IR_ENABLE, 0); /* disable clock */ - - if (aup->irlap) { - irlap_close(aup->irlap); - aup->irlap = NULL; - } - - netif_stop_queue(dev); - - /* disable the interrupt */ - free_irq(aup->irq_tx, dev); - free_irq(aup->irq_rx, dev); - - clk_disable_unprepare(aup->irda_clk); - clk_put(aup->irda_clk); - - return 0; -} - -/* - * Au1000 transmit routine. - */ -static int au1k_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - int speed = irda_get_next_speed(skb); - volatile struct ring_dest *ptxd; - struct db_dest *pDB; - u32 len, flags; - - if (speed != aup->speed && speed != -1) - aup->newspeed = speed; - - if ((skb->len == 0) && (aup->newspeed)) { - if (aup->tx_tail == aup->tx_head) { - au1k_irda_set_speed(dev, speed); - aup->newspeed = 0; - } - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - ptxd = aup->tx_ring[aup->tx_head]; - flags = ptxd->flags; - - if (flags & AU_OWN) { - printk(KERN_DEBUG "%s: tx_full\n", dev->name); - netif_stop_queue(dev); - aup->tx_full = 1; - return 1; - } else if (((aup->tx_head + 1) & (NUM_IR_DESC - 1)) == aup->tx_tail) { - printk(KERN_DEBUG "%s: tx_full\n", dev->name); - netif_stop_queue(dev); - aup->tx_full = 1; - return 1; - } - - pDB = aup->tx_db_inuse[aup->tx_head]; - -#if 0 - if (irda_read(aup, IR_RX_BYTE_CNT) != 0) { - printk(KERN_DEBUG "tx warning: rx byte cnt %x\n", - irda_read(aup, IR_RX_BYTE_CNT)); - } -#endif - - if (aup->speed == 4000000) { - /* FIR */ - skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len); - ptxd->count_0 = skb->len & 0xff; - ptxd->count_1 = (skb->len >> 8) & 0xff; - } else { - /* SIR */ - len = async_wrap_skb(skb, (u8 *)pDB->vaddr, MAX_BUF_SIZE); - ptxd->count_0 = len & 0xff; - ptxd->count_1 = (len >> 8) & 0xff; - ptxd->flags |= IR_DIS_CRC; - } - ptxd->flags |= AU_OWN; - wmb(); - - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) | IR_TX_ENABLE); - irda_write(aup, IR_RING_PROMPT, 0); - - dev_kfree_skb(skb); - aup->tx_head = (aup->tx_head + 1) & (NUM_IR_DESC - 1); - return NETDEV_TX_OK; -} - -/* - * The Tx ring has been full longer than the watchdog timeout - * value. The transmitter must be hung? - */ -static void au1k_tx_timeout(struct net_device *dev) -{ - u32 speed; - struct au1k_private *aup = netdev_priv(dev); - - printk(KERN_ERR "%s: tx timeout\n", dev->name); - speed = aup->speed; - aup->speed = 0; - au1k_irda_set_speed(dev, speed); - aup->tx_full = 0; - netif_wake_queue(dev); -} - -static int au1k_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) -{ - struct if_irda_req *rq = (struct if_irda_req *)ifreq; - struct au1k_private *aup = netdev_priv(dev); - int ret = -EOPNOTSUPP; - - switch (cmd) { - case SIOCSBANDWIDTH: - if (capable(CAP_NET_ADMIN)) { - /* - * We are unable to set the speed if the - * device is not running. - */ - if (aup->open) - ret = au1k_irda_set_speed(dev, - rq->ifr_baudrate); - else { - printk(KERN_ERR "%s ioctl: !netif_running\n", - dev->name); - ret = 0; - } - } - break; - - case SIOCSMEDIABUSY: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - irda_device_set_media_busy(dev, TRUE); - ret = 0; - } - break; - - case SIOCGRECEIVING: - rq->ifr_receiving = 0; - break; - default: - break; - } - return ret; -} - -static const struct net_device_ops au1k_irda_netdev_ops = { - .ndo_open = au1k_irda_start, - .ndo_stop = au1k_irda_stop, - .ndo_start_xmit = au1k_irda_hard_xmit, - .ndo_tx_timeout = au1k_tx_timeout, - .ndo_do_ioctl = au1k_irda_ioctl, -}; - -static int au1k_irda_net_init(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - struct db_dest *pDB, *pDBfree; - int i, err, retval = 0; - dma_addr_t temp; - - err = au1k_irda_init_iobuf(&aup->rx_buff, 14384); - if (err) - goto out1; - - dev->netdev_ops = &au1k_irda_netdev_ops; - - irda_init_max_qos_capabilies(&aup->qos); - - /* The only value we must override it the baudrate */ - aup->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 | - IR_57600 | IR_115200 | IR_576000 | (IR_4000000 << 8); - - aup->qos.min_turn_time.bits = qos_mtt_bits; - irda_qos_bits_to_value(&aup->qos); - - retval = -ENOMEM; - - /* Tx ring follows rx ring + 512 bytes */ - /* we need a 1k aligned buffer */ - aup->rx_ring[0] = (struct ring_dest *) - dma_alloc(2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)), - &temp); - if (!aup->rx_ring[0]) - goto out2; - - /* allocate the data buffers */ - aup->db[0].vaddr = - dma_alloc(MAX_BUF_SIZE * 2 * NUM_IR_DESC, &temp); - if (!aup->db[0].vaddr) - goto out3; - - setup_hw_rings(aup, (u32)aup->rx_ring[0], (u32)aup->rx_ring[0] + 512); - - pDBfree = NULL; - pDB = aup->db; - for (i = 0; i < (2 * NUM_IR_DESC); i++) { - pDB->pnext = pDBfree; - pDBfree = pDB; - pDB->vaddr = - (u32 *)((unsigned)aup->db[0].vaddr + (MAX_BUF_SIZE * i)); - pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr); - pDB++; - } - aup->pDBfree = pDBfree; - - /* attach a data buffer to each descriptor */ - for (i = 0; i < NUM_IR_DESC; i++) { - pDB = GetFreeDB(aup); - if (!pDB) - goto out3; - aup->rx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); - aup->rx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff); - aup->rx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff); - aup->rx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff); - aup->rx_db_inuse[i] = pDB; - } - for (i = 0; i < NUM_IR_DESC; i++) { - pDB = GetFreeDB(aup); - if (!pDB) - goto out3; - aup->tx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); - aup->tx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff); - aup->tx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff); - aup->tx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff); - aup->tx_ring[i]->count_0 = 0; - aup->tx_ring[i]->count_1 = 0; - aup->tx_ring[i]->flags = 0; - aup->tx_db_inuse[i] = pDB; - } - - return 0; - -out3: - dma_free((void *)aup->rx_ring[0], - 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest))); -out2: - kfree(aup->rx_buff.head); -out1: - printk(KERN_ERR "au1k_irda_net_init() failed. Returns %d\n", retval); - return retval; -} - -static int au1k_irda_probe(struct platform_device *pdev) -{ - struct au1k_private *aup; - struct net_device *dev; - struct resource *r; - struct clk *c; - int err; - - dev = alloc_irdadev(sizeof(struct au1k_private)); - if (!dev) - return -ENOMEM; - - aup = netdev_priv(dev); - - aup->platdata = pdev->dev.platform_data; - - err = -EINVAL; - r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!r) - goto out; - - aup->irq_tx = r->start; - - r = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - if (!r) - goto out; - - aup->irq_rx = r->start; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - goto out; - - err = -EBUSY; - aup->ioarea = request_mem_region(r->start, resource_size(r), - pdev->name); - if (!aup->ioarea) - goto out; - - /* bail out early if clock doesn't exist */ - c = clk_get(NULL, "irda_clk"); - if (IS_ERR(c)) { - err = PTR_ERR(c); - goto out; - } - clk_put(c); - - aup->iobase = ioremap_nocache(r->start, resource_size(r)); - if (!aup->iobase) - goto out2; - - dev->irq = aup->irq_rx; - - err = au1k_irda_net_init(dev); - if (err) - goto out3; - err = register_netdev(dev); - if (err) - goto out4; - - platform_set_drvdata(pdev, dev); - - printk(KERN_INFO "IrDA: Registered device %s\n", dev->name); - return 0; - -out4: - dma_free((void *)aup->db[0].vaddr, - MAX_BUF_SIZE * 2 * NUM_IR_DESC); - dma_free((void *)aup->rx_ring[0], - 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest))); - kfree(aup->rx_buff.head); -out3: - iounmap(aup->iobase); -out2: - release_resource(aup->ioarea); - kfree(aup->ioarea); -out: - free_netdev(dev); - return err; -} - -static int au1k_irda_remove(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct au1k_private *aup = netdev_priv(dev); - - unregister_netdev(dev); - - dma_free((void *)aup->db[0].vaddr, - MAX_BUF_SIZE * 2 * NUM_IR_DESC); - dma_free((void *)aup->rx_ring[0], - 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest))); - kfree(aup->rx_buff.head); - - iounmap(aup->iobase); - release_resource(aup->ioarea); - kfree(aup->ioarea); - - free_netdev(dev); - - return 0; -} - -static struct platform_driver au1k_irda_driver = { - .driver = { - .name = "au1000-irda", - }, - .probe = au1k_irda_probe, - .remove = au1k_irda_remove, -}; - -module_platform_driver(au1k_irda_driver); - -MODULE_AUTHOR("Pete Popov "); -MODULE_DESCRIPTION("Au1000 IrDA Device Driver"); diff --git a/drivers/net/irda/bfin_sir.c b/drivers/net/irda/bfin_sir.c deleted file mode 100644 index 3151b580dbd6..000000000000 --- a/drivers/net/irda/bfin_sir.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * Blackfin Infra-red Driver - * - * Copyright 2006-2009 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - * - */ -#include "bfin_sir.h" - -#ifdef CONFIG_SIR_BFIN_DMA -#define DMA_SIR_RX_XCNT 10 -#define DMA_SIR_RX_YCNT (PAGE_SIZE / DMA_SIR_RX_XCNT) -#define DMA_SIR_RX_FLUSH_JIFS (HZ * 4 / 250) -#endif - -#if ANOMALY_05000447 -static int max_rate = 57600; -#else -static int max_rate = 115200; -#endif - -static void turnaround_delay(int mtt) -{ - long ticks; - - mtt = mtt < 10000 ? 10000 : mtt; - ticks = 1 + mtt / (USEC_PER_SEC / HZ); - schedule_timeout_uninterruptible(ticks); -} - -static void bfin_sir_init_ports(struct bfin_sir_port *sp, struct platform_device *pdev) -{ - int i; - struct resource *res; - - for (i = 0; i < pdev->num_resources; i++) { - res = &pdev->resource[i]; - switch (res->flags) { - case IORESOURCE_MEM: - sp->membase = (void __iomem *)res->start; - break; - case IORESOURCE_IRQ: - sp->irq = res->start; - break; - case IORESOURCE_DMA: - sp->rx_dma_channel = res->start; - sp->tx_dma_channel = res->end; - break; - default: - break; - } - } - - sp->clk = get_sclk(); -#ifdef CONFIG_SIR_BFIN_DMA - sp->tx_done = 1; - init_timer(&(sp->rx_dma_timer)); -#endif -} - -static void bfin_sir_stop_tx(struct bfin_sir_port *port) -{ -#ifdef CONFIG_SIR_BFIN_DMA - disable_dma(port->tx_dma_channel); -#endif - - while (!(UART_GET_LSR(port) & THRE)) { - cpu_relax(); - continue; - } - - UART_CLEAR_IER(port, ETBEI); -} - -static void bfin_sir_enable_tx(struct bfin_sir_port *port) -{ - UART_SET_IER(port, ETBEI); -} - -static void bfin_sir_stop_rx(struct bfin_sir_port *port) -{ - UART_CLEAR_IER(port, ERBFI); -} - -static void bfin_sir_enable_rx(struct bfin_sir_port *port) -{ - UART_SET_IER(port, ERBFI); -} - -static int bfin_sir_set_speed(struct bfin_sir_port *port, int speed) -{ - int ret = -EINVAL; - unsigned int quot; - unsigned short val, lsr, lcr; - static int utime; - int count = 10; - - lcr = WLS(8); - - switch (speed) { - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: - - /* - * IRDA is not affected by anomaly 05000230, so there is no - * need to tweak the divisor like he UART driver (which will - * slightly speed up the baud rate on us). - */ - quot = (port->clk + (8 * speed)) / (16 * speed); - - do { - udelay(utime); - lsr = UART_GET_LSR(port); - } while (!(lsr & TEMT) && count--); - - /* The useconds for 1 bits to transmit */ - utime = 1000000 / speed + 1; - - /* Clear UCEN bit to reset the UART state machine - * and control registers - */ - val = UART_GET_GCTL(port); - val &= ~UCEN; - UART_PUT_GCTL(port, val); - - /* Set DLAB in LCR to Access THR RBR IER */ - UART_SET_DLAB(port); - SSYNC(); - - UART_PUT_DLL(port, quot & 0xFF); - UART_PUT_DLH(port, (quot >> 8) & 0xFF); - SSYNC(); - - /* Clear DLAB in LCR */ - UART_CLEAR_DLAB(port); - SSYNC(); - - UART_PUT_LCR(port, lcr); - - val = UART_GET_GCTL(port); - val |= UCEN; - UART_PUT_GCTL(port, val); - - ret = 0; - break; - default: - printk(KERN_WARNING "bfin_sir: Invalid speed %d\n", speed); - break; - } - - val = UART_GET_GCTL(port); - /* If not add the 'RPOLC', we can't catch the receive interrupt. - * It's related with the HW layout and the IR transiver. - */ - val |= UMOD_IRDA | RPOLC; - UART_PUT_GCTL(port, val); - return ret; -} - -static int bfin_sir_is_receiving(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - if (!(UART_GET_IER(port) & ERBFI)) - return 0; - return self->rx_buff.state != OUTSIDE_FRAME; -} - -#ifdef CONFIG_SIR_BFIN_PIO -static void bfin_sir_tx_chars(struct net_device *dev) -{ - unsigned int chr; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - if (self->tx_buff.len != 0) { - chr = *(self->tx_buff.data); - UART_PUT_CHAR(port, chr); - self->tx_buff.data++; - self->tx_buff.len--; - } else { - self->stats.tx_packets++; - self->stats.tx_bytes += self->tx_buff.data - self->tx_buff.head; - if (self->newspeed) { - bfin_sir_set_speed(port, self->newspeed); - self->speed = self->newspeed; - self->newspeed = 0; - } - bfin_sir_stop_tx(port); - bfin_sir_enable_rx(port); - /* I'm hungry! */ - netif_wake_queue(dev); - } -} - -static void bfin_sir_rx_chars(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - unsigned char ch; - - UART_CLEAR_LSR(port); - ch = UART_GET_CHAR(port); - async_unwrap_char(dev, &self->stats, &self->rx_buff, ch); -} - -static irqreturn_t bfin_sir_rx_int(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - spin_lock(&self->lock); - while ((UART_GET_LSR(port) & DR)) - bfin_sir_rx_chars(dev); - spin_unlock(&self->lock); - - return IRQ_HANDLED; -} - -static irqreturn_t bfin_sir_tx_int(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - spin_lock(&self->lock); - if (UART_GET_LSR(port) & THRE) - bfin_sir_tx_chars(dev); - spin_unlock(&self->lock); - - return IRQ_HANDLED; -} -#endif /* CONFIG_SIR_BFIN_PIO */ - -#ifdef CONFIG_SIR_BFIN_DMA -static void bfin_sir_dma_tx_chars(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - if (!port->tx_done) - return; - port->tx_done = 0; - - if (self->tx_buff.len == 0) { - self->stats.tx_packets++; - if (self->newspeed) { - bfin_sir_set_speed(port, self->newspeed); - self->speed = self->newspeed; - self->newspeed = 0; - } - bfin_sir_enable_rx(port); - port->tx_done = 1; - netif_wake_queue(dev); - return; - } - - blackfin_dcache_flush_range((unsigned long)(self->tx_buff.data), - (unsigned long)(self->tx_buff.data+self->tx_buff.len)); - set_dma_config(port->tx_dma_channel, - set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, - INTR_ON_BUF, DIMENSION_LINEAR, DATA_SIZE_8, - DMA_SYNC_RESTART)); - set_dma_start_addr(port->tx_dma_channel, - (unsigned long)(self->tx_buff.data)); - set_dma_x_count(port->tx_dma_channel, self->tx_buff.len); - set_dma_x_modify(port->tx_dma_channel, 1); - enable_dma(port->tx_dma_channel); -} - -static irqreturn_t bfin_sir_dma_tx_int(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - spin_lock(&self->lock); - if (!(get_dma_curr_irqstat(port->tx_dma_channel) & DMA_RUN)) { - clear_dma_irqstat(port->tx_dma_channel); - bfin_sir_stop_tx(port); - - self->stats.tx_packets++; - self->stats.tx_bytes += self->tx_buff.len; - self->tx_buff.len = 0; - if (self->newspeed) { - bfin_sir_set_speed(port, self->newspeed); - self->speed = self->newspeed; - self->newspeed = 0; - } - bfin_sir_enable_rx(port); - /* I'm hungry! */ - netif_wake_queue(dev); - port->tx_done = 1; - } - spin_unlock(&self->lock); - - return IRQ_HANDLED; -} - -static void bfin_sir_dma_rx_chars(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - int i; - - UART_CLEAR_LSR(port); - - for (i = port->rx_dma_buf.head; i < port->rx_dma_buf.tail; i++) - async_unwrap_char(dev, &self->stats, &self->rx_buff, port->rx_dma_buf.buf[i]); -} - -void bfin_sir_rx_dma_timeout(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - int x_pos, pos; - unsigned long flags; - - spin_lock_irqsave(&self->lock, flags); - x_pos = DMA_SIR_RX_XCNT - get_dma_curr_xcount(port->rx_dma_channel); - if (x_pos == DMA_SIR_RX_XCNT) - x_pos = 0; - - pos = port->rx_dma_nrows * DMA_SIR_RX_XCNT + x_pos; - - if (pos > port->rx_dma_buf.tail) { - port->rx_dma_buf.tail = pos; - bfin_sir_dma_rx_chars(dev); - port->rx_dma_buf.head = port->rx_dma_buf.tail; - } - spin_unlock_irqrestore(&self->lock, flags); -} - -static irqreturn_t bfin_sir_dma_rx_int(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - unsigned short irqstat; - - spin_lock(&self->lock); - - port->rx_dma_nrows++; - port->rx_dma_buf.tail = DMA_SIR_RX_XCNT * port->rx_dma_nrows; - bfin_sir_dma_rx_chars(dev); - if (port->rx_dma_nrows >= DMA_SIR_RX_YCNT) { - port->rx_dma_nrows = 0; - port->rx_dma_buf.tail = 0; - } - port->rx_dma_buf.head = port->rx_dma_buf.tail; - - irqstat = get_dma_curr_irqstat(port->rx_dma_channel); - clear_dma_irqstat(port->rx_dma_channel); - spin_unlock(&self->lock); - - mod_timer(&port->rx_dma_timer, jiffies + DMA_SIR_RX_FLUSH_JIFS); - return IRQ_HANDLED; -} -#endif /* CONFIG_SIR_BFIN_DMA */ - -static int bfin_sir_startup(struct bfin_sir_port *port, struct net_device *dev) -{ -#ifdef CONFIG_SIR_BFIN_DMA - dma_addr_t dma_handle; -#endif /* CONFIG_SIR_BFIN_DMA */ - - if (request_dma(port->rx_dma_channel, "BFIN_UART_RX") < 0) { - dev_warn(&dev->dev, "Unable to attach SIR RX DMA channel\n"); - return -EBUSY; - } - - if (request_dma(port->tx_dma_channel, "BFIN_UART_TX") < 0) { - dev_warn(&dev->dev, "Unable to attach SIR TX DMA channel\n"); - free_dma(port->rx_dma_channel); - return -EBUSY; - } - -#ifdef CONFIG_SIR_BFIN_DMA - - set_dma_callback(port->rx_dma_channel, bfin_sir_dma_rx_int, dev); - set_dma_callback(port->tx_dma_channel, bfin_sir_dma_tx_int, dev); - - port->rx_dma_buf.buf = dma_alloc_coherent(NULL, PAGE_SIZE, - &dma_handle, GFP_DMA); - port->rx_dma_buf.head = 0; - port->rx_dma_buf.tail = 0; - port->rx_dma_nrows = 0; - - set_dma_config(port->rx_dma_channel, - set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO, - INTR_ON_ROW, DIMENSION_2D, - DATA_SIZE_8, DMA_SYNC_RESTART)); - set_dma_x_count(port->rx_dma_channel, DMA_SIR_RX_XCNT); - set_dma_x_modify(port->rx_dma_channel, 1); - set_dma_y_count(port->rx_dma_channel, DMA_SIR_RX_YCNT); - set_dma_y_modify(port->rx_dma_channel, 1); - set_dma_start_addr(port->rx_dma_channel, (unsigned long)port->rx_dma_buf.buf); - enable_dma(port->rx_dma_channel); - - port->rx_dma_timer.data = (unsigned long)(dev); - port->rx_dma_timer.function = (void *)bfin_sir_rx_dma_timeout; - -#else - - if (request_irq(port->irq, bfin_sir_rx_int, 0, "BFIN_SIR_RX", dev)) { - dev_warn(&dev->dev, "Unable to attach SIR RX interrupt\n"); - return -EBUSY; - } - - if (request_irq(port->irq+1, bfin_sir_tx_int, 0, "BFIN_SIR_TX", dev)) { - dev_warn(&dev->dev, "Unable to attach SIR TX interrupt\n"); - free_irq(port->irq, dev); - return -EBUSY; - } -#endif - - return 0; -} - -static void bfin_sir_shutdown(struct bfin_sir_port *port, struct net_device *dev) -{ - unsigned short val; - - bfin_sir_stop_rx(port); - - val = UART_GET_GCTL(port); - val &= ~(UCEN | UMOD_MASK | RPOLC); - UART_PUT_GCTL(port, val); - -#ifdef CONFIG_SIR_BFIN_DMA - disable_dma(port->tx_dma_channel); - disable_dma(port->rx_dma_channel); - del_timer(&(port->rx_dma_timer)); - dma_free_coherent(NULL, PAGE_SIZE, port->rx_dma_buf.buf, 0); -#else - free_irq(port->irq+1, dev); - free_irq(port->irq, dev); -#endif - free_dma(port->tx_dma_channel); - free_dma(port->rx_dma_channel); -} - -#ifdef CONFIG_PM -static int bfin_sir_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct bfin_sir_port *sir_port; - struct net_device *dev; - struct bfin_sir_self *self; - - sir_port = platform_get_drvdata(pdev); - if (!sir_port) - return 0; - - dev = sir_port->dev; - self = netdev_priv(dev); - if (self->open) { - flush_work(&self->work); - bfin_sir_shutdown(self->sir_port, dev); - netif_device_detach(dev); - } - - return 0; -} -static int bfin_sir_resume(struct platform_device *pdev) -{ - struct bfin_sir_port *sir_port; - struct net_device *dev; - struct bfin_sir_self *self; - struct bfin_sir_port *port; - - sir_port = platform_get_drvdata(pdev); - if (!sir_port) - return 0; - - dev = sir_port->dev; - self = netdev_priv(dev); - port = self->sir_port; - if (self->open) { - if (self->newspeed) { - self->speed = self->newspeed; - self->newspeed = 0; - } - bfin_sir_startup(port, dev); - bfin_sir_set_speed(port, 9600); - bfin_sir_enable_rx(port); - netif_device_attach(dev); - } - return 0; -} -#else -#define bfin_sir_suspend NULL -#define bfin_sir_resume NULL -#endif - -static void bfin_sir_send_work(struct work_struct *work) -{ - struct bfin_sir_self *self = container_of(work, struct bfin_sir_self, work); - struct net_device *dev = self->sir_port->dev; - struct bfin_sir_port *port = self->sir_port; - unsigned short val; - int tx_cnt = 10; - - while (bfin_sir_is_receiving(dev) && --tx_cnt) - turnaround_delay(self->mtt); - - bfin_sir_stop_rx(port); - - /* To avoid losting RX interrupt, we reset IR function before - * sending data. We also can set the speed, which will - * reset all the UART. - */ - val = UART_GET_GCTL(port); - val &= ~(UMOD_MASK | RPOLC); - UART_PUT_GCTL(port, val); - SSYNC(); - val |= UMOD_IRDA | RPOLC; - UART_PUT_GCTL(port, val); - SSYNC(); - /* bfin_sir_set_speed(port, self->speed); */ - -#ifdef CONFIG_SIR_BFIN_DMA - bfin_sir_dma_tx_chars(dev); -#endif - bfin_sir_enable_tx(port); - netif_trans_update(dev); -} - -static int bfin_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - int speed = irda_get_next_speed(skb); - - netif_stop_queue(dev); - - self->mtt = irda_get_mtt(skb); - - if (speed != self->speed && speed != -1) - self->newspeed = speed; - - self->tx_buff.data = self->tx_buff.head; - if (skb->len == 0) - self->tx_buff.len = 0; - else - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); - - schedule_work(&self->work); - dev_kfree_skb(skb); - - return 0; -} - -static int bfin_sir_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) -{ - struct if_irda_req *rq = (struct if_irda_req *)ifreq; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: - if (capable(CAP_NET_ADMIN)) { - if (self->open) { - ret = bfin_sir_set_speed(port, rq->ifr_baudrate); - bfin_sir_enable_rx(port); - } else { - dev_warn(&dev->dev, "SIOCSBANDWIDTH: !netif_running\n"); - ret = 0; - } - } - break; - - case SIOCSMEDIABUSY: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - irda_device_set_media_busy(dev, TRUE); - ret = 0; - } - break; - - case SIOCGRECEIVING: - rq->ifr_receiving = bfin_sir_is_receiving(dev); - break; - - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - -static struct net_device_stats *bfin_sir_stats(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - - return &self->stats; -} - -static int bfin_sir_open(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - int err; - - self->newspeed = 0; - self->speed = 9600; - - spin_lock_init(&self->lock); - - err = bfin_sir_startup(port, dev); - if (err) - goto err_startup; - - bfin_sir_set_speed(port, 9600); - - self->irlap = irlap_open(dev, &self->qos, DRIVER_NAME); - if (!self->irlap) { - err = -ENOMEM; - goto err_irlap; - } - - INIT_WORK(&self->work, bfin_sir_send_work); - - /* - * Now enable the interrupt then start the queue - */ - self->open = 1; - bfin_sir_enable_rx(port); - - netif_start_queue(dev); - - return 0; - -err_irlap: - self->open = 0; - bfin_sir_shutdown(port, dev); -err_startup: - return err; -} - -static int bfin_sir_stop(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - - flush_work(&self->work); - bfin_sir_shutdown(self->sir_port, dev); - - if (self->rxskb) { - dev_kfree_skb(self->rxskb); - self->rxskb = NULL; - } - - /* Stop IrLAP */ - if (self->irlap) { - irlap_close(self->irlap); - self->irlap = NULL; - } - - netif_stop_queue(dev); - self->open = 0; - - return 0; -} - -static int bfin_sir_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL); - if (!io->head) - return -ENOMEM; - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - return 0; -} - -static const struct net_device_ops bfin_sir_ndo = { - .ndo_open = bfin_sir_open, - .ndo_stop = bfin_sir_stop, - .ndo_start_xmit = bfin_sir_hard_xmit, - .ndo_do_ioctl = bfin_sir_ioctl, - .ndo_get_stats = bfin_sir_stats, -}; - -static int bfin_sir_probe(struct platform_device *pdev) -{ - struct net_device *dev; - struct bfin_sir_self *self; - unsigned int baudrate_mask; - struct bfin_sir_port *sir_port; - int err; - - if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(per) && \ - per[pdev->id][3] == pdev->id) { - err = peripheral_request_list(per[pdev->id], DRIVER_NAME); - if (err) - return err; - } else { - dev_err(&pdev->dev, "Invalid pdev id, please check board file\n"); - return -ENODEV; - } - - err = -ENOMEM; - sir_port = kmalloc(sizeof(*sir_port), GFP_KERNEL); - if (!sir_port) - goto err_mem_0; - - bfin_sir_init_ports(sir_port, pdev); - - dev = alloc_irdadev(sizeof(*self)); - if (!dev) - goto err_mem_1; - - self = netdev_priv(dev); - self->dev = &pdev->dev; - self->sir_port = sir_port; - sir_port->dev = dev; - - err = bfin_sir_init_iobuf(&self->rx_buff, IRDA_SKB_MAX_MTU); - if (err) - goto err_mem_2; - err = bfin_sir_init_iobuf(&self->tx_buff, IRDA_SIR_MAX_FRAME); - if (err) - goto err_mem_3; - - dev->netdev_ops = &bfin_sir_ndo; - dev->irq = sir_port->irq; - - irda_init_max_qos_capabilies(&self->qos); - - baudrate_mask = IR_9600; - - switch (max_rate) { - case 115200: - baudrate_mask |= IR_115200; - case 57600: - baudrate_mask |= IR_57600; - case 38400: - baudrate_mask |= IR_38400; - case 19200: - baudrate_mask |= IR_19200; - case 9600: - break; - default: - dev_warn(&pdev->dev, "Invalid maximum baud rate, using 9600\n"); - } - - self->qos.baud_rate.bits &= baudrate_mask; - - self->qos.min_turn_time.bits = 1; /* 10 ms or more */ - - irda_qos_bits_to_value(&self->qos); - - err = register_netdev(dev); - - if (err) { - kfree(self->tx_buff.head); -err_mem_3: - kfree(self->rx_buff.head); -err_mem_2: - free_netdev(dev); -err_mem_1: - kfree(sir_port); -err_mem_0: - peripheral_free_list(per[pdev->id]); - } else - platform_set_drvdata(pdev, sir_port); - - return err; -} - -static int bfin_sir_remove(struct platform_device *pdev) -{ - struct bfin_sir_port *sir_port; - struct net_device *dev = NULL; - struct bfin_sir_self *self; - - sir_port = platform_get_drvdata(pdev); - if (!sir_port) - return 0; - dev = sir_port->dev; - self = netdev_priv(dev); - unregister_netdev(dev); - kfree(self->tx_buff.head); - kfree(self->rx_buff.head); - free_netdev(dev); - kfree(sir_port); - - return 0; -} - -static struct platform_driver bfin_ir_driver = { - .probe = bfin_sir_probe, - .remove = bfin_sir_remove, - .suspend = bfin_sir_suspend, - .resume = bfin_sir_resume, - .driver = { - .name = DRIVER_NAME, - }, -}; - -module_platform_driver(bfin_ir_driver); - -module_param(max_rate, int, 0); -MODULE_PARM_DESC(max_rate, "Maximum baud rate (115200, 57600, 38400, 19200, 9600)"); - -MODULE_AUTHOR("Graf Yang "); -MODULE_DESCRIPTION("Blackfin IrDA driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/bfin_sir.h b/drivers/net/irda/bfin_sir.h deleted file mode 100644 index d47cf14bb4a5..000000000000 --- a/drivers/net/irda/bfin_sir.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Blackfin Infra-red Driver - * - * Copyright 2006-2009 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#undef DRIVER_NAME - -#ifdef CONFIG_SIR_BFIN_DMA -struct dma_rx_buf { - char *buf; - int head; - int tail; -}; -#endif - -struct bfin_sir_port { - unsigned char __iomem *membase; - unsigned int irq; - unsigned int lsr; - unsigned long clk; - struct net_device *dev; -#ifdef CONFIG_SIR_BFIN_DMA - int tx_done; - struct dma_rx_buf rx_dma_buf; - struct timer_list rx_dma_timer; - int rx_dma_nrows; -#endif - unsigned int tx_dma_channel; - unsigned int rx_dma_channel; -}; - -struct bfin_sir_port_res { - unsigned long base_addr; - int irq; - unsigned int rx_dma_channel; - unsigned int tx_dma_channel; -}; - -struct bfin_sir_self { - struct bfin_sir_port *sir_port; - spinlock_t lock; - unsigned int open; - int speed; - int newspeed; - - struct sk_buff *txskb; - struct sk_buff *rxskb; - struct net_device_stats stats; - struct device *dev; - struct irlap_cb *irlap; - struct qos_info qos; - - iobuff_t tx_buff; - iobuff_t rx_buff; - - struct work_struct work; - int mtt; -}; - -#define DRIVER_NAME "bfin_sir" - -#include - -static const unsigned short per[][4] = { - /* rx pin tx pin NULL uart_number */ - {P_UART0_RX, P_UART0_TX, 0, 0}, - {P_UART1_RX, P_UART1_TX, 0, 1}, - {P_UART2_RX, P_UART2_TX, 0, 2}, - {P_UART3_RX, P_UART3_TX, 0, 3}, -}; diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c deleted file mode 100644 index b337e6d23a88..000000000000 --- a/drivers/net/irda/donauboe.c +++ /dev/null @@ -1,1732 +0,0 @@ -/***************************************************************** - * - * Filename: donauboe.c - * Version: 2.17 - * Description: Driver for the Toshiba OBOE (or type-O or 701) - * FIR Chipset, also supports the DONAUOBOE (type-DO - * or d01) FIR chipset which as far as I know is - * register compatible. - * Documentation: http://libxg.free.fr/irda/lib-irda.html - * Status: Experimental. - * Author: James McKenzie - * Created at: Sat May 8 12:35:27 1999 - * Modified: Paul Bristow - * Modified: Mon Nov 11 19:10:05 1999 - * Modified: James McKenzie - * Modified: Thu Mar 16 12:49:00 2000 (Substantial rewrite) - * Modified: Sat Apr 29 00:23:03 2000 (Added DONAUOBOE support) - * Modified: Wed May 24 23:45:02 2000 (Fixed chipio_t structure) - * Modified: 2.13 Christian Gennerat - * Modified: 2.13 dim jan 07 21:57:39 2001 (tested with kernel 2.4 & irnet/ppp) - * Modified: 2.14 Christian Gennerat - * Modified: 2.14 lun fev 05 17:55:59 2001 (adapted to patch-2.4.1-pre8-irda1) - * Modified: 2.15 Martin Lucina - * Modified: 2.15 Fri Jun 21 20:40:59 2002 (sync with 2.4.18, substantial fixes) - * Modified: 2.16 Martin Lucina - * Modified: 2.16 Sat Jun 22 18:54:29 2002 (fix freeregion, default to verbose) - * Modified: 2.17 Christian Gennerat - * Modified: 2.17 jeu sep 12 08:50:20 2002 (save_flags();cli(); replaced by spinlocks) - * Modified: 2.18 Christian Gennerat - * Modified: 2.18 ven jan 10 03:14:16 2003 Change probe default options - * - * Copyright (c) 1999 James McKenzie, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither James McKenzie nor Cambridge University admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - * Applicable Models : Libretto 100/110CT and many more. - * Toshiba refers to this chip as the type-O IR port, - * or the type-DO IR port. - * - ********************************************************************/ - -/* Look at toshoboe.h (currently in include/net/irda) for details of */ -/* Where to get documentation on the chip */ - -/* See below for a description of the logic in this driver */ - -/* User servicable parts */ -/* USE_PROBE Create the code which probes the chip and does a few tests */ -/* do_probe module parameter Enable this code */ -/* Probe code is very useful for understanding how the hardware works */ -/* Use it with various combinations of TT_LEN, RX_LEN */ -/* Strongly recommended, disable if the probe fails on your machine */ -/* and send me the output of dmesg */ -#define USE_PROBE 1 -#undef USE_PROBE - -/* Trace Transmit ring, interrupts, Receive ring or not ? */ -#define PROBE_VERBOSE 1 - -/* Debug option, examine sent and received raw data */ -/* Irdadump is better, but does not see all packets. enable it if you want. */ -#undef DUMP_PACKETS - -/* MIR mode has not been tested. Some behaviour is different */ -/* Seems to work against an Ericsson R520 for me. -Martin */ -#define USE_MIR - -/* Schedule back to back hardware transmits wherever possible, otherwise */ -/* we need an interrupt for every frame, unset if oboe works for a bit and */ -/* then hangs */ -#define OPTIMIZE_TX - -/* Set the number of slots in the rings */ -/* If you get rx/tx fifo overflows at high bitrates, you can try increasing */ -/* these */ - -#define RING_SIZE (OBOE_RING_SIZE_RX8 | OBOE_RING_SIZE_TX8) -#define TX_SLOTS 8 -#define RX_SLOTS 8 - - -/* Less user servicable parts below here */ - -/* Test, Transmit and receive buffer sizes, adjust at your peril */ -/* remarks: nfs usually needs 1k blocks */ -/* remarks: in SIR mode, CRC is received, -> RX_LEN=TX_LEN+2 */ -/* remarks: test accepts large blocks. Standard is 0x80 */ -/* When TT_LEN > RX_LEN (SIR mode) data is stored in successive slots. */ -/* When 3 or more slots are needed for each test packet, */ -/* data received in the first slots is overwritten, even */ -/* if OBOE_CTL_RX_HW_OWNS is not set, without any error! */ -#define TT_LEN 0x80 -#define TX_LEN 0xc00 -#define RX_LEN 0xc04 -/* Real transmitted length (SIR mode) is about 14+(2%*TX_LEN) more */ -/* long than user-defined length (see async_wrap_skb) and is less then 4K */ -/* Real received length is (max RX_LEN) differs from user-defined */ -/* length only b the CRC (2 or 4 bytes) */ -#define BUF_SAFETY 0x7a -#define RX_BUF_SZ (RX_LEN) -#define TX_BUF_SZ (TX_LEN+BUF_SAFETY) - - -/* Logic of the netdev part of this driver */ - -/* The RX ring is filled with buffers, when a packet arrives */ -/* it is DMA'd into the buffer which is marked used and RxDone called */ -/* RxDone forms an skb (and checks the CRC if in SIR mode) and ships */ -/* the packet off upstairs */ - -/* The transmitter on the oboe chip can work in one of two modes */ -/* for each ring->tx[] the transmitter can either */ -/* a) transmit the packet, leave the trasmitter enabled and proceed to */ -/* the next ring */ -/* OR */ -/* b) transmit the packet, switch off the transmitter and issue TxDone */ - -/* All packets are entered into the ring in mode b), if the ring was */ -/* empty the transmitter is started. */ - -/* If OPTIMIZE_TX is defined then in TxDone if the ring contains */ -/* more than one packet, all but the last are set to mode a) [HOWEVER */ -/* the hardware may not notice this, this is why we start in mode b) ] */ -/* then restart the transmitter */ - -/* If OPTIMIZE_TX is not defined then we just restart the transmitter */ -/* if the ring isn't empty */ - -/* Speed changes are delayed until the TxRing is empty */ -/* mtt is handled by generating packets with bad CRCs, before the data */ - -/* TODO: */ -/* check the mtt works ok */ -/* finish the watchdog */ - -/* No user servicable parts below here */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -//#include -//#include -#include -#include - -#include "donauboe.h" - -#define INB(port) inb_p(port) -#define OUTB(val,port) outb_p(val,port) -#define OUTBP(val,port) outb_p(val,port) - -#define PROMPT OUTB(OBOE_PROMPT_BIT,OBOE_PROMPT); - -#if PROBE_VERBOSE -#define PROBE_DEBUG(args...) (printk (args)) -#else -#define PROBE_DEBUG(args...) ; -#endif - -/* Set the DMA to be byte at a time */ -#define CONFIG0H_DMA_OFF OBOE_CONFIG0H_RCVANY -#define CONFIG0H_DMA_ON_NORX CONFIG0H_DMA_OFF| OBOE_CONFIG0H_ENDMAC -#define CONFIG0H_DMA_ON CONFIG0H_DMA_ON_NORX | OBOE_CONFIG0H_ENRX - -static const struct pci_device_id toshoboe_pci_tbl[] = { - { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIR701, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIRD01, PCI_ANY_ID, PCI_ANY_ID, }, - { } /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(pci, toshoboe_pci_tbl); - -#define DRIVER_NAME "toshoboe" -static char *driver_name = DRIVER_NAME; - -static int max_baud = 4000000; -#ifdef USE_PROBE -static bool do_probe = false; -#endif - - -/**********************************************************************/ -static int -toshoboe_checkfcs (unsigned char *buf, int len) -{ - int i; - union - { - __u16 value; - __u8 bytes[2]; - } - fcs; - - fcs.value = INIT_FCS; - - for (i = 0; i < len; ++i) - fcs.value = irda_fcs (fcs.value, *(buf++)); - - return fcs.value == GOOD_FCS; -} - -/***********************************************************************/ -/* Generic chip handling code */ -#ifdef DUMP_PACKETS -static unsigned char dump[50]; -static void -_dumpbufs (unsigned char *data, int len, char tete) -{ -int i,j; -char head=tete; -for (i=0;iint_tx, self->int_rx, self->int_txunder, self->int_rxover, - self->int_sip); - printk (KERN_ERR "RX %02x TX %02x RingBase %08x\n", - INB (OBOE_RXSLOT), INB (OBOE_TXSLOT), ringbase); - printk (KERN_ERR "RING_SIZE %02x IER %02x ISR %02x\n", - INB (OBOE_RING_SIZE), INB (OBOE_IER), INB (OBOE_ISR)); - printk (KERN_ERR "CONFIG1 %02x STATUS %02x\n", - INB (OBOE_CONFIG1), INB (OBOE_STATUS)); - printk (KERN_ERR "CONFIG0 %02x%02x ENABLE %02x%02x\n", - INB (OBOE_CONFIG0H), INB (OBOE_CONFIG0L), - INB (OBOE_ENABLEH), INB (OBOE_ENABLEL)); - printk (KERN_ERR "NEW_PCONFIG %02x%02x CURR_PCONFIG %02x%02x\n", - INB (OBOE_NEW_PCONFIGH), INB (OBOE_NEW_PCONFIGL), - INB (OBOE_CURR_PCONFIGH), INB (OBOE_CURR_PCONFIGL)); - printk (KERN_ERR "MAXLEN %02x%02x RXCOUNT %02x%02x\n", - INB (OBOE_MAXLENH), INB (OBOE_MAXLENL), - INB (OBOE_RXCOUNTL), INB (OBOE_RXCOUNTH)); - - if (self->ring) - { - int i; - ringbase = virt_to_bus (self->ring); - printk (KERN_ERR "Ring at %08x:\n", ringbase); - printk (KERN_ERR "RX:"); - for (i = 0; i < RX_SLOTS; ++i) - printk (" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); - printk ("\n"); - printk (KERN_ERR "TX:"); - for (i = 0; i < RX_SLOTS; ++i) - printk (" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); - printk ("\n"); - } -} -#endif - -/*Don't let the chip look at memory */ -static void -toshoboe_disablebm (struct toshoboe_cb *self) -{ - __u8 command; - pci_read_config_byte (self->pdev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_byte (self->pdev, PCI_COMMAND, command); - -} - -/* Shutdown the chip and point the taskfile reg somewhere else */ -static void -toshoboe_stopchip (struct toshoboe_cb *self) -{ - /*Disable interrupts */ - OUTB (0x0, OBOE_IER); - /*Disable DMA, Disable Rx, Disable Tx */ - OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); - /*Disable SIR MIR FIR, Tx and Rx */ - OUTB (0x00, OBOE_ENABLEH); - /*Point the ring somewhere safe */ - OUTB (0x3f, OBOE_RING_BASE2); - OUTB (0xff, OBOE_RING_BASE1); - OUTB (0xff, OBOE_RING_BASE0); - - OUTB (RX_LEN >> 8, OBOE_MAXLENH); - OUTB (RX_LEN & 0xff, OBOE_MAXLENL); - - /*Acknoledge any pending interrupts */ - OUTB (0xff, OBOE_ISR); - - /*Why */ - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - - /*switch it off */ - OUTB (OBOE_CONFIG1_OFF, OBOE_CONFIG1); - - toshoboe_disablebm (self); -} - -/* Transmitter initialization */ -static void -toshoboe_start_DMA (struct toshoboe_cb *self, int opts) -{ - OUTB (0x0, OBOE_ENABLEH); - OUTB (CONFIG0H_DMA_ON | opts, OBOE_CONFIG0H); - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - PROMPT; -} - -/*Set the baud rate */ -static void -toshoboe_setbaud (struct toshoboe_cb *self) -{ - __u16 pconfig = 0; - __u8 config0l = 0; - - pr_debug("%s(%d/%d)\n", __func__, self->speed, self->io.speed); - - switch (self->speed) - { - case 2400: - case 4800: - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: -#ifdef USE_MIR - case 1152000: -#endif - case 4000000: - break; - default: - - printk (KERN_ERR DRIVER_NAME ": switch to unsupported baudrate %d\n", - self->speed); - return; - } - - switch (self->speed) - { - /* For SIR the preamble is done by adding XBOFs */ - /* to the packet */ - /* set to filtered SIR mode, filter looks for BOF and EOF */ - case 2400: - pconfig |= 47 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 4800: - pconfig |= 23 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 9600: - pconfig |= 11 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 19200: - pconfig |= 5 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 38400: - pconfig |= 2 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 57600: - pconfig |= 1 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 115200: - pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - default: - /*Set to packet based reception */ - OUTB (RX_LEN >> 8, OBOE_MAXLENH); - OUTB (RX_LEN & 0xff, OBOE_MAXLENL); - break; - } - - switch (self->speed) - { - case 2400: - case 4800: - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: - config0l = OBOE_CONFIG0L_ENSIR; - if (self->async) - { - /*Set to character based reception */ - /*System will lock if MAXLEN=0 */ - /*so have to be careful */ - OUTB (0x01, OBOE_MAXLENH); - OUTB (0x01, OBOE_MAXLENL); - OUTB (0x00, OBOE_MAXLENH); - } - else - { - /*Set to packet based reception */ - config0l |= OBOE_CONFIG0L_ENSIRF; - OUTB (RX_LEN >> 8, OBOE_MAXLENH); - OUTB (RX_LEN & 0xff, OBOE_MAXLENL); - } - break; - -#ifdef USE_MIR - /* MIR mode */ - /* Set for 16 bit CRC and enable MIR */ - /* Preamble now handled by the chip */ - case 1152000: - pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 8 << OBOE_PCONFIG_WIDTHSHIFT; - pconfig |= 1 << OBOE_PCONFIG_PREAMBLESHIFT; - config0l = OBOE_CONFIG0L_CRC16 | OBOE_CONFIG0L_ENMIR; - break; -#endif - /* FIR mode */ - /* Set for 32 bit CRC and enable FIR */ - /* Preamble handled by the chip */ - case 4000000: - pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; - /* Documentation says 14, but toshiba use 15 in their drivers */ - pconfig |= 15 << OBOE_PCONFIG_PREAMBLESHIFT; - config0l = OBOE_CONFIG0L_ENFIR; - break; - } - - /* Copy into new PHY config buffer */ - OUTBP (pconfig >> 8, OBOE_NEW_PCONFIGH); - OUTB (pconfig & 0xff, OBOE_NEW_PCONFIGL); - OUTB (config0l, OBOE_CONFIG0L); - - /* Now make OBOE copy from new PHY to current PHY */ - OUTB (0x0, OBOE_ENABLEH); - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - PROMPT; - - /* speed change executed */ - self->new_speed = 0; - self->io.speed = self->speed; -} - -/*Let the chip look at memory */ -static void -toshoboe_enablebm (struct toshoboe_cb *self) -{ - pci_set_master (self->pdev); -} - -/*setup the ring */ -static void -toshoboe_initring (struct toshoboe_cb *self) -{ - int i; - - for (i = 0; i < TX_SLOTS; ++i) - { - self->ring->tx[i].len = 0; - self->ring->tx[i].control = 0x00; - self->ring->tx[i].address = virt_to_bus (self->tx_bufs[i]); - } - - for (i = 0; i < RX_SLOTS; ++i) - { - self->ring->rx[i].len = RX_LEN; - self->ring->rx[i].len = 0; - self->ring->rx[i].address = virt_to_bus (self->rx_bufs[i]); - self->ring->rx[i].control = OBOE_CTL_RX_HW_OWNS; - } -} - -static void -toshoboe_resetptrs (struct toshoboe_cb *self) -{ - /* Can reset pointers by twidling DMA */ - OUTB (0x0, OBOE_ENABLEH); - OUTBP (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - - self->rxs = inb_p (OBOE_RXSLOT) & OBOE_SLOT_MASK; - self->txs = inb_p (OBOE_TXSLOT) & OBOE_SLOT_MASK; -} - -/* Called in locked state */ -static void -toshoboe_initptrs (struct toshoboe_cb *self) -{ - - /* spin_lock_irqsave(self->spinlock, flags); */ - /* save_flags (flags); */ - - /* Can reset pointers by twidling DMA */ - toshoboe_resetptrs (self); - - OUTB (0x0, OBOE_ENABLEH); - OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - - self->txpending = 0; - - /* spin_unlock_irqrestore(self->spinlock, flags); */ - /* restore_flags (flags); */ -} - -/* Wake the chip up and get it looking at the rings */ -/* Called in locked state */ -static void -toshoboe_startchip (struct toshoboe_cb *self) -{ - __u32 physaddr; - - toshoboe_initring (self); - toshoboe_enablebm (self); - OUTBP (OBOE_CONFIG1_RESET, OBOE_CONFIG1); - OUTBP (OBOE_CONFIG1_ON, OBOE_CONFIG1); - - /* Stop the clocks */ - OUTB (0, OBOE_ENABLEH); - - /*Set size of rings */ - OUTB (RING_SIZE, OBOE_RING_SIZE); - - /*Acknoledge any pending interrupts */ - OUTB (0xff, OBOE_ISR); - - /*Enable ints */ - OUTB (OBOE_INT_TXDONE | OBOE_INT_RXDONE | - OBOE_INT_TXUNDER | OBOE_INT_RXOVER | OBOE_INT_SIP , OBOE_IER); - - /*Acknoledge any pending interrupts */ - OUTB (0xff, OBOE_ISR); - - /*Set the maximum packet length to 0xfff (4095) */ - OUTB (RX_LEN >> 8, OBOE_MAXLENH); - OUTB (RX_LEN & 0xff, OBOE_MAXLENL); - - /*Shutdown DMA */ - OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); - - /*Find out where the rings live */ - physaddr = virt_to_bus (self->ring); - - IRDA_ASSERT ((physaddr & 0x3ff) == 0, - printk (KERN_ERR DRIVER_NAME "ring not correctly aligned\n"); - return;); - - OUTB ((physaddr >> 10) & 0xff, OBOE_RING_BASE0); - OUTB ((physaddr >> 18) & 0xff, OBOE_RING_BASE1); - OUTB ((physaddr >> 26) & 0x3f, OBOE_RING_BASE2); - - /*Enable DMA controller in byte mode and RX */ - OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); - - /* Start up the clocks */ - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - - /*set to sensible speed */ - self->speed = 9600; - toshoboe_setbaud (self); - toshoboe_initptrs (self); -} - -static void -toshoboe_isntstuck (struct toshoboe_cb *self) -{ -} - -static void -toshoboe_checkstuck (struct toshoboe_cb *self) -{ - unsigned long flags; - - if (0) - { - spin_lock_irqsave(&self->spinlock, flags); - - /* This will reset the chip completely */ - printk (KERN_ERR DRIVER_NAME ": Resetting chip\n"); - - toshoboe_stopchip (self); - toshoboe_startchip (self); - spin_unlock_irqrestore(&self->spinlock, flags); - } -} - -/*Generate packet of about mtt us long */ -static int -toshoboe_makemttpacket (struct toshoboe_cb *self, void *buf, int mtt) -{ - int xbofs; - - xbofs = ((int) (mtt/100)) * (int) (self->speed); - xbofs=xbofs/80000; /*Eight bits per byte, and mtt is in us*/ - xbofs++; - - pr_debug(DRIVER_NAME ": generated mtt of %d bytes for %d us at %d baud\n", - xbofs, mtt, self->speed); - - if (xbofs > TX_LEN) - { - printk (KERN_ERR DRIVER_NAME ": wanted %d bytes MTT but TX_LEN is %d\n", - xbofs, TX_LEN); - xbofs = TX_LEN; - } - - /*xbofs will do for SIR, MIR and FIR,SIR mode doesn't generate a checksum anyway */ - memset (buf, XBOF, xbofs); - - return xbofs; -} - -#ifdef USE_PROBE -/***********************************************************************/ -/* Probe code */ - -static void -toshoboe_dumptx (struct toshoboe_cb *self) -{ - int i; - PROBE_DEBUG(KERN_WARNING "TX:"); - for (i = 0; i < RX_SLOTS; ++i) - PROBE_DEBUG(" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); - PROBE_DEBUG(" [%d]\n",self->speed); -} - -static void -toshoboe_dumprx (struct toshoboe_cb *self, int score) -{ - int i; - PROBE_DEBUG(" %d\nRX:",score); - for (i = 0; i < RX_SLOTS; ++i) - PROBE_DEBUG(" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); - PROBE_DEBUG("\n"); -} - -static inline int -stuff_byte (__u8 byte, __u8 * buf) -{ - switch (byte) - { - case BOF: /* FALLTHROUGH */ - case EOF: /* FALLTHROUGH */ - case CE: - /* Insert transparently coded */ - buf[0] = CE; /* Send link escape */ - buf[1] = byte ^ IRDA_TRANS; /* Complement bit 5 */ - return 2; - /* break; */ - default: - /* Non-special value, no transparency required */ - buf[0] = byte; - return 1; - /* break; */ - } -} - -static irqreturn_t -toshoboe_probeinterrupt (int irq, void *dev_id) -{ - struct toshoboe_cb *self = dev_id; - __u8 irqstat; - - irqstat = INB (OBOE_ISR); - -/* was it us */ - if (!(irqstat & OBOE_INT_MASK)) - return IRQ_NONE; - -/* Ack all the interrupts */ - OUTB (irqstat, OBOE_ISR); - - if (irqstat & OBOE_INT_TXDONE) - { - int txp; - - self->int_tx++; - PROBE_DEBUG("T"); - - txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; - if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) - { - self->int_tx+=100; - PROBE_DEBUG("S"); - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); - } - } - - if (irqstat & OBOE_INT_RXDONE) { - self->int_rx++; - PROBE_DEBUG("R"); } - if (irqstat & OBOE_INT_TXUNDER) { - self->int_txunder++; - PROBE_DEBUG("U"); } - if (irqstat & OBOE_INT_RXOVER) { - self->int_rxover++; - PROBE_DEBUG("O"); } - if (irqstat & OBOE_INT_SIP) { - self->int_sip++; - PROBE_DEBUG("I"); } - return IRQ_HANDLED; -} - -static int -toshoboe_maketestpacket (unsigned char *buf, int badcrc, int fir) -{ - int i; - int len = 0; - union - { - __u16 value; - __u8 bytes[2]; - } - fcs; - - if (fir) - { - memset (buf, 0, TT_LEN); - return TT_LEN; - } - - fcs.value = INIT_FCS; - - memset (buf, XBOF, 10); - len += 10; - buf[len++] = BOF; - - for (i = 0; i < TT_LEN; ++i) - { - len += stuff_byte (i, buf + len); - fcs.value = irda_fcs (fcs.value, i); - } - - len += stuff_byte (fcs.bytes[0] ^ badcrc, buf + len); - len += stuff_byte (fcs.bytes[1] ^ badcrc, buf + len); - buf[len++] = EOF; - len++; - return len; -} - -static int -toshoboe_probefail (struct toshoboe_cb *self, char *msg) -{ - printk (KERN_ERR DRIVER_NAME "probe(%d) failed %s\n",self-> speed, msg); - toshoboe_dumpregs (self); - toshoboe_stopchip (self); - free_irq (self->io.irq, (void *) self); - return 0; -} - -static int -toshoboe_numvalidrcvs (struct toshoboe_cb *self) -{ - int i, ret = 0; - for (i = 0; i < RX_SLOTS; ++i) - if ((self->ring->rx[i].control & 0xe0) == 0) - ret++; - - return ret; -} - -static int -toshoboe_numrcvs (struct toshoboe_cb *self) -{ - int i, ret = 0; - for (i = 0; i < RX_SLOTS; ++i) - if (!(self->ring->rx[i].control & OBOE_CTL_RX_HW_OWNS)) - ret++; - - return ret; -} - -static int -toshoboe_probe (struct toshoboe_cb *self) -{ - int i, j, n; -#ifdef USE_MIR - static const int bauds[] = { 9600, 115200, 4000000, 1152000 }; -#else - static const int bauds[] = { 9600, 115200, 4000000 }; -#endif - unsigned long flags; - - if (request_irq (self->io.irq, toshoboe_probeinterrupt, - self->io.irqflags, "toshoboe", (void *) self)) - { - printk (KERN_ERR DRIVER_NAME ": probe failed to allocate irq %d\n", - self->io.irq); - return 0; - } - - /* test 1: SIR filter and back to back */ - - for (j = 0; j < ARRAY_SIZE(bauds); ++j) - { - int fir = (j > 1); - toshoboe_stopchip (self); - - - spin_lock_irqsave(&self->spinlock, flags); - /*Address is already setup */ - toshoboe_startchip (self); - self->int_rx = self->int_tx = 0; - self->speed = bauds[j]; - toshoboe_setbaud (self); - toshoboe_initptrs (self); - spin_unlock_irqrestore(&self->spinlock, flags); - - self->ring->tx[self->txs].control = -/* (FIR only) OBOE_CTL_TX_SIP needed for switching to next slot */ -/* MIR: all received data is stored in one slot */ - (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX - : OBOE_CTL_TX_HW_OWNS ; - self->ring->tx[self->txs].len = - toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); - self->txs++; - self->txs %= TX_SLOTS; - - self->ring->tx[self->txs].control = - (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_SIP - : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; - self->ring->tx[self->txs].len = - toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); - self->txs++; - self->txs %= TX_SLOTS; - - self->ring->tx[self->txs].control = - (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX - : OBOE_CTL_TX_HW_OWNS ; - self->ring->tx[self->txs].len = - toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); - self->txs++; - self->txs %= TX_SLOTS; - - self->ring->tx[self->txs].control = - (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX - | OBOE_CTL_TX_SIP | OBOE_CTL_TX_BAD_CRC - : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; - self->ring->tx[self->txs].len = - toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); - self->txs++; - self->txs %= TX_SLOTS; - - toshoboe_dumptx (self); - /* Turn on TX and RX and loopback */ - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); - - i = 0; - n = fir ? 1 : 4; - while (toshoboe_numvalidrcvs (self) != n) - { - if (i > 4800) - return toshoboe_probefail (self, "filter test"); - udelay ((9600*(TT_LEN+16))/self->speed); - i++; - } - - n = fir ? 203 : 102; - while ((toshoboe_numrcvs(self) != self->int_rx) || (self->int_tx != n)) - { - if (i > 4800) - return toshoboe_probefail (self, "interrupt test"); - udelay ((9600*(TT_LEN+16))/self->speed); - i++; - } - toshoboe_dumprx (self,i); - - } - - /* test 2: SIR in char at a time */ - - toshoboe_stopchip (self); - self->int_rx = self->int_tx = 0; - - spin_lock_irqsave(&self->spinlock, flags); - toshoboe_startchip (self); - spin_unlock_irqrestore(&self->spinlock, flags); - - self->async = 1; - self->speed = 115200; - toshoboe_setbaud (self); - self->ring->tx[self->txs].control = - OBOE_CTL_TX_RTCENTX | OBOE_CTL_TX_HW_OWNS; - self->ring->tx[self->txs].len = 4; - - ((unsigned char *) self->tx_bufs[self->txs])[0] = 'f'; - ((unsigned char *) self->tx_bufs[self->txs])[1] = 'i'; - ((unsigned char *) self->tx_bufs[self->txs])[2] = 's'; - ((unsigned char *) self->tx_bufs[self->txs])[3] = 'h'; - toshoboe_dumptx (self); - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); - - i = 0; - while (toshoboe_numvalidrcvs (self) != 4) - { - if (i > 100) - return toshoboe_probefail (self, "Async test"); - udelay (100); - i++; - } - - while ((toshoboe_numrcvs (self) != self->int_rx) || (self->int_tx != 1)) - { - if (i > 100) - return toshoboe_probefail (self, "Async interrupt test"); - udelay (100); - i++; - } - toshoboe_dumprx (self,i); - - self->async = 0; - self->speed = 9600; - toshoboe_setbaud (self); - toshoboe_stopchip (self); - - free_irq (self->io.irq, (void *) self); - - printk (KERN_WARNING DRIVER_NAME ": Self test passed ok\n"); - - return 1; -} -#endif - -/******************************************************************/ -/* Netdev style code */ - -/* Transmit something */ -static netdev_tx_t -toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev) -{ - struct toshoboe_cb *self; - __s32 speed; - int mtt, len, ctl; - unsigned long flags; - struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; - - self = netdev_priv(dev); - - IRDA_ASSERT (self != NULL, return NETDEV_TX_OK; ); - - pr_debug("%s.tx:%x(%x)%x\n", - __func__, skb->len, self->txpending, INB(OBOE_ENABLEH)); - if (!cb->magic) { - pr_debug("%s.Not IrLAP:%x\n", __func__, cb->magic); -#ifdef DUMP_PACKETS - _dumpbufs(skb->data,skb->len,'>'); -#endif - } - - /* change speed pending, wait for its execution */ - if (self->new_speed) - return NETDEV_TX_BUSY; - - /* device stopped (apm) wait for restart */ - if (self->stopped) - return NETDEV_TX_BUSY; - - toshoboe_checkstuck (self); - - /* Check if we need to change the speed */ - /* But not now. Wait after transmission if mtt not required */ - speed=irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) - { - spin_lock_irqsave(&self->spinlock, flags); - - if (self->txpending || skb->len) - { - self->new_speed = speed; - pr_debug("%s: Queued TxDone scheduled speed change %d\n" , - __func__, speed); - /* if no data, that's all! */ - if (!skb->len) - { - spin_unlock_irqrestore(&self->spinlock, flags); - dev_kfree_skb (skb); - return NETDEV_TX_OK; - } - /* True packet, go on, but */ - /* do not accept anything before change speed execution */ - netif_stop_queue(dev); - /* ready to process TxDone interrupt */ - spin_unlock_irqrestore(&self->spinlock, flags); - } - else - { - /* idle and no data, change speed now */ - self->speed = speed; - toshoboe_setbaud (self); - spin_unlock_irqrestore(&self->spinlock, flags); - dev_kfree_skb (skb); - return NETDEV_TX_OK; - } - - } - - if ((mtt = irda_get_mtt(skb))) - { - /* This is fair since the queue should be empty anyway */ - spin_lock_irqsave(&self->spinlock, flags); - - if (self->txpending) - { - spin_unlock_irqrestore(&self->spinlock, flags); - return NETDEV_TX_BUSY; - } - - /* If in SIR mode we need to generate a string of XBOFs */ - /* In MIR and FIR we need to generate a string of data */ - /* which we will add a wrong checksum to */ - - mtt = toshoboe_makemttpacket (self, self->tx_bufs[self->txs], mtt); - pr_debug("%s.mtt:%x(%x)%d\n", __func__, skb->len, mtt, self->txpending); - if (mtt) - { - self->ring->tx[self->txs].len = mtt & 0xfff; - - ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; - if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) - { - ctl |= OBOE_CTL_TX_BAD_CRC | OBOE_CTL_TX_SIP ; - } -#ifdef USE_MIR - else if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_MIRON) - { - ctl |= OBOE_CTL_TX_BAD_CRC; - } -#endif - self->ring->tx[self->txs].control = ctl; - - OUTB (0x0, OBOE_ENABLEH); - /* It is only a timer. Do not send mtt packet outside! */ - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); - - self->txpending++; - - self->txs++; - self->txs %= TX_SLOTS; - - } - else - { - printk(KERN_ERR DRIVER_NAME ": problem with mtt packet - ignored\n"); - } - spin_unlock_irqrestore(&self->spinlock, flags); - } - -#ifdef DUMP_PACKETS -dumpbufs(skb->data,skb->len,'>'); -#endif - - spin_lock_irqsave(&self->spinlock, flags); - - if (self->ring->tx[self->txs].control & OBOE_CTL_TX_HW_OWNS) - { - pr_debug("%s.ful:%x(%x)%x\n", - __func__, skb->len, self->ring->tx[self->txs].control, - self->txpending); - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); - spin_unlock_irqrestore(&self->spinlock, flags); - return NETDEV_TX_BUSY; - } - - if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_SIRON) - { - len = async_wrap_skb (skb, self->tx_bufs[self->txs], TX_BUF_SZ); - } - else - { - len = skb->len; - skb_copy_from_linear_data(skb, self->tx_bufs[self->txs], len); - } - self->ring->tx[self->txs].len = len & 0x0fff; - - /*Sometimes the HW doesn't see us assert RTCENTX in the interrupt code */ - /*later this plays safe, we garuntee the last packet to be transmitted */ - /*has RTCENTX set */ - - ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; - if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) - { - ctl |= OBOE_CTL_TX_SIP ; - } - self->ring->tx[self->txs].control = ctl; - - /* If transmitter is idle start in one-shot mode */ - - if (!self->txpending) - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); - - self->txpending++; - - self->txs++; - self->txs %= TX_SLOTS; - - spin_unlock_irqrestore(&self->spinlock, flags); - dev_kfree_skb (skb); - - return NETDEV_TX_OK; -} - -/*interrupt handler */ -static irqreturn_t -toshoboe_interrupt (int irq, void *dev_id) -{ - struct toshoboe_cb *self = dev_id; - __u8 irqstat; - struct sk_buff *skb = NULL; - - irqstat = INB (OBOE_ISR); - -/* was it us */ - if (!(irqstat & OBOE_INT_MASK)) - return IRQ_NONE; - -/* Ack all the interrupts */ - OUTB (irqstat, OBOE_ISR); - - toshoboe_isntstuck (self); - -/* Txdone */ - if (irqstat & OBOE_INT_TXDONE) - { - int txp, txpc; - int i; - - txp = self->txpending; - self->txpending = 0; - - for (i = 0; i < TX_SLOTS; ++i) - { - if (self->ring->tx[i].control & OBOE_CTL_TX_HW_OWNS) - self->txpending++; - } - pr_debug("%s.txd(%x)%x/%x\n", __func__, irqstat, txp, self->txpending); - - txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; - - /* Got anything queued ? start it together */ - if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) - { - txpc = txp; -#ifdef OPTIMIZE_TX - while (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) - { - txp = txpc; - txpc++; - txpc %= TX_SLOTS; - self->netdev->stats.tx_packets++; - if (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) - self->ring->tx[txp].control &= ~OBOE_CTL_TX_RTCENTX; - } - self->netdev->stats.tx_packets--; -#else - self->netdev->stats.tx_packets++; -#endif - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); - } - - if ((!self->txpending) && (self->new_speed)) - { - self->speed = self->new_speed; - pr_debug("%s: Executed TxDone scheduled speed change %d\n", - __func__, self->speed); - toshoboe_setbaud (self); - } - - /* Tell network layer that we want more frames */ - if (!self->new_speed) - netif_wake_queue(self->netdev); - } - - if (irqstat & OBOE_INT_RXDONE) - { - while (!(self->ring->rx[self->rxs].control & OBOE_CTL_RX_HW_OWNS)) - { - int len = self->ring->rx[self->rxs].len; - skb = NULL; - pr_debug("%s.rcv:%x(%x)\n", __func__ - , len, self->ring->rx[self->rxs].control); - -#ifdef DUMP_PACKETS -dumpbufs(self->rx_bufs[self->rxs],len,'<'); -#endif - - if (self->ring->rx[self->rxs].control == 0) - { - __u8 enable = INB (OBOE_ENABLEH); - - /* In SIR mode we need to check the CRC as this */ - /* hasn't been done by the hardware */ - if (enable & OBOE_ENABLEH_SIRON) - { - if (!toshoboe_checkfcs (self->rx_bufs[self->rxs], len)) - len = 0; - /*Trim off the CRC */ - if (len > 1) - len -= 2; - else - len = 0; - pr_debug("%s.SIR:%x(%x)\n", __func__, len, enable); - } - -#ifdef USE_MIR - else if (enable & OBOE_ENABLEH_MIRON) - { - if (len > 1) - len -= 2; - else - len = 0; - pr_debug("%s.MIR:%x(%x)\n", __func__, len, enable); - } -#endif - else if (enable & OBOE_ENABLEH_FIRON) - { - if (len > 3) - len -= 4; /*FIXME: check this */ - else - len = 0; - pr_debug("%s.FIR:%x(%x)\n", __func__, len, enable); - } - else - pr_debug("%s.?IR:%x(%x)\n", __func__, len, enable); - - if (len) - { - skb = dev_alloc_skb (len + 1); - if (skb) - { - skb_reserve (skb, 1); - - skb_put (skb, len); - skb_copy_to_linear_data(skb, self->rx_bufs[self->rxs], - len); - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons (ETH_P_IRDA); - } - else - { - printk (KERN_INFO - "%s(), memory squeeze, dropping frame.\n", - __func__); - } - } - } - else - { - /* TODO: =========================================== */ - /* if OBOE_CTL_RX_LENGTH, our buffers are too small */ - /* (MIR or FIR) data is lost. */ - /* (SIR) data is splitted in several slots. */ - /* we have to join all the received buffers received */ - /*in a large buffer before checking CRC. */ - pr_debug("%s.err:%x(%x)\n", __func__ - , len, self->ring->rx[self->rxs].control); - } - - self->ring->rx[self->rxs].len = 0x0; - self->ring->rx[self->rxs].control = OBOE_CTL_RX_HW_OWNS; - - self->rxs++; - self->rxs %= RX_SLOTS; - - if (skb) - netif_rx (skb); - - } - } - - if (irqstat & OBOE_INT_TXUNDER) - { - printk (KERN_WARNING DRIVER_NAME ": tx fifo underflow\n"); - } - if (irqstat & OBOE_INT_RXOVER) - { - printk (KERN_WARNING DRIVER_NAME ": rx fifo overflow\n"); - } -/* This must be useful for something... */ - if (irqstat & OBOE_INT_SIP) - { - self->int_sip++; - pr_debug("%s.sip:%x(%x)%x\n", - __func__, self->int_sip, irqstat, self->txpending); - } - return IRQ_HANDLED; -} - - -static int -toshoboe_net_open (struct net_device *dev) -{ - struct toshoboe_cb *self; - unsigned long flags; - int rc; - - self = netdev_priv(dev); - - if (self->async) - return -EBUSY; - - if (self->stopped) - return 0; - - rc = request_irq (self->io.irq, toshoboe_interrupt, - IRQF_SHARED, dev->name, self); - if (rc) - return rc; - - spin_lock_irqsave(&self->spinlock, flags); - toshoboe_startchip (self); - spin_unlock_irqrestore(&self->spinlock, flags); - - /* Ready to play! */ - netif_start_queue(dev); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open (dev, &self->qos, driver_name); - - self->irdad = 1; - - return 0; -} - -static int -toshoboe_net_close (struct net_device *dev) -{ - struct toshoboe_cb *self; - - IRDA_ASSERT (dev != NULL, return -1; ); - self = netdev_priv(dev); - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close (self->irlap); - self->irlap = NULL; - - self->irdad = 0; - - free_irq (self->io.irq, (void *) self); - - if (!self->stopped) - { - toshoboe_stopchip (self); - } - - return 0; -} - -/* - * Function toshoboe_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int -toshoboe_net_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct toshoboe_cb *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT (dev != NULL, return -1; ); - - self = netdev_priv(dev); - - IRDA_ASSERT (self != NULL, return -1; ); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - /* Disable interrupts & save flags */ - spin_lock_irqsave(&self->spinlock, flags); - - switch (cmd) - { - case SIOCSBANDWIDTH: /* Set bandwidth */ - /* This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - pr_debug("%s(BANDWIDTH), %s, (%X/%ld\n", - __func__, dev->name, INB(OBOE_STATUS), irq->ifr_baudrate); - if (!in_interrupt () && !capable (CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - - /* self->speed=irq->ifr_baudrate; */ - /* toshoboe_setbaud(self); */ - /* Just change speed once - inserted by Paul Bristow */ - self->new_speed = irq->ifr_baudrate; - break; - case SIOCSMEDIABUSY: /* Set media busy */ - pr_debug("%s(MEDIABUSY), %s, (%X/%x)\n", - __func__, dev->name, - INB(OBOE_STATUS), capable(CAP_NET_ADMIN)); - if (!capable (CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - irda_device_set_media_busy (self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = (INB (OBOE_STATUS) & OBOE_STATUS_RXBUSY) ? 1 : 0; - pr_debug("%s(RECEIVING), %s, (%X/%x)\n", - __func__, dev->name, INB(OBOE_STATUS), irq->ifr_receiving); - break; - default: - pr_debug("%s(?), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - ret = -EOPNOTSUPP; - } -out: - spin_unlock_irqrestore(&self->spinlock, flags); - return ret; - -} - -MODULE_DESCRIPTION("Toshiba OBOE IrDA Device Driver"); -MODULE_AUTHOR("James McKenzie "); -MODULE_LICENSE("GPL"); - -module_param (max_baud, int, 0); -MODULE_PARM_DESC(max_baud, "Maximum baud rate"); - -#ifdef USE_PROBE -module_param (do_probe, bool, 0); -MODULE_PARM_DESC(do_probe, "Enable/disable chip probing and self-test"); -#endif - -static void -toshoboe_close (struct pci_dev *pci_dev) -{ - int i; - struct toshoboe_cb *self = pci_get_drvdata(pci_dev); - - IRDA_ASSERT (self != NULL, return; ); - - if (!self->stopped) - { - toshoboe_stopchip (self); - } - - release_region (self->io.fir_base, self->io.fir_ext); - - for (i = 0; i < TX_SLOTS; ++i) - { - kfree (self->tx_bufs[i]); - self->tx_bufs[i] = NULL; - } - - for (i = 0; i < RX_SLOTS; ++i) - { - kfree (self->rx_bufs[i]); - self->rx_bufs[i] = NULL; - } - - unregister_netdev(self->netdev); - - kfree (self->ringbuf); - self->ringbuf = NULL; - self->ring = NULL; - - free_netdev(self->netdev); -} - -static const struct net_device_ops toshoboe_netdev_ops = { - .ndo_open = toshoboe_net_open, - .ndo_stop = toshoboe_net_close, - .ndo_start_xmit = toshoboe_hard_xmit, - .ndo_do_ioctl = toshoboe_net_ioctl, -}; - -static int -toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid) -{ - struct toshoboe_cb *self; - struct net_device *dev; - int i = 0; - int ok = 0; - int err; - - if ((err=pci_enable_device(pci_dev))) - return err; - - dev = alloc_irdadev(sizeof (struct toshoboe_cb)); - if (dev == NULL) - { - printk (KERN_ERR DRIVER_NAME ": can't allocate memory for " - "IrDA control block\n"); - return -ENOMEM; - } - - self = netdev_priv(dev); - self->netdev = dev; - self->pdev = pci_dev; - self->base = pci_resource_start(pci_dev,0); - - self->io.fir_base = self->base; - self->io.fir_ext = OBOE_IO_EXTENT; - self->io.irq = pci_dev->irq; - self->io.irqflags = IRQF_SHARED; - - self->speed = self->io.speed = 9600; - self->async = 0; - - /* Lock the port that we need */ - if (NULL==request_region (self->io.fir_base, self->io.fir_ext, driver_name)) - { - printk (KERN_ERR DRIVER_NAME ": can't get iobase of 0x%03x\n" - ,self->io.fir_base); - err = -EBUSY; - goto freeself; - } - - spin_lock_init(&self->spinlock); - - irda_init_max_qos_capabilies (&self->qos); - self->qos.baud_rate.bits = 0; - - if (max_baud >= 2400) - self->qos.baud_rate.bits |= IR_2400; - /*if (max_baud>=4800) idev->qos.baud_rate.bits|=IR_4800; */ - if (max_baud >= 9600) - self->qos.baud_rate.bits |= IR_9600; - if (max_baud >= 19200) - self->qos.baud_rate.bits |= IR_19200; - if (max_baud >= 115200) - self->qos.baud_rate.bits |= IR_115200; -#ifdef USE_MIR - if (max_baud >= 1152000) - { - self->qos.baud_rate.bits |= IR_1152000; - } -#endif - if (max_baud >= 4000000) - { - self->qos.baud_rate.bits |= (IR_4000000 << 8); - } - - /*FIXME: work this out... */ - self->qos.min_turn_time.bits = 0xff; - - irda_qos_bits_to_value (&self->qos); - - /* Allocate twice the size to guarantee alignment */ - self->ringbuf = kmalloc(OBOE_RING_LEN << 1, GFP_KERNEL); - if (!self->ringbuf) - { - err = -ENOMEM; - goto freeregion; - } - -#if (BITS_PER_LONG == 64) -#error broken on 64-bit: casts pointer to 32-bit, and then back to pointer. -#endif - - /*We need to align the taskfile on a taskfile size boundary */ - { - unsigned long addr; - - addr = (__u32) self->ringbuf; - addr &= ~(OBOE_RING_LEN - 1); - addr += OBOE_RING_LEN; - self->ring = (struct OboeRing *) addr; - } - - memset (self->ring, 0, OBOE_RING_LEN); - self->io.mem_base = (__u32) self->ring; - - ok = 1; - for (i = 0; i < TX_SLOTS; ++i) - { - self->tx_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL); - if (!self->tx_bufs[i]) - ok = 0; - } - - for (i = 0; i < RX_SLOTS; ++i) - { - self->rx_bufs[i] = kmalloc (RX_BUF_SZ, GFP_KERNEL); - if (!self->rx_bufs[i]) - ok = 0; - } - - if (!ok) - { - err = -ENOMEM; - goto freebufs; - } - - -#ifdef USE_PROBE - if (do_probe) - if (!toshoboe_probe (self)) - { - err = -ENODEV; - goto freebufs; - } -#endif - - SET_NETDEV_DEV(dev, &pci_dev->dev); - dev->netdev_ops = &toshoboe_netdev_ops; - - err = register_netdev(dev); - if (err) - { - printk (KERN_ERR DRIVER_NAME ": register_netdev() failed\n"); - err = -ENOMEM; - goto freebufs; - } - printk (KERN_INFO "IrDA: Registered device %s\n", dev->name); - - pci_set_drvdata(pci_dev,self); - - printk (KERN_INFO DRIVER_NAME ": Using multiple tasks\n"); - - return 0; - -freebufs: - for (i = 0; i < TX_SLOTS; ++i) - kfree (self->tx_bufs[i]); - for (i = 0; i < RX_SLOTS; ++i) - kfree (self->rx_bufs[i]); - kfree(self->ringbuf); - -freeregion: - release_region (self->io.fir_base, self->io.fir_ext); - -freeself: - free_netdev(dev); - - return err; -} - -static int -toshoboe_gotosleep (struct pci_dev *pci_dev, pm_message_t crap) -{ - struct toshoboe_cb *self = pci_get_drvdata(pci_dev); - unsigned long flags; - int i = 10; - - if (!self || self->stopped) - return 0; - - if ((!self->irdad) && (!self->async)) - return 0; - -/* Flush all packets */ - while ((i--) && (self->txpending)) - msleep(10); - - spin_lock_irqsave(&self->spinlock, flags); - - toshoboe_stopchip (self); - self->stopped = 1; - self->txpending = 0; - - spin_unlock_irqrestore(&self->spinlock, flags); - return 0; -} - -static int -toshoboe_wakeup (struct pci_dev *pci_dev) -{ - struct toshoboe_cb *self = pci_get_drvdata(pci_dev); - unsigned long flags; - - if (!self || !self->stopped) - return 0; - - if ((!self->irdad) && (!self->async)) - return 0; - - spin_lock_irqsave(&self->spinlock, flags); - - toshoboe_startchip (self); - self->stopped = 0; - - netif_wake_queue(self->netdev); - spin_unlock_irqrestore(&self->spinlock, flags); - return 0; -} - -static struct pci_driver donauboe_pci_driver = { - .name = "donauboe", - .id_table = toshoboe_pci_tbl, - .probe = toshoboe_open, - .remove = toshoboe_close, - .suspend = toshoboe_gotosleep, - .resume = toshoboe_wakeup -}; - -module_pci_driver(donauboe_pci_driver); diff --git a/drivers/net/irda/donauboe.h b/drivers/net/irda/donauboe.h deleted file mode 100644 index d92d54e839b9..000000000000 --- a/drivers/net/irda/donauboe.h +++ /dev/null @@ -1,362 +0,0 @@ -/********************************************************************* - * - * Filename: toshoboe.h - * Version: 2.16 - * Description: Driver for the Toshiba OBOE (or type-O or 701) - * FIR Chipset, also supports the DONAUOBOE (type-DO - * or d01) FIR chipset which as far as I know is - * register compatible. - * Status: Experimental. - * Author: James McKenzie - * Created at: Sat May 8 12:35:27 1999 - * Modified: 2.16 Martin Lucina - * Modified: 2.16 Sat Jun 22 18:54:29 2002 (sync headers) - * Modified: 2.17 Christian Gennerat - * Modified: 2.17 jeu sep 12 08:50:20 2002 (add lock to be used by spinlocks) - * - * Copyright (c) 1999 James McKenzie, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither James McKenzie nor Cambridge University admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - * Applicable Models : Libretto 100/110CT and many more. - * Toshiba refers to this chip as the type-O IR port, - * or the type-DO IR port. - * - * IrDA chip set list from Toshiba Computer Engineering Corp. - * model method maker controller Version - * Portege 320CT FIR,SIR Toshiba Oboe(Triangle) - * Portege 3010CT FIR,SIR Toshiba Oboe(Sydney) - * Portege 3015CT FIR,SIR Toshiba Oboe(Sydney) - * Portege 3020CT FIR,SIR Toshiba Oboe(Sydney) - * Portege 7020CT FIR,SIR ? ? - * - * Satell. 4090XCDT FIR,SIR ? ? - * - * Libretto 100CT FIR,SIR Toshiba Oboe - * Libretto 1000CT FIR,SIR Toshiba Oboe - * - * TECRA750DVD FIR,SIR Toshiba Oboe(Triangle) REV ID=14h - * TECRA780 FIR,SIR Toshiba Oboe(Sandlot) REV ID=32h,33h - * TECRA750CDT FIR,SIR Toshiba Oboe(Triangle) REV ID=13h,14h - * TECRA8000 FIR,SIR Toshiba Oboe(ISKUR) REV ID=23h - * - ********************************************************************/ - -/* The documentation for this chip is allegedly released */ -/* However I have not seen it, not have I managed to contact */ -/* anyone who has. HOWEVER the chip bears a striking resemblance */ -/* to the IrDA controller in the Toshiba RISC TMPR3922 chip */ -/* the documentation for this is freely available at */ -/* http://www.madingley.org/james/resources/toshoboe/TMPR3922.pdf */ -/* The mapping between the registers in that document and the */ -/* Registers in the 701 oboe chip are as follows */ - - -/* 3922 reg 701 regs, by bit numbers */ -/* 7- 0 15- 8 24-16 31-25 */ -/* $28 0x0 0x1 */ -/* $2c SEE NOTE 1 */ -/* $30 0x6 0x7 */ -/* $34 0x8 0x9 SEE NOTE 2 */ -/* $38 0x10 0x11 */ -/* $3C 0xe SEE NOTE 3 */ -/* $40 0x12 0x13 */ -/* $44 0x14 0x15 */ -/* $48 0x16 0x17 */ -/* $4c 0x18 0x19 */ -/* $50 0x1a 0x1b */ - -/* FIXME: could be 0x1b 0x1a here */ - -/* $54 0x1d 0x1c */ -/* $5C 0xf SEE NOTE 4 */ -/* $130 SEE NOTE 5 */ -/* $134 SEE NOTE 6 */ -/* */ -/* NOTES: */ -/* 1. The pointer to ring is packed in most unceremoniusly */ -/* 701 Register Address bits (A9-A0 must be zero) */ -/* 0x4: A17 A16 A15 A14 A13 A12 A11 A10 */ -/* 0x5: A25 A24 A23 A22 A21 A20 A19 A18 */ -/* 0x2: 0 0 A31 A30 A29 A28 A27 A26 */ -/* */ -/* 2. The M$ drivers do a write 0x1 to 0x9, however the 3922 */ -/* documentation would suggest that a write of 0x1 to 0x8 */ -/* would be more appropriate. */ -/* */ -/* 3. This assignment is tenuous at best, register 0xe seems to */ -/* have bits arranged 0 0 0 R/W R/W R/W R/W R/W */ -/* if either of the lower two bits are set the chip seems to */ -/* switch off */ -/* */ -/* 4. Bits 7-4 seem to be different 4 seems just to be generic */ -/* receiver busy flag */ -/* */ -/* 5. and 6. The IER and ISR have a different bit assignment */ -/* The lower three bits of both read back as ones */ -/* ISR is register 0xc, IER is register 0xd */ -/* 7 6 5 4 3 2 1 0 */ -/* 0xc: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */ -/* 0xd: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */ -/* TxDone xmitt done (generated only if generate interrupt bit */ -/* is set in the ring) */ -/* RxDone recv completed (or other recv condition if you set it */ -/* up */ -/* TxUnder underflow in Transmit FIFO */ -/* RxOver overflow in Recv FIFO */ -/* SipRcv received serial gap (or other condition you set) */ -/* Interrupts are enabled by writing a one to the IER register */ -/* Interrupts are cleared by writing a one to the ISR register */ -/* */ -/* 6. The remaining registers: 0x6 and 0x3 appear to be */ -/* reserved parts of 16 or 32 bit registersthe remainder */ -/* 0xa 0xb 0x1e 0x1f could possibly be (by their behaviour) */ -/* the Unicast Filter register at $58. */ -/* */ -/* 7. While the core obviously expects 32 bit accesses all the */ -/* M$ drivers do 8 bit accesses, infact the Miniport ones */ -/* write and read back the byte serveral times (why?) */ - - -#ifndef TOSHOBOE_H -#define TOSHOBOE_H - -/* Registers */ - -#define OBOE_IO_EXTENT 0x1f - -/*Receive and transmit slot pointers */ -#define OBOE_REG(i) (i+(self->base)) -#define OBOE_RXSLOT OBOE_REG(0x0) -#define OBOE_TXSLOT OBOE_REG(0x1) -#define OBOE_SLOT_MASK 0x3f - -#define OBOE_TXRING_OFFSET 0x200 -#define OBOE_TXRING_OFFSET_IN_SLOTS 0x40 - -/*pointer to the ring */ -#define OBOE_RING_BASE0 OBOE_REG(0x4) -#define OBOE_RING_BASE1 OBOE_REG(0x5) -#define OBOE_RING_BASE2 OBOE_REG(0x2) -#define OBOE_RING_BASE3 OBOE_REG(0x3) - -/*Number of slots in the ring */ -#define OBOE_RING_SIZE OBOE_REG(0x7) -#define OBOE_RING_SIZE_RX4 0x00 -#define OBOE_RING_SIZE_RX8 0x01 -#define OBOE_RING_SIZE_RX16 0x03 -#define OBOE_RING_SIZE_RX32 0x07 -#define OBOE_RING_SIZE_RX64 0x0f -#define OBOE_RING_SIZE_TX4 0x00 -#define OBOE_RING_SIZE_TX8 0x10 -#define OBOE_RING_SIZE_TX16 0x30 -#define OBOE_RING_SIZE_TX32 0x70 -#define OBOE_RING_SIZE_TX64 0xf0 - -#define OBOE_RING_MAX_SIZE 64 - -/*Causes the gubbins to re-examine the ring */ -#define OBOE_PROMPT OBOE_REG(0x9) -#define OBOE_PROMPT_BIT 0x1 - -/* Interrupt Status Register */ -#define OBOE_ISR OBOE_REG(0xc) -/* Interrupt Enable Register */ -#define OBOE_IER OBOE_REG(0xd) -/* Interrupt bits for IER and ISR */ -#define OBOE_INT_TXDONE 0x80 -#define OBOE_INT_RXDONE 0x40 -#define OBOE_INT_TXUNDER 0x20 -#define OBOE_INT_RXOVER 0x10 -#define OBOE_INT_SIP 0x08 -#define OBOE_INT_MASK 0xf8 - -/*Reset Register */ -#define OBOE_CONFIG1 OBOE_REG(0xe) -#define OBOE_CONFIG1_RST 0x01 -#define OBOE_CONFIG1_DISABLE 0x02 -#define OBOE_CONFIG1_4 0x08 -#define OBOE_CONFIG1_8 0x08 - -#define OBOE_CONFIG1_ON 0x8 -#define OBOE_CONFIG1_RESET 0xf -#define OBOE_CONFIG1_OFF 0xe - -#define OBOE_STATUS OBOE_REG(0xf) -#define OBOE_STATUS_RXBUSY 0x10 -#define OBOE_STATUS_FIRRX 0x04 -#define OBOE_STATUS_MIRRX 0x02 -#define OBOE_STATUS_SIRRX 0x01 - - -/*Speed control registers */ -#define OBOE_CONFIG0L OBOE_REG(0x10) -#define OBOE_CONFIG0H OBOE_REG(0x11) - -#define OBOE_CONFIG0H_TXONLOOP 0x80 /*Transmit when looping (dangerous) */ -#define OBOE_CONFIG0H_LOOP 0x40 /*Loopback Tx->Rx */ -#define OBOE_CONFIG0H_ENTX 0x10 /*Enable Tx */ -#define OBOE_CONFIG0H_ENRX 0x08 /*Enable Rx */ -#define OBOE_CONFIG0H_ENDMAC 0x04 /*Enable/reset* the DMA controller */ -#define OBOE_CONFIG0H_RCVANY 0x02 /*DMA mode 1=bytes, 0=dwords */ - -#define OBOE_CONFIG0L_CRC16 0x80 /*CRC 1=16 bit 0=32 bit */ -#define OBOE_CONFIG0L_ENFIR 0x40 /*Enable FIR */ -#define OBOE_CONFIG0L_ENMIR 0x20 /*Enable MIR */ -#define OBOE_CONFIG0L_ENSIR 0x10 /*Enable SIR */ -#define OBOE_CONFIG0L_ENSIRF 0x08 /*Enable SIR framer */ -#define OBOE_CONFIG0L_SIRTEST 0x04 /*Enable SIR framer in MIR and FIR */ -#define OBOE_CONFIG0L_INVERTTX 0x02 /*Invert Tx Line */ -#define OBOE_CONFIG0L_INVERTRX 0x01 /*Invert Rx Line */ - -#define OBOE_BOF OBOE_REG(0x12) -#define OBOE_EOF OBOE_REG(0x13) - -#define OBOE_ENABLEL OBOE_REG(0x14) -#define OBOE_ENABLEH OBOE_REG(0x15) - -#define OBOE_ENABLEH_PHYANDCLOCK 0x80 /*Toggle low to copy config in */ -#define OBOE_ENABLEH_CONFIGERR 0x40 -#define OBOE_ENABLEH_FIRON 0x20 -#define OBOE_ENABLEH_MIRON 0x10 -#define OBOE_ENABLEH_SIRON 0x08 -#define OBOE_ENABLEH_ENTX 0x04 -#define OBOE_ENABLEH_ENRX 0x02 -#define OBOE_ENABLEH_CRC16 0x01 - -#define OBOE_ENABLEL_BROADCAST 0x01 - -#define OBOE_CURR_PCONFIGL OBOE_REG(0x16) /*Current config */ -#define OBOE_CURR_PCONFIGH OBOE_REG(0x17) - -#define OBOE_NEW_PCONFIGL OBOE_REG(0x18) -#define OBOE_NEW_PCONFIGH OBOE_REG(0x19) - -#define OBOE_PCONFIGH_BAUDMASK 0xfc -#define OBOE_PCONFIGH_WIDTHMASK 0x04 -#define OBOE_PCONFIGL_WIDTHMASK 0xe0 -#define OBOE_PCONFIGL_PREAMBLEMASK 0x1f - -#define OBOE_PCONFIG_BAUDMASK 0xfc00 -#define OBOE_PCONFIG_BAUDSHIFT 10 -#define OBOE_PCONFIG_WIDTHMASK 0x04e0 -#define OBOE_PCONFIG_WIDTHSHIFT 5 -#define OBOE_PCONFIG_PREAMBLEMASK 0x001f -#define OBOE_PCONFIG_PREAMBLESHIFT 0 - -#define OBOE_MAXLENL OBOE_REG(0x1a) -#define OBOE_MAXLENH OBOE_REG(0x1b) - -#define OBOE_RXCOUNTH OBOE_REG(0x1c) /*Reset on recipt */ -#define OBOE_RXCOUNTL OBOE_REG(0x1d) /*of whole packet */ - -/* The PCI ID of the OBOE chip */ -#ifndef PCI_DEVICE_ID_FIR701 -#define PCI_DEVICE_ID_FIR701 0x0701 -#endif - -#ifndef PCI_DEVICE_ID_FIRD01 -#define PCI_DEVICE_ID_FIRD01 0x0d01 -#endif - -struct OboeSlot -{ - __u16 len; /*Tweleve bits of packet length */ - __u8 unused; - __u8 control; /*Slot control/status see below */ - __u32 address; /*Slot buffer address */ -} -__packed; - -#define OBOE_NTASKS OBOE_TXRING_OFFSET_IN_SLOTS - -struct OboeRing -{ - struct OboeSlot rx[OBOE_NTASKS]; - struct OboeSlot tx[OBOE_NTASKS]; -}; - -#define OBOE_RING_LEN (sizeof(struct OboeRing)) - - -#define OBOE_CTL_TX_HW_OWNS 0x80 /*W/R This slot owned by the hardware */ -#define OBOE_CTL_TX_DISTX_CRC 0x40 /*W Disable CRC generation for [FM]IR */ -#define OBOE_CTL_TX_BAD_CRC 0x20 /*W Generate bad CRC */ -#define OBOE_CTL_TX_SIP 0x10 /*W Generate an SIP after xmittion */ -#define OBOE_CTL_TX_MKUNDER 0x08 /*W Generate an underrun error */ -#define OBOE_CTL_TX_RTCENTX 0x04 /*W Enable receiver and generate TXdone */ - /* After this slot is processed */ -#define OBOE_CTL_TX_UNDER 0x01 /*R Set by hardware to indicate underrun */ - - -#define OBOE_CTL_RX_HW_OWNS 0x80 /*W/R This slot owned by hardware */ -#define OBOE_CTL_RX_PHYERR 0x40 /*R Decoder error on receiption */ -#define OBOE_CTL_RX_CRCERR 0x20 /*R CRC error only set for [FM]IR */ -#define OBOE_CTL_RX_LENGTH 0x10 /*R Packet > max Rx length */ -#define OBOE_CTL_RX_OVER 0x08 /*R set to indicate an overflow */ -#define OBOE_CTL_RX_SIRBAD 0x04 /*R SIR had BOF in packet or ABORT sequence */ -#define OBOE_CTL_RX_RXEOF 0x02 /*R Finished receiving on this slot */ - - -struct toshoboe_cb -{ - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct tty_driver ttydev; - - struct irlap_cb *irlap; /* The link layer we are binded to */ - - chipio_t io; /* IrDA controller information */ - struct qos_info qos; /* QoS capabilities for this device */ - - __u32 flags; /* Interface flags */ - - struct pci_dev *pdev; /*PCI device */ - int base; /*IO base */ - - - int txpending; /*how many tx's are pending */ - int txs, rxs; /*Which slots are we at */ - - int irdad; /*Driver under control of netdev end */ - int async; /*Driver under control of async end */ - - - int stopped; /*Stopped by some or other APM stuff */ - - int filter; /*In SIR mode do we want to receive - frames or byte ranges */ - - void *ringbuf; /*The ring buffer */ - struct OboeRing *ring; /*The ring */ - - void *tx_bufs[OBOE_RING_MAX_SIZE]; /*The buffers */ - void *rx_bufs[OBOE_RING_MAX_SIZE]; - - - int speed; /*Current setting of the speed */ - int new_speed; /*Set to request a speed change */ - -/* The spinlock protect critical parts of the driver. - * Locking is done like this : - * spin_lock_irqsave(&self->spinlock, flags); - * Releasing the lock : - * spin_unlock_irqrestore(&self->spinlock, flags); - */ - spinlock_t spinlock; - /* Used for the probe and diagnostics code */ - int int_rx; - int int_tx; - int int_txunder; - int int_rxover; - int int_sip; -}; - - -#endif diff --git a/drivers/net/irda/esi-sir.c b/drivers/net/irda/esi-sir.c deleted file mode 100644 index 019a3e848bcb..000000000000 --- a/drivers/net/irda/esi-sir.c +++ /dev/null @@ -1,157 +0,0 @@ -/********************************************************************* - * - * Filename: esi.c - * Version: 1.6 - * Description: Driver for the Extended Systems JetEye PC dongle - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Feb 21 18:54:38 1998 - * Modified at: Sun Oct 27 22:01:04 2002 - * Modified by: Martin Diehl - * - * Copyright (c) 1999 Dag Brattli, , - * Copyright (c) 1998 Thomas Davis, , - * Copyright (c) 2002 Martin Diehl, , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int esi_open(struct sir_dev *); -static int esi_close(struct sir_dev *); -static int esi_change_speed(struct sir_dev *, unsigned); -static int esi_reset(struct sir_dev *); - -static struct dongle_driver esi = { - .owner = THIS_MODULE, - .driver_name = "JetEye PC ESI-9680 PC", - .type = IRDA_ESI_DONGLE, - .open = esi_open, - .close = esi_close, - .reset = esi_reset, - .set_speed = esi_change_speed, -}; - -static int __init esi_sir_init(void) -{ - return irda_register_dongle(&esi); -} - -static void __exit esi_sir_cleanup(void) -{ - irda_unregister_dongle(&esi); -} - -static int esi_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power up and set dongle to 9600 baud */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200; - qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int esi_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function esi_change_speed (task) - * - * Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle - * Apparently (see old esi-driver) no delays are needed here... - * - */ -static int esi_change_speed(struct sir_dev *dev, unsigned speed) -{ - int ret = 0; - int dtr, rts; - - switch (speed) { - case 19200: - dtr = TRUE; - rts = FALSE; - break; - case 115200: - dtr = rts = TRUE; - break; - default: - ret = -EINVAL; - speed = 9600; - /* fall through */ - case 9600: - dtr = FALSE; - rts = TRUE; - break; - } - - /* Change speed of dongle */ - sirdev_set_dtr_rts(dev, dtr, rts); - dev->speed = speed; - - return ret; -} - -/* - * Function esi_reset (task) - * - * Reset dongle; - * - */ -static int esi_reset(struct sir_dev *dev) -{ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - /* Hm, the old esi-driver left the dongle unpowered relying on - * the following speed change to repower. This might work for - * the esi because we only need the modem lines. However, now the - * general rule is reset must bring the dongle to some working - * well-known state because speed change might write to registers. - * The old esi-driver didn't any delay here - let's hope it' fine. - */ - - sirdev_set_dtr_rts(dev, FALSE, TRUE); - dev->speed = 9600; - - return 0; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-1"); /* IRDA_ESI_DONGLE */ - -module_init(esi_sir_init); -module_exit(esi_sir_cleanup); - diff --git a/drivers/net/irda/girbil-sir.c b/drivers/net/irda/girbil-sir.c deleted file mode 100644 index 7e0a5b8c6d53..000000000000 --- a/drivers/net/irda/girbil-sir.c +++ /dev/null @@ -1,252 +0,0 @@ -/********************************************************************* - * - * Filename: girbil.c - * Version: 1.2 - * Description: Implementation for the Greenwich GIrBIL dongle - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Feb 6 21:02:33 1999 - * Modified at: Fri Dec 17 09:13:20 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int girbil_reset(struct sir_dev *dev); -static int girbil_open(struct sir_dev *dev); -static int girbil_close(struct sir_dev *dev); -static int girbil_change_speed(struct sir_dev *dev, unsigned speed); - -/* Control register 1 */ -#define GIRBIL_TXEN 0x01 /* Enable transmitter */ -#define GIRBIL_RXEN 0x02 /* Enable receiver */ -#define GIRBIL_ECAN 0x04 /* Cancel self emitted data */ -#define GIRBIL_ECHO 0x08 /* Echo control characters */ - -/* LED Current Register (0x2) */ -#define GIRBIL_HIGH 0x20 -#define GIRBIL_MEDIUM 0x21 -#define GIRBIL_LOW 0x22 - -/* Baud register (0x3) */ -#define GIRBIL_2400 0x30 -#define GIRBIL_4800 0x31 -#define GIRBIL_9600 0x32 -#define GIRBIL_19200 0x33 -#define GIRBIL_38400 0x34 -#define GIRBIL_57600 0x35 -#define GIRBIL_115200 0x36 - -/* Mode register (0x4) */ -#define GIRBIL_IRDA 0x40 -#define GIRBIL_ASK 0x41 - -/* Control register 2 (0x5) */ -#define GIRBIL_LOAD 0x51 /* Load the new baud rate value */ - -static struct dongle_driver girbil = { - .owner = THIS_MODULE, - .driver_name = "Greenwich GIrBIL", - .type = IRDA_GIRBIL_DONGLE, - .open = girbil_open, - .close = girbil_close, - .reset = girbil_reset, - .set_speed = girbil_change_speed, -}; - -static int __init girbil_sir_init(void) -{ - return irda_register_dongle(&girbil); -} - -static void __exit girbil_sir_cleanup(void) -{ - irda_unregister_dongle(&girbil); -} - -static int girbil_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power on dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - qos->min_turn_time.bits = 0x03; - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int girbil_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function girbil_change_speed (dev, speed) - * - * Set the speed for the Girbil type dongle. - * - */ - -#define GIRBIL_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) - -static int girbil_change_speed(struct sir_dev *dev, unsigned speed) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 control[2]; - static int ret = 0; - - /* dongle alread reset - port and dongle at default speed */ - - switch(state) { - - case SIRDEV_STATE_DONGLE_SPEED: - - /* Set DTR and Clear RTS to enter command mode */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - udelay(25); /* better wait a little while */ - - ret = 0; - switch (speed) { - default: - ret = -EINVAL; - /* fall through */ - case 9600: - control[0] = GIRBIL_9600; - break; - case 19200: - control[0] = GIRBIL_19200; - break; - case 34800: - control[0] = GIRBIL_38400; - break; - case 57600: - control[0] = GIRBIL_57600; - break; - case 115200: - control[0] = GIRBIL_115200; - break; - } - control[1] = GIRBIL_LOAD; - - /* Write control bytes */ - sirdev_raw_write(dev, control, 2); - - dev->speed = speed; - - state = GIRBIL_STATE_WAIT_SPEED; - delay = 100; - break; - - case GIRBIL_STATE_WAIT_SPEED: - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - udelay(25); /* better wait a little while */ - break; - - default: - net_err_ratelimited("%s - undefined state %d\n", - __func__, state); - ret = -EINVAL; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -/* - * Function girbil_reset (driver) - * - * This function resets the girbil dongle. - * - * Algorithm: - * 0. set RTS, and wait at least 5 ms - * 1. clear RTS - */ - - -#define GIRBIL_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET + 1) -#define GIRBIL_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET + 2) -#define GIRBIL_STATE_WAIT3_RESET (SIRDEV_STATE_DONGLE_RESET + 3) - -static int girbil_reset(struct sir_dev *dev) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 control = GIRBIL_TXEN | GIRBIL_RXEN; - int ret = 0; - - switch (state) { - case SIRDEV_STATE_DONGLE_RESET: - /* Reset dongle */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - /* Sleep at least 5 ms */ - delay = 20; - state = GIRBIL_STATE_WAIT1_RESET; - break; - - case GIRBIL_STATE_WAIT1_RESET: - /* Set DTR and clear RTS to enter command mode */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - delay = 20; - state = GIRBIL_STATE_WAIT2_RESET; - break; - - case GIRBIL_STATE_WAIT2_RESET: - /* Write control byte */ - sirdev_raw_write(dev, &control, 1); - delay = 20; - state = GIRBIL_STATE_WAIT3_RESET; - break; - - case GIRBIL_STATE_WAIT3_RESET: - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - dev->speed = 9600; - break; - - default: - net_err_ratelimited("%s(), undefined state %d\n", - __func__, state); - ret = -1; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Greenwich GIrBIL dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-4"); /* IRDA_GIRBIL_DONGLE */ - -module_init(girbil_sir_init); -module_exit(girbil_sir_cleanup); diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c deleted file mode 100644 index 723e49bc4baa..000000000000 --- a/drivers/net/irda/irda-usb.c +++ /dev/null @@ -1,1914 +0,0 @@ -/***************************************************************************** - * - * Filename: irda-usb.c - * Version: 0.10 - * Description: IrDA-USB Driver - * Status: Experimental - * Author: Dag Brattli - * - * Copyright (C) 2000, Roman Weissgaerber - * Copyright (C) 2001, Dag Brattli - * Copyright (C) 2001, Jean Tourrilhes - * Copyright (C) 2004, SigmaTel, Inc. - * Copyright (C) 2005, Milan Beno - * Copyright (C) 2006, Nick Fedchik - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - *****************************************************************************/ - -/* - * IMPORTANT NOTE - * -------------- - * - * As of kernel 2.5.20, this is the state of compliance and testing of - * this driver (irda-usb) with regards to the USB low level drivers... - * - * This driver has been tested SUCCESSFULLY with the following drivers : - * o usb-uhci-hcd (For Intel/Via USB controllers) - * o uhci-hcd (Alternate/JE driver for Intel/Via USB controllers) - * o ohci-hcd (For other USB controllers) - * - * This driver has NOT been tested with the following drivers : - * o ehci-hcd (USB 2.0 controllers) - * - * Note that all HCD drivers do URB_ZERO_PACKET and timeout properly, - * so we don't have to worry about that anymore. - * One common problem is the failure to set the address on the dongle, - * but this happens before the driver gets loaded... - * - * Jean II - */ - -/*------------------------------------------------------------------*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "irda-usb.h" - -/*------------------------------------------------------------------*/ - -static int qos_mtt_bits = 0; - -/* These are the currently known IrDA USB dongles. Add new dongles here */ -static const struct usb_device_id dongles[] = { - /* ACTiSYS Corp., ACT-IR2000U FIR-USB Adapter */ - { USB_DEVICE(0x9c4, 0x011), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, - /* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */ - { USB_DEVICE(0x4428, 0x012), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, - /* KC Technology Inc., KC-180 USB IrDA Device */ - { USB_DEVICE(0x50f, 0x180), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, - /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ - { USB_DEVICE(0x8e9, 0x100), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, - /* SigmaTel STIR4210/4220/4116 USB IrDA (VFIR) Bridge */ - { USB_DEVICE(0x66f, 0x4210), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, - { USB_DEVICE(0x66f, 0x4220), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, - { USB_DEVICE(0x66f, 0x4116), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, - { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_APP_SPEC, - .bInterfaceSubClass = USB_CLASS_IRDA, - .driver_info = IUC_DEFAULT, }, - { }, /* The end */ -}; - -/* - * Important note : - * Devices based on the SigmaTel chipset (0x66f, 0x4200) are not designed - * using the "USB-IrDA specification" (yes, there exist such a thing), and - * therefore not supported by this driver (don't add them above). - * There is a Linux driver, stir4200, that support those USB devices. - * Jean II - */ - -MODULE_DEVICE_TABLE(usb, dongles); - -/*------------------------------------------------------------------*/ - -static void irda_usb_init_qos(struct irda_usb_cb *self) ; -static struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf); -static void irda_usb_disconnect(struct usb_interface *intf); -static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self); -static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, - struct net_device *dev); -static int irda_usb_open(struct irda_usb_cb *self); -static void irda_usb_close(struct irda_usb_cb *self); -static void speed_bulk_callback(struct urb *urb); -static void write_bulk_callback(struct urb *urb); -static void irda_usb_receive(struct urb *urb); -static void irda_usb_rx_defer_expired(unsigned long data); -static int irda_usb_net_open(struct net_device *dev); -static int irda_usb_net_close(struct net_device *dev); -static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void irda_usb_net_timeout(struct net_device *dev); - -/************************ TRANSMIT ROUTINES ************************/ -/* - * Receive packets from the IrDA stack and send them on the USB pipe. - * Handle speed change, timeout and lot's of ugliness... - */ - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_build_header(self, skb, header) - * - * Builds USB-IrDA outbound header - * - * When we send an IrDA frame over an USB pipe, we add to it a 1 byte - * header. This function create this header with the proper values. - * - * Important note : the USB-IrDA spec 1.0 say very clearly in chapter 5.4.2.2 - * that the setting of the link speed and xbof number in this outbound header - * should be applied *AFTER* the frame has been sent. - * Unfortunately, some devices are not compliant with that... It seems that - * reading the spec is far too difficult... - * Jean II - */ -static void irda_usb_build_header(struct irda_usb_cb *self, - __u8 *header, - int force) -{ - /* Here we check if we have an STIR421x chip, - * and if either speed or xbofs (or both) needs - * to be changed. - */ - if (self->capability & IUC_STIR421X && - ((self->new_speed != -1) || (self->new_xbofs != -1))) { - - /* With STIR421x, speed and xBOFs must be set at the same - * time, even if only one of them changes. - */ - if (self->new_speed == -1) - self->new_speed = self->speed ; - - if (self->new_xbofs == -1) - self->new_xbofs = self->xbofs ; - } - - /* Set the link speed */ - if (self->new_speed != -1) { - /* Hum... Ugly hack :-( - * Some device are not compliant with the spec and change - * parameters *before* sending the frame. - Jean II - */ - if ((self->capability & IUC_SPEED_BUG) && - (!force) && (self->speed != -1)) { - /* No speed and xbofs change here - * (we'll do it later in the write callback) */ - pr_debug("%s(), not changing speed yet\n", __func__); - *header = 0; - return; - } - - pr_debug("%s(), changing speed to %d\n", - __func__, self->new_speed); - self->speed = self->new_speed; - /* We will do ` self->new_speed = -1; ' in the completion - * handler just in case the current URB fail - Jean II */ - - switch (self->speed) { - case 2400: - *header = SPEED_2400; - break; - default: - case 9600: - *header = SPEED_9600; - break; - case 19200: - *header = SPEED_19200; - break; - case 38400: - *header = SPEED_38400; - break; - case 57600: - *header = SPEED_57600; - break; - case 115200: - *header = SPEED_115200; - break; - case 576000: - *header = SPEED_576000; - break; - case 1152000: - *header = SPEED_1152000; - break; - case 4000000: - *header = SPEED_4000000; - self->new_xbofs = 0; - break; - case 16000000: - *header = SPEED_16000000; - self->new_xbofs = 0; - break; - } - } else - /* No change */ - *header = 0; - - /* Set the negotiated additional XBOFS */ - if (self->new_xbofs != -1) { - pr_debug("%s(), changing xbofs to %d\n", - __func__, self->new_xbofs); - self->xbofs = self->new_xbofs; - /* We will do ` self->new_xbofs = -1; ' in the completion - * handler just in case the current URB fail - Jean II */ - - switch (self->xbofs) { - case 48: - *header |= 0x10; - break; - case 28: - case 24: /* USB spec 1.0 says 24 */ - *header |= 0x20; - break; - default: - case 12: - *header |= 0x30; - break; - case 5: /* Bug in IrLAP spec? (should be 6) */ - case 6: - *header |= 0x40; - break; - case 3: - *header |= 0x50; - break; - case 2: - *header |= 0x60; - break; - case 1: - *header |= 0x70; - break; - case 0: - *header |= 0x80; - break; - } - } -} - -/* -* calculate turnaround time for SigmaTel header -*/ -static __u8 get_turnaround_time(struct sk_buff *skb) -{ - int turnaround_time = irda_get_mtt(skb); - - if ( turnaround_time == 0 ) - return 0; - else if ( turnaround_time <= 10 ) - return 1; - else if ( turnaround_time <= 50 ) - return 2; - else if ( turnaround_time <= 100 ) - return 3; - else if ( turnaround_time <= 500 ) - return 4; - else if ( turnaround_time <= 1000 ) - return 5; - else if ( turnaround_time <= 5000 ) - return 6; - else - return 7; -} - - -/*------------------------------------------------------------------*/ -/* - * Send a command to change the speed of the dongle - * Need to be called with spinlock on. - */ -static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self) -{ - __u8 *frame; - struct urb *urb; - int ret; - - pr_debug("%s(), speed=%d, xbofs=%d\n", __func__, - self->new_speed, self->new_xbofs); - - /* Grab the speed URB */ - urb = self->speed_urb; - if (urb->status != 0) { - net_warn_ratelimited("%s(), URB still in use!\n", __func__); - return; - } - - /* Allocate the fake frame */ - frame = self->speed_buff; - - /* Set the new speed and xbofs in this fake frame */ - irda_usb_build_header(self, frame, 1); - - if (self->capability & IUC_STIR421X) { - if (frame[0] == 0) return ; // do nothing if no change - frame[1] = 0; // other parameters don't change here - frame[2] = 0; - } - - /* Submit the 0 length IrDA frame to trigger new speed settings */ - usb_fill_bulk_urb(urb, self->usbdev, - usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), - frame, IRDA_USB_SPEED_MTU, - speed_bulk_callback, self); - urb->transfer_buffer_length = self->header_length; - urb->transfer_flags = 0; - - /* Irq disabled -> GFP_ATOMIC */ - if ((ret = usb_submit_urb(urb, GFP_ATOMIC))) { - net_warn_ratelimited("%s(), failed Speed URB\n", __func__); - } -} - -/*------------------------------------------------------------------*/ -/* - * Speed URB callback - * Now, we can only get called for the speed URB. - */ -static void speed_bulk_callback(struct urb *urb) -{ - struct irda_usb_cb *self = urb->context; - - /* We should always have a context */ - IRDA_ASSERT(self != NULL, return;); - /* We should always be called for the speed URB */ - IRDA_ASSERT(urb == self->speed_urb, return;); - - /* Check for timeout and other USB nasties */ - if (urb->status != 0) { - /* I get a lot of -ECONNABORTED = -103 here - Jean II */ - pr_debug("%s(), URB complete status %d, transfer_flags 0x%04X\n", - __func__, urb->status, urb->transfer_flags); - - /* Don't do anything here, that might confuse the USB layer. - * Instead, we will wait for irda_usb_net_timeout(), the - * network layer watchdog, to fix the situation. - * Jean II */ - /* A reset of the dongle might be welcomed here - Jean II */ - return; - } - - /* urb is now available */ - //urb->status = 0; -> tested above - - /* New speed and xbof is now committed in hardware */ - self->new_speed = -1; - self->new_xbofs = -1; - - /* Allow the stack to send more packets */ - netif_wake_queue(self->netdev); -} - -/*------------------------------------------------------------------*/ -/* - * Send an IrDA frame to the USB dongle (for transmission) - */ -static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct irda_usb_cb *self = netdev_priv(netdev); - struct urb *urb = self->tx_urb; - unsigned long flags; - s32 speed; - s16 xbofs; - int res, mtt; - - pr_debug("%s() on %s\n", __func__, netdev->name); - - netif_stop_queue(netdev); - - /* Protect us from USB callbacks, net watchdog and else. */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if the device is still there. - * We need to check self->present under the spinlock because - * of irda_usb_disconnect() is synchronous - Jean II */ - if (!self->present) { - pr_debug("%s(), Device is gone...\n", __func__); - goto drop; - } - - /* Check if we need to change the number of xbofs */ - xbofs = irda_get_next_xbofs(skb); - if ((xbofs != self->xbofs) && (xbofs != -1)) { - self->new_xbofs = xbofs; - } - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->speed) && (speed != -1)) { - /* Set the desired speed */ - self->new_speed = speed; - - /* Check for empty frame */ - if (!skb->len) { - /* IrLAP send us an empty frame to make us change the - * speed. Changing speed with the USB adapter is in - * fact sending an empty frame to the adapter, so we - * could just let the present function do its job. - * However, we would wait for min turn time, - * do an extra memcpy and increment packet counters... - * Jean II */ - irda_usb_change_speed_xbofs(self); - netif_trans_update(netdev); - /* Will netif_wake_queue() in callback */ - goto drop; - } - } - - if (urb->status != 0) { - net_warn_ratelimited("%s(), URB still in use!\n", __func__); - goto drop; - } - - skb_copy_from_linear_data(skb, self->tx_buff + self->header_length, skb->len); - - /* Change setting for next frame */ - if (self->capability & IUC_STIR421X) { - __u8 turnaround_time; - __u8* frame = self->tx_buff; - turnaround_time = get_turnaround_time( skb ); - irda_usb_build_header(self, frame, 0); - frame[2] = turnaround_time; - if ((skb->len != 0) && - ((skb->len % 128) == 0) && - ((skb->len % 512) != 0)) { - /* add extra byte for special SigmaTel feature */ - frame[1] = 1; - skb_put(skb, 1); - } else { - frame[1] = 0; - } - } else { - irda_usb_build_header(self, self->tx_buff, 0); - } - - /* FIXME: Make macro out of this one */ - ((struct irda_skb_cb *)skb->cb)->context = self; - - usb_fill_bulk_urb(urb, self->usbdev, - usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), - self->tx_buff, skb->len + self->header_length, - write_bulk_callback, skb); - - /* This flag (URB_ZERO_PACKET) indicates that what we send is not - * a continuous stream of data but separate packets. - * In this case, the USB layer will insert an empty USB frame (TD) - * after each of our packets that is exact multiple of the frame size. - * This is how the dongle will detect the end of packet - Jean II */ - urb->transfer_flags = URB_ZERO_PACKET; - - /* Generate min turn time. FIXME: can we do better than this? */ - /* Trying to a turnaround time at this level is trying to measure - * processor clock cycle with a wrist-watch, approximate at best... - * - * What we know is the last time we received a frame over USB. - * Due to latency over USB that depend on the USB load, we don't - * know when this frame was received over IrDA (a few ms before ?) - * Then, same story for our outgoing frame... - * - * In theory, the USB dongle is supposed to handle the turnaround - * by itself (spec 1.0, chater 4, page 6). Who knows ??? That's - * why this code is enabled only for dongles that doesn't meet - * the spec. - * Jean II */ - if (self->capability & IUC_NO_TURN) { - mtt = irda_get_mtt(skb); - if (mtt) { - int diff; - diff = ktime_us_delta(ktime_get(), self->stamp); -#ifdef IU_USB_MIN_RTT - /* Factor in USB delays -> Get rid of udelay() that - * would be lost in the noise - Jean II */ - diff += IU_USB_MIN_RTT; -#endif /* IU_USB_MIN_RTT */ - - /* Check if the mtt is larger than the time we have - * already used by all the protocol processing - */ - if (mtt > diff) { - mtt -= diff; - if (mtt > 1000) - mdelay(mtt/1000); - else - udelay(mtt); - } - } - } - - /* Ask USB to send the packet - Irq disabled -> GFP_ATOMIC */ - if ((res = usb_submit_urb(urb, GFP_ATOMIC))) { - net_warn_ratelimited("%s(), failed Tx URB\n", __func__); - netdev->stats.tx_errors++; - /* Let USB recover : We will catch that in the watchdog */ - /*netif_start_queue(netdev);*/ - } else { - /* Increment packet stats */ - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - - netif_trans_update(netdev); - } - spin_unlock_irqrestore(&self->lock, flags); - - return NETDEV_TX_OK; - -drop: - /* Drop silently the skb and exit */ - dev_kfree_skb(skb); - spin_unlock_irqrestore(&self->lock, flags); - return NETDEV_TX_OK; -} - -/*------------------------------------------------------------------*/ -/* - * Note : this function will be called only for tx_urb... - */ -static void write_bulk_callback(struct urb *urb) -{ - unsigned long flags; - struct sk_buff *skb = urb->context; - struct irda_usb_cb *self = ((struct irda_skb_cb *) skb->cb)->context; - - /* We should always have a context */ - IRDA_ASSERT(self != NULL, return;); - /* We should always be called for the speed URB */ - IRDA_ASSERT(urb == self->tx_urb, return;); - - /* Free up the skb */ - dev_kfree_skb_any(skb); - urb->context = NULL; - - /* Check for timeout and other USB nasties */ - if (urb->status != 0) { - /* I get a lot of -ECONNABORTED = -103 here - Jean II */ - pr_debug("%s(), URB complete status %d, transfer_flags 0x%04X\n", - __func__, urb->status, urb->transfer_flags); - - /* Don't do anything here, that might confuse the USB layer, - * and we could go in recursion and blow the kernel stack... - * Instead, we will wait for irda_usb_net_timeout(), the - * network layer watchdog, to fix the situation. - * Jean II */ - /* A reset of the dongle might be welcomed here - Jean II */ - return; - } - - /* urb is now available */ - //urb->status = 0; -> tested above - - /* Make sure we read self->present properly */ - spin_lock_irqsave(&self->lock, flags); - - /* If the network is closed, stop everything */ - if ((!self->netopen) || (!self->present)) { - pr_debug("%s(), Network is gone...\n", __func__); - spin_unlock_irqrestore(&self->lock, flags); - return; - } - - /* If changes to speed or xbofs is pending... */ - if ((self->new_speed != -1) || (self->new_xbofs != -1)) { - if ((self->new_speed != self->speed) || - (self->new_xbofs != self->xbofs)) { - /* We haven't changed speed yet (because of - * IUC_SPEED_BUG), so do it now - Jean II */ - pr_debug("%s(), Changing speed now...\n", __func__); - irda_usb_change_speed_xbofs(self); - } else { - /* New speed and xbof is now committed in hardware */ - self->new_speed = -1; - self->new_xbofs = -1; - /* Done, waiting for next packet */ - netif_wake_queue(self->netdev); - } - } else { - /* Otherwise, allow the stack to send more packets */ - netif_wake_queue(self->netdev); - } - spin_unlock_irqrestore(&self->lock, flags); -} - -/*------------------------------------------------------------------*/ -/* - * Watchdog timer from the network layer. - * After a predetermined timeout, if we don't give confirmation that - * the packet has been sent (i.e. no call to netif_wake_queue()), - * the network layer will call this function. - * Note that URB that we submit have also a timeout. When the URB timeout - * expire, the normal URB callback is called (write_bulk_callback()). - */ -static void irda_usb_net_timeout(struct net_device *netdev) -{ - unsigned long flags; - struct irda_usb_cb *self = netdev_priv(netdev); - struct urb *urb; - int done = 0; /* If we have made any progress */ - - pr_debug("%s(), Network layer thinks we timed out!\n", __func__); - IRDA_ASSERT(self != NULL, return;); - - /* Protect us from USB callbacks, net Tx and else. */ - spin_lock_irqsave(&self->lock, flags); - - /* self->present *MUST* be read under spinlock */ - if (!self->present) { - net_warn_ratelimited("%s(), device not present!\n", __func__); - netif_stop_queue(netdev); - spin_unlock_irqrestore(&self->lock, flags); - return; - } - - /* Check speed URB */ - urb = self->speed_urb; - if (urb->status != 0) { - pr_debug("%s: Speed change timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", - netdev->name, urb->status, urb->transfer_flags); - - switch (urb->status) { - case -EINPROGRESS: - usb_unlink_urb(urb); - /* Note : above will *NOT* call netif_wake_queue() - * in completion handler, we will come back here. - * Jean II */ - done = 1; - break; - case -ECONNRESET: - case -ENOENT: /* urb unlinked by us */ - default: /* ??? - Play safe */ - urb->status = 0; - netif_wake_queue(self->netdev); - done = 1; - break; - } - } - - /* Check Tx URB */ - urb = self->tx_urb; - if (urb->status != 0) { - struct sk_buff *skb = urb->context; - - pr_debug("%s: Tx timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", - netdev->name, urb->status, urb->transfer_flags); - - /* Increase error count */ - netdev->stats.tx_errors++; - -#ifdef IU_BUG_KICK_TIMEOUT - /* Can't be a bad idea to reset the speed ;-) - Jean II */ - if(self->new_speed == -1) - self->new_speed = self->speed; - if(self->new_xbofs == -1) - self->new_xbofs = self->xbofs; - irda_usb_change_speed_xbofs(self); -#endif /* IU_BUG_KICK_TIMEOUT */ - - switch (urb->status) { - case -EINPROGRESS: - usb_unlink_urb(urb); - /* Note : above will *NOT* call netif_wake_queue() - * in completion handler, because urb->status will - * be -ENOENT. We will fix that at the next watchdog, - * leaving more time to USB to recover... - * Jean II */ - done = 1; - break; - case -ECONNRESET: - case -ENOENT: /* urb unlinked by us */ - default: /* ??? - Play safe */ - if(skb != NULL) { - dev_kfree_skb_any(skb); - urb->context = NULL; - } - urb->status = 0; - netif_wake_queue(self->netdev); - done = 1; - break; - } - } - spin_unlock_irqrestore(&self->lock, flags); - - /* Maybe we need a reset */ - /* Note : Some drivers seem to use a usb_set_interface() when they - * need to reset the hardware. Hum... - */ - - /* if(done == 0) */ -} - -/************************* RECEIVE ROUTINES *************************/ -/* - * Receive packets from the USB layer stack and pass them to the IrDA stack. - * Try to work around USB failures... - */ - -/* - * Note : - * Some of you may have noticed that most dongle have an interrupt in pipe - * that we don't use. Here is the little secret... - * When we hang a Rx URB on the bulk in pipe, it generates some USB traffic - * in every USB frame. This is unnecessary overhead. - * The interrupt in pipe will generate an event every time a packet is - * received. Reading an interrupt pipe adds minimal overhead, but has some - * latency (~1ms). - * If we are connected (speed != 9600), we want to minimise latency, so - * we just always hang the Rx URB and ignore the interrupt. - * If we are not connected (speed == 9600), there is usually no Rx traffic, - * and we want to minimise the USB overhead. In this case we should wait - * on the interrupt pipe and hang the Rx URB only when an interrupt is - * received. - * Jean II - * - * Note : don't read the above as what we are currently doing, but as - * something we could do with KC dongle. Also don't forget that the - * interrupt pipe is not part of the original standard, so this would - * need to be optional... - * Jean II - */ - -/*------------------------------------------------------------------*/ -/* - * Submit a Rx URB to the USB layer to handle reception of a frame - * Mostly called by the completion callback of the previous URB. - * - * Jean II - */ -static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, struct urb *urb) -{ - struct irda_skb_cb *cb; - int ret; - - /* This should never happen */ - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(urb != NULL, return;); - - /* Save ourselves in the skb */ - cb = (struct irda_skb_cb *) skb->cb; - cb->context = self; - - /* Reinitialize URB */ - usb_fill_bulk_urb(urb, self->usbdev, - usb_rcvbulkpipe(self->usbdev, self->bulk_in_ep), - skb->data, IRDA_SKB_MAX_MTU, - irda_usb_receive, skb); - urb->status = 0; - - /* Can be called from irda_usb_receive (irq handler) -> GFP_ATOMIC */ - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) { - /* If this ever happen, we are in deep s***. - * Basically, the Rx path will stop... */ - net_warn_ratelimited("%s(), Failed to submit Rx URB %d\n", - __func__, ret); - } -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_receive(urb) - * - * Called by the USB subsystem when a frame has been received - * - */ -static void irda_usb_receive(struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct irda_usb_cb *self; - struct irda_skb_cb *cb; - struct sk_buff *newskb; - struct sk_buff *dataskb; - struct urb *next_urb; - unsigned int len, docopy; - - pr_debug("%s(), len=%d\n", __func__, urb->actual_length); - - /* Find ourselves */ - cb = (struct irda_skb_cb *) skb->cb; - IRDA_ASSERT(cb != NULL, return;); - self = (struct irda_usb_cb *) cb->context; - IRDA_ASSERT(self != NULL, return;); - - /* If the network is closed or the device gone, stop everything */ - if ((!self->netopen) || (!self->present)) { - pr_debug("%s(), Network is gone!\n", __func__); - /* Don't re-submit the URB : will stall the Rx path */ - return; - } - - /* Check the status */ - if (urb->status != 0) { - switch (urb->status) { - case -EILSEQ: - self->netdev->stats.rx_crc_errors++; - /* Also precursor to a hot-unplug on UHCI. */ - /* Fallthrough... */ - case -ECONNRESET: - /* Random error, if I remember correctly */ - /* uhci_cleanup_unlink() is going to kill the Rx - * URB just after we return. No problem, at this - * point the URB will be idle ;-) - Jean II */ - case -ESHUTDOWN: - /* That's usually a hot-unplug. Submit will fail... */ - case -ETIME: - /* Usually precursor to a hot-unplug on OHCI. */ - default: - self->netdev->stats.rx_errors++; - pr_debug("%s(), RX status %d, transfer_flags 0x%04X\n", - __func__, urb->status, urb->transfer_flags); - break; - } - /* If we received an error, we don't want to resubmit the - * Rx URB straight away but to give the USB layer a little - * bit of breathing room. - * We are in the USB thread context, therefore there is a - * danger of recursion (new URB we submit fails, we come - * back here). - * With recent USB stack (2.6.15+), I'm seeing that on - * hot unplug of the dongle... - * Lowest effective timer is 10ms... - * Jean II */ - self->rx_defer_timer.function = irda_usb_rx_defer_expired; - self->rx_defer_timer.data = (unsigned long) urb; - mod_timer(&self->rx_defer_timer, - jiffies + msecs_to_jiffies(10)); - - return; - } - - /* Check for empty frames */ - if (urb->actual_length <= self->header_length) { - net_warn_ratelimited("%s(), empty frame!\n", __func__); - goto done; - } - - /* - * Remember the time we received this frame, so we can - * reduce the min turn time a bit since we will know - * how much time we have used for protocol processing - */ - self->stamp = ktime_get(); - - /* Check if we need to copy the data to a new skb or not. - * For most frames, we use ZeroCopy and pass the already - * allocated skb up the stack. - * If the frame is small, it is more efficient to copy it - * to save memory (copy will be fast anyway - that's - * called Rx-copy-break). Jean II */ - docopy = (urb->actual_length < IRDA_RX_COPY_THRESHOLD); - - /* Allocate a new skb */ - if (self->capability & IUC_STIR421X) - newskb = dev_alloc_skb(docopy ? urb->actual_length : - IRDA_SKB_MAX_MTU + - USB_IRDA_STIR421X_HEADER); - else - newskb = dev_alloc_skb(docopy ? urb->actual_length : - IRDA_SKB_MAX_MTU); - - if (!newskb) { - self->netdev->stats.rx_dropped++; - /* We could deliver the current skb, but this would stall - * the Rx path. Better drop the packet... Jean II */ - goto done; - } - - /* Make sure IP header get aligned (IrDA header is 5 bytes) */ - /* But IrDA-USB header is 1 byte. Jean II */ - //skb_reserve(newskb, USB_IRDA_HEADER - 1); - - if(docopy) { - /* Copy packet, so we can recycle the original */ - skb_copy_from_linear_data(skb, newskb->data, urb->actual_length); - /* Deliver this new skb */ - dataskb = newskb; - /* And hook the old skb to the URB - * Note : we don't need to "clean up" the old skb, - * as we never touched it. Jean II */ - } else { - /* We are using ZeroCopy. Deliver old skb */ - dataskb = skb; - /* And hook the new skb to the URB */ - skb = newskb; - } - - /* Set proper length on skb & remove USB-IrDA header */ - skb_put(dataskb, urb->actual_length); - skb_pull(dataskb, self->header_length); - - /* Ask the networking layer to queue the packet for the IrDA stack */ - dataskb->dev = self->netdev; - skb_reset_mac_header(dataskb); - dataskb->protocol = htons(ETH_P_IRDA); - len = dataskb->len; - netif_rx(dataskb); - - /* Keep stats up to date */ - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - -done: - /* Note : at this point, the URB we've just received (urb) - * is still referenced by the USB layer. For example, if we - * have received a -ECONNRESET, uhci_cleanup_unlink() will - * continue to process it (in fact, cleaning it up). - * If we were to submit this URB, disaster would ensue. - * Therefore, we submit our idle URB, and put this URB in our - * idle slot.... - * Jean II */ - /* Note : with this scheme, we could submit the idle URB before - * processing the Rx URB. I don't think it would buy us anything as - * we are running in the USB thread context. Jean II */ - next_urb = self->idle_rx_urb; - - /* Recycle Rx URB : Now, the idle URB is the present one */ - urb->context = NULL; - self->idle_rx_urb = urb; - - /* Submit the idle URB to replace the URB we've just received. - * Do it last to avoid race conditions... Jean II */ - irda_usb_submit(self, skb, next_urb); -} - -/*------------------------------------------------------------------*/ -/* - * In case of errors, we want the USB layer to have time to recover. - * Now, it is time to resubmit ouur Rx URB... - */ -static void irda_usb_rx_defer_expired(unsigned long data) -{ - struct urb *urb = (struct urb *) data; - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct irda_usb_cb *self; - struct irda_skb_cb *cb; - struct urb *next_urb; - - /* Find ourselves */ - cb = (struct irda_skb_cb *) skb->cb; - IRDA_ASSERT(cb != NULL, return;); - self = (struct irda_usb_cb *) cb->context; - IRDA_ASSERT(self != NULL, return;); - - /* Same stuff as when Rx is done, see above... */ - next_urb = self->idle_rx_urb; - urb->context = NULL; - self->idle_rx_urb = urb; - irda_usb_submit(self, skb, next_urb); -} - -/*------------------------------------------------------------------*/ -/* - * Callbak from IrDA layer. IrDA wants to know if we have - * started receiving anything. - */ -static int irda_usb_is_receiving(struct irda_usb_cb *self) -{ - /* Note : because of the way UHCI works, it's almost impossible - * to get this info. The Controller DMA directly to memory and - * signal only when the whole frame is finished. To know if the - * first TD of the URB has been filled or not seems hard work... - * - * The other solution would be to use the "receiving" command - * on the default decriptor with a usb_control_msg(), but that - * would add USB traffic and would return result only in the - * next USB frame (~1ms). - * - * I've been told that current dongles send status info on their - * interrupt endpoint, and that's what the Windows driver uses - * to know this info. Unfortunately, this is not yet in the spec... - * - * Jean II - */ - - return 0; /* For now */ -} - -#define STIR421X_PATCH_PRODUCT_VER "Product Version: " -#define STIR421X_PATCH_STMP_TAG "STMP" -#define STIR421X_PATCH_CODE_OFFSET 512 /* patch image starts before here */ -/* marks end of patch file header (PC DOS text file EOF character) */ -#define STIR421X_PATCH_END_OF_HDR_TAG 0x1A -#define STIR421X_PATCH_BLOCK_SIZE 1023 - -/* - * Function stir421x_fwupload (struct irda_usb_cb *self, - * unsigned char *patch, - * const unsigned int patch_len) - * - * Upload firmware code to SigmaTel 421X IRDA-USB dongle - */ -static int stir421x_fw_upload(struct irda_usb_cb *self, - const unsigned char *patch, - const unsigned int patch_len) -{ - int ret = -ENOMEM; - int actual_len = 0; - unsigned int i; - unsigned int block_size = 0; - unsigned char *patch_block; - - patch_block = kzalloc(STIR421X_PATCH_BLOCK_SIZE, GFP_KERNEL); - if (patch_block == NULL) - return -ENOMEM; - - /* break up patch into 1023-byte sections */ - for (i = 0; i < patch_len; i += block_size) { - block_size = patch_len - i; - - if (block_size > STIR421X_PATCH_BLOCK_SIZE) - block_size = STIR421X_PATCH_BLOCK_SIZE; - - /* upload the patch section */ - memcpy(patch_block, patch + i, block_size); - - ret = usb_bulk_msg(self->usbdev, - usb_sndbulkpipe(self->usbdev, - self->bulk_out_ep), - patch_block, block_size, - &actual_len, msecs_to_jiffies(500)); - pr_debug("%s(): Bulk send %u bytes, ret=%d\n", - __func__, actual_len, ret); - - if (ret < 0) - break; - - mdelay(10); - } - - kfree(patch_block); - - return ret; - } - -/* - * Function stir421x_patch_device(struct irda_usb_cb *self) - * - * Get a firmware code from userspase using hotplug request_firmware() call - */ -static int stir421x_patch_device(struct irda_usb_cb *self) -{ - unsigned int i; - int ret; - char stir421x_fw_name[12]; - const struct firmware *fw; - const unsigned char *fw_version_ptr; /* pointer to version string */ - unsigned long fw_version = 0; - - /* - * Known firmware patch file names for STIR421x dongles - * are "42101001.sb" or "42101002.sb" - */ - sprintf(stir421x_fw_name, "4210%4X.sb", - le16_to_cpu(self->usbdev->descriptor.bcdDevice)); - ret = request_firmware(&fw, stir421x_fw_name, &self->usbdev->dev); - if (ret < 0) - return ret; - - /* We get a patch from userspace */ - net_info_ratelimited("%s(): Received firmware %s (%zu bytes)\n", - __func__, stir421x_fw_name, fw->size); - - ret = -EINVAL; - - /* Get the bcd product version */ - if (!memcmp(fw->data, STIR421X_PATCH_PRODUCT_VER, - sizeof(STIR421X_PATCH_PRODUCT_VER) - 1)) { - fw_version_ptr = fw->data + - sizeof(STIR421X_PATCH_PRODUCT_VER) - 1; - - /* Let's check if the product version is dotted */ - if (fw_version_ptr[3] == '.' && - fw_version_ptr[7] == '.') { - unsigned long major, minor, build; - major = simple_strtoul(fw_version_ptr, NULL, 10); - minor = simple_strtoul(fw_version_ptr + 4, NULL, 10); - build = simple_strtoul(fw_version_ptr + 8, NULL, 10); - - fw_version = (major << 12) - + (minor << 8) - + ((build / 10) << 4) - + (build % 10); - - pr_debug("%s(): Firmware Product version %ld\n", - __func__, fw_version); - } - } - - if (self->usbdev->descriptor.bcdDevice == cpu_to_le16(fw_version)) { - /* - * If we're here, we've found a correct patch - * The actual image starts after the "STMP" keyword - * so forward to the firmware header tag - */ - for (i = 0; i < fw->size && fw->data[i] != - STIR421X_PATCH_END_OF_HDR_TAG; i++) ; - /* here we check for the out of buffer case */ - if (i < STIR421X_PATCH_CODE_OFFSET && i < fw->size && - STIR421X_PATCH_END_OF_HDR_TAG == fw->data[i]) { - if (!memcmp(fw->data + i + 1, STIR421X_PATCH_STMP_TAG, - sizeof(STIR421X_PATCH_STMP_TAG) - 1)) { - - /* We can upload the patch to the target */ - i += sizeof(STIR421X_PATCH_STMP_TAG); - ret = stir421x_fw_upload(self, &fw->data[i], - fw->size - i); - } - } - } - - release_firmware(fw); - - return ret; -} - - -/********************** IRDA DEVICE CALLBACKS **********************/ -/* - * Main calls from the IrDA/Network subsystem. - * Mostly registering a new irda-usb device and removing it.... - * We only deal with the IrDA side of the business, the USB side will - * be dealt with below... - */ - - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - * - * Note : don't mess with self->netopen - Jean II - */ -static int irda_usb_net_open(struct net_device *netdev) -{ - struct irda_usb_cb *self; - unsigned long flags; - char hwname[16]; - int i; - - IRDA_ASSERT(netdev != NULL, return -1;); - self = netdev_priv(netdev); - IRDA_ASSERT(self != NULL, return -1;); - - spin_lock_irqsave(&self->lock, flags); - /* Can only open the device if it's there */ - if(!self->present) { - spin_unlock_irqrestore(&self->lock, flags); - net_warn_ratelimited("%s(), device not present!\n", __func__); - return -1; - } - - if(self->needspatch) { - spin_unlock_irqrestore(&self->lock, flags); - net_warn_ratelimited("%s(), device needs patch\n", __func__); - return -EIO ; - } - - /* Initialise default speed and xbofs value - * (IrLAP will change that soon) */ - self->speed = -1; - self->xbofs = -1; - self->new_speed = -1; - self->new_xbofs = -1; - - /* To do *before* submitting Rx urbs and starting net Tx queue - * Jean II */ - self->netopen = 1; - spin_unlock_irqrestore(&self->lock, flags); - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - * Note : will send immediately a speed change... - */ - sprintf(hwname, "usb#%d", self->usbdev->devnum); - self->irlap = irlap_open(netdev, &self->qos, hwname); - IRDA_ASSERT(self->irlap != NULL, return -1;); - - /* Allow IrLAP to send data to us */ - netif_start_queue(netdev); - - /* We submit all the Rx URB except for one that we keep idle. - * Need to be initialised before submitting other USBs, because - * in some cases as soon as we submit the URBs the USB layer - * will trigger a dummy receive - Jean II */ - self->idle_rx_urb = self->rx_urb[IU_MAX_ACTIVE_RX_URBS]; - self->idle_rx_urb->context = NULL; - - /* Now that we can pass data to IrLAP, allow the USB layer - * to send us some data... */ - for (i = 0; i < IU_MAX_ACTIVE_RX_URBS; i++) { - struct sk_buff *skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!skb) { - /* If this ever happen, we are in deep s***. - * Basically, we can't start the Rx path... */ - return -1; - } - //skb_reserve(newskb, USB_IRDA_HEADER - 1); - irda_usb_submit(self, skb, self->rx_urb[i]); - } - - /* Ready to play !!! */ - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_net_close (self) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int irda_usb_net_close(struct net_device *netdev) -{ - struct irda_usb_cb *self; - int i; - - IRDA_ASSERT(netdev != NULL, return -1;); - self = netdev_priv(netdev); - IRDA_ASSERT(self != NULL, return -1;); - - /* Clear this flag *before* unlinking the urbs and *before* - * stopping the network Tx queue - Jean II */ - self->netopen = 0; - - /* Stop network Tx queue */ - netif_stop_queue(netdev); - - /* Kill defered Rx URB */ - del_timer(&self->rx_defer_timer); - - /* Deallocate all the Rx path buffers (URBs and skb) */ - for (i = 0; i < self->max_rx_urb; i++) { - struct urb *urb = self->rx_urb[i]; - struct sk_buff *skb = (struct sk_buff *) urb->context; - /* Cancel the receive command */ - usb_kill_urb(urb); - /* The skb is ours, free it */ - if(skb) { - dev_kfree_skb(skb); - urb->context = NULL; - } - } - /* Cancel Tx and speed URB - need to be synchronous to avoid races */ - usb_kill_urb(self->tx_urb); - usb_kill_urb(self->speed_urb); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - unsigned long flags; - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct irda_usb_cb *self; - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - /* Protect us from USB callbacks, net watchdog and else. */ - spin_lock_irqsave(&self->lock, flags); - /* Check if the device is still there */ - if(self->present) { - /* Set the desired speed */ - self->new_speed = irq->ifr_baudrate; - irda_usb_change_speed_xbofs(self); - } - spin_unlock_irqrestore(&self->lock, flags); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - /* Check if the IrDA stack is still there */ - if(self->netopen) - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = irda_usb_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -/*------------------------------------------------------------------*/ - -/********************* IRDA CONFIG SUBROUTINES *********************/ -/* - * Various subroutines dealing with IrDA and network stuff we use to - * configure and initialise each irda-usb instance. - * These functions are used below in the main calls of the driver... - */ - -/*------------------------------------------------------------------*/ -/* - * Set proper values in the IrDA QOS structure - */ -static inline void irda_usb_init_qos(struct irda_usb_cb *self) -{ - struct irda_class_desc *desc; - - - desc = self->irda_desc; - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* See spec section 7.2 for meaning. - * Values are little endian (as most USB stuff), the IrDA stack - * use it in native order (see parameters.c). - Jean II */ - self->qos.baud_rate.bits = le16_to_cpu(desc->wBaudRate); - self->qos.min_turn_time.bits = desc->bmMinTurnaroundTime; - self->qos.additional_bofs.bits = desc->bmAdditionalBOFs; - self->qos.window_size.bits = desc->bmWindowSize; - self->qos.data_size.bits = desc->bmDataSize; - - pr_debug("%s(), dongle says speed=0x%X, size=0x%X, window=0x%X, bofs=0x%X, turn=0x%X\n", - __func__, self->qos.baud_rate.bits, self->qos.data_size.bits, - self->qos.window_size.bits, self->qos.additional_bofs.bits, - self->qos.min_turn_time.bits); - - /* Don't always trust what the dongle tell us */ - if(self->capability & IUC_SIR_ONLY) - self->qos.baud_rate.bits &= 0x00ff; - if(self->capability & IUC_SMALL_PKT) - self->qos.data_size.bits = 0x07; - if(self->capability & IUC_NO_WINDOW) - self->qos.window_size.bits = 0x01; - if(self->capability & IUC_MAX_WINDOW) - self->qos.window_size.bits = 0x7f; - if(self->capability & IUC_MAX_XBOFS) - self->qos.additional_bofs.bits = 0x01; - -#if 1 - /* Module parameter can override the rx window size */ - if (qos_mtt_bits) - self->qos.min_turn_time.bits = qos_mtt_bits; -#endif - /* - * Note : most of those values apply only for the receive path, - * the transmit path will be set differently - Jean II - */ - irda_qos_bits_to_value(&self->qos); -} - -/*------------------------------------------------------------------*/ -static const struct net_device_ops irda_usb_netdev_ops = { - .ndo_open = irda_usb_net_open, - .ndo_stop = irda_usb_net_close, - .ndo_do_ioctl = irda_usb_net_ioctl, - .ndo_start_xmit = irda_usb_hard_xmit, - .ndo_tx_timeout = irda_usb_net_timeout, -}; - -/* - * Initialise the network side of the irda-usb instance - * Called when a new USB instance is registered in irda_usb_probe() - */ -static inline int irda_usb_open(struct irda_usb_cb *self) -{ - struct net_device *netdev = self->netdev; - - netdev->netdev_ops = &irda_usb_netdev_ops; - - irda_usb_init_qos(self); - - return register_netdev(netdev); -} - -/*------------------------------------------------------------------*/ -/* - * Cleanup the network side of the irda-usb instance - * Called when a USB instance is removed in irda_usb_disconnect() - */ -static inline void irda_usb_close(struct irda_usb_cb *self) -{ - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Remove the speed buffer */ - kfree(self->speed_buff); - self->speed_buff = NULL; - - kfree(self->tx_buff); - self->tx_buff = NULL; -} - -/********************** USB CONFIG SUBROUTINES **********************/ -/* - * Various subroutines dealing with USB stuff we use to configure and - * initialise each irda-usb instance. - * These functions are used below in the main calls of the driver... - */ - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_parse_endpoints(dev, ifnum) - * - * Parse the various endpoints and find the one we need. - * - * The endpoint are the pipes used to communicate with the USB device. - * The spec defines 2 endpoints of type bulk transfer, one in, and one out. - * These are used to pass frames back and forth with the dongle. - * Most dongle have also an interrupt endpoint, that will be probably - * documented in the next spec... - */ -static inline int irda_usb_parse_endpoints(struct irda_usb_cb *self, struct usb_host_endpoint *endpoint, int ennum) -{ - int i; /* Endpoint index in table */ - - /* Init : no endpoints */ - self->bulk_in_ep = 0; - self->bulk_out_ep = 0; - self->bulk_int_ep = 0; - - /* Let's look at all those endpoints */ - for(i = 0; i < ennum; i++) { - /* All those variables will get optimised by the compiler, - * so let's aim for clarity... - Jean II */ - __u8 ep; /* Endpoint address */ - __u8 dir; /* Endpoint direction */ - __u8 attr; /* Endpoint attribute */ - __u16 psize; /* Endpoint max packet size in bytes */ - - /* Get endpoint address, direction and attribute */ - ep = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - dir = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK; - attr = endpoint[i].desc.bmAttributes; - psize = le16_to_cpu(endpoint[i].desc.wMaxPacketSize); - - /* Is it a bulk endpoint ??? */ - if(attr == USB_ENDPOINT_XFER_BULK) { - /* We need to find an IN and an OUT */ - if(dir == USB_DIR_IN) { - /* This is our Rx endpoint */ - self->bulk_in_ep = ep; - } else { - /* This is our Tx endpoint */ - self->bulk_out_ep = ep; - self->bulk_out_mtu = psize; - } - } else { - if((attr == USB_ENDPOINT_XFER_INT) && - (dir == USB_DIR_IN)) { - /* This is our interrupt endpoint */ - self->bulk_int_ep = ep; - } else { - net_err_ratelimited("%s(), Unrecognised endpoint %02X\n", - __func__, ep); - } - } - } - - pr_debug("%s(), And our endpoints are : in=%02X, out=%02X (%d), int=%02X\n", - __func__, self->bulk_in_ep, self->bulk_out_ep, - self->bulk_out_mtu, self->bulk_int_ep); - - return (self->bulk_in_ep != 0) && (self->bulk_out_ep != 0); -} - -#ifdef IU_DUMP_CLASS_DESC -/*------------------------------------------------------------------*/ -/* - * Function usb_irda_dump_class_desc(desc) - * - * Prints out the contents of the IrDA class descriptor - * - */ -static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) -{ - /* Values are little endian */ - printk("bLength=%x\n", desc->bLength); - printk("bDescriptorType=%x\n", desc->bDescriptorType); - printk("bcdSpecRevision=%x\n", le16_to_cpu(desc->bcdSpecRevision)); - printk("bmDataSize=%x\n", desc->bmDataSize); - printk("bmWindowSize=%x\n", desc->bmWindowSize); - printk("bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime); - printk("wBaudRate=%x\n", le16_to_cpu(desc->wBaudRate)); - printk("bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs); - printk("bIrdaRateSniff=%x\n", desc->bIrdaRateSniff); - printk("bMaxUnicastList=%x\n", desc->bMaxUnicastList); -} -#endif /* IU_DUMP_CLASS_DESC */ - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_find_class_desc(intf) - * - * Returns instance of IrDA class descriptor, or NULL if not found - * - * The class descriptor is some extra info that IrDA USB devices will - * offer to us, describing their IrDA characteristics. We will use that in - * irda_usb_init_qos() - */ -static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf) -{ - struct usb_device *dev = interface_to_usbdev (intf); - struct irda_class_desc *desc; - int ret; - - desc = kzalloc(sizeof(*desc), GFP_KERNEL); - if (!desc) - return NULL; - - /* USB-IrDA class spec 1.0: - * 6.1.3: Standard "Get Descriptor" Device Request is not - * appropriate to retrieve class-specific descriptor - * 6.2.5: Class Specific "Get Class Descriptor" Interface Request - * is mandatory and returns the USB-IrDA class descriptor - */ - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), - IU_REQ_GET_CLASS_DESC, - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, intf->altsetting->desc.bInterfaceNumber, desc, - sizeof(*desc), 500); - - pr_debug("%s(), ret=%d\n", __func__, ret); - if (ret < sizeof(*desc)) { - net_warn_ratelimited("usb-irda: class_descriptor read %s (%d)\n", - ret < 0 ? "failed" : "too short", ret); - } - else if (desc->bDescriptorType != USB_DT_IRDA) { - net_warn_ratelimited("usb-irda: bad class_descriptor type\n"); - } - else { -#ifdef IU_DUMP_CLASS_DESC - irda_usb_dump_class_desc(desc); -#endif /* IU_DUMP_CLASS_DESC */ - - return desc; - } - kfree(desc); - return NULL; -} - -/*********************** USB DEVICE CALLBACKS ***********************/ -/* - * Main calls from the USB subsystem. - * Mostly registering a new irda-usb device and removing it.... - */ - -/*------------------------------------------------------------------*/ -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - * The USB layer protect us from reentrancy (via BKL), so we don't need - * to spinlock in there... Jean II - */ -static int irda_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct net_device *net; - struct usb_device *dev = interface_to_usbdev(intf); - struct irda_usb_cb *self; - struct usb_host_interface *interface; - struct irda_class_desc *irda_desc; - int ret = -ENOMEM; - int i; /* Driver instance index / Rx URB index */ - - /* Note : the probe make sure to call us only for devices that - * matches the list of dongle (top of the file). So, we - * don't need to check if the dongle is really ours. - * Jean II */ - - net_info_ratelimited("IRDA-USB found at address %d, Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - net = alloc_irdadev(sizeof(*self)); - if (!net) - goto err_out; - - SET_NETDEV_DEV(net, &intf->dev); - self = netdev_priv(net); - self->netdev = net; - spin_lock_init(&self->lock); - init_timer(&self->rx_defer_timer); - - self->capability = id->driver_info; - self->needspatch = ((self->capability & IUC_STIR421X) != 0); - - /* Create all of the needed urbs */ - if (self->capability & IUC_STIR421X) { - self->max_rx_urb = IU_SIGMATEL_MAX_RX_URBS; - self->header_length = USB_IRDA_STIR421X_HEADER; - } else { - self->max_rx_urb = IU_MAX_RX_URBS; - self->header_length = USB_IRDA_HEADER; - } - - self->rx_urb = kcalloc(self->max_rx_urb, sizeof(struct urb *), - GFP_KERNEL); - if (!self->rx_urb) - goto err_free_net; - - for (i = 0; i < self->max_rx_urb; i++) { - self->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); - if (!self->rx_urb[i]) { - goto err_out_1; - } - } - self->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!self->tx_urb) { - goto err_out_1; - } - self->speed_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!self->speed_urb) { - goto err_out_2; - } - - /* Is this really necessary? (no, except maybe for broken devices) */ - if (usb_reset_configuration (dev) < 0) { - dev_err(&intf->dev, "reset_configuration failed\n"); - ret = -EIO; - goto err_out_3; - } - - /* Is this really necessary? */ - /* Note : some driver do hardcode the interface number, some others - * specify an alternate, but very few driver do like this. - * Jean II */ - ret = usb_set_interface(dev, intf->altsetting->desc.bInterfaceNumber, 0); - pr_debug("usb-irda: set interface %d result %d\n", - intf->altsetting->desc.bInterfaceNumber, ret); - switch (ret) { - case 0: - break; - case -EPIPE: /* -EPIPE = -32 */ - /* Martin Diehl says if we get a -EPIPE we should - * be fine and we don't need to do a usb_clear_halt(). - * - Jean II */ - pr_debug("%s(), Received -EPIPE, ignoring...\n", - __func__); - break; - default: - pr_debug("%s(), Unknown error %d\n", __func__, ret); - ret = -EIO; - goto err_out_3; - } - - /* Find our endpoints */ - interface = intf->cur_altsetting; - if(!irda_usb_parse_endpoints(self, interface->endpoint, - interface->desc.bNumEndpoints)) { - net_err_ratelimited("%s(), Bogus endpoints...\n", __func__); - ret = -EIO; - goto err_out_3; - } - - self->usbdev = dev; - - /* Find IrDA class descriptor */ - irda_desc = irda_usb_find_class_desc(intf); - ret = -ENODEV; - if (!irda_desc) - goto err_out_3; - - if (self->needspatch) { - ret = usb_control_msg (self->usbdev, usb_sndctrlpipe (self->usbdev, 0), - 0x02, 0x40, 0, 0, NULL, 0, 500); - if (ret < 0) { - pr_debug("usb_control_msg failed %d\n", ret); - goto err_out_3; - } else { - mdelay(10); - } - } - - self->irda_desc = irda_desc; - self->present = 1; - self->netopen = 0; - self->usbintf = intf; - - /* Allocate the buffer for speed changes */ - /* Don't change this buffer size and allocation without doing - * some heavy and complete testing. Don't ask why :-( - * Jean II */ - ret = -ENOMEM; - self->speed_buff = kzalloc(IRDA_USB_SPEED_MTU, GFP_KERNEL); - if (!self->speed_buff) - goto err_out_3; - - self->tx_buff = kzalloc(IRDA_SKB_MAX_MTU + self->header_length, - GFP_KERNEL); - if (!self->tx_buff) - goto err_out_4; - - ret = irda_usb_open(self); - if (ret) - goto err_out_5; - - net_info_ratelimited("IrDA: Registered device %s\n", net->name); - usb_set_intfdata(intf, self); - - if (self->needspatch) { - /* Now we fetch and upload the firmware patch */ - ret = stir421x_patch_device(self); - self->needspatch = (ret < 0); - if (self->needspatch) { - net_err_ratelimited("STIR421X: Couldn't upload patch\n"); - goto err_out_6; - } - - /* replace IrDA class descriptor with what patched device is now reporting */ - irda_desc = irda_usb_find_class_desc (self->usbintf); - if (!irda_desc) { - ret = -ENODEV; - goto err_out_6; - } - kfree(self->irda_desc); - self->irda_desc = irda_desc; - irda_usb_init_qos(self); - } - - return 0; -err_out_6: - unregister_netdev(self->netdev); -err_out_5: - kfree(self->tx_buff); -err_out_4: - kfree(self->speed_buff); -err_out_3: - /* Free all urbs that we may have created */ - usb_free_urb(self->speed_urb); -err_out_2: - usb_free_urb(self->tx_urb); -err_out_1: - for (i = 0; i < self->max_rx_urb; i++) - usb_free_urb(self->rx_urb[i]); - kfree(self->rx_urb); -err_free_net: - free_netdev(net); -err_out: - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * The current irda-usb device is removed, the USB layer tell us - * to shut it down... - * One of the constraints is that when we exit this function, - * we cannot use the usb_device no more. Gone. Destroyed. kfree(). - * Most other subsystem allow you to destroy the instance at a time - * when it's convenient to you, to postpone it to a later date, but - * not the USB subsystem. - * So, we must make bloody sure that everything gets deactivated. - * Jean II - */ -static void irda_usb_disconnect(struct usb_interface *intf) -{ - unsigned long flags; - struct irda_usb_cb *self = usb_get_intfdata(intf); - int i; - - usb_set_intfdata(intf, NULL); - if (!self) - return; - - /* Make sure that the Tx path is not executing. - Jean II */ - spin_lock_irqsave(&self->lock, flags); - - /* Oups ! We are not there any more. - * This will stop/desactivate the Tx path. - Jean II */ - self->present = 0; - - /* Kill defered Rx URB */ - del_timer(&self->rx_defer_timer); - - /* We need to have irq enabled to unlink the URBs. That's OK, - * at this point the Tx path is gone - Jean II */ - spin_unlock_irqrestore(&self->lock, flags); - - /* Hum... Check if networking is still active (avoid races) */ - if((self->netopen) || (self->irlap)) { - /* Accept no more transmissions */ - /*netif_device_detach(self->netdev);*/ - netif_stop_queue(self->netdev); - /* Stop all the receive URBs. Must be synchronous. */ - for (i = 0; i < self->max_rx_urb; i++) - usb_kill_urb(self->rx_urb[i]); - /* Cancel Tx and speed URB. - * Make sure it's synchronous to avoid races. */ - usb_kill_urb(self->tx_urb); - usb_kill_urb(self->speed_urb); - } - - /* Cleanup the device stuff */ - irda_usb_close(self); - /* No longer attached to USB bus */ - self->usbdev = NULL; - self->usbintf = NULL; - - /* Clean up our urbs */ - for (i = 0; i < self->max_rx_urb; i++) - usb_free_urb(self->rx_urb[i]); - kfree(self->rx_urb); - /* Clean up Tx and speed URB */ - usb_free_urb(self->tx_urb); - usb_free_urb(self->speed_urb); - - /* Free self and network device */ - free_netdev(self->netdev); - pr_debug("%s(), USB IrDA Disconnected\n", __func__); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int irda_usb_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct irda_usb_cb *self = usb_get_intfdata(intf); - int i; - - netif_device_detach(self->netdev); - - if (self->tx_urb != NULL) - usb_kill_urb(self->tx_urb); - if (self->speed_urb != NULL) - usb_kill_urb(self->speed_urb); - for (i = 0; i < self->max_rx_urb; i++) { - if (self->rx_urb[i] != NULL) - usb_kill_urb(self->rx_urb[i]); - } - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int irda_usb_resume(struct usb_interface *intf) -{ - struct irda_usb_cb *self = usb_get_intfdata(intf); - int i; - - for (i = 0; i < self->max_rx_urb; i++) { - if (self->rx_urb[i] != NULL) - usb_submit_urb(self->rx_urb[i], GFP_KERNEL); - } - - netif_device_attach(self->netdev); - return 0; -} -#endif - -/*------------------------------------------------------------------*/ -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "irda-usb", - .probe = irda_usb_probe, - .disconnect = irda_usb_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = irda_usb_suspend, - .resume = irda_usb_resume, -#endif -}; - -module_usb_driver(irda_driver); - -/* - * Module parameters - */ -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); -MODULE_AUTHOR("Roman Weissgaerber , Dag Brattli , Jean Tourrilhes and Nick Fedchik "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/irda-usb.h b/drivers/net/irda/irda-usb.h deleted file mode 100644 index 8ac389fa9348..000000000000 --- a/drivers/net/irda/irda-usb.h +++ /dev/null @@ -1,174 +0,0 @@ -/***************************************************************************** - * - * Filename: irda-usb.h - * Version: 0.10 - * Description: IrDA-USB Driver - * Status: Experimental - * Author: Dag Brattli - * - * Copyright (C) 2001, Roman Weissgaerber - * Copyright (C) 2000, Dag Brattli - * Copyright (C) 2001, Jean Tourrilhes - * Copyright (C) 2004, SigmaTel, Inc. - * Copyright (C) 2005, Milan Beno - * Copyright (C) 2006, Nick FEdchik - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - *****************************************************************************/ - -#include - -#include -#include /* struct irlap_cb */ - -#define RX_COPY_THRESHOLD 200 -#define IRDA_USB_MAX_MTU 2051 -#define IRDA_USB_SPEED_MTU 64 /* Weird, but work like this */ - -/* Maximum number of active URB on the Rx path - * This is the amount of buffers the we keep between the USB harware and the - * IrDA stack. - * - * Note : the network layer does also queue the packets between us and the - * IrDA stack, and is actually pretty fast and efficient in doing that. - * Therefore, we don't need to have a large number of URBs, and we can - * perfectly live happy with only one. We certainly don't need to keep the - * full IrTTP window around here... - * I repeat for those who have trouble to understand : 1 URB is plenty - * good enough to handle back-to-back (brickwalled) frames. I tried it, - * it works (it's the hardware that has trouble doing it). - * - * Having 2 URBs would allow the USB stack to process one URB while we take - * care of the other and then swap the URBs... - * On the other hand, increasing the number of URB will have penalities - * in term of latency and will interact with the link management in IrLAP... - * Jean II */ -#define IU_MAX_ACTIVE_RX_URBS 1 /* Don't touch !!! */ - -/* When a Rx URB is passed back to us, we can't reuse it immediately, - * because it may still be referenced by the USB layer. Therefore we - * need to keep one extra URB in the Rx path. - * Jean II */ -#define IU_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + 1) - -/* Various ugly stuff to try to workaround generic problems */ -/* Send speed command in case of timeout, just for trying to get things sane */ -#define IU_BUG_KICK_TIMEOUT -/* Show the USB class descriptor */ -#undef IU_DUMP_CLASS_DESC -/* Assume a minimum round trip latency for USB transfer (in us)... - * USB transfer are done in the next USB slot if there is no traffic - * (1/19 msec) and is done at 12 Mb/s : - * Waiting for slot + tx = (53us + 16us) * 2 = 137us minimum. - * Rx notification will only be done at the end of the USB frame period : - * OHCI : frame period = 1ms - * UHCI : frame period = 1ms, but notification can take 2 or 3 ms :-( - * EHCI : frame period = 125us */ -#define IU_USB_MIN_RTT 500 /* This should be safe in most cases */ - -/* Inbound header */ -#define MEDIA_BUSY 0x80 - -#define SPEED_2400 0x01 -#define SPEED_9600 0x02 -#define SPEED_19200 0x03 -#define SPEED_38400 0x04 -#define SPEED_57600 0x05 -#define SPEED_115200 0x06 -#define SPEED_576000 0x07 -#define SPEED_1152000 0x08 -#define SPEED_4000000 0x09 -#define SPEED_16000000 0x0a - -/* Basic capabilities */ -#define IUC_DEFAULT 0x00 /* Basic device compliant with 1.0 spec */ -/* Main bugs */ -#define IUC_SPEED_BUG 0x01 /* Device doesn't set speed after the frame */ -#define IUC_NO_WINDOW 0x02 /* Device doesn't behave with big Rx window */ -#define IUC_NO_TURN 0x04 /* Device doesn't do turnaround by itself */ -/* Not currently used */ -#define IUC_SIR_ONLY 0x08 /* Device doesn't behave at FIR speeds */ -#define IUC_SMALL_PKT 0x10 /* Device doesn't behave with big Rx packets */ -#define IUC_MAX_WINDOW 0x20 /* Device underestimate the Rx window */ -#define IUC_MAX_XBOFS 0x40 /* Device need more xbofs than advertised */ -#define IUC_STIR421X 0x80 /* SigmaTel 4210/4220/4116 VFIR */ - -/* USB class definitions */ -#define USB_IRDA_HEADER 0x01 -#define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */ -#define USB_DT_IRDA 0x21 -#define USB_IRDA_STIR421X_HEADER 0x03 -#define IU_SIGMATEL_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + \ - USB_IRDA_STIR421X_HEADER) - -struct irda_class_desc { - __u8 bLength; - __u8 bDescriptorType; - __le16 bcdSpecRevision; - __u8 bmDataSize; - __u8 bmWindowSize; - __u8 bmMinTurnaroundTime; - __le16 wBaudRate; - __u8 bmAdditionalBOFs; - __u8 bIrdaRateSniff; - __u8 bMaxUnicastList; -} __packed; - -/* class specific interface request to get the IrDA-USB class descriptor - * (6.2.5, USB-IrDA class spec 1.0) */ - -#define IU_REQ_GET_CLASS_DESC 0x06 -#define STIR421X_MAX_PATCH_DOWNLOAD_SIZE 1023 - -struct irda_usb_cb { - struct irda_class_desc *irda_desc; - struct usb_device *usbdev; /* init: probe_irda */ - struct usb_interface *usbintf; /* init: probe_irda */ - int netopen; /* Device is active for network */ - int present; /* Device is present on the bus */ - __u32 capability; /* Capability of the hardware */ - __u8 bulk_in_ep; /* Rx Endpoint assignments */ - __u8 bulk_out_ep; /* Tx Endpoint assignments */ - __u16 bulk_out_mtu; /* Max Tx packet size in bytes */ - __u8 bulk_int_ep; /* Interrupt Endpoint assignments */ - - __u8 max_rx_urb; - struct urb **rx_urb; /* URBs used to receive data frames */ - struct urb *idle_rx_urb; /* Pointer to idle URB in Rx path */ - struct urb *tx_urb; /* URB used to send data frames */ - struct urb *speed_urb; /* URB used to send speed commands */ - - struct net_device *netdev; /* Yes! we are some kind of netdev. */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; - char *speed_buff; /* Buffer for speed changes */ - char *tx_buff; - - ktime_t stamp; - - spinlock_t lock; /* For serializing Tx operations */ - - __u16 xbofs; /* Current xbofs setting */ - __s16 new_xbofs; /* xbofs we need to set */ - __u32 speed; /* Current speed */ - __s32 new_speed; /* speed we need to set */ - - __u8 header_length; /* USB-IrDA frame header size */ - int needspatch; /* device needs firmware patch */ - - struct timer_list rx_defer_timer; /* Wait for Rx error to clear */ -}; - diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c deleted file mode 100644 index 7a20a9a4663a..000000000000 --- a/drivers/net/irda/irtty-sir.c +++ /dev/null @@ -1,570 +0,0 @@ -/********************************************************************* - * - * Filename: irtty-sir.c - * Version: 2.0 - * Description: IrDA line discipline implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Dec 9 21:18:38 1997 - * Modified at: Sun Oct 27 22:13:30 2002 - * Modified by: Martin Diehl - * Sources: slip.c by Laurence Culhane, - * Fred N. van Kempen, - * - * Copyright (c) 1998-2000 Dag Brattli, - * Copyright (c) 2002 Martin Diehl, - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "sir-dev.h" -#include "irtty-sir.h" - -static int qos_mtt_bits = 0x03; /* 5 ms or more */ - -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); - -/* ------------------------------------------------------- */ - -/* device configuration callbacks always invoked with irda-thread context */ - -/* find out, how many chars we have in buffers below us - * this is allowed to lie, i.e. return less chars than we - * actually have. The returned value is used to determine - * how long the irdathread should wait before doing the - * real blocking wait_until_sent() - */ - -static int irtty_chars_in_buffer(struct sir_dev *dev) -{ - struct sirtty_cb *priv = dev->priv; - - IRDA_ASSERT(priv != NULL, return -1;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - - return tty_chars_in_buffer(priv->tty); -} - -/* Wait (sleep) until underlaying hardware finished transmission - * i.e. hardware buffers are drained - * this must block and not return before all characters are really sent - * - * If the tty sits on top of a 16550A-like uart, there are typically - * up to 16 bytes in the fifo - f.e. 9600 bps 8N1 needs 16.7 msec - * - * With usbserial the uart-fifo is basically replaced by the converter's - * outgoing endpoint buffer, which can usually hold 64 bytes (at least). - * With pl2303 it appears we are safe with 60msec here. - * - * I really wish all serial drivers would provide - * correct implementation of wait_until_sent() - */ - -#define USBSERIAL_TX_DONE_DELAY 60 - -static void irtty_wait_until_sent(struct sir_dev *dev) -{ - struct sirtty_cb *priv = dev->priv; - struct tty_struct *tty; - - IRDA_ASSERT(priv != NULL, return;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); - - tty = priv->tty; - if (tty->ops->wait_until_sent) { - tty->ops->wait_until_sent(tty, msecs_to_jiffies(100)); - } - else { - msleep(USBSERIAL_TX_DONE_DELAY); - } -} - -/* - * Function irtty_change_speed (dev, speed) - * - * Change the speed of the serial port. - * - * This may sleep in set_termios (usbserial driver f.e.) and must - * not be called from interrupt/timer/tasklet therefore. - * All such invocations are deferred to kIrDAd now so we can sleep there. - */ - -static int irtty_change_speed(struct sir_dev *dev, unsigned speed) -{ - struct sirtty_cb *priv = dev->priv; - struct tty_struct *tty; - struct ktermios old_termios; - int cflag; - - IRDA_ASSERT(priv != NULL, return -1;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - - tty = priv->tty; - - down_write(&tty->termios_rwsem); - old_termios = tty->termios; - cflag = tty->termios.c_cflag; - tty_encode_baud_rate(tty, speed, speed); - if (tty->ops->set_termios) - tty->ops->set_termios(tty, &old_termios); - priv->io.speed = speed; - up_write(&tty->termios_rwsem); - - return 0; -} - -/* - * Function irtty_set_dtr_rts (dev, dtr, rts) - * - * This function can be used by dongles etc. to set or reset the status - * of the dtr and rts lines - */ - -static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) -{ - struct sirtty_cb *priv = dev->priv; - int set = 0; - int clear = 0; - - IRDA_ASSERT(priv != NULL, return -1;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - - if (rts) - set |= TIOCM_RTS; - else - clear |= TIOCM_RTS; - if (dtr) - set |= TIOCM_DTR; - else - clear |= TIOCM_DTR; - - /* - * We can't use ioctl() because it expects a non-null file structure, - * and we don't have that here. - * This function is not yet defined for all tty driver, so - * let's be careful... Jean II - */ - IRDA_ASSERT(priv->tty->ops->tiocmset != NULL, return -1;); - priv->tty->ops->tiocmset(priv->tty, set, clear); - - return 0; -} - -/* ------------------------------------------------------- */ - -/* called from sir_dev when there is more data to send - * context is either netdev->hard_xmit or some transmit-completion bh - * i.e. we are under spinlock here and must not sleep. - */ - -static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t len) -{ - struct sirtty_cb *priv = dev->priv; - struct tty_struct *tty; - int writelen; - - IRDA_ASSERT(priv != NULL, return -1;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - - tty = priv->tty; - if (!tty->ops->write) - return 0; - set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - writelen = tty_write_room(tty); - if (writelen > len) - writelen = len; - return tty->ops->write(tty, ptr, writelen); -} - -/* ------------------------------------------------------- */ - -/* irda line discipline callbacks */ - -/* - * Function irtty_receive_buf( tty, cp, count) - * - * Handle the 'receiver data ready' interrupt. This function is called - * by the 'tty_io' module in the kernel when a block of IrDA data has - * been received, which can now be decapsulated and delivered for - * further processing - * - * calling context depends on underlying driver and tty->port->low_latency! - * for example (low_latency: 1 / 0): - * serial.c: uart-interrupt / softint - * usbserial: urb-complete-interrupt / softint - */ - -static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - struct sir_dev *dev; - struct sirtty_cb *priv = tty->disc_data; - int i; - - IRDA_ASSERT(priv != NULL, return;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); - - if (unlikely(count==0)) /* yes, this happens */ - return; - - dev = priv->dev; - if (!dev) { - net_warn_ratelimited("%s(), not ready yet!\n", __func__); - return; - } - - for (i = 0; i < count; i++) { - /* - * Characters received with a parity error, etc? - */ - if (fp && *fp++) { - pr_debug("Framing or parity error!\n"); - sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */ - return; - } - } - - sirdev_receive(dev, cp, count); -} - -/* - * Function irtty_write_wakeup (tty) - * - * Called by the driver when there's room for more data. If we have - * more packets to send, we send them here. - * - */ -static void irtty_write_wakeup(struct tty_struct *tty) -{ - struct sirtty_cb *priv = tty->disc_data; - - IRDA_ASSERT(priv != NULL, return;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); - - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - if (priv->dev) - sirdev_write_complete(priv->dev); -} - -/* ------------------------------------------------------- */ - -/* - * Function irtty_stop_receiver (tty, stop) - * - */ - -static inline void irtty_stop_receiver(struct tty_struct *tty, int stop) -{ - struct ktermios old_termios; - int cflag; - - down_write(&tty->termios_rwsem); - old_termios = tty->termios; - cflag = tty->termios.c_cflag; - - if (stop) - cflag &= ~CREAD; - else - cflag |= CREAD; - - tty->termios.c_cflag = cflag; - if (tty->ops->set_termios) - tty->ops->set_termios(tty, &old_termios); - up_write(&tty->termios_rwsem); -} - -/*****************************************************************/ - -/* serialize ldisc open/close with sir_dev */ -static DEFINE_MUTEX(irtty_mutex); - -/* notifier from sir_dev when irda% device gets opened (ifup) */ - -static int irtty_start_dev(struct sir_dev *dev) -{ - struct sirtty_cb *priv; - struct tty_struct *tty; - - /* serialize with ldisc open/close */ - mutex_lock(&irtty_mutex); - - priv = dev->priv; - if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) { - mutex_unlock(&irtty_mutex); - return -ESTALE; - } - - tty = priv->tty; - - if (tty->ops->start) - tty->ops->start(tty); - /* Make sure we can receive more data */ - irtty_stop_receiver(tty, FALSE); - - mutex_unlock(&irtty_mutex); - return 0; -} - -/* notifier from sir_dev when irda% device gets closed (ifdown) */ - -static int irtty_stop_dev(struct sir_dev *dev) -{ - struct sirtty_cb *priv; - struct tty_struct *tty; - - /* serialize with ldisc open/close */ - mutex_lock(&irtty_mutex); - - priv = dev->priv; - if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) { - mutex_unlock(&irtty_mutex); - return -ESTALE; - } - - tty = priv->tty; - - /* Make sure we don't receive more data */ - irtty_stop_receiver(tty, TRUE); - if (tty->ops->stop) - tty->ops->stop(tty); - - mutex_unlock(&irtty_mutex); - - return 0; -} - -/* ------------------------------------------------------- */ - -static struct sir_driver sir_tty_drv = { - .owner = THIS_MODULE, - .driver_name = "sir_tty", - .start_dev = irtty_start_dev, - .stop_dev = irtty_stop_dev, - .do_write = irtty_do_write, - .chars_in_buffer = irtty_chars_in_buffer, - .wait_until_sent = irtty_wait_until_sent, - .set_speed = irtty_change_speed, - .set_dtr_rts = irtty_set_dtr_rts, -}; - -/* ------------------------------------------------------- */ - -/* - * Function irtty_ioctl (tty, file, cmd, arg) - * - * The Swiss army knife of system calls :-) - * - */ -static int irtty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct irtty_info { char name[6]; } info; - struct sir_dev *dev; - struct sirtty_cb *priv = tty->disc_data; - int err = 0; - - IRDA_ASSERT(priv != NULL, return -ENODEV;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EBADR;); - - pr_debug("%s(cmd=0x%X)\n", __func__, cmd); - - dev = priv->dev; - IRDA_ASSERT(dev != NULL, return -1;); - - switch (cmd) { - case IRTTY_IOCTDONGLE: - /* this call blocks for completion */ - err = sirdev_set_dongle(dev, (IRDA_DONGLE) arg); - break; - - case IRTTY_IOCGET: - IRDA_ASSERT(dev->netdev != NULL, return -1;); - - memset(&info, 0, sizeof(info)); - strncpy(info.name, dev->netdev->name, sizeof(info.name)-1); - - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - err = -EFAULT; - break; - default: - err = tty_mode_ioctl(tty, file, cmd, arg); - break; - } - return err; -} - - -/* - * Function irtty_open(tty) - * - * This function is called by the TTY module when the IrDA line - * discipline is called for. Because we are sure the tty line exists, - * we only have to link it to a free IrDA channel. - */ -static int irtty_open(struct tty_struct *tty) -{ - struct sir_dev *dev; - struct sirtty_cb *priv; - int ret = 0; - - /* Module stuff handled via irda_ldisc.owner - Jean II */ - - /* stop the underlying driver */ - irtty_stop_receiver(tty, TRUE); - if (tty->ops->stop) - tty->ops->stop(tty); - - tty_driver_flush_buffer(tty); - - /* apply mtt override */ - sir_tty_drv.qos_mtt_bits = qos_mtt_bits; - - /* get a sir device instance for this driver */ - dev = sirdev_get_instance(&sir_tty_drv, tty->name); - if (!dev) { - ret = -ENODEV; - goto out; - } - - /* allocate private device info block */ - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto out_put; - } - - priv->magic = IRTTY_MAGIC; - priv->tty = tty; - priv->dev = dev; - - /* serialize with start_dev - in case we were racing with ifup */ - mutex_lock(&irtty_mutex); - - dev->priv = priv; - tty->disc_data = priv; - tty->receive_room = 65536; - - mutex_unlock(&irtty_mutex); - - pr_debug("%s - %s: irda line discipline opened\n", __func__, tty->name); - - return 0; - -out_put: - sirdev_put_instance(dev); -out: - return ret; -} - -/* - * Function irtty_close (tty) - * - * Close down a IrDA channel. This means flushing out any pending queues, - * and then restoring the TTY line discipline to what it was before it got - * hooked to IrDA (which usually is TTY again). - */ -static void irtty_close(struct tty_struct *tty) -{ - struct sirtty_cb *priv = tty->disc_data; - - IRDA_ASSERT(priv != NULL, return;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); - - /* Hm, with a dongle attached the dongle driver wants - * to close the dongle - which requires the use of - * some tty write and/or termios or ioctl operations. - * Are we allowed to call those when already requested - * to shutdown the ldisc? - * If not, we should somehow mark the dev being staled. - * Question remains, how to close the dongle in this case... - * For now let's assume we are granted to issue tty driver calls - * until we return here from the ldisc close. I'm just wondering - * how this behaves with hotpluggable serial hardware like - * rs232-pcmcia card or usb-serial... - * - * priv->tty = NULL?; - */ - - /* we are dead now */ - tty->disc_data = NULL; - - sirdev_put_instance(priv->dev); - - /* Stop tty */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - if (tty->ops->stop) - tty->ops->stop(tty); - - kfree(priv); - - pr_debug("%s - %s: irda line discipline closed\n", __func__, tty->name); -} - -/* ------------------------------------------------------- */ - -static struct tty_ldisc_ops irda_ldisc = { - .magic = TTY_LDISC_MAGIC, - .name = "irda", - .flags = 0, - .open = irtty_open, - .close = irtty_close, - .read = NULL, - .write = NULL, - .ioctl = irtty_ioctl, - .poll = NULL, - .receive_buf = irtty_receive_buf, - .write_wakeup = irtty_write_wakeup, - .owner = THIS_MODULE, -}; - -/* ------------------------------------------------------- */ - -static int __init irtty_sir_init(void) -{ - int err; - - if ((err = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0) - net_err_ratelimited("IrDA: can't register line discipline (err = %d)\n", - err); - return err; -} - -static void __exit irtty_sir_cleanup(void) -{ - int err; - - if ((err = tty_unregister_ldisc(N_IRDA))) { - net_err_ratelimited("%s(), can't unregister line discipline (err = %d)\n", - __func__, err); - } -} - -module_init(irtty_sir_init); -module_exit(irtty_sir_cleanup); - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("IrDA TTY device driver"); -MODULE_ALIAS_LDISC(N_IRDA); -MODULE_LICENSE("GPL"); - diff --git a/drivers/net/irda/irtty-sir.h b/drivers/net/irda/irtty-sir.h deleted file mode 100644 index b132d8f6eb13..000000000000 --- a/drivers/net/irda/irtty-sir.h +++ /dev/null @@ -1,34 +0,0 @@ -/********************************************************************* - * - * sir_tty.h: definitions for the irtty_sir client driver (former irtty) - * - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#ifndef IRTTYSIR_H -#define IRTTYSIR_H - -#include -#include // chipio_t - -#define IRTTY_IOC_MAGIC 'e' -#define IRTTY_IOCTDONGLE _IO(IRTTY_IOC_MAGIC, 1) -#define IRTTY_IOCGET _IOR(IRTTY_IOC_MAGIC, 2, struct irtty_info) -#define IRTTY_IOC_MAXNR 2 - -struct sirtty_cb { - magic_t magic; - - struct sir_dev *dev; - struct tty_struct *tty; - - chipio_t io; /* IrDA controller information */ -}; - -#endif diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c deleted file mode 100644 index 4fd4ac2fe09f..000000000000 --- a/drivers/net/irda/kingsun-sir.c +++ /dev/null @@ -1,634 +0,0 @@ -/***************************************************************************** -* -* Filename: kingsun-sir.c -* Version: 0.1.1 -* Description: Irda KingSun/DonShine USB Dongle -* Status: Experimental -* Author: Alex Villacís Lasso -* -* Based on stir4200 and mcs7780 drivers, with (strange?) differences -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * This is my current (2007-04-25) understanding of how this dongle is supposed - * to work. This is based on reverse-engineering and examination of the packet - * data sent and received by the WinXP driver using USBSnoopy. Feel free to - * update here as more of this dongle is known: - * - * General: Unlike the other USB IrDA dongles, this particular dongle exposes, - * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle, - * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in - * order to receive data. - * Transmission: Just like stir4200, this dongle uses a raw stream of data, - * which needs to be wrapped and escaped in a similar way as in stir4200.c. - * Reception: Poll-based, as in stir4200. Each read returns the contents of a - * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes - * (1-7) of valid data contained within the remaining 7 bytes. For example, if - * the buffer had the following contents: - * 06 ff ff ff c0 01 04 aa - * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the - * end is garbage (left over from a previous reception) and is discarded. - * If a read returns an "impossible" value as the length of valid data (such as - * 0x36) in the first byte, then the buffer is uninitialized (as is the case of - * first plug-in) and its contents should be discarded. There is currently no - * evidence that the top 5 bits of the 1st byte of the buffer can have values - * other than 0 once reception begins. - * Once valid bytes are collected, the assembled stream is a sequence of - * wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c. - * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after - * a successful read from the host, which means that in absence of further - * reception, repeated reads from the dongle will return the exact same - * contents repeatedly. Attempts to be smart and cache a previous read seem - * to result in corrupted packets, so this driver depends on the unwrap logic - * to sort out any repeated reads. - * Speed change: no commands observed so far to change speed, assumed fixed - * 9600bps (SIR). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -/* - * According to lsusb, 0x07c0 is assigned to - * "Code Mercenaries Hard- und Software GmbH" - */ -#define KING_VENDOR_ID 0x07c0 -#define KING_PRODUCT_ID 0x4200 - -/* These are the currently known USB ids */ -static const struct usb_device_id dongles[] = { - /* KingSun Co,Ltd IrDA/USB Bridge */ - { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, dongles); - -#define KINGSUN_MTT 0x07 - -#define KINGSUN_FIFO_SIZE 4096 -#define KINGSUN_EP_IN 0 -#define KINGSUN_EP_OUT 1 - -struct kingsun_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - struct qos_info qos; - - __u8 *in_buf; /* receive buffer */ - __u8 *out_buf; /* transmit buffer */ - __u8 max_rx; /* max. atomic read from dongle - (usually 8), also size of in_buf */ - __u8 max_tx; /* max. atomic write to dongle - (usually 8) */ - - iobuff_t rx_buff; /* receive unwrap state machine */ - spinlock_t lock; - int receiving; - - __u8 ep_in; - __u8 ep_out; - - struct urb *tx_urb; - struct urb *rx_urb; -}; - -/* Callback transmission routine */ -static void kingsun_send_irq(struct urb *urb) -{ - struct kingsun_cb *kingsun = urb->context; - struct net_device *netdev = kingsun->netdev; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - dev_err(&kingsun->usbdev->dev, - "kingsun_send_irq: Network not running!\n"); - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "kingsun_send_irq: urb asynchronously failed - %d\n", - urb->status); - } - netif_wake_queue(netdev); -} - -/* - * Called from net/core when new frame is available. - */ -static netdev_tx_t kingsun_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct kingsun_cb *kingsun; - int wraplen; - int ret = 0; - - netif_stop_queue(netdev); - - /* the IRDA wrapping routines don't deal with non linear skb */ - SKB_LINEAR_ASSERT(skb); - - kingsun = netdev_priv(netdev); - - spin_lock(&kingsun->lock); - - /* Append data to the end of whatever data remains to be transmitted */ - wraplen = async_wrap_skb(skb, - kingsun->out_buf, - KINGSUN_FIFO_SIZE); - - /* Calculate how much data can be transmitted in this urb */ - usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, - usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), - kingsun->out_buf, wraplen, kingsun_send_irq, - kingsun, 1); - - if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) { - dev_err(&kingsun->usbdev->dev, - "kingsun_hard_xmit: failed tx_urb submit: %d\n", ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } else { - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - } - - dev_kfree_skb(skb); - spin_unlock(&kingsun->lock); - - return NETDEV_TX_OK; -} - -/* Receive callback function */ -static void kingsun_rcv_irq(struct urb *urb) -{ - struct kingsun_cb *kingsun = urb->context; - int ret; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - kingsun->receiving = 0; - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "kingsun_rcv_irq: urb asynchronously failed - %d\n", - urb->status); - kingsun->receiving = 0; - return; - } - - if (urb->actual_length == kingsun->max_rx) { - __u8 *bytes = urb->transfer_buffer; - int i; - - /* The very first byte in the buffer indicates the length of - valid data in the read. This byte must be in the range - 1..kingsun->max_rx -1 . Values outside this range indicate - an uninitialized Rx buffer when the dongle has just been - plugged in. */ - if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) { - for (i = 1; i <= bytes[0]; i++) { - async_unwrap_char(kingsun->netdev, - &kingsun->netdev->stats, - &kingsun->rx_buff, bytes[i]); - } - kingsun->receiving = - (kingsun->rx_buff.state != OUTSIDE_FRAME) - ? 1 : 0; - } - } else if (urb->actual_length > 0) { - dev_err(&kingsun->usbdev->dev, - "%s(): Unexpected response length, expected %d got %d\n", - __func__, kingsun->max_rx, urb->actual_length); - } - /* This urb has already been filled in kingsun_net_open */ - ret = usb_submit_urb(urb, GFP_ATOMIC); -} - -/* - * Function kingsun_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - */ -static int kingsun_net_open(struct net_device *netdev) -{ - struct kingsun_cb *kingsun = netdev_priv(netdev); - int err = -ENOMEM; - char hwname[16]; - - /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ - kingsun->receiving = 0; - - /* Initialize for SIR to copy data directly into skb. */ - kingsun->rx_buff.in_frame = FALSE; - kingsun->rx_buff.state = OUTSIDE_FRAME; - kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU; - kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!kingsun->rx_buff.skb) - goto free_mem; - - skb_reserve(kingsun->rx_buff.skb, 1); - kingsun->rx_buff.head = kingsun->rx_buff.skb->data; - - kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->rx_urb) - goto free_mem; - - kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->tx_urb) - goto free_mem; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - */ - sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); - kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); - if (!kingsun->irlap) { - dev_err(&kingsun->usbdev->dev, "irlap_open failed\n"); - goto free_mem; - } - - /* Start first reception */ - usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, - usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), - kingsun->in_buf, kingsun->max_rx, - kingsun_rcv_irq, kingsun, 1); - kingsun->rx_urb->status = 0; - err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - if (err) { - dev_err(&kingsun->usbdev->dev, - "first urb-submit failed: %d\n", err); - goto close_irlap; - } - - netif_start_queue(netdev); - - /* Situation at this point: - - all work buffers allocated - - urbs allocated and ready to fill - - max rx packet known (in max_rx) - - unwrap state machine initialized, in state outside of any frame - - receive request in progress - - IrLAP layer started, about to hand over packets to send - */ - - return 0; - - close_irlap: - irlap_close(kingsun->irlap); - free_mem: - if (kingsun->tx_urb) { - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - } - if (kingsun->rx_urb) { - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - } - if (kingsun->rx_buff.skb) { - kfree_skb(kingsun->rx_buff.skb); - kingsun->rx_buff.skb = NULL; - kingsun->rx_buff.head = NULL; - } - return err; -} - -/* - * Function kingsun_net_close (kingsun) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int kingsun_net_close(struct net_device *netdev) -{ - struct kingsun_cb *kingsun = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - /* Mop up receive && transmit urb's */ - usb_kill_urb(kingsun->tx_urb); - usb_kill_urb(kingsun->rx_urb); - - usb_free_urb(kingsun->tx_urb); - usb_free_urb(kingsun->rx_urb); - - kingsun->tx_urb = NULL; - kingsun->rx_urb = NULL; - - kfree_skb(kingsun->rx_buff.skb); - kingsun->rx_buff.skb = NULL; - kingsun->rx_buff.head = NULL; - kingsun->rx_buff.in_frame = FALSE; - kingsun->rx_buff.state = OUTSIDE_FRAME; - kingsun->receiving = 0; - - /* Stop and remove instance of IrLAP */ - if (kingsun->irlap) - irlap_close(kingsun->irlap); - - kingsun->irlap = NULL; - - return 0; -} - -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, - int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct kingsun_cb *kingsun = netdev_priv(netdev); - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the device is still there */ - if (netif_device_present(kingsun->netdev)) - /* No observed commands for speed change */ - ret = -EOPNOTSUPP; - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the IrDA stack is still there */ - if (netif_running(kingsun->netdev)) - irda_device_set_media_busy(kingsun->netdev, TRUE); - break; - - case SIOCGRECEIVING: - /* Only approximately true */ - irq->ifr_receiving = kingsun->receiving; - break; - - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -static const struct net_device_ops kingsun_ops = { - .ndo_start_xmit = kingsun_hard_xmit, - .ndo_open = kingsun_net_open, - .ndo_stop = kingsun_net_close, - .ndo_do_ioctl = kingsun_net_ioctl, -}; - -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - */ -static int kingsun_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - - struct usb_device *dev = interface_to_usbdev(intf); - struct kingsun_cb *kingsun = NULL; - struct net_device *net = NULL; - int ret = -ENOMEM; - int pipe, maxp_in, maxp_out; - __u8 ep_in; - __u8 ep_out; - - /* Check that there really are two interrupt endpoints. - Check based on the one in drivers/usb/input/usbmouse.c - */ - interface = intf->cur_altsetting; - if (interface->desc.bNumEndpoints != 2) { - dev_err(&intf->dev, - "kingsun-sir: expected 2 endpoints, found %d\n", - interface->desc.bNumEndpoints); - return -ENODEV; - } - endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; - if (!usb_endpoint_is_int_in(endpoint)) { - dev_err(&intf->dev, - "kingsun-sir: endpoint 0 is not interrupt IN\n"); - return -ENODEV; - } - - ep_in = endpoint->bEndpointAddress; - pipe = usb_rcvintpipe(dev, ep_in); - maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - if (maxp_in > 255 || maxp_in <= 1) { - dev_err(&intf->dev, - "endpoint 0 has max packet size %d not in range\n", - maxp_in); - return -ENODEV; - } - - endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; - if (!usb_endpoint_is_int_out(endpoint)) { - dev_err(&intf->dev, - "kingsun-sir: endpoint 1 is not interrupt OUT\n"); - return -ENODEV; - } - - ep_out = endpoint->bEndpointAddress; - pipe = usb_sndintpipe(dev, ep_out); - maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - /* Allocate network device container. */ - net = alloc_irdadev(sizeof(*kingsun)); - if(!net) - goto err_out1; - - SET_NETDEV_DEV(net, &intf->dev); - kingsun = netdev_priv(net); - kingsun->irlap = NULL; - kingsun->tx_urb = NULL; - kingsun->rx_urb = NULL; - kingsun->ep_in = ep_in; - kingsun->ep_out = ep_out; - kingsun->in_buf = NULL; - kingsun->out_buf = NULL; - kingsun->max_rx = (__u8)maxp_in; - kingsun->max_tx = (__u8)maxp_out; - kingsun->netdev = net; - kingsun->usbdev = dev; - kingsun->rx_buff.in_frame = FALSE; - kingsun->rx_buff.state = OUTSIDE_FRAME; - kingsun->rx_buff.skb = NULL; - kingsun->receiving = 0; - spin_lock_init(&kingsun->lock); - - /* Allocate input buffer */ - kingsun->in_buf = kmalloc(kingsun->max_rx, GFP_KERNEL); - if (!kingsun->in_buf) - goto free_mem; - - /* Allocate output buffer */ - kingsun->out_buf = kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL); - if (!kingsun->out_buf) - goto free_mem; - - printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, " - "Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&kingsun->qos); - - /* That's the Rx capability. */ - kingsun->qos.baud_rate.bits &= IR_9600; - kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; - irda_qos_bits_to_value(&kingsun->qos); - - /* Override the network functions we need to use */ - net->netdev_ops = &kingsun_ops; - - ret = register_netdev(net); - if (ret != 0) - goto free_mem; - - dev_info(&net->dev, "IrDA: Registered KingSun/DonShine device %s\n", - net->name); - - usb_set_intfdata(intf, kingsun); - - /* Situation at this point: - - all work buffers allocated - - urbs not allocated, set to NULL - - max rx packet known (in max_rx) - - unwrap state machine (partially) initialized, but skb == NULL - */ - - return 0; - -free_mem: - kfree(kingsun->out_buf); - kfree(kingsun->in_buf); - free_netdev(net); -err_out1: - return ret; -} - -/* - * The current device is removed, the USB layer tell us to shut it down... - */ -static void kingsun_disconnect(struct usb_interface *intf) -{ - struct kingsun_cb *kingsun = usb_get_intfdata(intf); - - if (!kingsun) - return; - - unregister_netdev(kingsun->netdev); - - /* Mop up receive && transmit urb's */ - if (kingsun->tx_urb != NULL) { - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - } - if (kingsun->rx_urb != NULL) { - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - } - - kfree(kingsun->out_buf); - kfree(kingsun->in_buf); - free_netdev(kingsun->netdev); - - usb_set_intfdata(intf, NULL); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int kingsun_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct kingsun_cb *kingsun = usb_get_intfdata(intf); - - netif_device_detach(kingsun->netdev); - if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb); - if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb); - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int kingsun_resume(struct usb_interface *intf) -{ - struct kingsun_cb *kingsun = usb_get_intfdata(intf); - - if (kingsun->rx_urb != NULL) - usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - netif_device_attach(kingsun->netdev); - - return 0; -} -#endif - -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "kingsun-sir", - .probe = kingsun_probe, - .disconnect = kingsun_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = kingsun_suspend, - .resume = kingsun_resume, -#endif -}; - -module_usb_driver(irda_driver); - -MODULE_AUTHOR("Alex Villacís Lasso "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/ks959-sir.c b/drivers/net/irda/ks959-sir.c deleted file mode 100644 index 8025741e7586..000000000000 --- a/drivers/net/irda/ks959-sir.c +++ /dev/null @@ -1,912 +0,0 @@ -/***************************************************************************** -* -* Filename: ks959-sir.c -* Version: 0.1.2 -* Description: Irda KingSun KS-959 USB Dongle -* Status: Experimental -* Author: Alex Villacís Lasso -* with help from Domen Puncer -* -* Based on stir4200, mcs7780, kingsun-sir drivers. -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * Following is my most current (2007-07-17) understanding of how the Kingsun - * KS-959 dongle is supposed to work. This information was deduced by - * reverse-engineering and examining the USB traffic captured with USBSnoopy - * from the WinXP driver. Feel free to update here as more of the dongle is - * known. - * - * My most sincere thanks must go to Domen Puncer for - * invaluable help in cracking the obfuscation and padding required for this - * dongle. - * - * General: This dongle exposes one interface with one interrupt IN endpoint. - * However, the interrupt endpoint is NOT used at all for this dongle. Instead, - * this dongle uses control transfers for everything, including sending and - * receiving the IrDA frame data. Apparently the interrupt endpoint is just a - * dummy to ensure the dongle has a valid interface to present to the PC.And I - * thought the DonShine dongle was weird... In addition, this dongle uses - * obfuscation (?!?!), applied at the USB level, to hide the traffic, both sent - * and received, from the dongle. I call it obfuscation because the XOR keying - * and padding required to produce an USB traffic acceptable for the dongle can - * not be explained by any other technical requirement. - * - * Transmission: To transmit an IrDA frame, the driver must prepare a control - * URB with the following as a setup packet: - * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - * bRequest 0x09 - * wValue - * wIndex 0x0000 - * wLength - * The payload packet must be manually wrapped and escaped (as in stir4200.c), - * then padded and obfuscated before being sent. Both padding and obfuscation - * are implemented in the procedure obfuscate_tx_buffer(). Suffice to say, the - * designer/programmer of the dongle used his name as a source for the - * obfuscation. WTF?! - * Apparently the dongle cannot handle payloads larger than 256 bytes. The - * driver has to perform fragmentation in order to send anything larger than - * this limit. - * - * Reception: To receive data, the driver must poll the dongle regularly (like - * kingsun-sir.c) with control URBs and the following as a setup packet: - * bRequestType USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE - * bRequest 0x01 - * wValue 0x0200 - * wIndex 0x0000 - * wLength 0x0800 (size of available buffer) - * If there is data to be read, it will be returned as the response payload. - * This data is (apparently) not padded, but it is obfuscated. To de-obfuscate - * it, the driver must XOR every byte, in sequence, with a value that starts at - * 1 and is incremented with each byte processed, and then with 0x55. The value - * incremented with each byte processed overflows as an unsigned char. The - * resulting bytes form a wrapped SIR frame that is unwrapped and unescaped - * as in stir4200.c The incremented value is NOT reset with each frame, but is - * kept across the entire session with the dongle. Also, the dongle inserts an - * extra garbage byte with value 0x95 (after decoding) every 0xff bytes, which - * must be skipped. - * - * Speed change: To change the speed of the dongle, the driver prepares a - * control URB with the following as a setup packet: - * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - * bRequest 0x09 - * wValue 0x0200 - * wIndex 0x0001 - * wLength 0x0008 (length of the payload) - * The payload is a 8-byte record, apparently identical to the one used in - * drivers/usb/serial/cypress_m8.c to change speed: - * __u32 baudSpeed; - * unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits - * unsigned int : 1; - * unsigned int stopBits : 1; - * unsigned int parityEnable : 1; - * unsigned int parityType : 1; - * unsigned int : 1; - * unsigned int reset : 1; - * unsigned char reserved[3]; // set to 0 - * - * For now only SIR speeds have been observed with this dongle. Therefore, - * nothing is known on what changes (if any) must be done to frame wrapping / - * unwrapping for higher than SIR speeds. This driver assumes no change is - * necessary and announces support for all the way to 57600 bps. Although the - * package announces support for up to 4MBps, tests with a Sony Ericcson K300 - * phone show corruption when receiving large frames at 115200 bps, the highest - * speed announced by the phone. However, transmission at 115200 bps is OK. Go - * figure. Since I don't know whether the phone or the dongle is at fault, max - * announced speed is 57600 bps until someone produces a device that can run - * at higher speeds with this dongle. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#define KS959_VENDOR_ID 0x07d0 -#define KS959_PRODUCT_ID 0x4959 - -/* These are the currently known USB ids */ -static const struct usb_device_id dongles[] = { - /* KingSun Co,Ltd IrDA/USB Bridge */ - {USB_DEVICE(KS959_VENDOR_ID, KS959_PRODUCT_ID)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, dongles); - -#define KINGSUN_MTT 0x07 -#define KINGSUN_REQ_RECV 0x01 -#define KINGSUN_REQ_SEND 0x09 - -#define KINGSUN_RCV_FIFO_SIZE 2048 /* Max length we can receive */ -#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */ -#define KINGSUN_SND_PACKET_SIZE 256 /* Max packet dongle can handle */ - -struct ks959_speedparams { - __le32 baudrate; /* baud rate, little endian */ - __u8 flags; - __u8 reserved[3]; -} __packed; - -#define KS_DATA_5_BITS 0x00 -#define KS_DATA_6_BITS 0x01 -#define KS_DATA_7_BITS 0x02 -#define KS_DATA_8_BITS 0x03 - -#define KS_STOP_BITS_1 0x00 -#define KS_STOP_BITS_2 0x08 - -#define KS_PAR_DISABLE 0x00 -#define KS_PAR_EVEN 0x10 -#define KS_PAR_ODD 0x30 -#define KS_RESET 0x80 - -struct ks959_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - struct qos_info qos; - - struct usb_ctrlrequest *tx_setuprequest; - struct urb *tx_urb; - __u8 *tx_buf_clear; - unsigned int tx_buf_clear_used; - unsigned int tx_buf_clear_sent; - __u8 *tx_buf_xored; - - struct usb_ctrlrequest *rx_setuprequest; - struct urb *rx_urb; - __u8 *rx_buf; - __u8 rx_variable_xormask; - iobuff_t rx_unwrap_buff; - - struct usb_ctrlrequest *speed_setuprequest; - struct urb *speed_urb; - struct ks959_speedparams speedparams; - unsigned int new_speed; - - spinlock_t lock; - int receiving; -}; - -/* Procedure to perform the obfuscation/padding expected by the dongle - * - * buf_cleartext (IN) Cleartext version of the IrDA frame to transmit - * len_cleartext (IN) Length of the cleartext version of IrDA frame - * buf_xoredtext (OUT) Obfuscated version of frame built by proc - * len_maxbuf (OUT) Maximum space available at buf_xoredtext - * - * (return) length of obfuscated frame with padding - * - * If not enough space (as indicated by len_maxbuf vs. required padding), - * zero is returned - * - * The value of lookup_string is actually a required portion of the algorithm. - * Seems the designer of the dongle wanted to state who exactly is responsible - * for implementing obfuscation. Send your best (or other) wishes to him ]:-) - */ -static unsigned int obfuscate_tx_buffer(const __u8 * buf_cleartext, - unsigned int len_cleartext, - __u8 * buf_xoredtext, - unsigned int len_maxbuf) -{ - unsigned int len_xoredtext; - - /* Calculate required length with padding, check for necessary space */ - len_xoredtext = ((len_cleartext + 7) & ~0x7) + 0x10; - if (len_xoredtext <= len_maxbuf) { - static const __u8 lookup_string[] = "wangshuofei19710"; - __u8 xor_mask; - - /* Unlike the WinXP driver, we *do* clear out the padding */ - memset(buf_xoredtext, 0, len_xoredtext); - - xor_mask = lookup_string[(len_cleartext & 0x0f) ^ 0x06] ^ 0x55; - - while (len_cleartext-- > 0) { - *buf_xoredtext++ = *buf_cleartext++ ^ xor_mask; - } - } else { - len_xoredtext = 0; - } - return len_xoredtext; -} - -/* Callback transmission routine */ -static void ks959_speed_irq(struct urb *urb) -{ - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&urb->dev->dev, - "ks959_speed_irq: urb asynchronously failed - %d\n", - urb->status); - } -} - -/* Send a control request to change speed of the dongle */ -static int ks959_change_speed(struct ks959_cb *kingsun, unsigned speed) -{ - static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400, - 57600, 115200, 576000, 1152000, 4000000, 0 - }; - int err; - unsigned int i; - - if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL) - return -ENOMEM; - - /* Check that requested speed is among the supported ones */ - for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ; - if (supported_speeds[i] == 0) - return -EOPNOTSUPP; - - memset(&(kingsun->speedparams), 0, sizeof(struct ks959_speedparams)); - kingsun->speedparams.baudrate = cpu_to_le32(speed); - kingsun->speedparams.flags = KS_DATA_8_BITS; - - /* speed_setuprequest pre-filled in ks959_probe */ - usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev, - usb_sndctrlpipe(kingsun->usbdev, 0), - (unsigned char *)kingsun->speed_setuprequest, - &(kingsun->speedparams), - sizeof(struct ks959_speedparams), ks959_speed_irq, - kingsun); - kingsun->speed_urb->status = 0; - err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC); - - return err; -} - -/* Submit one fragment of an IrDA frame to the dongle */ -static void ks959_send_irq(struct urb *urb); -static int ks959_submit_tx_fragment(struct ks959_cb *kingsun) -{ - unsigned int padlen; - unsigned int wraplen; - int ret; - - /* Check whether current plaintext can produce a padded buffer that fits - within the range handled by the dongle */ - wraplen = (KINGSUN_SND_PACKET_SIZE & ~0x7) - 0x10; - if (wraplen > kingsun->tx_buf_clear_used) - wraplen = kingsun->tx_buf_clear_used; - - /* Perform dongle obfuscation. Also remove the portion of the frame that - was just obfuscated and will now be sent to the dongle. */ - padlen = obfuscate_tx_buffer(kingsun->tx_buf_clear, wraplen, - kingsun->tx_buf_xored, - KINGSUN_SND_PACKET_SIZE); - - /* Calculate how much data can be transmitted in this urb */ - kingsun->tx_setuprequest->wValue = cpu_to_le16(wraplen); - kingsun->tx_setuprequest->wLength = cpu_to_le16(padlen); - /* Rest of the fields were filled in ks959_probe */ - usb_fill_control_urb(kingsun->tx_urb, kingsun->usbdev, - usb_sndctrlpipe(kingsun->usbdev, 0), - (unsigned char *)kingsun->tx_setuprequest, - kingsun->tx_buf_xored, padlen, - ks959_send_irq, kingsun); - kingsun->tx_urb->status = 0; - ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC); - - /* Remember how much data was sent, in order to update at callback */ - kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0; - return ret; -} - -/* Callback transmission routine */ -static void ks959_send_irq(struct urb *urb) -{ - struct ks959_cb *kingsun = urb->context; - struct net_device *netdev = kingsun->netdev; - int ret = 0; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - dev_err(&kingsun->usbdev->dev, - "ks959_send_irq: Network not running!\n"); - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "ks959_send_irq: urb asynchronously failed - %d\n", - urb->status); - return; - } - - if (kingsun->tx_buf_clear_used > 0) { - /* Update data remaining to be sent */ - if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) { - memmove(kingsun->tx_buf_clear, - kingsun->tx_buf_clear + - kingsun->tx_buf_clear_sent, - kingsun->tx_buf_clear_used - - kingsun->tx_buf_clear_sent); - } - kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent; - kingsun->tx_buf_clear_sent = 0; - - if (kingsun->tx_buf_clear_used > 0) { - /* There is more data to be sent */ - if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) { - dev_err(&kingsun->usbdev->dev, - "ks959_send_irq: failed tx_urb submit: %d\n", - ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } - } else { - /* All data sent, send next speed && wake network queue */ - if (kingsun->new_speed != -1 && - cpu_to_le32(kingsun->new_speed) != - kingsun->speedparams.baudrate) - ks959_change_speed(kingsun, kingsun->new_speed); - - netif_wake_queue(netdev); - } - } -} - -/* - * Called from net/core when new frame is available. - */ -static netdev_tx_t ks959_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct ks959_cb *kingsun; - unsigned int wraplen; - int ret = 0; - - netif_stop_queue(netdev); - - /* the IRDA wrapping routines don't deal with non linear skb */ - SKB_LINEAR_ASSERT(skb); - - kingsun = netdev_priv(netdev); - - spin_lock(&kingsun->lock); - kingsun->new_speed = irda_get_next_speed(skb); - - /* Append data to the end of whatever data remains to be transmitted */ - wraplen = - async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE); - kingsun->tx_buf_clear_used = wraplen; - - if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) { - dev_err(&kingsun->usbdev->dev, - "ks959_hard_xmit: failed tx_urb submit: %d\n", ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } else { - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - - } - - dev_kfree_skb(skb); - spin_unlock(&kingsun->lock); - - return NETDEV_TX_OK; -} - -/* Receive callback function */ -static void ks959_rcv_irq(struct urb *urb) -{ - struct ks959_cb *kingsun = urb->context; - int ret; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - kingsun->receiving = 0; - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "kingsun_rcv_irq: urb asynchronously failed - %d\n", - urb->status); - kingsun->receiving = 0; - return; - } - - if (urb->actual_length > 0) { - __u8 *bytes = urb->transfer_buffer; - unsigned int i; - - for (i = 0; i < urb->actual_length; i++) { - /* De-obfuscation implemented here: variable portion of - xormask is incremented, and then used with the encoded - byte for the XOR. The result of the operation is used - to unwrap the SIR frame. */ - kingsun->rx_variable_xormask++; - bytes[i] = - bytes[i] ^ kingsun->rx_variable_xormask ^ 0x55u; - - /* rx_variable_xormask doubles as an index counter so we - can skip the byte at 0xff (wrapped around to 0). - */ - if (kingsun->rx_variable_xormask != 0) { - async_unwrap_char(kingsun->netdev, - &kingsun->netdev->stats, - &kingsun->rx_unwrap_buff, - bytes[i]); - } - } - kingsun->receiving = - (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0; - } - - /* This urb has already been filled in kingsun_net_open. Setup - packet must be re-filled, but it is assumed that urb keeps the - pointer to the initial setup packet, as well as the payload buffer. - Setup packet is already pre-filled at ks959_probe. - */ - urb->status = 0; - ret = usb_submit_urb(urb, GFP_ATOMIC); -} - -/* - * Function kingsun_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - */ -static int ks959_net_open(struct net_device *netdev) -{ - struct ks959_cb *kingsun = netdev_priv(netdev); - int err = -ENOMEM; - char hwname[16]; - - /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ - kingsun->receiving = 0; - - /* Initialize for SIR to copy data directly into skb. */ - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU; - kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!kingsun->rx_unwrap_buff.skb) - goto free_mem; - - skb_reserve(kingsun->rx_unwrap_buff.skb, 1); - kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data; - - kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->rx_urb) - goto free_mem; - - kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->tx_urb) - goto free_mem; - - kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->speed_urb) - goto free_mem; - - /* Initialize speed for dongle */ - kingsun->new_speed = 9600; - err = ks959_change_speed(kingsun, 9600); - if (err < 0) - goto free_mem; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - */ - sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); - kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); - if (!kingsun->irlap) { - err = -ENOMEM; - dev_err(&kingsun->usbdev->dev, "irlap_open failed\n"); - goto free_mem; - } - - /* Start reception. Setup request already pre-filled in ks959_probe */ - usb_fill_control_urb(kingsun->rx_urb, kingsun->usbdev, - usb_rcvctrlpipe(kingsun->usbdev, 0), - (unsigned char *)kingsun->rx_setuprequest, - kingsun->rx_buf, KINGSUN_RCV_FIFO_SIZE, - ks959_rcv_irq, kingsun); - kingsun->rx_urb->status = 0; - err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - if (err) { - dev_err(&kingsun->usbdev->dev, - "first urb-submit failed: %d\n", err); - goto close_irlap; - } - - netif_start_queue(netdev); - - /* Situation at this point: - - all work buffers allocated - - urbs allocated and ready to fill - - max rx packet known (in max_rx) - - unwrap state machine initialized, in state outside of any frame - - receive request in progress - - IrLAP layer started, about to hand over packets to send - */ - - return 0; - - close_irlap: - irlap_close(kingsun->irlap); - free_mem: - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - if (kingsun->rx_unwrap_buff.skb) { - kfree_skb(kingsun->rx_unwrap_buff.skb); - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->rx_unwrap_buff.head = NULL; - } - return err; -} - -/* - * Function kingsun_net_close (kingsun) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int ks959_net_close(struct net_device *netdev) -{ - struct ks959_cb *kingsun = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - /* Mop up receive && transmit urb's */ - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - - usb_kill_urb(kingsun->speed_urb); - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - - kfree_skb(kingsun->rx_unwrap_buff.skb); - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->rx_unwrap_buff.head = NULL; - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->receiving = 0; - - /* Stop and remove instance of IrLAP */ - if (kingsun->irlap) - irlap_close(kingsun->irlap); - - kingsun->irlap = NULL; - - return 0; -} - -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int ks959_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *)rq; - struct ks959_cb *kingsun = netdev_priv(netdev); - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the device is still there */ - if (netif_device_present(kingsun->netdev)) - return ks959_change_speed(kingsun, irq->ifr_baudrate); - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the IrDA stack is still there */ - if (netif_running(kingsun->netdev)) - irda_device_set_media_busy(kingsun->netdev, TRUE); - break; - - case SIOCGRECEIVING: - /* Only approximately true */ - irq->ifr_receiving = kingsun->receiving; - break; - - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -static const struct net_device_ops ks959_ops = { - .ndo_start_xmit = ks959_hard_xmit, - .ndo_open = ks959_net_open, - .ndo_stop = ks959_net_close, - .ndo_do_ioctl = ks959_net_ioctl, -}; -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - */ -static int ks959_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct ks959_cb *kingsun = NULL; - struct net_device *net = NULL; - int ret = -ENOMEM; - - /* Allocate network device container. */ - net = alloc_irdadev(sizeof(*kingsun)); - if (!net) - goto err_out1; - - SET_NETDEV_DEV(net, &intf->dev); - kingsun = netdev_priv(net); - kingsun->netdev = net; - kingsun->usbdev = dev; - kingsun->irlap = NULL; - kingsun->tx_setuprequest = NULL; - kingsun->tx_urb = NULL; - kingsun->tx_buf_clear = NULL; - kingsun->tx_buf_xored = NULL; - kingsun->tx_buf_clear_used = 0; - kingsun->tx_buf_clear_sent = 0; - - kingsun->rx_setuprequest = NULL; - kingsun->rx_urb = NULL; - kingsun->rx_buf = NULL; - kingsun->rx_variable_xormask = 0; - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->receiving = 0; - spin_lock_init(&kingsun->lock); - - kingsun->speed_setuprequest = NULL; - kingsun->speed_urb = NULL; - kingsun->speedparams.baudrate = 0; - - /* Allocate input buffer */ - kingsun->rx_buf = kmalloc(KINGSUN_RCV_FIFO_SIZE, GFP_KERNEL); - if (!kingsun->rx_buf) - goto free_mem; - - /* Allocate input setup packet */ - kingsun->rx_setuprequest = - kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); - if (!kingsun->rx_setuprequest) - goto free_mem; - kingsun->rx_setuprequest->bRequestType = - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kingsun->rx_setuprequest->bRequest = KINGSUN_REQ_RECV; - kingsun->rx_setuprequest->wValue = cpu_to_le16(0x0200); - kingsun->rx_setuprequest->wIndex = 0; - kingsun->rx_setuprequest->wLength = cpu_to_le16(KINGSUN_RCV_FIFO_SIZE); - - /* Allocate output buffer */ - kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL); - if (!kingsun->tx_buf_clear) - goto free_mem; - kingsun->tx_buf_xored = kmalloc(KINGSUN_SND_PACKET_SIZE, GFP_KERNEL); - if (!kingsun->tx_buf_xored) - goto free_mem; - - /* Allocate and initialize output setup packet */ - kingsun->tx_setuprequest = - kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); - if (!kingsun->tx_setuprequest) - goto free_mem; - kingsun->tx_setuprequest->bRequestType = - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kingsun->tx_setuprequest->bRequest = KINGSUN_REQ_SEND; - kingsun->tx_setuprequest->wValue = 0; - kingsun->tx_setuprequest->wIndex = 0; - kingsun->tx_setuprequest->wLength = 0; - - /* Allocate and initialize speed setup packet */ - kingsun->speed_setuprequest = - kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); - if (!kingsun->speed_setuprequest) - goto free_mem; - kingsun->speed_setuprequest->bRequestType = - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND; - kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200); - kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001); - kingsun->speed_setuprequest->wLength = - cpu_to_le16(sizeof(struct ks959_speedparams)); - - printk(KERN_INFO "KingSun KS-959 IRDA/USB found at address %d, " - "Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&kingsun->qos); - - /* Baud rates known to be supported. Please uncomment if devices (other - than a SonyEriccson K300 phone) can be shown to support higher speed - with this dongle. - */ - kingsun->qos.baud_rate.bits = - IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600; - kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; - irda_qos_bits_to_value(&kingsun->qos); - - /* Override the network functions we need to use */ - net->netdev_ops = &ks959_ops; - - ret = register_netdev(net); - if (ret != 0) - goto free_mem; - - dev_info(&net->dev, "IrDA: Registered KingSun KS-959 device %s\n", - net->name); - - usb_set_intfdata(intf, kingsun); - - /* Situation at this point: - - all work buffers allocated - - setup requests pre-filled - - urbs not allocated, set to NULL - - max rx packet known (is KINGSUN_FIFO_SIZE) - - unwrap state machine (partially) initialized, but skb == NULL - */ - - return 0; - - free_mem: - kfree(kingsun->speed_setuprequest); - kfree(kingsun->tx_setuprequest); - kfree(kingsun->tx_buf_xored); - kfree(kingsun->tx_buf_clear); - kfree(kingsun->rx_setuprequest); - kfree(kingsun->rx_buf); - free_netdev(net); - err_out1: - return ret; -} - -/* - * The current device is removed, the USB layer tell us to shut it down... - */ -static void ks959_disconnect(struct usb_interface *intf) -{ - struct ks959_cb *kingsun = usb_get_intfdata(intf); - - if (!kingsun) - return; - - unregister_netdev(kingsun->netdev); - - /* Mop up receive && transmit urb's */ - if (kingsun->speed_urb != NULL) { - usb_kill_urb(kingsun->speed_urb); - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - } - if (kingsun->tx_urb != NULL) { - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - } - if (kingsun->rx_urb != NULL) { - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - } - - kfree(kingsun->speed_setuprequest); - kfree(kingsun->tx_setuprequest); - kfree(kingsun->tx_buf_xored); - kfree(kingsun->tx_buf_clear); - kfree(kingsun->rx_setuprequest); - kfree(kingsun->rx_buf); - free_netdev(kingsun->netdev); - - usb_set_intfdata(intf, NULL); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int ks959_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct ks959_cb *kingsun = usb_get_intfdata(intf); - - netif_device_detach(kingsun->netdev); - if (kingsun->speed_urb != NULL) - usb_kill_urb(kingsun->speed_urb); - if (kingsun->tx_urb != NULL) - usb_kill_urb(kingsun->tx_urb); - if (kingsun->rx_urb != NULL) - usb_kill_urb(kingsun->rx_urb); - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int ks959_resume(struct usb_interface *intf) -{ - struct ks959_cb *kingsun = usb_get_intfdata(intf); - - if (kingsun->rx_urb != NULL) { - /* Setup request already filled in ks959_probe */ - usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - } - netif_device_attach(kingsun->netdev); - - return 0; -} -#endif - -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "ks959-sir", - .probe = ks959_probe, - .disconnect = ks959_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = ks959_suspend, - .resume = ks959_resume, -#endif -}; - -module_usb_driver(irda_driver); - -MODULE_AUTHOR("Alex Villacís Lasso "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun KS-959"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/ksdazzle-sir.c b/drivers/net/irda/ksdazzle-sir.c deleted file mode 100644 index d2a0755df596..000000000000 --- a/drivers/net/irda/ksdazzle-sir.c +++ /dev/null @@ -1,813 +0,0 @@ -/***************************************************************************** -* -* Filename: ksdazzle.c -* Version: 0.1.2 -* Description: Irda KingSun Dazzle USB Dongle -* Status: Experimental -* Author: Alex Villacís Lasso -* -* Based on stir4200, mcs7780, kingsun-sir drivers. -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * Following is my most current (2007-07-26) understanding of how the Kingsun - * 07D0:4100 dongle (sometimes known as the MA-660) is supposed to work. This - * information was deduced by examining the USB traffic captured with USBSnoopy - * from the WinXP driver. Feel free to update here as more of the dongle is - * known. - * - * General: This dongle exposes one interface with two interrupt endpoints, one - * IN and one OUT. In this regard, it is similar to what the Kingsun/Donshine - * dongle (07c0:4200) exposes. Traffic is raw and needs to be wrapped and - * unwrapped manually as in stir4200, kingsun-sir, and ks959-sir. - * - * Transmission: To transmit an IrDA frame, it is necessary to wrap it, then - * split it into multiple segments of up to 7 bytes each, and transmit each in - * sequence. It seems that sending a single big block (like kingsun-sir does) - * won't work with this dongle. Each segment needs to be prefixed with a value - * equal to (unsigned char)0xF8 + , inside a payload - * of exactly 8 bytes. For example, a segment of 1 byte gets prefixed by 0xF9, - * and one of 7 bytes gets prefixed by 0xFF. The bytes at the end of the - * payload, not considered by the prefix, are ignored (set to 0 by this - * implementation). - * - * Reception: To receive data, the driver must poll the dongle regularly (like - * kingsun-sir.c) with interrupt URBs. If data is available, it will be returned - * in payloads from 0 to 8 bytes long. When concatenated, these payloads form - * a raw IrDA stream that needs to be unwrapped as in stir4200 and kingsun-sir - * - * Speed change: To change the speed of the dongle, the driver prepares a - * control URB with the following as a setup packet: - * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - * bRequest 0x09 - * wValue 0x0200 - * wIndex 0x0001 - * wLength 0x0008 (length of the payload) - * The payload is a 8-byte record, apparently identical to the one used in - * drivers/usb/serial/cypress_m8.c to change speed: - * __u32 baudSpeed; - * unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits - * unsigned int : 1; - * unsigned int stopBits : 1; - * unsigned int parityEnable : 1; - * unsigned int parityType : 1; - * unsigned int : 1; - * unsigned int reset : 1; - * unsigned char reserved[3]; // set to 0 - * - * For now only SIR speeds have been observed with this dongle. Therefore, - * nothing is known on what changes (if any) must be done to frame wrapping / - * unwrapping for higher than SIR speeds. This driver assumes no change is - * necessary and announces support for all the way to 115200 bps. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#define KSDAZZLE_VENDOR_ID 0x07d0 -#define KSDAZZLE_PRODUCT_ID 0x4100 - -/* These are the currently known USB ids */ -static const struct usb_device_id dongles[] = { - /* KingSun Co,Ltd IrDA/USB Bridge */ - {USB_DEVICE(KSDAZZLE_VENDOR_ID, KSDAZZLE_PRODUCT_ID)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, dongles); - -#define KINGSUN_MTT 0x07 -#define KINGSUN_REQ_RECV 0x01 -#define KINGSUN_REQ_SEND 0x09 - -#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */ -#define KINGSUN_RCV_MAX 2048 /* Max transfer we can receive */ - -struct ksdazzle_speedparams { - __le32 baudrate; /* baud rate, little endian */ - __u8 flags; - __u8 reserved[3]; -} __packed; - -#define KS_DATA_5_BITS 0x00 -#define KS_DATA_6_BITS 0x01 -#define KS_DATA_7_BITS 0x02 -#define KS_DATA_8_BITS 0x03 - -#define KS_STOP_BITS_1 0x00 -#define KS_STOP_BITS_2 0x08 - -#define KS_PAR_DISABLE 0x00 -#define KS_PAR_EVEN 0x10 -#define KS_PAR_ODD 0x30 -#define KS_RESET 0x80 - -#define KINGSUN_EP_IN 0 -#define KINGSUN_EP_OUT 1 - -struct ksdazzle_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - struct qos_info qos; - - struct urb *tx_urb; - __u8 *tx_buf_clear; - unsigned int tx_buf_clear_used; - unsigned int tx_buf_clear_sent; - __u8 tx_payload[8]; - - struct urb *rx_urb; - __u8 *rx_buf; - iobuff_t rx_unwrap_buff; - - struct usb_ctrlrequest *speed_setuprequest; - struct urb *speed_urb; - struct ksdazzle_speedparams speedparams; - unsigned int new_speed; - - __u8 ep_in; - __u8 ep_out; - - spinlock_t lock; - int receiving; -}; - -/* Callback transmission routine */ -static void ksdazzle_speed_irq(struct urb *urb) -{ - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) - dev_err(&urb->dev->dev, - "ksdazzle_speed_irq: urb asynchronously failed - %d\n", - urb->status); -} - -/* Send a control request to change speed of the dongle */ -static int ksdazzle_change_speed(struct ksdazzle_cb *kingsun, unsigned speed) -{ - static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400, - 57600, 115200, 576000, 1152000, 4000000, 0 - }; - int err; - unsigned int i; - - if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL) - return -ENOMEM; - - /* Check that requested speed is among the supported ones */ - for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ; - if (supported_speeds[i] == 0) - return -EOPNOTSUPP; - - memset(&(kingsun->speedparams), 0, sizeof(struct ksdazzle_speedparams)); - kingsun->speedparams.baudrate = cpu_to_le32(speed); - kingsun->speedparams.flags = KS_DATA_8_BITS; - - /* speed_setuprequest pre-filled in ksdazzle_probe */ - usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev, - usb_sndctrlpipe(kingsun->usbdev, 0), - (unsigned char *)kingsun->speed_setuprequest, - &(kingsun->speedparams), - sizeof(struct ksdazzle_speedparams), - ksdazzle_speed_irq, kingsun); - kingsun->speed_urb->status = 0; - err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC); - - return err; -} - -/* Submit one fragment of an IrDA frame to the dongle */ -static void ksdazzle_send_irq(struct urb *urb); -static int ksdazzle_submit_tx_fragment(struct ksdazzle_cb *kingsun) -{ - unsigned int wraplen; - int ret; - - /* We can send at most 7 bytes of payload at a time */ - wraplen = 7; - if (wraplen > kingsun->tx_buf_clear_used) - wraplen = kingsun->tx_buf_clear_used; - - /* Prepare payload prefix with used length */ - memset(kingsun->tx_payload, 0, 8); - kingsun->tx_payload[0] = (unsigned char)0xf8 + wraplen; - memcpy(kingsun->tx_payload + 1, kingsun->tx_buf_clear, wraplen); - - usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, - usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), - kingsun->tx_payload, 8, ksdazzle_send_irq, kingsun, 1); - kingsun->tx_urb->status = 0; - ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC); - - /* Remember how much data was sent, in order to update at callback */ - kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0; - return ret; -} - -/* Callback transmission routine */ -static void ksdazzle_send_irq(struct urb *urb) -{ - struct ksdazzle_cb *kingsun = urb->context; - struct net_device *netdev = kingsun->netdev; - int ret = 0; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_send_irq: Network not running!\n"); - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_send_irq: urb asynchronously failed - %d\n", - urb->status); - return; - } - - if (kingsun->tx_buf_clear_used > 0) { - /* Update data remaining to be sent */ - if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) { - memmove(kingsun->tx_buf_clear, - kingsun->tx_buf_clear + - kingsun->tx_buf_clear_sent, - kingsun->tx_buf_clear_used - - kingsun->tx_buf_clear_sent); - } - kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent; - kingsun->tx_buf_clear_sent = 0; - - if (kingsun->tx_buf_clear_used > 0) { - /* There is more data to be sent */ - if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_send_irq: failed tx_urb submit: %d\n", - ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } - } else { - /* All data sent, send next speed && wake network queue */ - if (kingsun->new_speed != -1 && - cpu_to_le32(kingsun->new_speed) != - kingsun->speedparams.baudrate) - ksdazzle_change_speed(kingsun, - kingsun->new_speed); - - netif_wake_queue(netdev); - } - } -} - -/* - * Called from net/core when new frame is available. - */ -static netdev_tx_t ksdazzle_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct ksdazzle_cb *kingsun; - unsigned int wraplen; - int ret = 0; - - netif_stop_queue(netdev); - - /* the IRDA wrapping routines don't deal with non linear skb */ - SKB_LINEAR_ASSERT(skb); - - kingsun = netdev_priv(netdev); - - spin_lock(&kingsun->lock); - kingsun->new_speed = irda_get_next_speed(skb); - - /* Append data to the end of whatever data remains to be transmitted */ - wraplen = - async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE); - kingsun->tx_buf_clear_used = wraplen; - - if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_hard_xmit: failed tx_urb submit: %d\n", ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } else { - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - - } - - dev_kfree_skb(skb); - spin_unlock(&kingsun->lock); - - return NETDEV_TX_OK; -} - -/* Receive callback function */ -static void ksdazzle_rcv_irq(struct urb *urb) -{ - struct ksdazzle_cb *kingsun = urb->context; - struct net_device *netdev = kingsun->netdev; - - /* in process of stopping, just drop data */ - if (!netif_running(netdev)) { - kingsun->receiving = 0; - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_rcv_irq: urb asynchronously failed - %d\n", - urb->status); - kingsun->receiving = 0; - return; - } - - if (urb->actual_length > 0) { - __u8 *bytes = urb->transfer_buffer; - unsigned int i; - - for (i = 0; i < urb->actual_length; i++) { - async_unwrap_char(netdev, &netdev->stats, - &kingsun->rx_unwrap_buff, bytes[i]); - } - kingsun->receiving = - (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0; - } - - /* This urb has already been filled in ksdazzle_net_open. It is assumed that - urb keeps the pointer to the payload buffer. - */ - urb->status = 0; - usb_submit_urb(urb, GFP_ATOMIC); -} - -/* - * Function ksdazzle_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - */ -static int ksdazzle_net_open(struct net_device *netdev) -{ - struct ksdazzle_cb *kingsun = netdev_priv(netdev); - int err = -ENOMEM; - char hwname[16]; - - /* At this point, urbs are NULL, and skb is NULL (see ksdazzle_probe) */ - kingsun->receiving = 0; - - /* Initialize for SIR to copy data directly into skb. */ - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU; - kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!kingsun->rx_unwrap_buff.skb) - goto free_mem; - - skb_reserve(kingsun->rx_unwrap_buff.skb, 1); - kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data; - - kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->rx_urb) - goto free_mem; - - kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->tx_urb) - goto free_mem; - - kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->speed_urb) - goto free_mem; - - /* Initialize speed for dongle */ - kingsun->new_speed = 9600; - err = ksdazzle_change_speed(kingsun, 9600); - if (err < 0) - goto free_mem; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - */ - sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); - kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); - if (!kingsun->irlap) { - err = -ENOMEM; - dev_err(&kingsun->usbdev->dev, "irlap_open failed\n"); - goto free_mem; - } - - /* Start reception. */ - usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, - usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), - kingsun->rx_buf, KINGSUN_RCV_MAX, ksdazzle_rcv_irq, - kingsun, 1); - kingsun->rx_urb->status = 0; - err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - if (err) { - dev_err(&kingsun->usbdev->dev, "first urb-submit failed: %d\n", err); - goto close_irlap; - } - - netif_start_queue(netdev); - - /* Situation at this point: - - all work buffers allocated - - urbs allocated and ready to fill - - max rx packet known (in max_rx) - - unwrap state machine initialized, in state outside of any frame - - receive request in progress - - IrLAP layer started, about to hand over packets to send - */ - - return 0; - - close_irlap: - irlap_close(kingsun->irlap); - free_mem: - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - if (kingsun->rx_unwrap_buff.skb) { - kfree_skb(kingsun->rx_unwrap_buff.skb); - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->rx_unwrap_buff.head = NULL; - } - return err; -} - -/* - * Function ksdazzle_net_close (dev) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int ksdazzle_net_close(struct net_device *netdev) -{ - struct ksdazzle_cb *kingsun = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - /* Mop up receive && transmit urb's */ - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - - usb_kill_urb(kingsun->speed_urb); - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - - kfree_skb(kingsun->rx_unwrap_buff.skb); - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->rx_unwrap_buff.head = NULL; - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->receiving = 0; - - /* Stop and remove instance of IrLAP */ - irlap_close(kingsun->irlap); - - kingsun->irlap = NULL; - - return 0; -} - -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int ksdazzle_net_ioctl(struct net_device *netdev, struct ifreq *rq, - int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *)rq; - struct ksdazzle_cb *kingsun = netdev_priv(netdev); - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the device is still there */ - if (netif_device_present(kingsun->netdev)) - return ksdazzle_change_speed(kingsun, - irq->ifr_baudrate); - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the IrDA stack is still there */ - if (netif_running(kingsun->netdev)) - irda_device_set_media_busy(kingsun->netdev, TRUE); - break; - - case SIOCGRECEIVING: - /* Only approximately true */ - irq->ifr_receiving = kingsun->receiving; - break; - - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -static const struct net_device_ops ksdazzle_ops = { - .ndo_start_xmit = ksdazzle_hard_xmit, - .ndo_open = ksdazzle_net_open, - .ndo_stop = ksdazzle_net_close, - .ndo_do_ioctl = ksdazzle_net_ioctl, -}; - -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - */ -static int ksdazzle_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - - struct usb_device *dev = interface_to_usbdev(intf); - struct ksdazzle_cb *kingsun = NULL; - struct net_device *net = NULL; - int ret = -ENOMEM; - int pipe, maxp_in, maxp_out; - __u8 ep_in; - __u8 ep_out; - - /* Check that there really are two interrupt endpoints. Check based on the - one in drivers/usb/input/usbmouse.c - */ - interface = intf->cur_altsetting; - if (interface->desc.bNumEndpoints != 2) { - dev_err(&intf->dev, "ksdazzle: expected 2 endpoints, found %d\n", - interface->desc.bNumEndpoints); - return -ENODEV; - } - endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; - if (!usb_endpoint_is_int_in(endpoint)) { - dev_err(&intf->dev, - "ksdazzle: endpoint 0 is not interrupt IN\n"); - return -ENODEV; - } - - ep_in = endpoint->bEndpointAddress; - pipe = usb_rcvintpipe(dev, ep_in); - maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - if (maxp_in > 255 || maxp_in <= 1) { - dev_err(&intf->dev, - "ksdazzle: endpoint 0 has max packet size %d not in range [2..255]\n", - maxp_in); - return -ENODEV; - } - - endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; - if (!usb_endpoint_is_int_out(endpoint)) { - dev_err(&intf->dev, - "ksdazzle: endpoint 1 is not interrupt OUT\n"); - return -ENODEV; - } - - ep_out = endpoint->bEndpointAddress; - pipe = usb_sndintpipe(dev, ep_out); - maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - /* Allocate network device container. */ - net = alloc_irdadev(sizeof(*kingsun)); - if (!net) - goto err_out1; - - SET_NETDEV_DEV(net, &intf->dev); - kingsun = netdev_priv(net); - kingsun->netdev = net; - kingsun->usbdev = dev; - kingsun->ep_in = ep_in; - kingsun->ep_out = ep_out; - kingsun->irlap = NULL; - kingsun->tx_urb = NULL; - kingsun->tx_buf_clear = NULL; - kingsun->tx_buf_clear_used = 0; - kingsun->tx_buf_clear_sent = 0; - - kingsun->rx_urb = NULL; - kingsun->rx_buf = NULL; - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->receiving = 0; - spin_lock_init(&kingsun->lock); - - kingsun->speed_setuprequest = NULL; - kingsun->speed_urb = NULL; - kingsun->speedparams.baudrate = 0; - - /* Allocate input buffer */ - kingsun->rx_buf = kmalloc(KINGSUN_RCV_MAX, GFP_KERNEL); - if (!kingsun->rx_buf) - goto free_mem; - - /* Allocate output buffer */ - kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL); - if (!kingsun->tx_buf_clear) - goto free_mem; - - /* Allocate and initialize speed setup packet */ - kingsun->speed_setuprequest = - kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); - if (!kingsun->speed_setuprequest) - goto free_mem; - kingsun->speed_setuprequest->bRequestType = - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND; - kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200); - kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001); - kingsun->speed_setuprequest->wLength = - cpu_to_le16(sizeof(struct ksdazzle_speedparams)); - - printk(KERN_INFO "KingSun/Dazzle IRDA/USB found at address %d, " - "Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&kingsun->qos); - - /* Baud rates known to be supported. Please uncomment if devices (other - than a SonyEriccson K300 phone) can be shown to support higher speeds - with this dongle. - */ - kingsun->qos.baud_rate.bits = - IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200; - kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; - irda_qos_bits_to_value(&kingsun->qos); - - /* Override the network functions we need to use */ - net->netdev_ops = &ksdazzle_ops; - - ret = register_netdev(net); - if (ret != 0) - goto free_mem; - - dev_info(&net->dev, "IrDA: Registered KingSun/Dazzle device %s\n", - net->name); - - usb_set_intfdata(intf, kingsun); - - /* Situation at this point: - - all work buffers allocated - - setup requests pre-filled - - urbs not allocated, set to NULL - - max rx packet known (is KINGSUN_FIFO_SIZE) - - unwrap state machine (partially) initialized, but skb == NULL - */ - - return 0; - - free_mem: - kfree(kingsun->speed_setuprequest); - kfree(kingsun->tx_buf_clear); - kfree(kingsun->rx_buf); - free_netdev(net); - err_out1: - return ret; -} - -/* - * The current device is removed, the USB layer tell us to shut it down... - */ -static void ksdazzle_disconnect(struct usb_interface *intf) -{ - struct ksdazzle_cb *kingsun = usb_get_intfdata(intf); - - if (!kingsun) - return; - - unregister_netdev(kingsun->netdev); - - /* Mop up receive && transmit urb's */ - usb_kill_urb(kingsun->speed_urb); - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - - kfree(kingsun->speed_setuprequest); - kfree(kingsun->tx_buf_clear); - kfree(kingsun->rx_buf); - free_netdev(kingsun->netdev); - - usb_set_intfdata(intf, NULL); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int ksdazzle_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct ksdazzle_cb *kingsun = usb_get_intfdata(intf); - - netif_device_detach(kingsun->netdev); - if (kingsun->speed_urb != NULL) - usb_kill_urb(kingsun->speed_urb); - if (kingsun->tx_urb != NULL) - usb_kill_urb(kingsun->tx_urb); - if (kingsun->rx_urb != NULL) - usb_kill_urb(kingsun->rx_urb); - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int ksdazzle_resume(struct usb_interface *intf) -{ - struct ksdazzle_cb *kingsun = usb_get_intfdata(intf); - - if (kingsun->rx_urb != NULL) { - /* Setup request already filled in ksdazzle_probe */ - usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - } - netif_device_attach(kingsun->netdev); - - return 0; -} -#endif - -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "ksdazzle-sir", - .probe = ksdazzle_probe, - .disconnect = ksdazzle_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = ksdazzle_suspend, - .resume = ksdazzle_resume, -#endif -}; - -module_usb_driver(irda_driver); - -MODULE_AUTHOR("Alex Villacís Lasso "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun Dazzle"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/litelink-sir.c b/drivers/net/irda/litelink-sir.c deleted file mode 100644 index 8eefcb44bac3..000000000000 --- a/drivers/net/irda/litelink-sir.c +++ /dev/null @@ -1,199 +0,0 @@ -/********************************************************************* - * - * Filename: litelink.c - * Version: 1.1 - * Description: Driver for the Parallax LiteLink dongle - * Status: Stable - * Author: Dag Brattli - * Created at: Fri May 7 12:50:33 1999 - * Modified at: Fri Dec 17 09:14:23 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -/* - * Modified at: Thu Jan 15 2003 - * Modified by: Eugene Crosser - * - * Convert to "new" IRDA infrastructure for kernel 2.6 - */ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */ -#define MAX_DELAY 10000 /* 1 ms */ - -static int litelink_open(struct sir_dev *dev); -static int litelink_close(struct sir_dev *dev); -static int litelink_change_speed(struct sir_dev *dev, unsigned speed); -static int litelink_reset(struct sir_dev *dev); - -/* These are the baudrates supported - 9600 must be last one! */ -static unsigned baud_rates[] = { 115200, 57600, 38400, 19200, 9600 }; - -static struct dongle_driver litelink = { - .owner = THIS_MODULE, - .driver_name = "Parallax LiteLink", - .type = IRDA_LITELINK_DONGLE, - .open = litelink_open, - .close = litelink_close, - .reset = litelink_reset, - .set_speed = litelink_change_speed, -}; - -static int __init litelink_sir_init(void) -{ - return irda_register_dongle(&litelink); -} - -static void __exit litelink_sir_cleanup(void) -{ - irda_unregister_dongle(&litelink); -} - -static int litelink_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power up dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Set the speeds we can accept */ - qos->baud_rate.bits &= IR_115200|IR_57600|IR_38400|IR_19200|IR_9600; - qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int litelink_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function litelink_change_speed (task) - * - * Change speed of the Litelink dongle. To cycle through the available - * baud rates, pulse RTS low for a few ms. - */ -static int litelink_change_speed(struct sir_dev *dev, unsigned speed) -{ - int i; - - /* dongle already reset by irda-thread - current speed (dongle and - * port) is the default speed (115200 for litelink!) - */ - - /* Cycle through avaiable baudrates until we reach the correct one */ - for (i = 0; baud_rates[i] != speed; i++) { - - /* end-of-list reached due to invalid speed request */ - if (baud_rates[i] == 9600) - break; - - /* Set DTR, clear RTS */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - - /* Set DTR, Set RTS */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - } - - dev->speed = baud_rates[i]; - - /* invalid baudrate should not happen - but if, we return -EINVAL and - * the dongle configured for 9600 so the stack has a chance to recover - */ - - return (dev->speed == speed) ? 0 : -EINVAL; -} - -/* - * Function litelink_reset (task) - * - * Reset the Litelink type dongle. - * - */ -static int litelink_reset(struct sir_dev *dev) -{ - /* probably the power-up can be dropped here, but with only - * 15 usec delay it's not worth the risk unless somebody with - * the hardware confirms it doesn't break anything... - */ - - /* Power on dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - - /* Clear RTS to reset dongle */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - - /* This dongles speed defaults to 115200 bps */ - dev->speed = 115200; - - return 0; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Parallax Litelink dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-5"); /* IRDA_LITELINK_DONGLE */ - -/* - * Function init_module (void) - * - * Initialize Litelink module - * - */ -module_init(litelink_sir_init); - -/* - * Function cleanup_module (void) - * - * Cleanup Litelink module - * - */ -module_exit(litelink_sir_cleanup); diff --git a/drivers/net/irda/ma600-sir.c b/drivers/net/irda/ma600-sir.c deleted file mode 100644 index a764817b47f1..000000000000 --- a/drivers/net/irda/ma600-sir.c +++ /dev/null @@ -1,253 +0,0 @@ -/********************************************************************* - * - * Filename: ma600.c - * Version: 0.1 - * Description: Implementation of the MA600 dongle - * Status: Experimental. - * Author: Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95 - * Created at: Sat Jun 10 20:02:35 2000 - * Modified at: Sat Aug 16 09:34:13 2003 - * Modified by: Martin Diehl (modified for new sir_dev) - * - * Note: very thanks to Mr. Maru Wang for providing - * information on the MA600 dongle - * - * Copyright (c) 2000 Leung, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int ma600_open(struct sir_dev *); -static int ma600_close(struct sir_dev *); -static int ma600_change_speed(struct sir_dev *, unsigned); -static int ma600_reset(struct sir_dev *); - -/* control byte for MA600 */ -#define MA600_9600 0x00 -#define MA600_19200 0x01 -#define MA600_38400 0x02 -#define MA600_57600 0x03 -#define MA600_115200 0x04 -#define MA600_DEV_ID1 0x05 -#define MA600_DEV_ID2 0x06 -#define MA600_2400 0x08 - -static struct dongle_driver ma600 = { - .owner = THIS_MODULE, - .driver_name = "MA600", - .type = IRDA_MA600_DONGLE, - .open = ma600_open, - .close = ma600_close, - .reset = ma600_reset, - .set_speed = ma600_change_speed, -}; - - -static int __init ma600_sir_init(void) -{ - return irda_register_dongle(&ma600); -} - -static void __exit ma600_sir_cleanup(void) -{ - irda_unregister_dongle(&ma600); -} - -/* - Power on: - (0) Clear RTS and DTR for 1 second - (1) Set RTS and DTR for 1 second - (2) 9600 bps now - Note: assume RTS, DTR are clear before -*/ -static int ma600_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Explicitly set the speeds we can accept */ - qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400 - |IR_57600|IR_115200; - /* Hm, 0x01 means 10ms - for >= 1ms we would need 0x07 */ - qos->min_turn_time.bits = 0x01; /* Needs at least 1 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int ma600_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -static __u8 get_control_byte(__u32 speed) -{ - __u8 byte; - - switch (speed) { - default: - case 115200: - byte = MA600_115200; - break; - case 57600: - byte = MA600_57600; - break; - case 38400: - byte = MA600_38400; - break; - case 19200: - byte = MA600_19200; - break; - case 9600: - byte = MA600_9600; - break; - case 2400: - byte = MA600_2400; - break; - } - - return byte; -} - -/* - * Function ma600_change_speed (dev, speed) - * - * Set the speed for the MA600 type dongle. - * - * The dongle has already been reset to a known state (dongle default) - * We cycle through speeds by pulsing RTS low and then high. - */ - -/* - * Function ma600_change_speed (dev, speed) - * - * Set the speed for the MA600 type dongle. - * - * Algorithm - * 1. Reset (already done by irda thread state machine) - * 2. clear RTS, set DTR and wait for 1ms - * 3. send Control Byte to the MA600 through TXD to set new baud rate - * wait until the stop bit of Control Byte is sent (for 9600 baud rate, - * it takes about 10 msec) - * 4. set RTS, set DTR (return to NORMAL Operation) - * 5. wait at least 10 ms, new setting (baud rate, etc) takes effect here - * after - */ - -/* total delays are only about 20ms - let's just sleep for now to - * avoid the state machine complexity before we get things working - */ - -static int ma600_change_speed(struct sir_dev *dev, unsigned speed) -{ - u8 byte; - - pr_debug("%s(), speed=%d (was %d)\n", __func__, - speed, dev->speed); - - /* dongle already reset, dongle and port at default speed (9600) */ - - /* Set RTS low for 1 ms */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - mdelay(1); - - /* Write control byte */ - byte = get_control_byte(speed); - sirdev_raw_write(dev, &byte, sizeof(byte)); - - /* Wait at least 10ms: fake wait_until_sent - 10 bits at 9600 baud*/ - msleep(15); /* old ma600 uses 15ms */ - -#if 1 - /* read-back of the control byte. ma600 is the first dongle driver - * which uses this so there might be some unidentified issues. - * Disable this in case of problems with readback. - */ - - sirdev_raw_read(dev, &byte, sizeof(byte)); - if (byte != get_control_byte(speed)) { - net_warn_ratelimited("%s(): bad control byte read-back %02x != %02x\n", - __func__, (unsigned)byte, - (unsigned)get_control_byte(speed)); - return -1; - } - else - pr_debug("%s() control byte write read OK\n", __func__); -#endif - - /* Set DTR, Set RTS */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Wait at least 10ms */ - msleep(10); - - /* dongle is now switched to the new speed */ - dev->speed = speed; - - return 0; -} - -/* - * Function ma600_reset (dev) - * - * This function resets the ma600 dongle. - * - * Algorithm: - * 0. DTR=0, RTS=1 and wait 10 ms - * 1. DTR=1, RTS=1 and wait 10 ms - * 2. 9600 bps now - */ - -/* total delays are only about 20ms - let's just sleep for now to - * avoid the state machine complexity before we get things working - */ - -static int ma600_reset(struct sir_dev *dev) -{ - /* Reset the dongle : set DTR low for 10 ms */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - msleep(10); - - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - msleep(10); - - dev->speed = 9600; /* That's the dongle-default */ - - return 0; -} - -MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95"); -MODULE_DESCRIPTION("MA600 dongle driver version 0.1"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */ - -module_init(ma600_sir_init); -module_exit(ma600_sir_cleanup); - diff --git a/drivers/net/irda/mcp2120-sir.c b/drivers/net/irda/mcp2120-sir.c deleted file mode 100644 index 2e33f91bfe8f..000000000000 --- a/drivers/net/irda/mcp2120-sir.c +++ /dev/null @@ -1,224 +0,0 @@ -/********************************************************************* - * - * - * Filename: mcp2120.c - * Version: 1.0 - * Description: Implementation for the MCP2120 (Microchip) - * Status: Experimental. - * Author: Felix Tang (tangf@eyetap.org) - * Created at: Sun Mar 31 19:32:12 EST 2002 - * Based on code by: Dag Brattli - * - * Copyright (c) 2002 Felix Tang, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int mcp2120_reset(struct sir_dev *dev); -static int mcp2120_open(struct sir_dev *dev); -static int mcp2120_close(struct sir_dev *dev); -static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed); - -#define MCP2120_9600 0x87 -#define MCP2120_19200 0x8B -#define MCP2120_38400 0x85 -#define MCP2120_57600 0x83 -#define MCP2120_115200 0x81 - -#define MCP2120_COMMIT 0x11 - -static struct dongle_driver mcp2120 = { - .owner = THIS_MODULE, - .driver_name = "Microchip MCP2120", - .type = IRDA_MCP2120_DONGLE, - .open = mcp2120_open, - .close = mcp2120_close, - .reset = mcp2120_reset, - .set_speed = mcp2120_change_speed, -}; - -static int __init mcp2120_sir_init(void) -{ - return irda_register_dongle(&mcp2120); -} - -static void __exit mcp2120_sir_cleanup(void) -{ - irda_unregister_dongle(&mcp2120); -} - -static int mcp2120_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* seems no explicit power-on required here and reset switching it on anyway */ - - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - qos->min_turn_time.bits = 0x01; - irda_qos_bits_to_value(qos); - - return 0; -} - -static int mcp2120_close(struct sir_dev *dev) -{ - /* Power off dongle */ - /* reset and inhibit mcp2120 */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - // sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function mcp2120_change_speed (dev, speed) - * - * Set the speed for the MCP2120. - * - */ - -#define MCP2120_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED+1) - -static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 control[2]; - static int ret = 0; - - switch (state) { - case SIRDEV_STATE_DONGLE_SPEED: - /* Set DTR to enter command mode */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - udelay(500); - - ret = 0; - switch (speed) { - default: - speed = 9600; - ret = -EINVAL; - /* fall through */ - case 9600: - control[0] = MCP2120_9600; - //printk("mcp2120 9600\n"); - break; - case 19200: - control[0] = MCP2120_19200; - //printk("mcp2120 19200\n"); - break; - case 34800: - control[0] = MCP2120_38400; - //printk("mcp2120 38400\n"); - break; - case 57600: - control[0] = MCP2120_57600; - //printk("mcp2120 57600\n"); - break; - case 115200: - control[0] = MCP2120_115200; - //printk("mcp2120 115200\n"); - break; - } - control[1] = MCP2120_COMMIT; - - /* Write control bytes */ - sirdev_raw_write(dev, control, 2); - dev->speed = speed; - - state = MCP2120_STATE_WAIT_SPEED; - delay = 100; - //printk("mcp2120_change_speed: dongle_speed\n"); - break; - - case MCP2120_STATE_WAIT_SPEED: - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - //printk("mcp2120_change_speed: mcp_wait\n"); - break; - - default: - net_err_ratelimited("%s(), undefine state %d\n", - __func__, state); - ret = -EINVAL; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -/* - * Function mcp2120_reset (driver) - * - * This function resets the mcp2120 dongle. - * - * Info: -set RTS to reset mcp2120 - * -set DTR to set mcp2120 software command mode - * -mcp2120 defaults to 9600 baud after reset - * - * Algorithm: - * 0. Set RTS to reset mcp2120. - * 1. Clear RTS and wait for device reset timer of 30 ms (max). - * - */ - -#define MCP2120_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1) -#define MCP2120_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2) - -static int mcp2120_reset(struct sir_dev *dev) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - int ret = 0; - - switch (state) { - case SIRDEV_STATE_DONGLE_RESET: - //printk("mcp2120_reset: dongle_reset\n"); - /* Reset dongle by setting RTS*/ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - state = MCP2120_STATE_WAIT1_RESET; - delay = 50; - break; - - case MCP2120_STATE_WAIT1_RESET: - //printk("mcp2120_reset: mcp2120_wait1\n"); - /* clear RTS and wait for at least 30 ms. */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - state = MCP2120_STATE_WAIT2_RESET; - delay = 50; - break; - - case MCP2120_STATE_WAIT2_RESET: - //printk("mcp2120_reset mcp2120_wait2\n"); - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - break; - - default: - net_err_ratelimited("%s(), undefined state %d\n", - __func__, state); - ret = -EINVAL; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -MODULE_AUTHOR("Felix Tang "); -MODULE_DESCRIPTION("Microchip MCP2120"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */ - -module_init(mcp2120_sir_init); -module_exit(mcp2120_sir_cleanup); diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c deleted file mode 100644 index c3f0b254b344..000000000000 --- a/drivers/net/irda/mcs7780.c +++ /dev/null @@ -1,987 +0,0 @@ -/***************************************************************************** -* -* Filename: mcs7780.c -* Version: 0.4-alpha -* Description: Irda MosChip USB Dongle Driver -* Authors: Lukasz Stelmach -* Brian Pugh -* Judy Fischbach -* -* Based on stir4200 driver, but some things done differently. -* Based on earlier driver by Paul Stewart -* -* Copyright (C) 2000, Roman Weissgaerber -* Copyright (C) 2001, Dag Brattli -* Copyright (C) 2001, Jean Tourrilhes -* Copyright (C) 2004, Stephen Hemminger -* Copyright (C) 2005, Lukasz Stelmach -* Copyright (C) 2005, Brian Pugh -* Copyright (C) 2005, Judy Fischbach -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * MCS7780 is a simple USB to IrDA bridge by MosChip. It is neither - * compatibile with irda-usb nor with stir4200. Although it is quite - * similar to the later as far as general idea of operation is concerned. - * That is it requires the software to do all the framing job at SIR speeds. - * The hardware does take care of the framing at MIR and FIR speeds. - * It supports all speeds from 2400 through 4Mbps - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "mcs7780.h" - -#define MCS_VENDOR_ID 0x9710 -#define MCS_PRODUCT_ID 0x7780 - -static const struct usb_device_id mcs_table[] = { - /* MosChip Corp., MCS7780 FIR-USB Adapter */ - {USB_DEVICE(MCS_VENDOR_ID, MCS_PRODUCT_ID)}, - {}, -}; - -MODULE_AUTHOR("Brian Pugh "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for MosChip MCS7780"); -MODULE_VERSION("0.3alpha"); -MODULE_LICENSE("GPL"); - -MODULE_DEVICE_TABLE(usb, mcs_table); - -static int qos_mtt_bits = 0x07 /* > 1ms */ ; -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); - -static int receive_mode = 0x1; -module_param(receive_mode, int, 0); -MODULE_PARM_DESC(receive_mode, - "Receive mode of the device (1:fast, 0:slow, default:1)"); - -static int sir_tweak = 1; -module_param(sir_tweak, int, 0444); -MODULE_PARM_DESC(sir_tweak, - "Default pulse width (1:1.6us, 0:3/16 bit, default:1)."); - -static int transceiver_type = MCS_TSC_VISHAY; -module_param(transceiver_type, int, 0444); -MODULE_PARM_DESC(transceiver_type, "IR transceiver type, see mcs7780.h."); - -static struct usb_driver mcs_driver = { - .name = "mcs7780", - .probe = mcs_probe, - .disconnect = mcs_disconnect, - .id_table = mcs_table, -}; - -/* speed flag selection by direct addressing. -addr = (speed >> 8) & 0x0f - -0x1 57600 0x2 115200 0x4 1152000 0x5 9600 -0x6 38400 0x9 2400 0xa 576000 0xb 19200 - -4Mbps (or 2400) must be checked separately. Since it also has -to be programmed in a different manner that is not a big problem. -*/ -static __u16 mcs_speed_set[16] = { 0, - MCS_SPEED_57600, - MCS_SPEED_115200, - 0, - MCS_SPEED_1152000, - MCS_SPEED_9600, - MCS_SPEED_38400, - 0, 0, - MCS_SPEED_2400, - MCS_SPEED_576000, - MCS_SPEED_19200, - 0, 0, 0, -}; - -/* Set given 16 bit register with a 16 bit value. Send control message - * to set dongle register. */ -static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val) -{ - struct usb_device *dev = mcs->usbdev; - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, - MCS_WR_RTYPE, val, reg, NULL, 0, - msecs_to_jiffies(MCS_CTRL_TIMEOUT)); -} - -/* Get 16 bit register value. Send contol message to read dongle register. */ -static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val) -{ - struct usb_device *dev = mcs->usbdev; - void *dmabuf; - int ret; - - dmabuf = kmalloc(sizeof(__u16), GFP_KERNEL); - if (!dmabuf) - return -ENOMEM; - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, - MCS_RD_RTYPE, 0, reg, dmabuf, 2, - msecs_to_jiffies(MCS_CTRL_TIMEOUT)); - - memcpy(val, dmabuf, sizeof(__u16)); - kfree(dmabuf); - - return ret; -} - -/* Setup a communication between mcs7780 and TFDU chips. It is described - * in more detail in the data sheet. The setup sequence puts the the - * vishay tranceiver into high speed mode. It will also receive SIR speed - * packets but at reduced sensitivity. - */ - -/* 0: OK 1:ERROR */ -static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs) -{ - int ret = 0; - __u16 rval; - - /* mcs_get_reg should read exactly two bytes from the dongle */ - ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); - if (unlikely(ret != 2)) { - ret = -EIO; - goto error; - } - - /* The MCS_XCVR_CONF bit puts the transceiver into configuration - * mode. The MCS_MODE0 bit must start out high (1) and then - * transition to low and the MCS_STFIR and MCS_MODE1 bits must - * be low. - */ - rval |= (MCS_MODE0 | MCS_XCVR_CONF); - rval &= ~MCS_STFIR; - rval &= ~MCS_MODE1; - ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); - if (unlikely(ret)) - goto error; - - rval &= ~MCS_MODE0; - ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); - if (unlikely(ret)) - goto error; - - rval &= ~MCS_XCVR_CONF; - ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); - if (unlikely(ret)) - goto error; - - ret = 0; -error: - return ret; -} - -/* Setup a communication between mcs7780 and agilent chip. */ -static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs) -{ - net_warn_ratelimited("This transceiver type is not supported yet\n"); - return 1; -} - -/* Setup a communication between mcs7780 and sharp chip. */ -static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs) -{ - net_warn_ratelimited("This transceiver type is not supported yet\n"); - return 1; -} - -/* Common setup for all transceivers */ -static inline int mcs_setup_transceiver(struct mcs_cb *mcs) -{ - int ret = 0; - __u16 rval; - const char *msg; - - msg = "Basic transceiver setup error"; - - /* read value of MODE Register, set the DRIVER and RESET bits - * and write value back out to MODE Register - */ - ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); - if(unlikely(ret != 2)) - goto error; - rval |= MCS_DRIVER; /* put the mcs7780 into configuration mode. */ - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - if(unlikely(ret)) - goto error; - - rval = 0; /* set min pulse width to 0 initially. */ - ret = mcs_set_reg(mcs, MCS_MINRXPW_REG, rval); - if(unlikely(ret)) - goto error; - - ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); - if(unlikely(ret != 2)) - goto error; - - rval &= ~MCS_FIR; /* turn off fir mode. */ - if(mcs->sir_tweak) - rval |= MCS_SIR16US; /* 1.6us pulse width */ - else - rval &= ~MCS_SIR16US; /* 3/16 bit time pulse width */ - - /* make sure ask mode and back to back packets are off. */ - rval &= ~(MCS_BBTG | MCS_ASK); - - rval &= ~MCS_SPEED_MASK; - rval |= MCS_SPEED_9600; /* make sure initial speed is 9600. */ - mcs->speed = 9600; - mcs->new_speed = 0; /* new_speed is set to 0 */ - rval &= ~MCS_PLLPWDN; /* disable power down. */ - - /* make sure device determines direction and that the auto send sip - * pulse are on. - */ - rval |= MCS_DTD | MCS_SIPEN; - - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - if(unlikely(ret)) - goto error; - - msg = "transceiver model specific setup error"; - switch (mcs->transceiver_type) { - case MCS_TSC_VISHAY: - ret = mcs_setup_transceiver_vishay(mcs); - break; - - case MCS_TSC_SHARP: - ret = mcs_setup_transceiver_sharp(mcs); - break; - - case MCS_TSC_AGILENT: - ret = mcs_setup_transceiver_agilent(mcs); - break; - - default: - net_warn_ratelimited("Unknown transceiver type: %d\n", - mcs->transceiver_type); - ret = 1; - } - if (unlikely(ret)) - goto error; - - /* If transceiver is not SHARP, then if receive mode set - * on the RXFAST bit in the XCVR Register otherwise unset it - */ - if (mcs->transceiver_type != MCS_TSC_SHARP) { - - ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); - if (unlikely(ret != 2)) - goto error; - if (mcs->receive_mode) - rval |= MCS_RXFAST; - else - rval &= ~MCS_RXFAST; - ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); - if (unlikely(ret)) - goto error; - } - - msg = "transceiver reset"; - - ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); - if (unlikely(ret != 2)) - goto error; - - /* reset the mcs7780 so all changes take effect. */ - rval &= ~MCS_RESET; - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - if (unlikely(ret)) - goto error; - else - return ret; - -error: - net_err_ratelimited("%s\n", msg); - return ret; -} - -/* Wraps the data in format for SIR */ -static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf) -{ - int wraplen; - - /* 2: full frame length, including "the length" */ - wraplen = async_wrap_skb(skb, buf + 2, 4094); - - wraplen += 2; - buf[0] = wraplen & 0xff; - buf[1] = (wraplen >> 8) & 0xff; - - return wraplen; -} - -/* Wraps the data in format for FIR */ -static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf) -{ - unsigned int len = 0; - __u32 fcs = ~(crc32_le(~0, skb->data, skb->len)); - - /* add 2 bytes for length value and 4 bytes for fcs. */ - len = skb->len + 6; - - /* The mcs7780 requires that the first two bytes are the packet - * length in little endian order. Note: the length value includes - * the two bytes for the length value itself. - */ - buf[0] = len & 0xff; - buf[1] = (len >> 8) & 0xff; - /* copy the data into the tx buffer. */ - skb_copy_from_linear_data(skb, buf + 2, skb->len); - /* put the fcs in the last four bytes in little endian order. */ - buf[len - 4] = fcs & 0xff; - buf[len - 3] = (fcs >> 8) & 0xff; - buf[len - 2] = (fcs >> 16) & 0xff; - buf[len - 1] = (fcs >> 24) & 0xff; - - return len; -} - -/* Wraps the data in format for MIR */ -static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf) -{ - __u16 fcs = 0; - int len = skb->len + 4; - - fcs = ~(irda_calc_crc16(~fcs, skb->data, skb->len)); - /* put the total packet length in first. Note: packet length - * value includes the two bytes that hold the packet length - * itself. - */ - buf[0] = len & 0xff; - buf[1] = (len >> 8) & 0xff; - /* copy the data */ - skb_copy_from_linear_data(skb, buf + 2, skb->len); - /* put the fcs in last two bytes in little endian order. */ - buf[len - 2] = fcs & 0xff; - buf[len - 1] = (fcs >> 8) & 0xff; - - return len; -} - -/* Unwrap received packets at MIR speed. A 16 bit crc_ccitt checksum is - * used for the fcs. When performed over the entire packet the result - * should be GOOD_FCS = 0xf0b8. Hands the unwrapped data off to the IrDA - * layer via a sk_buff. - */ -static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len) -{ - __u16 fcs; - int new_len; - struct sk_buff *skb; - - /* Assume that the frames are going to fill a single packet - * rather than span multiple packets. - */ - - new_len = len - 2; - if(unlikely(new_len <= 0)) { - net_err_ratelimited("%s short frame length %d\n", - mcs->netdev->name, new_len); - ++mcs->netdev->stats.rx_errors; - ++mcs->netdev->stats.rx_length_errors; - return; - } - fcs = 0; - fcs = irda_calc_crc16(~fcs, buf, len); - - if(fcs != GOOD_FCS) { - net_err_ratelimited("crc error calc 0x%x len %d\n", - fcs, new_len); - mcs->netdev->stats.rx_errors++; - mcs->netdev->stats.rx_crc_errors++; - return; - } - - skb = dev_alloc_skb(new_len + 1); - if(unlikely(!skb)) { - ++mcs->netdev->stats.rx_dropped; - return; - } - - skb_reserve(skb, 1); - skb_copy_to_linear_data(skb, buf, new_len); - skb_put(skb, new_len); - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - skb->dev = mcs->netdev; - - netif_rx(skb); - - mcs->netdev->stats.rx_packets++; - mcs->netdev->stats.rx_bytes += new_len; -} - -/* Unwrap received packets at FIR speed. A 32 bit crc_ccitt checksum is - * used for the fcs. Hands the unwrapped data off to the IrDA - * layer via a sk_buff. - */ -static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len) -{ - __u32 fcs; - int new_len; - struct sk_buff *skb; - - /* Assume that the frames are going to fill a single packet - * rather than span multiple packets. This is most likely a false - * assumption. - */ - - new_len = len - 4; - if(unlikely(new_len <= 0)) { - net_err_ratelimited("%s short frame length %d\n", - mcs->netdev->name, new_len); - ++mcs->netdev->stats.rx_errors; - ++mcs->netdev->stats.rx_length_errors; - return; - } - - fcs = ~(crc32_le(~0, buf, new_len)); - if(fcs != get_unaligned_le32(buf + new_len)) { - net_err_ratelimited("crc error calc 0x%x len %d\n", - fcs, new_len); - mcs->netdev->stats.rx_errors++; - mcs->netdev->stats.rx_crc_errors++; - return; - } - - skb = dev_alloc_skb(new_len + 1); - if(unlikely(!skb)) { - ++mcs->netdev->stats.rx_dropped; - return; - } - - skb_reserve(skb, 1); - skb_copy_to_linear_data(skb, buf, new_len); - skb_put(skb, new_len); - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - skb->dev = mcs->netdev; - - netif_rx(skb); - - mcs->netdev->stats.rx_packets++; - mcs->netdev->stats.rx_bytes += new_len; -} - - -/* Allocates urbs for both receive and transmit. - * If alloc fails return error code 0 (fail) otherwise - * return error code 1 (success). - */ -static inline int mcs_setup_urbs(struct mcs_cb *mcs) -{ - mcs->rx_urb = NULL; - - mcs->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mcs->tx_urb) - return 0; - - mcs->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mcs->rx_urb) { - usb_free_urb(mcs->tx_urb); - mcs->tx_urb = NULL; - return 0; - } - - return 1; -} - -/* Sets up state to be initially outside frame, gets receive urb, - * sets status to successful and then submits the urb to start - * receiving the data. - */ -static inline int mcs_receive_start(struct mcs_cb *mcs) -{ - mcs->rx_buff.in_frame = FALSE; - mcs->rx_buff.state = OUTSIDE_FRAME; - - usb_fill_bulk_urb(mcs->rx_urb, mcs->usbdev, - usb_rcvbulkpipe(mcs->usbdev, mcs->ep_in), - mcs->in_buf, 4096, mcs_receive_irq, mcs); - - mcs->rx_urb->status = 0; - return usb_submit_urb(mcs->rx_urb, GFP_KERNEL); -} - -/* Finds the in and out endpoints for the mcs control block */ -static inline int mcs_find_endpoints(struct mcs_cb *mcs, - struct usb_host_endpoint *ep, int epnum) -{ - int i; - int ret = 0; - - /* If no place to store the endpoints just return */ - if (!ep) - return ret; - - /* cycle through all endpoints, find the first two that are DIR_IN */ - for (i = 0; i < epnum; i++) { - if (ep[i].desc.bEndpointAddress & USB_DIR_IN) - mcs->ep_in = ep[i].desc.bEndpointAddress; - else - mcs->ep_out = ep[i].desc.bEndpointAddress; - - /* MosChip says that the chip has only two bulk - * endpoints. Find one for each direction and move on. - */ - if ((mcs->ep_in != 0) && (mcs->ep_out != 0)) { - ret = 1; - break; - } - } - - return ret; -} - -static void mcs_speed_work(struct work_struct *work) -{ - struct mcs_cb *mcs = container_of(work, struct mcs_cb, work); - struct net_device *netdev = mcs->netdev; - - mcs_speed_change(mcs); - netif_wake_queue(netdev); -} - -/* Function to change the speed of the mcs7780. Fully supports SIR, - * MIR, and FIR speeds. - */ -static int mcs_speed_change(struct mcs_cb *mcs) -{ - int ret = 0; - int rst = 0; - int cnt = 0; - __u16 nspeed; - __u16 rval; - - nspeed = mcs_speed_set[(mcs->new_speed >> 8) & 0x0f]; - - do { - mcs_get_reg(mcs, MCS_RESV_REG, &rval); - } while(cnt++ < 100 && (rval & MCS_IRINTX)); - - if (cnt > 100) { - net_err_ratelimited("unable to change speed\n"); - ret = -EIO; - goto error; - } - - mcs_get_reg(mcs, MCS_MODE_REG, &rval); - - /* MINRXPW values recommended by MosChip */ - if (mcs->new_speed <= 115200) { - rval &= ~MCS_FIR; - - if ((rst = (mcs->speed > 115200))) - mcs_set_reg(mcs, MCS_MINRXPW_REG, 0); - - } else if (mcs->new_speed <= 1152000) { - rval &= ~MCS_FIR; - - if ((rst = !(mcs->speed == 576000 || mcs->speed == 1152000))) - mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); - - } else { - rval |= MCS_FIR; - - if ((rst = (mcs->speed != 4000000))) - mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); - - } - - rval &= ~MCS_SPEED_MASK; - rval |= nspeed; - - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - if (unlikely(ret)) - goto error; - - if (rst) - switch (mcs->transceiver_type) { - case MCS_TSC_VISHAY: - ret = mcs_setup_transceiver_vishay(mcs); - break; - - case MCS_TSC_SHARP: - ret = mcs_setup_transceiver_sharp(mcs); - break; - - case MCS_TSC_AGILENT: - ret = mcs_setup_transceiver_agilent(mcs); - break; - - default: - ret = 1; - net_warn_ratelimited("Unknown transceiver type: %d\n", - mcs->transceiver_type); - } - if (unlikely(ret)) - goto error; - - mcs_get_reg(mcs, MCS_MODE_REG, &rval); - rval &= ~MCS_RESET; - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - - mcs->speed = mcs->new_speed; -error: - mcs->new_speed = 0; - return ret; -} - -/* Ioctl calls not supported at this time. Can be an area of future work. */ -static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -{ - /* struct if_irda_req *irq = (struct if_irda_req *)rq; */ - /* struct mcs_cb *mcs = netdev_priv(netdev); */ - int ret = 0; - - switch (cmd) { - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -/* Network device is taken down, done by "ifconfig irda0 down" */ -static int mcs_net_close(struct net_device *netdev) -{ - int ret = 0; - struct mcs_cb *mcs = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - kfree_skb(mcs->rx_buff.skb); - - /* kill and free the receive and transmit URBs */ - usb_kill_urb(mcs->rx_urb); - usb_free_urb(mcs->rx_urb); - usb_kill_urb(mcs->tx_urb); - usb_free_urb(mcs->tx_urb); - - /* Stop and remove instance of IrLAP */ - if (mcs->irlap) - irlap_close(mcs->irlap); - - mcs->irlap = NULL; - return ret; -} - -/* Network device is taken up, done by "ifconfig irda0 up" */ -static int mcs_net_open(struct net_device *netdev) -{ - struct mcs_cb *mcs = netdev_priv(netdev); - char hwname[16]; - int ret = 0; - - ret = usb_clear_halt(mcs->usbdev, - usb_sndbulkpipe(mcs->usbdev, mcs->ep_in)); - if (ret) - goto error1; - ret = usb_clear_halt(mcs->usbdev, - usb_rcvbulkpipe(mcs->usbdev, mcs->ep_out)); - if (ret) - goto error1; - - ret = mcs_setup_transceiver(mcs); - if (ret) - goto error1; - - ret = -ENOMEM; - - /* Initialize for SIR/FIR to copy data directly into skb. */ - mcs->receiving = 0; - mcs->rx_buff.truesize = IRDA_SKB_MAX_MTU; - mcs->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!mcs->rx_buff.skb) - goto error1; - - skb_reserve(mcs->rx_buff.skb, 1); - mcs->rx_buff.head = mcs->rx_buff.skb->data; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - * Note : will send immediately a speed change... - */ - sprintf(hwname, "usb#%d", mcs->usbdev->devnum); - mcs->irlap = irlap_open(netdev, &mcs->qos, hwname); - if (!mcs->irlap) { - net_err_ratelimited("mcs7780: irlap_open failed\n"); - goto error2; - } - - if (!mcs_setup_urbs(mcs)) - goto error3; - - ret = mcs_receive_start(mcs); - if (ret) - goto error4; - - netif_start_queue(netdev); - return 0; - -error4: - usb_free_urb(mcs->rx_urb); - usb_free_urb(mcs->tx_urb); -error3: - irlap_close(mcs->irlap); -error2: - kfree_skb(mcs->rx_buff.skb); -error1: - return ret; -} - -/* Receive callback function. */ -static void mcs_receive_irq(struct urb *urb) -{ - __u8 *bytes; - struct mcs_cb *mcs = urb->context; - int i; - int ret; - - if (!netif_running(mcs->netdev)) - return; - - if (urb->status) - return; - - if (urb->actual_length > 0) { - bytes = urb->transfer_buffer; - - /* MCS returns frames without BOF and EOF - * I assume it returns whole frames. - */ - /* SIR speed */ - if(mcs->speed < 576000) { - async_unwrap_char(mcs->netdev, &mcs->netdev->stats, - &mcs->rx_buff, 0xc0); - - for (i = 0; i < urb->actual_length; i++) - async_unwrap_char(mcs->netdev, &mcs->netdev->stats, - &mcs->rx_buff, bytes[i]); - - async_unwrap_char(mcs->netdev, &mcs->netdev->stats, - &mcs->rx_buff, 0xc1); - } - /* MIR speed */ - else if(mcs->speed == 576000 || mcs->speed == 1152000) { - mcs_unwrap_mir(mcs, urb->transfer_buffer, - urb->actual_length); - } - /* FIR speed */ - else { - mcs_unwrap_fir(mcs, urb->transfer_buffer, - urb->actual_length); - } - } - - ret = usb_submit_urb(urb, GFP_ATOMIC); -} - -/* Transmit callback function. */ -static void mcs_send_irq(struct urb *urb) -{ - struct mcs_cb *mcs = urb->context; - struct net_device *ndev = mcs->netdev; - - if (unlikely(mcs->new_speed)) - schedule_work(&mcs->work); - else - netif_wake_queue(ndev); -} - -/* Transmit callback function. */ -static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - unsigned long flags; - struct mcs_cb *mcs; - int wraplen; - int ret = 0; - - netif_stop_queue(ndev); - mcs = netdev_priv(ndev); - - spin_lock_irqsave(&mcs->lock, flags); - - mcs->new_speed = irda_get_next_speed(skb); - if (likely(mcs->new_speed == mcs->speed)) - mcs->new_speed = 0; - - /* SIR speed */ - if(mcs->speed < 576000) { - wraplen = mcs_wrap_sir_skb(skb, mcs->out_buf); - } - /* MIR speed */ - else if(mcs->speed == 576000 || mcs->speed == 1152000) { - wraplen = mcs_wrap_mir_skb(skb, mcs->out_buf); - } - /* FIR speed */ - else { - wraplen = mcs_wrap_fir_skb(skb, mcs->out_buf); - } - usb_fill_bulk_urb(mcs->tx_urb, mcs->usbdev, - usb_sndbulkpipe(mcs->usbdev, mcs->ep_out), - mcs->out_buf, wraplen, mcs_send_irq, mcs); - - if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) { - net_err_ratelimited("failed tx_urb: %d\n", ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - mcs->netdev->stats.tx_errors++; - netif_start_queue(ndev); - } - } else { - mcs->netdev->stats.tx_packets++; - mcs->netdev->stats.tx_bytes += skb->len; - } - - dev_kfree_skb(skb); - spin_unlock_irqrestore(&mcs->lock, flags); - return NETDEV_TX_OK; -} - -static const struct net_device_ops mcs_netdev_ops = { - .ndo_open = mcs_net_open, - .ndo_stop = mcs_net_close, - .ndo_start_xmit = mcs_hard_xmit, - .ndo_do_ioctl = mcs_net_ioctl, -}; - -/* - * This function is called by the USB subsystem for each new device in the - * system. Need to verify the device and if it is, then start handling it. - */ -static int mcs_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct net_device *ndev = NULL; - struct mcs_cb *mcs; - int ret = -ENOMEM; - - ndev = alloc_irdadev(sizeof(*mcs)); - if (!ndev) - goto error1; - - pr_debug("MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum); - - SET_NETDEV_DEV(ndev, &intf->dev); - - ret = usb_reset_configuration(udev); - if (ret != 0) { - net_err_ratelimited("mcs7780: usb reset configuration failed\n"); - goto error2; - } - - mcs = netdev_priv(ndev); - mcs->usbdev = udev; - mcs->netdev = ndev; - spin_lock_init(&mcs->lock); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&mcs->qos); - - /* That's the Rx capability. */ - mcs->qos.baud_rate.bits &= - IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 - | IR_576000 | IR_1152000 | (IR_4000000 << 8); - - - mcs->qos.min_turn_time.bits &= qos_mtt_bits; - irda_qos_bits_to_value(&mcs->qos); - - /* Speed change work initialisation*/ - INIT_WORK(&mcs->work, mcs_speed_work); - - ndev->netdev_ops = &mcs_netdev_ops; - - if (!intf->cur_altsetting) { - ret = -ENOMEM; - goto error2; - } - - ret = mcs_find_endpoints(mcs, intf->cur_altsetting->endpoint, - intf->cur_altsetting->desc.bNumEndpoints); - if (!ret) { - ret = -ENODEV; - goto error2; - } - - ret = register_netdev(ndev); - if (ret != 0) - goto error2; - - pr_debug("IrDA: Registered MosChip MCS7780 device as %s\n", - ndev->name); - - mcs->transceiver_type = transceiver_type; - mcs->sir_tweak = sir_tweak; - mcs->receive_mode = receive_mode; - - usb_set_intfdata(intf, mcs); - return 0; - -error2: - free_netdev(ndev); - -error1: - return ret; -} - -/* The current device is removed, the USB layer tells us to shut down. */ -static void mcs_disconnect(struct usb_interface *intf) -{ - struct mcs_cb *mcs = usb_get_intfdata(intf); - - if (!mcs) - return; - - cancel_work_sync(&mcs->work); - - unregister_netdev(mcs->netdev); - free_netdev(mcs->netdev); - - usb_set_intfdata(intf, NULL); - pr_debug("MCS7780 now disconnected.\n"); -} - -module_usb_driver(mcs_driver); diff --git a/drivers/net/irda/mcs7780.h b/drivers/net/irda/mcs7780.h deleted file mode 100644 index a6e8f7dbafc9..000000000000 --- a/drivers/net/irda/mcs7780.h +++ /dev/null @@ -1,165 +0,0 @@ -/***************************************************************************** -* -* Filename: mcs7780.h -* Version: 0.2-alpha -* Description: Irda MosChip USB Dongle -* Status: Experimental -* Authors: Lukasz Stelmach -* Brian Pugh -* -* Copyright (C) 2005, Lukasz Stelmach -* Copyright (C) 2005, Brian Pugh -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ -#ifndef _MCS7780_H -#define _MCS7780_H - -#define MCS_MODE_SIR 0 -#define MCS_MODE_MIR 1 -#define MCS_MODE_FIR 2 - -#define MCS_CTRL_TIMEOUT 500 -#define MCS_XMIT_TIMEOUT 500 -/* Possible transceiver types */ -#define MCS_TSC_VISHAY 0 /* Vishay TFD, default choice */ -#define MCS_TSC_AGILENT 1 /* Agilent 3602/3600 */ -#define MCS_TSC_SHARP 2 /* Sharp GP2W1000YP */ - -/* Requests */ -#define MCS_RD_RTYPE 0xC0 -#define MCS_WR_RTYPE 0x40 -#define MCS_RDREQ 0x0F -#define MCS_WRREQ 0x0E - -/* Register 0x00 */ -#define MCS_MODE_REG 0 -#define MCS_FIR ((__u16)0x0001) -#define MCS_SIR16US ((__u16)0x0002) -#define MCS_BBTG ((__u16)0x0004) -#define MCS_ASK ((__u16)0x0008) -#define MCS_PARITY ((__u16)0x0010) - -/* SIR/MIR speed constants */ -#define MCS_SPEED_SHIFT 5 -#define MCS_SPEED_MASK ((__u16)0x00E0) -#define MCS_SPEED(x) ((x & MCS_SPEED_MASK) >> MCS_SPEED_SHIFT) -#define MCS_SPEED_2400 ((0 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_9600 ((1 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_19200 ((2 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_38400 ((3 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_57600 ((4 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_115200 ((5 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_576000 ((6 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_1152000 ((7 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) - -#define MCS_PLLPWDN ((__u16)0x0100) -#define MCS_DRIVER ((__u16)0x0200) -#define MCS_DTD ((__u16)0x0400) -#define MCS_DIR ((__u16)0x0800) -#define MCS_SIPEN ((__u16)0x1000) -#define MCS_SENDSIP ((__u16)0x2000) -#define MCS_CHGDIR ((__u16)0x4000) -#define MCS_RESET ((__u16)0x8000) - -/* Register 0x02 */ -#define MCS_XCVR_REG 2 -#define MCS_MODE0 ((__u16)0x0001) -#define MCS_STFIR ((__u16)0x0002) -#define MCS_XCVR_CONF ((__u16)0x0004) -#define MCS_RXFAST ((__u16)0x0008) -/* TXCUR [6:4] */ -#define MCS_TXCUR_SHIFT 4 -#define MCS_TXCUR_MASK ((__u16)0x0070) -#define MCS_TXCUR(x) ((x & MCS_TXCUR_MASK) >> MCS_TXCUR_SHIFT) -#define MCS_SETTXCUR(x,y) \ - ((x & ~MCS_TXCUR_MASK) | (y << MCS_TXCUR_SHIFT) & MCS_TXCUR_MASK) - -#define MCS_MODE1 ((__u16)0x0080) -#define MCS_SMODE0 ((__u16)0x0100) -#define MCS_SMODE1 ((__u16)0x0200) -#define MCS_INVTX ((__u16)0x0400) -#define MCS_INVRX ((__u16)0x0800) - -#define MCS_MINRXPW_REG 4 - -#define MCS_RESV_REG 7 -#define MCS_IRINTX ((__u16)0x0001) -#define MCS_IRINRX ((__u16)0x0002) - -struct mcs_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; - unsigned int speed; /* Current speed */ - unsigned int new_speed; /* new speed */ - - struct work_struct work; /* Change speed work */ - - struct sk_buff *tx_pending; - char in_buf[4096]; /* transmit/receive buffer */ - char out_buf[4096]; /* transmit/receive buffer */ - __u8 *fifo_status; - - iobuff_t rx_buff; /* receive unwrap state machine */ - spinlock_t lock; - int receiving; - - __u8 ep_in; - __u8 ep_out; - - struct urb *rx_urb; - struct urb *tx_urb; - - int transceiver_type; - int sir_tweak; - int receive_mode; -}; - -static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val); -static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val); - -static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs); -static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs); -static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs); -static inline int mcs_setup_transceiver(struct mcs_cb *mcs); -static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf); -static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf); -static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf); -static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len); -static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len); -static inline int mcs_setup_urbs(struct mcs_cb *mcs); -static inline int mcs_receive_start(struct mcs_cb *mcs); -static inline int mcs_find_endpoints(struct mcs_cb *mcs, - struct usb_host_endpoint *ep, int epnum); - -static int mcs_speed_change(struct mcs_cb *mcs); - -static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd); -static int mcs_net_close(struct net_device *netdev); -static int mcs_net_open(struct net_device *netdev); - -static void mcs_receive_irq(struct urb *urb); -static void mcs_send_irq(struct urb *urb); -static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb, - struct net_device *netdev); - -static int mcs_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void mcs_disconnect(struct usb_interface *intf); - -#endif /* _MCS7780_H */ diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c deleted file mode 100644 index 7beae147be11..000000000000 --- a/drivers/net/irda/nsc-ircc.c +++ /dev/null @@ -1,2410 +0,0 @@ -/********************************************************************* - * - * Filename: nsc-ircc.c - * Version: 1.0 - * Description: Driver for the NSC PC'108 and PC'338 IrDA chipsets - * Status: Stable. - * Author: Dag Brattli - * Created at: Sat Nov 7 21:43:15 1998 - * Modified at: Wed Mar 1 11:29:34 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli - * Copyright (c) 1998 Lichen Wang, - * Copyright (c) 1998 Actisys Corp., www.actisys.com - * Copyright (c) 2000-2004 Jean Tourrilhes - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - * Notice that all functions that needs to access the chip in _any_ - * way, must save BSR register on entry, and restore it on exit. - * It is _very_ important to follow this policy! - * - * __u8 bank; - * - * bank = inb(iobase+BSR); - * - * do_your_stuff_here(); - * - * outb(bank, iobase+BSR); - * - * If you find bugs in this file, its very likely that the same bug - * will also be in w83977af_ir.c since the implementations are quite - * similar. - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "nsc-ircc.h" - -#define CHIP_IO_EXTENT 8 -#define BROKEN_DONGLE_ID - -static char *driver_name = "nsc-ircc"; - -/* Power Management */ -#define NSC_IRCC_DRIVER_NAME "nsc-ircc" -static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state); -static int nsc_ircc_resume(struct platform_device *dev); - -static struct platform_driver nsc_ircc_driver = { - .suspend = nsc_ircc_suspend, - .resume = nsc_ircc_resume, - .driver = { - .name = NSC_IRCC_DRIVER_NAME, - }, -}; - -/* Module parameters */ -static int qos_mtt_bits = 0x07; /* 1 ms or more */ -static int dongle_id; - -/* Use BIOS settions by default, but user may supply module parameters */ -static unsigned int io[] = { ~0, ~0, ~0, ~0, ~0 }; -static unsigned int irq[] = { 0, 0, 0, 0, 0 }; -static unsigned int dma[] = { 0, 0, 0, 0, 0 }; - -static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info); -#ifdef CONFIG_PNP -static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id); -#endif - -/* These are the known NSC chips */ -static nsc_chip_t chips[] = { -/* Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */ - { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, - nsc_ircc_probe_108, nsc_ircc_init_108 }, - { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8, - nsc_ircc_probe_338, nsc_ircc_init_338 }, - /* Contributed by Steffen Pingel - IBM X40 */ - { "PC8738x", { 0x164e, 0x4e, 0x2e }, 0x20, 0xf4, 0xff, - nsc_ircc_probe_39x, nsc_ircc_init_39x }, - /* Contributed by Jan Frey - IBM A30/A31 */ - { "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff, - nsc_ircc_probe_39x, nsc_ircc_init_39x }, - /* IBM ThinkPads using PC8738x (T60/X60/Z60) */ - { "IBM-PC8738x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff, - nsc_ircc_probe_39x, nsc_ircc_init_39x }, - /* IBM ThinkPads using PC8394T (T43/R52/?) */ - { "IBM-PC8394T", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf9, 0xff, - nsc_ircc_probe_39x, nsc_ircc_init_39x }, - { NULL } -}; - -static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL, NULL }; - -static char *dongle_types[] = { - "Differential serial interface", - "Differential serial interface", - "Reserved", - "Reserved", - "Sharp RY5HD01", - "Reserved", - "Single-ended serial interface", - "Consumer-IR only", - "HP HSDL-2300, HP HSDL-3600/HSDL-3610", - "IBM31T1100 or Temic TFDS6000/TFDS6500", - "Reserved", - "Reserved", - "HP HSDL-1100/HSDL-2100", - "HP HSDL-1100/HSDL-2100", - "Supports SIR Mode only", - "No dongle connected", -}; - -/* PNP probing */ -static chipio_t pnp_info; -static const struct pnp_device_id nsc_ircc_pnp_table[] = { - { .id = "NSC6001", .driver_data = 0 }, - { .id = "HWPC224", .driver_data = 0 }, - { .id = "IBM0071", .driver_data = NSC_FORCE_DONGLE_TYPE9 }, - { } -}; - -MODULE_DEVICE_TABLE(pnp, nsc_ircc_pnp_table); - -static struct pnp_driver nsc_ircc_pnp_driver = { -#ifdef CONFIG_PNP - .name = "nsc-ircc", - .id_table = nsc_ircc_pnp_table, - .probe = nsc_ircc_pnp_probe, -#endif -}; - -/* Some prototypes */ -static int nsc_ircc_open(chipio_t *info); -static int nsc_ircc_close(struct nsc_ircc_cb *self); -static int nsc_ircc_setup(chipio_t *info); -static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self); -static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self); -static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase); -static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev); -static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev); -static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size); -static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase); -static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud); -static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self); -static int nsc_ircc_read_dongle_id (int iobase); -static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id); - -static int nsc_ircc_net_open(struct net_device *dev); -static int nsc_ircc_net_close(struct net_device *dev); -static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); - -/* Globals */ -static int pnp_registered; -static int pnp_succeeded; - -/* - * Function nsc_ircc_init () - * - * Initialize chip. Just try to find out how many chips we are dealing with - * and where they are - */ -static int __init nsc_ircc_init(void) -{ - chipio_t info; - nsc_chip_t *chip; - int ret; - int cfg_base; - int cfg, id; - int reg; - int i = 0; - - ret = platform_driver_register(&nsc_ircc_driver); - if (ret) { - net_err_ratelimited("%s, Can't register driver!\n", - driver_name); - return ret; - } - - /* Register with PnP subsystem to detect disable ports */ - ret = pnp_register_driver(&nsc_ircc_pnp_driver); - - if (!ret) - pnp_registered = 1; - - ret = -ENODEV; - - /* Probe for all the NSC chipsets we know about */ - for (chip = chips; chip->name ; chip++) { - pr_debug("%s(), Probing for %s ...\n", __func__, - chip->name); - - /* Try all config registers for this chip */ - for (cfg = 0; cfg < ARRAY_SIZE(chip->cfg); cfg++) { - cfg_base = chip->cfg[cfg]; - if (!cfg_base) - continue; - - /* Read index register */ - reg = inb(cfg_base); - if (reg == 0xff) { - pr_debug("%s() no chip at 0x%03x\n", - __func__, cfg_base); - continue; - } - - /* Read chip identification register */ - outb(chip->cid_index, cfg_base); - id = inb(cfg_base+1); - if ((id & chip->cid_mask) == chip->cid_value) { - pr_debug("%s() Found %s chip, revision=%d\n", - __func__, chip->name, - id & ~chip->cid_mask); - - /* - * If we found a correct PnP setting, - * we first try it. - */ - if (pnp_succeeded) { - memset(&info, 0, sizeof(chipio_t)); - info.cfg_base = cfg_base; - info.fir_base = pnp_info.fir_base; - info.dma = pnp_info.dma; - info.irq = pnp_info.irq; - - if (info.fir_base < 0x2000) { - net_info_ratelimited("%s, chip->init\n", - driver_name); - chip->init(chip, &info); - } else - chip->probe(chip, &info); - - if (nsc_ircc_open(&info) >= 0) - ret = 0; - } - - /* - * Opening based on PnP values failed. - * Let's fallback to user values, or probe - * the chip. - */ - if (ret) { - pr_debug("%s, PnP init failed\n", - driver_name); - memset(&info, 0, sizeof(chipio_t)); - info.cfg_base = cfg_base; - info.fir_base = io[i]; - info.dma = dma[i]; - info.irq = irq[i]; - - /* - * If the user supplies the base address, then - * we init the chip, if not we probe the values - * set by the BIOS - */ - if (io[i] < 0x2000) { - chip->init(chip, &info); - } else - chip->probe(chip, &info); - - if (nsc_ircc_open(&info) >= 0) - ret = 0; - } - i++; - } else { - pr_debug("%s(), Wrong chip id=0x%02x\n", - __func__, id); - } - } - } - - if (ret) { - platform_driver_unregister(&nsc_ircc_driver); - pnp_unregister_driver(&nsc_ircc_pnp_driver); - pnp_registered = 0; - } - - return ret; -} - -/* - * Function nsc_ircc_cleanup () - * - * Close all configured chips - * - */ -static void __exit nsc_ircc_cleanup(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dev_self); i++) { - if (dev_self[i]) - nsc_ircc_close(dev_self[i]); - } - - platform_driver_unregister(&nsc_ircc_driver); - - if (pnp_registered) - pnp_unregister_driver(&nsc_ircc_pnp_driver); - - pnp_registered = 0; -} - -static const struct net_device_ops nsc_ircc_sir_ops = { - .ndo_open = nsc_ircc_net_open, - .ndo_stop = nsc_ircc_net_close, - .ndo_start_xmit = nsc_ircc_hard_xmit_sir, - .ndo_do_ioctl = nsc_ircc_net_ioctl, -}; - -static const struct net_device_ops nsc_ircc_fir_ops = { - .ndo_open = nsc_ircc_net_open, - .ndo_stop = nsc_ircc_net_close, - .ndo_start_xmit = nsc_ircc_hard_xmit_fir, - .ndo_do_ioctl = nsc_ircc_net_ioctl, -}; - -/* - * Function nsc_ircc_open (iobase, irq) - * - * Open driver instance - * - */ -static int __init nsc_ircc_open(chipio_t *info) -{ - struct net_device *dev; - struct nsc_ircc_cb *self; - void *ret; - int err, chip_index; - - for (chip_index = 0; chip_index < ARRAY_SIZE(dev_self); chip_index++) { - if (!dev_self[chip_index]) - break; - } - - if (chip_index == ARRAY_SIZE(dev_self)) { - net_err_ratelimited("%s(), maximum number of supported chips reached!\n", - __func__); - return -ENOMEM; - } - - net_info_ratelimited("%s, Found chip at base=0x%03x\n", - driver_name, info->cfg_base); - - if ((nsc_ircc_setup(info)) == -1) - return -1; - - net_info_ratelimited("%s, driver loaded (Dag Brattli)\n", driver_name); - - dev = alloc_irdadev(sizeof(struct nsc_ircc_cb)); - if (dev == NULL) { - net_err_ratelimited("%s(), can't allocate memory for control block!\n", - __func__); - return -ENOMEM; - } - - self = netdev_priv(dev); - self->netdev = dev; - spin_lock_init(&self->lock); - - /* Need to store self somewhere */ - dev_self[chip_index] = self; - self->index = chip_index; - - /* Initialize IO */ - self->io.cfg_base = info->cfg_base; - self->io.fir_base = info->fir_base; - self->io.irq = info->irq; - self->io.fir_ext = CHIP_IO_EXTENT; - self->io.dma = info->dma; - self->io.fifo_size = 32; - - /* Reserve the ioports that we need */ - ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name); - if (!ret) { - net_warn_ratelimited("%s(), can't get iobase of 0x%03x\n", - __func__, self->io.fir_base); - err = -ENODEV; - goto out1; - } - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* The only value we must override it the baudrate */ - self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| - IR_115200|IR_576000|IR_1152000 |(IR_4000000 << 8); - - self->qos.min_turn_time.bits = qos_mtt_bits; - irda_qos_bits_to_value(&self->qos); - - /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ - self->rx_buff.truesize = 14384; - self->tx_buff.truesize = 14384; - - /* Allocate memory if needed */ - self->rx_buff.head = - dma_zalloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (self->rx_buff.head == NULL) { - err = -ENOMEM; - goto out2; - - } - - self->tx_buff.head = - dma_zalloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (self->tx_buff.head == NULL) { - err = -ENOMEM; - goto out3; - } - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Tx queue info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Override the network functions we need to use */ - dev->netdev_ops = &nsc_ircc_sir_ops; - - err = register_netdev(dev); - if (err) { - net_err_ratelimited("%s(), register_netdev() failed!\n", - __func__); - goto out4; - } - net_info_ratelimited("IrDA: Registered device %s\n", dev->name); - - /* Check if user has supplied a valid dongle id or not */ - if ((dongle_id <= 0) || - (dongle_id >= ARRAY_SIZE(dongle_types))) { - dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base); - - net_info_ratelimited("%s, Found dongle: %s\n", - driver_name, dongle_types[dongle_id]); - } else { - net_info_ratelimited("%s, Using dongle: %s\n", - driver_name, dongle_types[dongle_id]); - } - - self->io.dongle_id = dongle_id; - nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id); - - self->pldev = platform_device_register_simple(NSC_IRCC_DRIVER_NAME, - self->index, NULL, 0); - if (IS_ERR(self->pldev)) { - err = PTR_ERR(self->pldev); - goto out5; - } - platform_set_drvdata(self->pldev, self); - - return chip_index; - - out5: - unregister_netdev(dev); - out4: - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - out3: - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - out2: - release_region(self->io.fir_base, self->io.fir_ext); - out1: - free_netdev(dev); - dev_self[chip_index] = NULL; - return err; -} - -/* - * Function nsc_ircc_close (self) - * - * Close driver instance - * - */ -static int __exit nsc_ircc_close(struct nsc_ircc_cb *self) -{ - int iobase; - - IRDA_ASSERT(self != NULL, return -1;); - - iobase = self->io.fir_base; - - platform_device_unregister(self->pldev); - - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Release the PORT that this driver is using */ - pr_debug("%s(), Releasing Region %03x\n", - __func__, self->io.fir_base); - release_region(self->io.fir_base, self->io.fir_ext); - - if (self->tx_buff.head) - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - - if (self->rx_buff.head) - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - dev_self[self->index] = NULL; - free_netdev(self->netdev); - - return 0; -} - -/* - * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma) - * - * Initialize the NSC '108 chip - * - */ -static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - __u8 temp=0; - - outb(2, cfg_base); /* Mode Control Register (MCTL) */ - outb(0x00, cfg_base+1); /* Disable device */ - - /* Base Address and Interrupt Control Register (BAIC) */ - outb(CFG_108_BAIC, cfg_base); - switch (info->fir_base) { - case 0x3e8: outb(0x14, cfg_base+1); break; - case 0x2e8: outb(0x15, cfg_base+1); break; - case 0x3f8: outb(0x16, cfg_base+1); break; - case 0x2f8: outb(0x17, cfg_base+1); break; - default: net_err_ratelimited("%s(), invalid base_address\n", __func__); - } - - /* Control Signal Routing Register (CSRT) */ - switch (info->irq) { - case 3: temp = 0x01; break; - case 4: temp = 0x02; break; - case 5: temp = 0x03; break; - case 7: temp = 0x04; break; - case 9: temp = 0x05; break; - case 11: temp = 0x06; break; - case 15: temp = 0x07; break; - default: net_err_ratelimited("%s(), invalid irq\n", __func__); - } - outb(CFG_108_CSRT, cfg_base); - - switch (info->dma) { - case 0: outb(0x08+temp, cfg_base+1); break; - case 1: outb(0x10+temp, cfg_base+1); break; - case 3: outb(0x18+temp, cfg_base+1); break; - default: net_err_ratelimited("%s(), invalid dma\n", __func__); - } - - outb(CFG_108_MCTL, cfg_base); /* Mode Control Register (MCTL) */ - outb(0x03, cfg_base+1); /* Enable device */ - - return 0; -} - -/* - * Function nsc_ircc_probe_108 (chip, info) - * - * - * - */ -static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int reg; - - /* Read address and interrupt control register (BAIC) */ - outb(CFG_108_BAIC, cfg_base); - reg = inb(cfg_base+1); - - switch (reg & 0x03) { - case 0: - info->fir_base = 0x3e8; - break; - case 1: - info->fir_base = 0x2e8; - break; - case 2: - info->fir_base = 0x3f8; - break; - case 3: - info->fir_base = 0x2f8; - break; - } - info->sir_base = info->fir_base; - pr_debug("%s(), probing fir_base=0x%03x\n", __func__, - info->fir_base); - - /* Read control signals routing register (CSRT) */ - outb(CFG_108_CSRT, cfg_base); - reg = inb(cfg_base+1); - - switch (reg & 0x07) { - case 0: - info->irq = -1; - break; - case 1: - info->irq = 3; - break; - case 2: - info->irq = 4; - break; - case 3: - info->irq = 5; - break; - case 4: - info->irq = 7; - break; - case 5: - info->irq = 9; - break; - case 6: - info->irq = 11; - break; - case 7: - info->irq = 15; - break; - } - pr_debug("%s(), probing irq=%d\n", __func__, info->irq); - - /* Currently we only read Rx DMA but it will also be used for Tx */ - switch ((reg >> 3) & 0x03) { - case 0: - info->dma = -1; - break; - case 1: - info->dma = 0; - break; - case 2: - info->dma = 1; - break; - case 3: - info->dma = 3; - break; - } - pr_debug("%s(), probing dma=%d\n", __func__, info->dma); - - /* Read mode control register (MCTL) */ - outb(CFG_108_MCTL, cfg_base); - reg = inb(cfg_base+1); - - info->enabled = reg & 0x01; - info->suspended = !((reg >> 1) & 0x01); - - return 0; -} - -/* - * Function nsc_ircc_init_338 (chip, info) - * - * Initialize the NSC '338 chip. Remember that the 87338 needs two - * consecutive writes to the data registers while CPU interrupts are - * disabled. The 97338 does not require this, but shouldn't be any - * harm if we do it anyway. - */ -static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info) -{ - /* No init yet */ - - return 0; -} - -/* - * Function nsc_ircc_probe_338 (chip, info) - * - * - * - */ -static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int reg, com = 0; - int pnp; - - /* Read function enable register (FER) */ - outb(CFG_338_FER, cfg_base); - reg = inb(cfg_base+1); - - info->enabled = (reg >> 2) & 0x01; - - /* Check if we are in Legacy or PnP mode */ - outb(CFG_338_PNP0, cfg_base); - reg = inb(cfg_base+1); - - pnp = (reg >> 3) & 0x01; - if (pnp) { - pr_debug("(), Chip is in PnP mode\n"); - outb(0x46, cfg_base); - reg = (inb(cfg_base+1) & 0xfe) << 2; - - outb(0x47, cfg_base); - reg |= ((inb(cfg_base+1) & 0xfc) << 8); - - info->fir_base = reg; - } else { - /* Read function address register (FAR) */ - outb(CFG_338_FAR, cfg_base); - reg = inb(cfg_base+1); - - switch ((reg >> 4) & 0x03) { - case 0: - info->fir_base = 0x3f8; - break; - case 1: - info->fir_base = 0x2f8; - break; - case 2: - com = 3; - break; - case 3: - com = 4; - break; - } - - if (com) { - switch ((reg >> 6) & 0x03) { - case 0: - if (com == 3) - info->fir_base = 0x3e8; - else - info->fir_base = 0x2e8; - break; - case 1: - if (com == 3) - info->fir_base = 0x338; - else - info->fir_base = 0x238; - break; - case 2: - if (com == 3) - info->fir_base = 0x2e8; - else - info->fir_base = 0x2e0; - break; - case 3: - if (com == 3) - info->fir_base = 0x220; - else - info->fir_base = 0x228; - break; - } - } - } - info->sir_base = info->fir_base; - - /* Read PnP register 1 (PNP1) */ - outb(CFG_338_PNP1, cfg_base); - reg = inb(cfg_base+1); - - info->irq = reg >> 4; - - /* Read PnP register 3 (PNP3) */ - outb(CFG_338_PNP3, cfg_base); - reg = inb(cfg_base+1); - - info->dma = (reg & 0x07) - 1; - - /* Read power and test register (PTR) */ - outb(CFG_338_PTR, cfg_base); - reg = inb(cfg_base+1); - - info->suspended = reg & 0x01; - - return 0; -} - - -/* - * Function nsc_ircc_init_39x (chip, info) - * - * Now that we know it's a '39x (see probe below), we need to - * configure it so we can use it. - * - * The NSC '338 chip is a Super I/O chip with a "bank" architecture, - * the configuration of the different functionality (serial, parallel, - * floppy...) are each in a different bank (Logical Device Number). - * The base address, irq and dma configuration registers are common - * to all functionalities (index 0x30 to 0x7F). - * There is only one configuration register specific to the - * serial port, CFG_39X_SPC. - * JeanII - * - * Note : this code was written by Jan Frey - */ -static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int enabled; - - /* User is sure about his config... accept it. */ - pr_debug("%s(): nsc_ircc_init_39x (user settings): io=0x%04x, irq=%d, dma=%d\n", - __func__, info->fir_base, info->irq, info->dma); - - /* Access bank for SP2 */ - outb(CFG_39X_LDN, cfg_base); - outb(0x02, cfg_base+1); - - /* Configure SP2 */ - - /* We want to enable the device if not enabled */ - outb(CFG_39X_ACT, cfg_base); - enabled = inb(cfg_base+1) & 0x01; - - if (!enabled) { - /* Enable the device */ - outb(CFG_39X_SIOCF1, cfg_base); - outb(0x01, cfg_base+1); - /* May want to update info->enabled. Jean II */ - } - - /* Enable UART bank switching (bit 7) ; Sets the chip to normal - * power mode (wake up from sleep mode) (bit 1) */ - outb(CFG_39X_SPC, cfg_base); - outb(0x82, cfg_base+1); - - return 0; -} - -/* - * Function nsc_ircc_probe_39x (chip, info) - * - * Test if we really have a '39x chip at the given address - * - * Note : this code was written by Jan Frey - */ -static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int reg1, reg2, irq, irqt, dma1, dma2; - int enabled, susp; - - pr_debug("%s(), nsc_ircc_probe_39x, base=%d\n", - __func__, cfg_base); - - /* This function should be executed with irq off to avoid - * another driver messing with the Super I/O bank - Jean II */ - - /* Access bank for SP2 */ - outb(CFG_39X_LDN, cfg_base); - outb(0x02, cfg_base+1); - - /* Read infos about SP2 ; store in info struct */ - outb(CFG_39X_BASEH, cfg_base); - reg1 = inb(cfg_base+1); - outb(CFG_39X_BASEL, cfg_base); - reg2 = inb(cfg_base+1); - info->fir_base = (reg1 << 8) | reg2; - - outb(CFG_39X_IRQNUM, cfg_base); - irq = inb(cfg_base+1); - outb(CFG_39X_IRQSEL, cfg_base); - irqt = inb(cfg_base+1); - info->irq = irq; - - outb(CFG_39X_DMA0, cfg_base); - dma1 = inb(cfg_base+1); - outb(CFG_39X_DMA1, cfg_base); - dma2 = inb(cfg_base+1); - info->dma = dma1 -1; - - outb(CFG_39X_ACT, cfg_base); - info->enabled = enabled = inb(cfg_base+1) & 0x01; - - outb(CFG_39X_SPC, cfg_base); - susp = 1 - ((inb(cfg_base+1) & 0x02) >> 1); - - pr_debug("%s(): io=0x%02x%02x, irq=%d (type %d), rxdma=%d, txdma=%d, enabled=%d (suspended=%d)\n", - __func__, reg1, reg2, irq, irqt, dma1, dma2, enabled, susp); - - /* Configure SP2 */ - - /* We want to enable the device if not enabled */ - outb(CFG_39X_ACT, cfg_base); - enabled = inb(cfg_base+1) & 0x01; - - if (!enabled) { - /* Enable the device */ - outb(CFG_39X_SIOCF1, cfg_base); - outb(0x01, cfg_base+1); - /* May want to update info->enabled. Jean II */ - } - - /* Enable UART bank switching (bit 7) ; Sets the chip to normal - * power mode (wake up from sleep mode) (bit 1) */ - outb(CFG_39X_SPC, cfg_base); - outb(0x82, cfg_base+1); - - return 0; -} - -#ifdef CONFIG_PNP -/* PNP probing */ -static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id) -{ - memset(&pnp_info, 0, sizeof(chipio_t)); - pnp_info.irq = -1; - pnp_info.dma = -1; - pnp_succeeded = 1; - - if (id->driver_data & NSC_FORCE_DONGLE_TYPE9) - dongle_id = 0x9; - - /* There doesn't seem to be any way of getting the cfg_base. - * On my box, cfg_base is in the PnP descriptor of the - * motherboard. Oh well... Jean II */ - - if (pnp_port_valid(dev, 0) && - !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) - pnp_info.fir_base = pnp_port_start(dev, 0); - - if (pnp_irq_valid(dev, 0) && - !(pnp_irq_flags(dev, 0) & IORESOURCE_DISABLED)) - pnp_info.irq = pnp_irq(dev, 0); - - if (pnp_dma_valid(dev, 0) && - !(pnp_dma_flags(dev, 0) & IORESOURCE_DISABLED)) - pnp_info.dma = pnp_dma(dev, 0); - - pr_debug("%s() : From PnP, found firbase 0x%03X ; irq %d ; dma %d.\n", - __func__, pnp_info.fir_base, pnp_info.irq, pnp_info.dma); - - if((pnp_info.fir_base == 0) || - (pnp_info.irq == -1) || (pnp_info.dma == -1)) { - /* Returning an error will disable the device. Yuck ! */ - //return -EINVAL; - pnp_succeeded = 0; - } - - return 0; -} -#endif - -/* - * Function nsc_ircc_setup (info) - * - * Returns non-negative on success. - * - */ -static int nsc_ircc_setup(chipio_t *info) -{ - int version; - int iobase = info->fir_base; - - /* Read the Module ID */ - switch_bank(iobase, BANK3); - version = inb(iobase+MID); - - pr_debug("%s() Driver %s Found chip version %02x\n", - __func__, driver_name, version); - - /* Should be 0x2? */ - if (0x20 != (version & 0xf0)) { - net_err_ratelimited("%s, Wrong chip version %02x\n", - driver_name, version); - return -1; - } - - /* Switch to advanced mode */ - switch_bank(iobase, BANK2); - outb(ECR1_EXT_SL, iobase+ECR1); - switch_bank(iobase, BANK0); - - /* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */ - switch_bank(iobase, BANK0); - outb(FCR_RXTH|FCR_TXTH|FCR_TXSR|FCR_RXSR|FCR_FIFO_EN, iobase+FCR); - - outb(0x03, iobase+LCR); /* 8 bit word length */ - outb(MCR_SIR, iobase+MCR); /* Start at SIR-mode, also clears LSR*/ - - /* Set FIFO size to 32 */ - switch_bank(iobase, BANK2); - outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2); - - /* IRCR2: FEND_MD is not set */ - switch_bank(iobase, BANK5); - outb(0x02, iobase+4); - - /* Make sure that some defaults are OK */ - switch_bank(iobase, BANK6); - outb(0x20, iobase+0); /* Set 32 bits FIR CRC */ - outb(0x0a, iobase+1); /* Set MIR pulse width */ - outb(0x0d, iobase+2); /* Set SIR pulse width to 1.6us */ - outb(0x2a, iobase+4); /* Set beginning frag, and preamble length */ - - /* Enable receive interrupts */ - switch_bank(iobase, BANK0); - outb(IER_RXHDL_IE, iobase+IER); - - return 0; -} - -/* - * Function nsc_ircc_read_dongle_id (void) - * - * Try to read dongle identification. This procedure needs to be executed - * once after power-on/reset. It also needs to be used whenever you suspect - * that the user may have plugged/unplugged the IrDA Dongle. - */ -static int nsc_ircc_read_dongle_id (int iobase) -{ - int dongle_id; - __u8 bank; - - bank = inb(iobase+BSR); - - /* Select Bank 7 */ - switch_bank(iobase, BANK7); - - /* IRCFG4: IRSL0_DS and IRSL21_DS are cleared */ - outb(0x00, iobase+7); - - /* ID0, 1, and 2 are pulled up/down very slowly */ - udelay(50); - - /* IRCFG1: read the ID bits */ - dongle_id = inb(iobase+4) & 0x0f; - -#ifdef BROKEN_DONGLE_ID - if (dongle_id == 0x0a) - dongle_id = 0x09; -#endif - /* Go back to bank 0 before returning */ - switch_bank(iobase, BANK0); - - outb(bank, iobase+BSR); - - return dongle_id; -} - -/* - * Function nsc_ircc_init_dongle_interface (iobase, dongle_id) - * - * This function initializes the dongle for the transceiver that is - * used. This procedure needs to be executed once after - * power-on/reset. It also needs to be used whenever you suspect that - * the dongle is changed. - */ -static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id) -{ - int bank; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Select Bank 7 */ - switch_bank(iobase, BANK7); - - /* IRCFG4: set according to dongle_id */ - switch (dongle_id) { - case 0x00: /* same as */ - case 0x01: /* Differential serial interface */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x02: /* same as */ - case 0x03: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x04: /* Sharp RY5HD01 */ - break; - case 0x05: /* Reserved, but this is what the Thinkpad reports */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x06: /* Single-ended serial interface */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x07: /* Consumer-IR only */ - pr_debug("%s(), %s is not for IrDA mode\n", - __func__, dongle_types[dongle_id]); - break; - case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - pr_debug("%s(), %s\n", - __func__, dongle_types[dongle_id]); - break; - case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - outb(0x28, iobase+7); /* Set irsl[0-2] as output */ - break; - case 0x0A: /* same as */ - case 0x0B: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x0C: /* same as */ - case 0x0D: /* HP HSDL-1100/HSDL-2100 */ - /* - * Set irsl0 as input, irsl[1-2] as output, and separate - * inputs are used for SIR and MIR/FIR - */ - outb(0x48, iobase+7); - break; - case 0x0E: /* Supports SIR Mode only */ - outb(0x28, iobase+7); /* Set irsl[0-2] as output */ - break; - case 0x0F: /* No dongle connected */ - pr_debug("%s(), %s\n", - __func__, dongle_types[dongle_id]); - - switch_bank(iobase, BANK0); - outb(0x62, iobase+MCR); - break; - default: - pr_debug("%s(), invalid dongle_id %#x", - __func__, dongle_id); - } - - /* IRCFG1: IRSL1 and 2 are set to IrDA mode */ - outb(0x00, iobase+4); - - /* Restore bank register */ - outb(bank, iobase+BSR); - -} /* set_up_dongle_interface */ - -/* - * Function nsc_ircc_change_dongle_speed (iobase, speed, dongle_id) - * - * Change speed of the attach dongle - * - */ -static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id) -{ - __u8 bank; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Select Bank 7 */ - switch_bank(iobase, BANK7); - - /* IRCFG1: set according to dongle_id */ - switch (dongle_id) { - case 0x00: /* same as */ - case 0x01: /* Differential serial interface */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x02: /* same as */ - case 0x03: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x04: /* Sharp RY5HD01 */ - break; - case 0x05: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x06: /* Single-ended serial interface */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x07: /* Consumer-IR only */ - pr_debug("%s(), %s is not for IrDA mode\n", - __func__, dongle_types[dongle_id]); - break; - case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - pr_debug("%s(), %s\n", - __func__, dongle_types[dongle_id]); - outb(0x00, iobase+4); - if (speed > 115200) - outb(0x01, iobase+4); - break; - case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - outb(0x01, iobase+4); - - if (speed == 4000000) { - /* There was a cli() there, but we now are already - * under spin_lock_irqsave() - JeanII */ - outb(0x81, iobase+4); - outb(0x80, iobase+4); - } else - outb(0x00, iobase+4); - break; - case 0x0A: /* same as */ - case 0x0B: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x0C: /* same as */ - case 0x0D: /* HP HSDL-1100/HSDL-2100 */ - break; - case 0x0E: /* Supports SIR Mode only */ - break; - case 0x0F: /* No dongle connected */ - pr_debug("%s(), %s is not for IrDA mode\n", - __func__, dongle_types[dongle_id]); - - switch_bank(iobase, BANK0); - outb(0x62, iobase+MCR); - break; - default: - pr_debug("%s(), invalid data_rate\n", __func__); - } - /* Restore bank register */ - outb(bank, iobase+BSR); -} - -/* - * Function nsc_ircc_change_speed (self, baud) - * - * Change the speed of the device - * - * This function *must* be called with irq off and spin-lock. - */ -static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed) -{ - struct net_device *dev; - __u8 mcr = MCR_SIR; - int iobase; - __u8 bank; - __u8 ier; /* Interrupt enable register */ - - pr_debug("%s(), speed=%d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return 0;); - - dev = self->netdev; - iobase = self->io.fir_base; - - /* Update accounting for new speed */ - self->io.speed = speed; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Disable interrupts */ - switch_bank(iobase, BANK0); - outb(0, iobase+IER); - - /* Select Bank 2 */ - switch_bank(iobase, BANK2); - - outb(0x00, iobase+BGDH); - switch (speed) { - case 9600: outb(0x0c, iobase+BGDL); break; - case 19200: outb(0x06, iobase+BGDL); break; - case 38400: outb(0x03, iobase+BGDL); break; - case 57600: outb(0x02, iobase+BGDL); break; - case 115200: outb(0x01, iobase+BGDL); break; - case 576000: - switch_bank(iobase, BANK5); - - /* IRCR2: MDRS is set */ - outb(inb(iobase+4) | 0x04, iobase+4); - - mcr = MCR_MIR; - pr_debug("%s(), handling baud of 576000\n", __func__); - break; - case 1152000: - mcr = MCR_MIR; - pr_debug("%s(), handling baud of 1152000\n", __func__); - break; - case 4000000: - mcr = MCR_FIR; - pr_debug("%s(), handling baud of 4000000\n", __func__); - break; - default: - mcr = MCR_FIR; - pr_debug("%s(), unknown baud rate of %d\n", - __func__, speed); - break; - } - - /* Set appropriate speed mode */ - switch_bank(iobase, BANK0); - outb(mcr | MCR_TX_DFR, iobase+MCR); - - /* Give some hits to the transceiver */ - nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); - - /* Set FIFO threshold to TX17, RX16 */ - switch_bank(iobase, BANK0); - outb(0x00, iobase+FCR); - outb(FCR_FIFO_EN, iobase+FCR); - outb(FCR_RXTH| /* Set Rx FIFO threshold */ - FCR_TXTH| /* Set Tx FIFO threshold */ - FCR_TXSR| /* Reset Tx FIFO */ - FCR_RXSR| /* Reset Rx FIFO */ - FCR_FIFO_EN, /* Enable FIFOs */ - iobase+FCR); - - /* Set FIFO size to 32 */ - switch_bank(iobase, BANK2); - outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2); - - /* Enable some interrupts so we can receive frames */ - switch_bank(iobase, BANK0); - if (speed > 115200) { - /* Install FIR xmit handler */ - dev->netdev_ops = &nsc_ircc_fir_ops; - ier = IER_SFIF_IE; - nsc_ircc_dma_receive(self); - } else { - /* Install SIR xmit handler */ - dev->netdev_ops = &nsc_ircc_sir_ops; - ier = IER_RXHDL_IE; - } - /* Set our current interrupt mask */ - outb(ier, iobase+IER); - - /* Restore BSR */ - outb(bank, iobase+BSR); - - /* Make sure interrupt handlers keep the proper interrupt mask */ - return ier; -} - -/* - * Function nsc_ircc_hard_xmit (skb, dev) - * - * Transmit the frame! - * - */ -static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev) -{ - struct nsc_ircc_cb *self; - unsigned long flags; - int iobase; - __s32 speed; - __u8 bank; - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - - iobase = self->io.fir_base; - - netif_stop_queue(dev); - - /* Make sure tests *& speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame. */ - if (!skb->len) { - /* If we just sent a frame, we get called before - * the last bytes get out (because of the SIR FIFO). - * If this is the case, let interrupt handler change - * the speed itself... Jean II */ - if (self->io.direction == IO_RECV) { - nsc_ircc_change_speed(self, speed); - /* TODO : For SIR->SIR, the next packet - * may get corrupted - Jean II */ - netif_wake_queue(dev); - } else { - self->new_speed = speed; - /* Queue will be restarted after speed change - * to make sure packets gets through the - * proper xmit handler - Jean II */ - } - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - - /* Save current bank */ - bank = inb(iobase+BSR); - - self->tx_buff.data = self->tx_buff.head; - - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - dev->stats.tx_bytes += self->tx_buff.len; - - /* Add interrupt on tx low level (will fire immediately) */ - switch_bank(iobase, BANK0); - outb(IER_TXLDL_IE, iobase+IER); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev) -{ - struct nsc_ircc_cb *self; - unsigned long flags; - int iobase; - __s32 speed; - __u8 bank; - int mtt, diff; - - self = netdev_priv(dev); - iobase = self->io.fir_base; - - netif_stop_queue(dev); - - /* Make sure tests *& speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame. */ - if (!skb->len) { - /* If we are currently transmitting, defer to - * interrupt handler. - Jean II */ - if(self->tx_fifo.len == 0) { - nsc_ircc_change_speed(self, speed); - netif_wake_queue(dev); - } else { - self->new_speed = speed; - /* Keep queue stopped : - * the speed change operation may change the - * xmit handler, and we want to make sure - * the next packet get through the proper - * Tx path, so block the Tx queue until - * the speed change has been done. - * Jean II */ - } - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else { - /* Change speed after current frame */ - self->new_speed = speed; - } - } - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Register and copy this frame to DMA memory */ - self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; - self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; - self->tx_fifo.tail += skb->len; - - dev->stats.tx_bytes += skb->len; - - skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, - skb->len); - self->tx_fifo.len++; - self->tx_fifo.free++; - - /* Start transmit only if there is currently no transmit going on */ - if (self->tx_fifo.len == 1) { - /* Check if we must wait the min turn time or not */ - mtt = irda_get_mtt(skb); - if (mtt) { - /* Check how much time we have used already */ - diff = ktime_us_delta(ktime_get(), self->stamp); - - /* Check if the mtt is larger than the time we have - * already used by all the protocol processing - */ - if (mtt > diff) { - mtt -= diff; - - /* - * Use timer if delay larger than 125 us, and - * use udelay for smaller values which should - * be acceptable - */ - if (mtt > 125) { - /* Adjust for timer resolution */ - mtt = mtt / 125; - - /* Setup timer */ - switch_bank(iobase, BANK4); - outb(mtt & 0xff, iobase+TMRL); - outb((mtt >> 8) & 0x0f, iobase+TMRH); - - /* Start timer */ - outb(IRCR1_TMR_EN, iobase+IRCR1); - self->io.direction = IO_XMIT; - - /* Enable timer interrupt */ - switch_bank(iobase, BANK0); - outb(IER_TMR_IE, iobase+IER); - - /* Timer will take care of the rest */ - goto out; - } else - udelay(mtt); - } - } - /* Enable DMA interrupt */ - switch_bank(iobase, BANK0); - outb(IER_DMA_IE, iobase+IER); - - /* Transmit frame */ - nsc_ircc_dma_xmit(self, iobase); - } - out: - /* Not busy transmitting anymore if window is not full, - * and if we don't need to change speed */ - if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) - netif_wake_queue(self->netdev); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* - * Function nsc_ircc_dma_xmit (self, iobase) - * - * Transmit data using DMA - * - */ -static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase) -{ - int bsr; - - /* Save current bank */ - bsr = inb(iobase+BSR); - - /* Disable DMA */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); - - self->io.direction = IO_XMIT; - - /* Choose transmit DMA channel */ - switch_bank(iobase, BANK2); - outb(ECR1_DMASWP|ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1); - - irda_setup_dma(self->io.dma, - ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - - self->tx_buff.head) + self->tx_buff_dma, - self->tx_fifo.queue[self->tx_fifo.ptr].len, - DMA_TX_MODE); - - /* Enable DMA and SIR interaction pulse */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR)|MCR_TX_DFR|MCR_DMA_EN|MCR_IR_PLS, iobase+MCR); - - /* Restore bank register */ - outb(bsr, iobase+BSR); -} - -/* - * Function nsc_ircc_pio_xmit (self, iobase) - * - * Transmit data using PIO. Returns the number of bytes that actually - * got transferred - * - */ -static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size) -{ - int actual = 0; - __u8 bank; - - /* Save current bank */ - bank = inb(iobase+BSR); - - switch_bank(iobase, BANK0); - if (!(inb_p(iobase+LSR) & LSR_TXEMP)) { - pr_debug("%s(), warning, FIFO not empty yet!\n", - __func__); - - /* FIFO may still be filled to the Tx interrupt threshold */ - fifo_size -= 17; - } - - /* Fill FIFO with current frame */ - while ((fifo_size-- > 0) && (actual < len)) { - /* Transmit next byte */ - outb(buf[actual++], iobase+TXD); - } - - pr_debug("%s(), fifo_size %d ; %d sent of %d\n", - __func__, fifo_size, actual, len); - - /* Restore bank */ - outb(bank, iobase+BSR); - - return actual; -} - -/* - * Function nsc_ircc_dma_xmit_complete (self) - * - * The transfer of a frame in finished. This function will only be called - * by the interrupt handler - * - */ -static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self) -{ - int iobase; - __u8 bank; - int ret = TRUE; - - iobase = self->io.fir_base; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Disable DMA */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); - - /* Check for underrun! */ - if (inb(iobase+ASCR) & ASCR_TXUR) { - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - - /* Clear bit, by writing 1 into it */ - outb(ASCR_TXUR, iobase+ASCR); - } else { - self->netdev->stats.tx_packets++; - } - - /* Finished with this frame, so prepare for next */ - self->tx_fifo.ptr++; - self->tx_fifo.len--; - - /* Any frames to be sent back-to-back? */ - if (self->tx_fifo.len) { - nsc_ircc_dma_xmit(self, iobase); - - /* Not finished yet! */ - ret = FALSE; - } else { - /* Reset Tx FIFO info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - } - - /* Make sure we have room for more frames and - * that we don't need to change speed */ - if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) { - /* Not busy transmitting anymore */ - /* Tell the network layer, that we can accept more frames */ - netif_wake_queue(self->netdev); - } - - /* Restore bank */ - outb(bank, iobase+BSR); - - return ret; -} - -/* - * Function nsc_ircc_dma_receive (self) - * - * Get ready for receiving a frame. The device will initiate a DMA - * if it starts to receive a frame. - * - */ -static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self) -{ - int iobase; - __u8 bsr; - - iobase = self->io.fir_base; - - /* Reset Tx FIFO info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Save current bank */ - bsr = inb(iobase+BSR); - - /* Disable DMA */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); - - /* Choose DMA Rx, DMA Fairness, and Advanced mode */ - switch_bank(iobase, BANK2); - outb(ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1); - - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Rx FIFO. This will also flush the ST_FIFO */ - switch_bank(iobase, BANK0); - outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR); - - self->st_fifo.len = self->st_fifo.pending_bytes = 0; - self->st_fifo.tail = self->st_fifo.head = 0; - - irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, - DMA_RX_MODE); - - /* Enable DMA */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR)|MCR_DMA_EN, iobase+MCR); - - /* Restore bank register */ - outb(bsr, iobase+BSR); - - return 0; -} - -/* - * Function nsc_ircc_dma_receive_complete (self) - * - * Finished with receiving frames - * - * - */ -static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) -{ - struct st_fifo *st_fifo; - struct sk_buff *skb; - __u8 status; - __u8 bank; - int len; - - st_fifo = &self->st_fifo; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Read all entries in status FIFO */ - switch_bank(iobase, BANK5); - while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) { - /* We must empty the status FIFO no matter what */ - len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8); - - if (st_fifo->tail >= MAX_RX_WINDOW) { - pr_debug("%s(), window is full!\n", __func__); - continue; - } - - st_fifo->entries[st_fifo->tail].status = status; - st_fifo->entries[st_fifo->tail].len = len; - st_fifo->pending_bytes += len; - st_fifo->tail++; - st_fifo->len++; - } - /* Try to process all entries in status FIFO */ - while (st_fifo->len > 0) { - /* Get first entry */ - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->pending_bytes -= len; - st_fifo->head++; - st_fifo->len--; - - /* Check for errors */ - if (status & FRM_ST_ERR_MSK) { - if (status & FRM_ST_LOST_FR) { - /* Add number of lost frames to stats */ - self->netdev->stats.rx_errors += len; - } else { - /* Skip frame */ - self->netdev->stats.rx_errors++; - - self->rx_buff.data += len; - - if (status & FRM_ST_MAX_LEN) - self->netdev->stats.rx_length_errors++; - - if (status & FRM_ST_PHY_ERR) - self->netdev->stats.rx_frame_errors++; - - if (status & FRM_ST_BAD_CRC) - self->netdev->stats.rx_crc_errors++; - } - /* The errors below can be reported in both cases */ - if (status & FRM_ST_OVR1) - self->netdev->stats.rx_fifo_errors++; - - if (status & FRM_ST_OVR2) - self->netdev->stats.rx_fifo_errors++; - } else { - /* - * First we must make sure that the frame we - * want to deliver is all in main memory. If we - * cannot tell, then we check if the Rx FIFO is - * empty. If not then we will have to take a nap - * and try again later. - */ - if (st_fifo->pending_bytes < self->io.fifo_size) { - switch_bank(iobase, BANK0); - if (inb(iobase+LSR) & LSR_RXDA) { - /* Put this entry back in fifo */ - st_fifo->head--; - st_fifo->len++; - st_fifo->pending_bytes += len; - st_fifo->entries[st_fifo->head].status = status; - st_fifo->entries[st_fifo->head].len = len; - /* - * DMA not finished yet, so try again - * later, set timer value, resolution - * 125 us - */ - switch_bank(iobase, BANK4); - outb(0x02, iobase+TMRL); /* x 125 us */ - outb(0x00, iobase+TMRH); - - /* Start timer */ - outb(IRCR1_TMR_EN, iobase+IRCR1); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - return FALSE; /* I'll be back! */ - } - } - - /* - * Remember the time we received this frame, so we can - * reduce the min turn time a bit since we will know - * how much time we have used for protocol processing - */ - self->stamp = ktime_get(); - - skb = dev_alloc_skb(len+1); - if (skb == NULL) { - self->netdev->stats.rx_dropped++; - - /* Restore bank register */ - outb(bank, iobase+BSR); - - return FALSE; - } - - /* Make sure IP header gets aligned */ - skb_reserve(skb, 1); - - /* Copy frame without CRC */ - if (self->io.speed < 4000000) { - skb_put(skb, len-2); - skb_copy_to_linear_data(skb, - self->rx_buff.data, - len - 2); - } else { - skb_put(skb, len-4); - skb_copy_to_linear_data(skb, - self->rx_buff.data, - len - 4); - } - - /* Move to next frame */ - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - } - } - /* Restore bank register */ - outb(bank, iobase+BSR); - - return TRUE; -} - -/* - * Function nsc_ircc_pio_receive (self) - * - * Receive all data in receiver FIFO - * - */ -static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self) -{ - __u8 byte; - int iobase; - - iobase = self->io.fir_base; - - /* Receive all characters in Rx FIFO */ - do { - byte = inb(iobase+RXD); - async_unwrap_char(self->netdev, &self->netdev->stats, - &self->rx_buff, byte); - } while (inb(iobase+LSR) & LSR_RXDA); /* Data available */ -} - -/* - * Function nsc_ircc_sir_interrupt (self, eir) - * - * Handle SIR interrupt - * - */ -static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir) -{ - int actual; - - /* Check if transmit FIFO is low on data */ - if (eir & EIR_TXLDL_EV) { - /* Write data left in transmit buffer */ - actual = nsc_ircc_pio_write(self->io.fir_base, - self->tx_buff.data, - self->tx_buff.len, - self->io.fifo_size); - self->tx_buff.data += actual; - self->tx_buff.len -= actual; - - self->io.direction = IO_XMIT; - - /* Check if finished */ - if (self->tx_buff.len > 0) - self->ier = IER_TXLDL_IE; - else { - - self->netdev->stats.tx_packets++; - netif_wake_queue(self->netdev); - self->ier = IER_TXEMP_IE; - } - - } - /* Check if transmission has completed */ - if (eir & EIR_TXEMP_EV) { - /* Turn around and get ready to receive some data */ - self->io.direction = IO_RECV; - self->ier = IER_RXHDL_IE; - /* Check if we need to change the speed? - * Need to be after self->io.direction to avoid race with - * nsc_ircc_hard_xmit_sir() - Jean II */ - if (self->new_speed) { - pr_debug("%s(), Changing speed!\n", __func__); - self->ier = nsc_ircc_change_speed(self, - self->new_speed); - self->new_speed = 0; - netif_wake_queue(self->netdev); - - /* Check if we are going to FIR */ - if (self->io.speed > 115200) { - /* No need to do anymore SIR stuff */ - return; - } - } - } - - /* Rx FIFO threshold or timeout */ - if (eir & EIR_RXHDL_EV) { - nsc_ircc_pio_receive(self); - - /* Keep receiving */ - self->ier = IER_RXHDL_IE; - } -} - -/* - * Function nsc_ircc_fir_interrupt (self, eir) - * - * Handle MIR/FIR interrupt - * - */ -static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase, - int eir) -{ - __u8 bank; - - bank = inb(iobase+BSR); - - /* Status FIFO event*/ - if (eir & EIR_SFIF_EV) { - /* Check if DMA has finished */ - if (nsc_ircc_dma_receive_complete(self, iobase)) { - /* Wait for next status FIFO interrupt */ - self->ier = IER_SFIF_IE; - } else { - self->ier = IER_SFIF_IE | IER_TMR_IE; - } - } else if (eir & EIR_TMR_EV) { /* Timer finished */ - /* Disable timer */ - switch_bank(iobase, BANK4); - outb(0, iobase+IRCR1); - - /* Clear timer event */ - switch_bank(iobase, BANK0); - outb(ASCR_CTE, iobase+ASCR); - - /* Check if this is a Tx timer interrupt */ - if (self->io.direction == IO_XMIT) { - nsc_ircc_dma_xmit(self, iobase); - - /* Interrupt on DMA */ - self->ier = IER_DMA_IE; - } else { - /* Check (again) if DMA has finished */ - if (nsc_ircc_dma_receive_complete(self, iobase)) { - self->ier = IER_SFIF_IE; - } else { - self->ier = IER_SFIF_IE | IER_TMR_IE; - } - } - } else if (eir & EIR_DMA_EV) { - /* Finished with all transmissions? */ - if (nsc_ircc_dma_xmit_complete(self)) { - if(self->new_speed != 0) { - /* As we stop the Tx queue, the speed change - * need to be done when the Tx fifo is - * empty. Ask for a Tx done interrupt */ - self->ier = IER_TXEMP_IE; - } else { - /* Check if there are more frames to be - * transmitted */ - if (irda_device_txqueue_empty(self->netdev)) { - /* Prepare for receive */ - nsc_ircc_dma_receive(self); - self->ier = IER_SFIF_IE; - } else - net_warn_ratelimited("%s(), potential Tx queue lockup !\n", - __func__); - } - } else { - /* Not finished yet, so interrupt on DMA again */ - self->ier = IER_DMA_IE; - } - } else if (eir & EIR_TXEMP_EV) { - /* The Tx FIFO has totally drained out, so now we can change - * the speed... - Jean II */ - self->ier = nsc_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - netif_wake_queue(self->netdev); - /* Note : nsc_ircc_change_speed() restarted Rx fifo */ - } - - outb(bank, iobase+BSR); -} - -/* - * Function nsc_ircc_interrupt (irq, dev_id, regs) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t nsc_ircc_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct nsc_ircc_cb *self; - __u8 bsr, eir; - int iobase; - - self = netdev_priv(dev); - - spin_lock(&self->lock); - - iobase = self->io.fir_base; - - bsr = inb(iobase+BSR); /* Save current bank */ - - switch_bank(iobase, BANK0); - self->ier = inb(iobase+IER); - eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */ - - outb(0, iobase+IER); /* Disable interrupts */ - - if (eir) { - /* Dispatch interrupt handler for the current speed */ - if (self->io.speed > 115200) - nsc_ircc_fir_interrupt(self, iobase, eir); - else - nsc_ircc_sir_interrupt(self, eir); - } - - outb(self->ier, iobase+IER); /* Restore interrupts */ - outb(bsr, iobase+BSR); /* Restore bank register */ - - spin_unlock(&self->lock); - return IRQ_RETVAL(eir); -} - -/* - * Function nsc_ircc_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self) -{ - unsigned long flags; - int status = FALSE; - int iobase; - __u8 bank; - - IRDA_ASSERT(self != NULL, return FALSE;); - - spin_lock_irqsave(&self->lock, flags); - - if (self->io.speed > 115200) { - iobase = self->io.fir_base; - - /* Check if rx FIFO is not empty */ - bank = inb(iobase+BSR); - switch_bank(iobase, BANK2); - if ((inb(iobase+RXFLV) & 0x3f) != 0) { - /* We are receiving something */ - status = TRUE; - } - outb(bank, iobase+BSR); - } else - status = (self->rx_buff.state != OUTSIDE_FRAME); - - spin_unlock_irqrestore(&self->lock, flags); - - return status; -} - -/* - * Function nsc_ircc_net_open (dev) - * - * Start the device - * - */ -static int nsc_ircc_net_open(struct net_device *dev) -{ - struct nsc_ircc_cb *self; - int iobase; - char hwname[32]; - __u8 bank; - - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return 0;); - - iobase = self->io.fir_base; - - if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) { - net_warn_ratelimited("%s, unable to allocate irq=%d\n", - driver_name, self->io.irq); - return -EAGAIN; - } - /* - * Always allocate the DMA channel after the IRQ, and clean up on - * failure. - */ - if (request_dma(self->io.dma, dev->name)) { - net_warn_ratelimited("%s, unable to allocate dma=%d\n", - driver_name, self->io.dma); - free_irq(self->io.irq, dev); - return -EAGAIN; - } - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* turn on interrupts */ - switch_bank(iobase, BANK0); - outb(IER_LS_IE | IER_RXHDL_IE, iobase+IER); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - /* Ready to play! */ - netif_start_queue(dev); - - /* Give self a hardware name */ - sprintf(hwname, "NSC-FIR @ 0x%03x", self->io.fir_base); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open(dev, &self->qos, hwname); - - return 0; -} - -/* - * Function nsc_ircc_net_close (dev) - * - * Stop the device - * - */ -static int nsc_ircc_net_close(struct net_device *dev) -{ - struct nsc_ircc_cb *self; - int iobase; - __u8 bank; - - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - iobase = self->io.fir_base; - - disable_dma(self->io.dma); - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Disable interrupts */ - switch_bank(iobase, BANK0); - outb(0, iobase+IER); - - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - return 0; -} - -/* - * Function nsc_ircc_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct nsc_ircc_cb *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - spin_lock_irqsave(&self->lock, flags); - nsc_ircc_change_speed(self, irq->ifr_baudrate); - spin_unlock_irqrestore(&self->lock, flags); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - /* This is already protected */ - irq->ifr_receiving = nsc_ircc_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } - return ret; -} - -static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state) -{ - struct nsc_ircc_cb *self = platform_get_drvdata(dev); - int bank; - unsigned long flags; - int iobase = self->io.fir_base; - - if (self->io.suspended) - return 0; - - pr_debug("%s, Suspending\n", driver_name); - - rtnl_lock(); - if (netif_running(self->netdev)) { - netif_device_detach(self->netdev); - spin_lock_irqsave(&self->lock, flags); - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Disable interrupts */ - switch_bank(iobase, BANK0); - outb(0, iobase+IER); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - spin_unlock_irqrestore(&self->lock, flags); - free_irq(self->io.irq, self->netdev); - disable_dma(self->io.dma); - } - self->io.suspended = 1; - rtnl_unlock(); - - return 0; -} - -static int nsc_ircc_resume(struct platform_device *dev) -{ - struct nsc_ircc_cb *self = platform_get_drvdata(dev); - unsigned long flags; - - if (!self->io.suspended) - return 0; - - pr_debug("%s, Waking up\n", driver_name); - - rtnl_lock(); - nsc_ircc_setup(&self->io); - nsc_ircc_init_dongle_interface(self->io.fir_base, self->io.dongle_id); - - if (netif_running(self->netdev)) { - if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, - self->netdev->name, self->netdev)) { - net_warn_ratelimited("%s, unable to allocate irq=%d\n", - driver_name, self->io.irq); - - /* - * Don't fail resume process, just kill this - * network interface - */ - unregister_netdevice(self->netdev); - } else { - spin_lock_irqsave(&self->lock, flags); - nsc_ircc_change_speed(self, self->io.speed); - spin_unlock_irqrestore(&self->lock, flags); - netif_device_attach(self->netdev); - } - - } else { - spin_lock_irqsave(&self->lock, flags); - nsc_ircc_change_speed(self, 9600); - spin_unlock_irqrestore(&self->lock, flags); - } - self->io.suspended = 0; - rtnl_unlock(); - - return 0; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("NSC IrDA Device Driver"); -MODULE_LICENSE("GPL"); - - -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); -module_param_hw_array(io, int, ioport, NULL, 0); -MODULE_PARM_DESC(io, "Base I/O addresses"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "IRQ lines"); -module_param_hw_array(dma, int, dma, NULL, 0); -MODULE_PARM_DESC(dma, "DMA channels"); -module_param(dongle_id, int, 0); -MODULE_PARM_DESC(dongle_id, "Type-id of used dongle"); - -module_init(nsc_ircc_init); -module_exit(nsc_ircc_cleanup); - diff --git a/drivers/net/irda/nsc-ircc.h b/drivers/net/irda/nsc-ircc.h deleted file mode 100644 index 7be5acb56532..000000000000 --- a/drivers/net/irda/nsc-ircc.h +++ /dev/null @@ -1,281 +0,0 @@ -/********************************************************************* - * - * Filename: nsc-ircc.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Fri Nov 13 14:37:40 1998 - * Modified at: Sun Jan 23 17:47:00 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli - * Copyright (c) 1998 Lichen Wang, - * Copyright (c) 1998 Actisys Corp., www.actisys.com - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef NSC_IRCC_H -#define NSC_IRCC_H - -#include - -#include -#include -#include -#include - -/* Features for chips (set in driver_data) */ -#define NSC_FORCE_DONGLE_TYPE9 0x00000001 - -/* DMA modes needed */ -#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ -#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ - -/* Config registers for the '108 */ -#define CFG_108_BAIC 0x00 -#define CFG_108_CSRT 0x01 -#define CFG_108_MCTL 0x02 - -/* Config registers for the '338 */ -#define CFG_338_FER 0x00 -#define CFG_338_FAR 0x01 -#define CFG_338_PTR 0x02 -#define CFG_338_PNP0 0x1b -#define CFG_338_PNP1 0x1c -#define CFG_338_PNP3 0x4f - -/* Config registers for the '39x (in the logical device bank) */ -#define CFG_39X_LDN 0x07 /* Logical device number (Super I/O bank) */ -#define CFG_39X_SIOCF1 0x21 /* SuperI/O Config */ -#define CFG_39X_ACT 0x30 /* Device activation */ -#define CFG_39X_BASEH 0x60 /* Device base address (high bits) */ -#define CFG_39X_BASEL 0x61 /* Device base address (low bits) */ -#define CFG_39X_IRQNUM 0x70 /* Interrupt number & wake up enable */ -#define CFG_39X_IRQSEL 0x71 /* Interrupt select (edge/level + polarity) */ -#define CFG_39X_DMA0 0x74 /* DMA 0 configuration */ -#define CFG_39X_DMA1 0x75 /* DMA 1 configuration */ -#define CFG_39X_SPC 0xF0 /* Serial port configuration register */ - -/* Flags for configuration register CRF0 */ -#define APEDCRC 0x02 -#define ENBNKSEL 0x01 - -/* Set 0 */ -#define TXD 0x00 /* Transmit data port */ -#define RXD 0x00 /* Receive data port */ - -/* Register 1 */ -#define IER 0x01 /* Interrupt Enable Register*/ -#define IER_RXHDL_IE 0x01 /* Receiver high data level interrupt */ -#define IER_TXLDL_IE 0x02 /* Transeiver low data level interrupt */ -#define IER_LS_IE 0x04//* Link Status Interrupt */ -#define IER_ETXURI 0x04 /* Tx underrun */ -#define IER_DMA_IE 0x10 /* DMA finished interrupt */ -#define IER_TXEMP_IE 0x20 -#define IER_SFIF_IE 0x40 /* Frame status FIFO intr */ -#define IER_TMR_IE 0x80 /* Timer event */ - -#define FCR 0x02 /* (write only) */ -#define FCR_FIFO_EN 0x01 /* Enable FIFO's */ -#define FCR_RXSR 0x02 /* Rx FIFO soft reset */ -#define FCR_TXSR 0x04 /* Tx FIFO soft reset */ -#define FCR_RXTH 0x40 /* Rx FIFO threshold (set to 16) */ -#define FCR_TXTH 0x20 /* Tx FIFO threshold (set to 17) */ - -#define EIR 0x02 /* (read only) */ -#define EIR_RXHDL_EV 0x01 -#define EIR_TXLDL_EV 0x02 -#define EIR_LS_EV 0x04 -#define EIR_DMA_EV 0x10 -#define EIR_TXEMP_EV 0x20 -#define EIR_SFIF_EV 0x40 -#define EIR_TMR_EV 0x80 - -#define LCR 0x03 /* Link control register */ -#define LCR_WLS_8 0x03 /* 8 bits */ - -#define BSR 0x03 /* Bank select register */ -#define BSR_BKSE 0x80 -#define BANK0 LCR_WLS_8 /* Must make sure that we set 8N1 */ -#define BANK1 0x80 -#define BANK2 0xe0 -#define BANK3 0xe4 -#define BANK4 0xe8 -#define BANK5 0xec -#define BANK6 0xf0 -#define BANK7 0xf4 - -#define MCR 0x04 /* Mode Control Register */ -#define MCR_MODE_MASK ~(0xd0) -#define MCR_UART 0x00 -#define MCR_RESERVED 0x20 -#define MCR_SHARP_IR 0x40 -#define MCR_SIR 0x60 -#define MCR_MIR 0x80 -#define MCR_FIR 0xa0 -#define MCR_CEIR 0xb0 -#define MCR_IR_PLS 0x10 -#define MCR_DMA_EN 0x04 -#define MCR_EN_IRQ 0x08 -#define MCR_TX_DFR 0x08 - -#define LSR 0x05 /* Link status register */ -#define LSR_RXDA 0x01 /* Receiver data available */ -#define LSR_TXRDY 0x20 /* Transmitter ready */ -#define LSR_TXEMP 0x40 /* Transmitter empty */ - -#define ASCR 0x07 /* Auxiliary Status and Control Register */ -#define ASCR_RXF_TOUT 0x01 /* Rx FIFO timeout */ -#define ASCR_FEND_INF 0x02 /* Frame end bytes in rx FIFO */ -#define ASCR_S_EOT 0x04 /* Set end of transmission */ -#define ASCT_RXBSY 0x20 /* Rx busy */ -#define ASCR_TXUR 0x40 /* Transeiver underrun */ -#define ASCR_CTE 0x80 /* Clear timer event */ - -/* Bank 2 */ -#define BGDL 0x00 /* Baud Generator Divisor Port (Low Byte) */ -#define BGDH 0x01 /* Baud Generator Divisor Port (High Byte) */ - -#define ECR1 0x02 /* Extended Control Register 1 */ -#define ECR1_EXT_SL 0x01 /* Extended Mode Select */ -#define ECR1_DMANF 0x02 /* DMA Fairness */ -#define ECR1_DMATH 0x04 /* DMA Threshold */ -#define ECR1_DMASWP 0x08 /* DMA Swap */ - -#define EXCR2 0x04 -#define EXCR2_TFSIZ 0x01 /* Rx FIFO size = 32 */ -#define EXCR2_RFSIZ 0x04 /* Tx FIFO size = 32 */ - -#define TXFLV 0x06 /* Tx FIFO level */ -#define RXFLV 0x07 /* Rx FIFO level */ - -/* Bank 3 */ -#define MID 0x00 - -/* Bank 4 */ -#define TMRL 0x00 /* Timer low byte */ -#define TMRH 0x01 /* Timer high byte */ -#define IRCR1 0x02 /* Infrared control register 1 */ -#define IRCR1_TMR_EN 0x01 /* Timer enable */ - -#define TFRLL 0x04 -#define TFRLH 0x05 -#define RFRLL 0x06 -#define RFRLH 0x07 - -/* Bank 5 */ -#define IRCR2 0x04 /* Infrared control register 2 */ -#define IRCR2_MDRS 0x04 /* MIR data rate select */ -#define IRCR2_FEND_MD 0x20 /* */ - -#define FRM_ST 0x05 /* Frame status FIFO */ -#define FRM_ST_VLD 0x80 /* Frame status FIFO data valid */ -#define FRM_ST_ERR_MSK 0x5f -#define FRM_ST_LOST_FR 0x40 /* Frame lost */ -#define FRM_ST_MAX_LEN 0x10 /* Max frame len exceeded */ -#define FRM_ST_PHY_ERR 0x08 /* Physical layer error */ -#define FRM_ST_BAD_CRC 0x04 -#define FRM_ST_OVR1 0x02 /* Rx FIFO overrun */ -#define FRM_ST_OVR2 0x01 /* Frame status FIFO overrun */ - -#define RFLFL 0x06 -#define RFLFH 0x07 - -/* Bank 6 */ -#define IR_CFG2 0x00 -#define IR_CFG2_DIS_CRC 0x02 - -/* Bank 7 */ -#define IRM_CR 0x07 /* Infrared module control register */ -#define IRM_CR_IRX_MSL 0x40 -#define IRM_CR_AF_MNT 0x80 /* Automatic format */ - -/* NSC chip information */ -struct nsc_chip { - char *name; /* Name of chipset */ - int cfg[3]; /* Config registers */ - u_int8_t cid_index; /* Chip identification index reg */ - u_int8_t cid_value; /* Chip identification expected value */ - u_int8_t cid_mask; /* Chip identification revision mask */ - - /* Functions for probing and initializing the specific chip */ - int (*probe)(struct nsc_chip *chip, chipio_t *info); - int (*init)(struct nsc_chip *chip, chipio_t *info); -}; -typedef struct nsc_chip nsc_chip_t; - -/* For storing entries in the status FIFO */ -struct st_fifo_entry { - int status; - int len; -}; - -#define MAX_TX_WINDOW 7 -#define MAX_RX_WINDOW 7 - -struct st_fifo { - struct st_fifo_entry entries[MAX_RX_WINDOW]; - int pending_bytes; - int head; - int tail; - int len; -}; - -struct frame_cb { - void *start; /* Start of frame in DMA mem */ - int len; /* Length of frame in DMA mem */ -}; - -struct tx_fifo { - struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */ - int ptr; /* Currently being sent */ - int len; /* Length of queue */ - int free; /* Next free slot */ - void *tail; /* Next free start in DMA mem */ -}; - -/* Private data for each instance */ -struct nsc_ircc_cb { - struct st_fifo st_fifo; /* Info about received frames */ - struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ - - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; /* QoS capabilities for this device */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - __u8 ier; /* Interrupt enable register */ - - ktime_t stamp; - - spinlock_t lock; /* For serializing operations */ - - __u32 new_speed; - int index; /* Instance index */ - - struct platform_device *pldev; -}; - -static inline void switch_bank(int iobase, int bank) -{ - outb(bank, iobase+BSR); -} - -#endif /* NSC_IRCC_H */ diff --git a/drivers/net/irda/old_belkin-sir.c b/drivers/net/irda/old_belkin-sir.c deleted file mode 100644 index a7c2e990ae69..000000000000 --- a/drivers/net/irda/old_belkin-sir.c +++ /dev/null @@ -1,146 +0,0 @@ -/********************************************************************* - * - * Filename: old_belkin.c - * Version: 1.1 - * Description: Driver for the Belkin (old) SmartBeam dongle - * Status: Experimental... - * Author: Jean Tourrilhes - * Created at: 22/11/99 - * Modified at: Fri Dec 17 09:13:32 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Jean Tourrilhes, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include - -#include -// #include - -#include "sir-dev.h" - -/* - * Belkin is selling a dongle called the SmartBeam. - * In fact, there is two hardware version of this dongle, of course with - * the same name and looking the exactly same (grrr...). - * I guess that I've got the old one, because inside I don't have - * a jumper for IrDA/ASK... - * - * As far as I can make it from info on their web site, the old dongle - * support only 9600 b/s, which make our life much simpler as far as - * the driver is concerned, but you might not like it very much ;-) - * The new SmartBeam does 115 kb/s, and I've not tested it... - * - * Belkin claim that the correct driver for the old dongle (in Windows) - * is the generic Parallax 9500a driver, but the Linux LiteLink driver - * fails for me (probably because Linux-IrDA doesn't rate fallback), - * so I created this really dumb driver... - * - * In fact, this driver doesn't do much. The only thing it does is to - * prevent Linux-IrDA to use any other speed than 9600 b/s ;-) This - * driver is called "old_belkin" so that when the new SmartBeam is supported - * its driver can be called "belkin" instead of "new_belkin". - * - * Note : this driver was written without any info/help from Belkin, - * so a lot of info here might be totally wrong. Blame me ;-) - */ - -static int old_belkin_open(struct sir_dev *dev); -static int old_belkin_close(struct sir_dev *dev); -static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed); -static int old_belkin_reset(struct sir_dev *dev); - -static struct dongle_driver old_belkin = { - .owner = THIS_MODULE, - .driver_name = "Old Belkin SmartBeam", - .type = IRDA_OLD_BELKIN_DONGLE, - .open = old_belkin_open, - .close = old_belkin_close, - .reset = old_belkin_reset, - .set_speed = old_belkin_change_speed, -}; - -static int __init old_belkin_sir_init(void) -{ - return irda_register_dongle(&old_belkin); -} - -static void __exit old_belkin_sir_cleanup(void) -{ - irda_unregister_dongle(&old_belkin); -} - -static int old_belkin_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power on dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Not too fast, please... */ - qos->baud_rate.bits &= IR_9600; - /* Needs at least 10 ms (totally wild guess, can do probably better) */ - qos->min_turn_time.bits = 0x01; - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int old_belkin_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function old_belkin_change_speed (task) - * - * With only one speed available, not much to do... - */ -static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed) -{ - dev->speed = 9600; - return (speed==dev->speed) ? 0 : -EINVAL; -} - -/* - * Function old_belkin_reset (task) - * - * Reset the Old-Belkin type dongle. - * - */ -static int old_belkin_reset(struct sir_dev *dev) -{ - /* This dongles speed "defaults" to 9600 bps ;-) */ - dev->speed = 9600; - - return 0; -} - -MODULE_AUTHOR("Jean Tourrilhes "); -MODULE_DESCRIPTION("Belkin (old) SmartBeam dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-7"); /* IRDA_OLD_BELKIN_DONGLE */ - -module_init(old_belkin_sir_init); -module_exit(old_belkin_sir_cleanup); diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c deleted file mode 100644 index 1dba16bc7f8d..000000000000 --- a/drivers/net/irda/pxaficp_ir.c +++ /dev/null @@ -1,1076 +0,0 @@ -/* - * linux/drivers/net/irda/pxaficp_ir.c - * - * Based on sa1100_ir.c by Russell King - * - * Changes copyright (C) 2003-2005 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Infra-red driver (SIR/FIR) for the PXA2xx embedded microprocessor - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#undef __REG -#define __REG(x) ((x) & 0xffff) -#include - -#define ICCR0 0x0000 /* ICP Control Register 0 */ -#define ICCR1 0x0004 /* ICP Control Register 1 */ -#define ICCR2 0x0008 /* ICP Control Register 2 */ -#define ICDR 0x000c /* ICP Data Register */ -#define ICSR0 0x0014 /* ICP Status Register 0 */ -#define ICSR1 0x0018 /* ICP Status Register 1 */ - -#define ICCR0_AME (1 << 7) /* Address match enable */ -#define ICCR0_TIE (1 << 6) /* Transmit FIFO interrupt enable */ -#define ICCR0_RIE (1 << 5) /* Receive FIFO interrupt enable */ -#define ICCR0_RXE (1 << 4) /* Receive enable */ -#define ICCR0_TXE (1 << 3) /* Transmit enable */ -#define ICCR0_TUS (1 << 2) /* Transmit FIFO underrun select */ -#define ICCR0_LBM (1 << 1) /* Loopback mode */ -#define ICCR0_ITR (1 << 0) /* IrDA transmission */ - -#define ICCR2_RXP (1 << 3) /* Receive Pin Polarity select */ -#define ICCR2_TXP (1 << 2) /* Transmit Pin Polarity select */ -#define ICCR2_TRIG (3 << 0) /* Receive FIFO Trigger threshold */ -#define ICCR2_TRIG_8 (0 << 0) /* >= 8 bytes */ -#define ICCR2_TRIG_16 (1 << 0) /* >= 16 bytes */ -#define ICCR2_TRIG_32 (2 << 0) /* >= 32 bytes */ - -#define ICSR0_EOC (1 << 6) /* DMA End of Descriptor Chain */ -#define ICSR0_FRE (1 << 5) /* Framing error */ -#define ICSR0_RFS (1 << 4) /* Receive FIFO service request */ -#define ICSR0_TFS (1 << 3) /* Transnit FIFO service request */ -#define ICSR0_RAB (1 << 2) /* Receiver abort */ -#define ICSR0_TUR (1 << 1) /* Trunsmit FIFO underun */ -#define ICSR0_EIF (1 << 0) /* End/Error in FIFO */ - -#define ICSR1_ROR (1 << 6) /* Receiver FIFO underrun */ -#define ICSR1_CRE (1 << 5) /* CRC error */ -#define ICSR1_EOF (1 << 4) /* End of frame */ -#define ICSR1_TNF (1 << 3) /* Transmit FIFO not full */ -#define ICSR1_RNE (1 << 2) /* Receive FIFO not empty */ -#define ICSR1_TBY (1 << 1) /* Tramsmiter busy flag */ -#define ICSR1_RSY (1 << 0) /* Recevier synchronized flag */ - -#define IrSR_RXPL_NEG_IS_ZERO (1<<4) -#define IrSR_RXPL_POS_IS_ZERO 0x0 -#define IrSR_TXPL_NEG_IS_ZERO (1<<3) -#define IrSR_TXPL_POS_IS_ZERO 0x0 -#define IrSR_XMODE_PULSE_1_6 (1<<2) -#define IrSR_XMODE_PULSE_3_16 0x0 -#define IrSR_RCVEIR_IR_MODE (1<<1) -#define IrSR_RCVEIR_UART_MODE 0x0 -#define IrSR_XMITIR_IR_MODE (1<<0) -#define IrSR_XMITIR_UART_MODE 0x0 - -#define IrSR_IR_RECEIVE_ON (\ - IrSR_RXPL_NEG_IS_ZERO | \ - IrSR_TXPL_POS_IS_ZERO | \ - IrSR_XMODE_PULSE_3_16 | \ - IrSR_RCVEIR_IR_MODE | \ - IrSR_XMITIR_UART_MODE) - -#define IrSR_IR_TRANSMIT_ON (\ - IrSR_RXPL_NEG_IS_ZERO | \ - IrSR_TXPL_POS_IS_ZERO | \ - IrSR_XMODE_PULSE_3_16 | \ - IrSR_RCVEIR_UART_MODE | \ - IrSR_XMITIR_IR_MODE) - -/* macros for registers read/write */ -#define ficp_writel(irda, val, off) \ - do { \ - dev_vdbg(irda->dev, \ - "%s():%d ficp_writel(0x%x, %s)\n", \ - __func__, __LINE__, (val), #off); \ - writel_relaxed((val), (irda)->irda_base + (off)); \ - } while (0) - -#define ficp_readl(irda, off) \ - ({ \ - unsigned int _v; \ - _v = readl_relaxed((irda)->irda_base + (off)); \ - dev_vdbg(irda->dev, \ - "%s():%d ficp_readl(%s): 0x%x\n", \ - __func__, __LINE__, #off, _v); \ - _v; \ - }) - -#define stuart_writel(irda, val, off) \ - do { \ - dev_vdbg(irda->dev, \ - "%s():%d stuart_writel(0x%x, %s)\n", \ - __func__, __LINE__, (val), #off); \ - writel_relaxed((val), (irda)->stuart_base + (off)); \ - } while (0) - -#define stuart_readl(irda, off) \ - ({ \ - unsigned int _v; \ - _v = readl_relaxed((irda)->stuart_base + (off)); \ - dev_vdbg(irda->dev, \ - "%s():%d stuart_readl(%s): 0x%x\n", \ - __func__, __LINE__, #off, _v); \ - _v; \ - }) - -struct pxa_irda { - int speed; - int newspeed; - unsigned long long last_clk; - - void __iomem *stuart_base; - void __iomem *irda_base; - unsigned char *dma_rx_buff; - unsigned char *dma_tx_buff; - dma_addr_t dma_rx_buff_phy; - dma_addr_t dma_tx_buff_phy; - unsigned int dma_tx_buff_len; - struct dma_chan *txdma; - struct dma_chan *rxdma; - dma_cookie_t rx_cookie; - dma_cookie_t tx_cookie; - int drcmr_rx; - int drcmr_tx; - - int uart_irq; - int icp_irq; - - struct irlap_cb *irlap; - struct qos_info qos; - - iobuff_t tx_buff; - iobuff_t rx_buff; - - struct device *dev; - struct pxaficp_platform_data *pdata; - struct clk *fir_clk; - struct clk *sir_clk; - struct clk *cur_clk; -}; - -static int pxa_irda_set_speed(struct pxa_irda *si, int speed); - -static inline void pxa_irda_disable_clk(struct pxa_irda *si) -{ - if (si->cur_clk) - clk_disable_unprepare(si->cur_clk); - si->cur_clk = NULL; -} - -static inline void pxa_irda_enable_firclk(struct pxa_irda *si) -{ - si->cur_clk = si->fir_clk; - clk_prepare_enable(si->fir_clk); -} - -static inline void pxa_irda_enable_sirclk(struct pxa_irda *si) -{ - si->cur_clk = si->sir_clk; - clk_prepare_enable(si->sir_clk); -} - - -#define IS_FIR(si) ((si)->speed >= 4000000) -#define IRDA_FRAME_SIZE_LIMIT 2047 - -static void pxa_irda_fir_dma_rx_irq(void *data); -static void pxa_irda_fir_dma_tx_irq(void *data); - -inline static void pxa_irda_fir_dma_rx_start(struct pxa_irda *si) -{ - struct dma_async_tx_descriptor *tx; - - tx = dmaengine_prep_slave_single(si->rxdma, si->dma_rx_buff_phy, - IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE, - DMA_PREP_INTERRUPT); - if (!tx) { - dev_err(si->dev, "prep_slave_sg() failed\n"); - return; - } - tx->callback = pxa_irda_fir_dma_rx_irq; - tx->callback_param = si; - si->rx_cookie = dmaengine_submit(tx); - dma_async_issue_pending(si->rxdma); -} - -inline static void pxa_irda_fir_dma_tx_start(struct pxa_irda *si) -{ - struct dma_async_tx_descriptor *tx; - - tx = dmaengine_prep_slave_single(si->txdma, si->dma_tx_buff_phy, - si->dma_tx_buff_len, DMA_TO_DEVICE, - DMA_PREP_INTERRUPT); - if (!tx) { - dev_err(si->dev, "prep_slave_sg() failed\n"); - return; - } - tx->callback = pxa_irda_fir_dma_tx_irq; - tx->callback_param = si; - si->tx_cookie = dmaengine_submit(tx); - dma_async_issue_pending(si->rxdma); -} - -/* - * Set the IrDA communications mode. - */ -static void pxa_irda_set_mode(struct pxa_irda *si, int mode) -{ - if (si->pdata->transceiver_mode) - si->pdata->transceiver_mode(si->dev, mode); - else { - if (gpio_is_valid(si->pdata->gpio_pwdown)) - gpio_set_value(si->pdata->gpio_pwdown, - !(mode & IR_OFF) ^ - !si->pdata->gpio_pwdown_inverted); - pxa2xx_transceiver_mode(si->dev, mode); - } -} - -/* - * Set the IrDA communications speed. - */ -static int pxa_irda_set_speed(struct pxa_irda *si, int speed) -{ - unsigned long flags; - unsigned int divisor; - - switch (speed) { - case 9600: case 19200: case 38400: - case 57600: case 115200: - - /* refer to PXA250/210 Developer's Manual 10-7 */ - /* BaudRate = 14.7456 MHz / (16*Divisor) */ - divisor = 14745600 / (16 * speed); - - local_irq_save(flags); - - if (IS_FIR(si)) { - /* stop RX DMA */ - dmaengine_terminate_all(si->rxdma); - /* disable FICP */ - ficp_writel(si, 0, ICCR0); - pxa_irda_disable_clk(si); - - /* set board transceiver to SIR mode */ - pxa_irda_set_mode(si, IR_SIRMODE); - - /* enable the STUART clock */ - pxa_irda_enable_sirclk(si); - } - - /* disable STUART first */ - stuart_writel(si, 0, STIER); - - /* access DLL & DLH */ - stuart_writel(si, stuart_readl(si, STLCR) | LCR_DLAB, STLCR); - stuart_writel(si, divisor & 0xff, STDLL); - stuart_writel(si, divisor >> 8, STDLH); - stuart_writel(si, stuart_readl(si, STLCR) & ~LCR_DLAB, STLCR); - - si->speed = speed; - stuart_writel(si, IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6, - STISR); - stuart_writel(si, IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE, - STIER); - - local_irq_restore(flags); - break; - - case 4000000: - local_irq_save(flags); - - /* disable STUART */ - stuart_writel(si, 0, STIER); - stuart_writel(si, 0, STISR); - pxa_irda_disable_clk(si); - - /* disable FICP first */ - ficp_writel(si, 0, ICCR0); - - /* set board transceiver to FIR mode */ - pxa_irda_set_mode(si, IR_FIRMODE); - - /* enable the FICP clock */ - pxa_irda_enable_firclk(si); - - si->speed = speed; - pxa_irda_fir_dma_rx_start(si); - ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); - - local_irq_restore(flags); - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* SIR interrupt service routine. */ -static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct pxa_irda *si = netdev_priv(dev); - int iir, lsr, data; - - iir = stuart_readl(si, STIIR); - - switch (iir & 0x0F) { - case 0x06: /* Receiver Line Status */ - lsr = stuart_readl(si, STLSR); - while (lsr & LSR_FIFOE) { - data = stuart_readl(si, STRBR); - if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) { - printk(KERN_DEBUG "pxa_ir: sir receiving error\n"); - dev->stats.rx_errors++; - if (lsr & LSR_FE) - dev->stats.rx_frame_errors++; - if (lsr & LSR_OE) - dev->stats.rx_fifo_errors++; - } else { - dev->stats.rx_bytes++; - async_unwrap_char(dev, &dev->stats, - &si->rx_buff, data); - } - lsr = stuart_readl(si, STLSR); - } - si->last_clk = sched_clock(); - break; - - case 0x04: /* Received Data Available */ - /* forth through */ - - case 0x0C: /* Character Timeout Indication */ - do { - dev->stats.rx_bytes++; - async_unwrap_char(dev, &dev->stats, &si->rx_buff, - stuart_readl(si, STRBR)); - } while (stuart_readl(si, STLSR) & LSR_DR); - si->last_clk = sched_clock(); - break; - - case 0x02: /* Transmit FIFO Data Request */ - while ((si->tx_buff.len) && - (stuart_readl(si, STLSR) & LSR_TDRQ)) { - stuart_writel(si, *si->tx_buff.data++, STTHR); - si->tx_buff.len -= 1; - } - - if (si->tx_buff.len == 0) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head; - - /* We need to ensure that the transmitter has finished. */ - while ((stuart_readl(si, STLSR) & LSR_TEMT) == 0) - cpu_relax(); - si->last_clk = sched_clock(); - - /* - * Ok, we've finished transmitting. Now enable - * the receiver. Sometimes we get a receive IRQ - * immediately after a transmit... - */ - if (si->newspeed) { - pxa_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } else { - /* enable IR Receiver, disable IR Transmitter */ - stuart_writel(si, IrSR_IR_RECEIVE_ON | - IrSR_XMODE_PULSE_1_6, STISR); - /* enable STUART and receive interrupts */ - stuart_writel(si, IER_UUE | IER_RLSE | - IER_RAVIE | IER_RTIOE, STIER); - } - /* I'm hungry! */ - netif_wake_queue(dev); - } - break; - } - - return IRQ_HANDLED; -} - -/* FIR Receive DMA interrupt handler */ -static void pxa_irda_fir_dma_rx_irq(void *data) -{ - struct net_device *dev = data; - struct pxa_irda *si = netdev_priv(dev); - - dmaengine_terminate_all(si->rxdma); - netdev_dbg(dev, "pxa_ir: fir rx dma bus error\n"); -} - -/* FIR Transmit DMA interrupt handler */ -static void pxa_irda_fir_dma_tx_irq(void *data) -{ - struct net_device *dev = data; - struct pxa_irda *si = netdev_priv(dev); - - dmaengine_terminate_all(si->txdma); - if (dmaengine_tx_status(si->txdma, si->tx_cookie, NULL) == DMA_ERROR) { - dev->stats.tx_errors++; - } else { - dev->stats.tx_packets++; - dev->stats.tx_bytes += si->dma_tx_buff_len; - } - - while (ficp_readl(si, ICSR1) & ICSR1_TBY) - cpu_relax(); - si->last_clk = sched_clock(); - - /* - * HACK: It looks like the TBY bit is dropped too soon. - * Without this delay things break. - */ - udelay(120); - - if (si->newspeed) { - pxa_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } else { - int i = 64; - - ficp_writel(si, 0, ICCR0); - pxa_irda_fir_dma_rx_start(si); - while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--) - ficp_readl(si, ICDR); - ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); - - if (i < 0) - printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); - } - netif_wake_queue(dev); -} - -/* EIF(Error in FIFO/End in Frame) handler for FIR */ -static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, int icsr0) -{ - unsigned int len, stat, data; - struct dma_tx_state state; - - /* Get the current data position. */ - - dmaengine_tx_status(si->rxdma, si->rx_cookie, &state); - len = IRDA_FRAME_SIZE_LIMIT - state.residue; - - do { - /* Read Status, and then Data. */ - stat = ficp_readl(si, ICSR1); - rmb(); - data = ficp_readl(si, ICDR); - - if (stat & (ICSR1_CRE | ICSR1_ROR)) { - dev->stats.rx_errors++; - if (stat & ICSR1_CRE) { - printk(KERN_DEBUG "pxa_ir: fir receive CRC error\n"); - dev->stats.rx_crc_errors++; - } - if (stat & ICSR1_ROR) { - printk(KERN_DEBUG "pxa_ir: fir receive overrun\n"); - dev->stats.rx_over_errors++; - } - } else { - si->dma_rx_buff[len++] = data; - } - /* If we hit the end of frame, there's no point in continuing. */ - if (stat & ICSR1_EOF) - break; - } while (ficp_readl(si, ICSR0) & ICSR0_EIF); - - if (stat & ICSR1_EOF) { - /* end of frame. */ - struct sk_buff *skb; - - if (icsr0 & ICSR0_FRE) { - printk(KERN_ERR "pxa_ir: dropping erroneous frame\n"); - dev->stats.rx_dropped++; - return; - } - - skb = alloc_skb(len+1,GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "pxa_ir: fir out of memory for receive skb\n"); - dev->stats.rx_dropped++; - return; - } - - /* Align IP header to 20 bytes */ - skb_reserve(skb, 1); - skb_copy_to_linear_data(skb, si->dma_rx_buff, len); - skb_put(skb, len); - - /* Feed it to IrLAP */ - skb->dev = dev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - } -} - -/* FIR interrupt handler */ -static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct pxa_irda *si = netdev_priv(dev); - int icsr0, i = 64; - - /* stop RX DMA */ - dmaengine_terminate_all(si->rxdma); - si->last_clk = sched_clock(); - icsr0 = ficp_readl(si, ICSR0); - - if (icsr0 & (ICSR0_FRE | ICSR0_RAB)) { - if (icsr0 & ICSR0_FRE) { - printk(KERN_DEBUG "pxa_ir: fir receive frame error\n"); - dev->stats.rx_frame_errors++; - } else { - printk(KERN_DEBUG "pxa_ir: fir receive abort\n"); - dev->stats.rx_errors++; - } - ficp_writel(si, icsr0 & (ICSR0_FRE | ICSR0_RAB), ICSR0); - } - - if (icsr0 & ICSR0_EIF) { - /* An error in FIFO occurred, or there is a end of frame */ - pxa_irda_fir_irq_eif(si, dev, icsr0); - } - - ficp_writel(si, 0, ICCR0); - pxa_irda_fir_dma_rx_start(si); - while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--) - ficp_readl(si, ICDR); - ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); - - if (i < 0) - printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); - - return IRQ_HANDLED; -} - -/* hard_xmit interface of irda device */ -static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct pxa_irda *si = netdev_priv(dev); - int speed = irda_get_next_speed(skb); - - /* - * Does this packet contain a request to change the interface - * speed? If so, remember it until we complete the transmission - * of this frame. - */ - if (speed != si->speed && speed != -1) - si->newspeed = speed; - - /* - * If this is an empty frame, we can bypass a lot. - */ - if (skb->len == 0) { - if (si->newspeed) { - si->newspeed = 0; - pxa_irda_set_speed(si, speed); - } - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - netif_stop_queue(dev); - - if (!IS_FIR(si)) { - si->tx_buff.data = si->tx_buff.head; - si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, si->tx_buff.truesize); - - /* Disable STUART interrupts and switch to transmit mode. */ - stuart_writel(si, 0, STIER); - stuart_writel(si, IrSR_IR_TRANSMIT_ON | IrSR_XMODE_PULSE_1_6, - STISR); - - /* enable STUART and transmit interrupts */ - stuart_writel(si, IER_UUE | IER_TIE, STIER); - } else { - unsigned long mtt = irda_get_mtt(skb); - - si->dma_tx_buff_len = skb->len; - skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len); - - if (mtt) - while ((sched_clock() - si->last_clk) * 1000 < mtt) - cpu_relax(); - - /* stop RX DMA, disable FICP */ - dmaengine_terminate_all(si->rxdma); - ficp_writel(si, 0, ICCR0); - - pxa_irda_fir_dma_tx_start(si); - ficp_writel(si, ICCR0_ITR | ICCR0_TXE, ICCR0); - } - - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static int pxa_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) -{ - struct if_irda_req *rq = (struct if_irda_req *)ifreq; - struct pxa_irda *si = netdev_priv(dev); - int ret; - - switch (cmd) { - case SIOCSBANDWIDTH: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - /* - * We are unable to set the speed if the - * device is not running. - */ - if (netif_running(dev)) { - ret = pxa_irda_set_speed(si, - rq->ifr_baudrate); - } else { - printk(KERN_INFO "pxa_ir: SIOCSBANDWIDTH: !netif_running\n"); - ret = 0; - } - } - break; - - case SIOCSMEDIABUSY: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - irda_device_set_media_busy(dev, TRUE); - ret = 0; - } - break; - - case SIOCGRECEIVING: - ret = 0; - rq->ifr_receiving = IS_FIR(si) ? 0 - : si->rx_buff.state != OUTSIDE_FRAME; - break; - - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - -static void pxa_irda_startup(struct pxa_irda *si) -{ - /* Disable STUART interrupts */ - stuart_writel(si, 0, STIER); - /* enable STUART interrupt to the processor */ - stuart_writel(si, MCR_OUT2, STMCR); - /* configure SIR frame format: StartBit - Data 7 ... Data 0 - Stop Bit */ - stuart_writel(si, LCR_WLS0 | LCR_WLS1, STLCR); - /* enable FIFO, we use FIFO to improve performance */ - stuart_writel(si, FCR_TRFIFOE | FCR_ITL_32, STFCR); - - /* disable FICP */ - ficp_writel(si, 0, ICCR0); - /* configure FICP ICCR2 */ - ficp_writel(si, ICCR2_TXP | ICCR2_TRIG_32, ICCR2); - - /* force SIR reinitialization */ - si->speed = 4000000; - pxa_irda_set_speed(si, 9600); - - printk(KERN_DEBUG "pxa_ir: irda startup\n"); -} - -static void pxa_irda_shutdown(struct pxa_irda *si) -{ - unsigned long flags; - - local_irq_save(flags); - - /* disable STUART and interrupt */ - stuart_writel(si, 0, STIER); - /* disable STUART SIR mode */ - stuart_writel(si, 0, STISR); - - /* disable DMA */ - dmaengine_terminate_all(si->rxdma); - dmaengine_terminate_all(si->txdma); - /* disable FICP */ - ficp_writel(si, 0, ICCR0); - - /* disable the STUART or FICP clocks */ - pxa_irda_disable_clk(si); - - local_irq_restore(flags); - - /* power off board transceiver */ - pxa_irda_set_mode(si, IR_OFF); - - printk(KERN_DEBUG "pxa_ir: irda shutdown\n"); -} - -static int pxa_irda_start(struct net_device *dev) -{ - struct pxa_irda *si = netdev_priv(dev); - dma_cap_mask_t mask; - struct dma_slave_config config; - struct pxad_param param; - int err; - - si->speed = 9600; - - err = request_irq(si->uart_irq, pxa_irda_sir_irq, 0, dev->name, dev); - if (err) - goto err_irq1; - - err = request_irq(si->icp_irq, pxa_irda_fir_irq, 0, dev->name, dev); - if (err) - goto err_irq2; - - /* - * The interrupt must remain disabled for now. - */ - disable_irq(si->uart_irq); - disable_irq(si->icp_irq); - - err = -EBUSY; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - param.prio = PXAD_PRIO_LOWEST; - - memset(&config, 0, sizeof(config)); - config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - config.src_addr = (dma_addr_t)si->irda_base + ICDR; - config.dst_addr = (dma_addr_t)si->irda_base + ICDR; - config.src_maxburst = 32; - config.dst_maxburst = 32; - - param.drcmr = si->drcmr_rx; - si->rxdma = dma_request_slave_channel_compat(mask, pxad_filter_fn, - ¶m, &dev->dev, "rx"); - if (!si->rxdma) - goto err_rx_dma; - - param.drcmr = si->drcmr_tx; - si->txdma = dma_request_slave_channel_compat(mask, pxad_filter_fn, - ¶m, &dev->dev, "tx"); - if (!si->txdma) - goto err_tx_dma; - - err = dmaengine_slave_config(si->rxdma, &config); - if (err) - goto err_dma_rx_buff; - err = dmaengine_slave_config(si->txdma, &config); - if (err) - goto err_dma_rx_buff; - - err = -ENOMEM; - si->dma_rx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, - &si->dma_rx_buff_phy, GFP_KERNEL); - if (!si->dma_rx_buff) - goto err_dma_rx_buff; - - si->dma_tx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, - &si->dma_tx_buff_phy, GFP_KERNEL); - if (!si->dma_tx_buff) - goto err_dma_tx_buff; - - /* Setup the serial port for the initial speed. */ - pxa_irda_startup(si); - - /* - * Open a new IrLAP layer instance. - */ - si->irlap = irlap_open(dev, &si->qos, "pxa"); - err = -ENOMEM; - if (!si->irlap) - goto err_irlap; - - /* - * Now enable the interrupt and start the queue - */ - enable_irq(si->uart_irq); - enable_irq(si->icp_irq); - netif_start_queue(dev); - - printk(KERN_DEBUG "pxa_ir: irda driver opened\n"); - - return 0; - -err_irlap: - pxa_irda_shutdown(si); - dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy); -err_dma_tx_buff: - dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy); -err_dma_rx_buff: - dma_release_channel(si->txdma); -err_tx_dma: - dma_release_channel(si->rxdma); -err_rx_dma: - free_irq(si->icp_irq, dev); -err_irq2: - free_irq(si->uart_irq, dev); -err_irq1: - - return err; -} - -static int pxa_irda_stop(struct net_device *dev) -{ - struct pxa_irda *si = netdev_priv(dev); - - netif_stop_queue(dev); - - pxa_irda_shutdown(si); - - /* Stop IrLAP */ - if (si->irlap) { - irlap_close(si->irlap); - si->irlap = NULL; - } - - free_irq(si->uart_irq, dev); - free_irq(si->icp_irq, dev); - - dmaengine_terminate_all(si->rxdma); - dmaengine_terminate_all(si->txdma); - dma_release_channel(si->rxdma); - dma_release_channel(si->txdma); - - if (si->dma_rx_buff) - dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy); - if (si->dma_tx_buff) - dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy); - - printk(KERN_DEBUG "pxa_ir: irda driver closed\n"); - return 0; -} - -static int pxa_irda_suspend(struct platform_device *_dev, pm_message_t state) -{ - struct net_device *dev = platform_get_drvdata(_dev); - struct pxa_irda *si; - - if (dev && netif_running(dev)) { - si = netdev_priv(dev); - netif_device_detach(dev); - pxa_irda_shutdown(si); - } - - return 0; -} - -static int pxa_irda_resume(struct platform_device *_dev) -{ - struct net_device *dev = platform_get_drvdata(_dev); - struct pxa_irda *si; - - if (dev && netif_running(dev)) { - si = netdev_priv(dev); - pxa_irda_startup(si); - netif_device_attach(dev); - netif_wake_queue(dev); - } - - return 0; -} - - -static int pxa_irda_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL | GFP_DMA); - if (io->head != NULL) { - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - } - return io->head ? 0 : -ENOMEM; -} - -static const struct net_device_ops pxa_irda_netdev_ops = { - .ndo_open = pxa_irda_start, - .ndo_stop = pxa_irda_stop, - .ndo_start_xmit = pxa_irda_hard_xmit, - .ndo_do_ioctl = pxa_irda_ioctl, -}; - -static int pxa_irda_probe(struct platform_device *pdev) -{ - struct net_device *dev; - struct resource *res; - struct pxa_irda *si; - void __iomem *ficp, *stuart; - unsigned int baudrate_mask; - int err; - - if (!pdev->dev.platform_data) - return -ENODEV; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ficp = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(ficp)) { - dev_err(&pdev->dev, "resource ficp not defined\n"); - return PTR_ERR(ficp); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - stuart = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(stuart)) { - dev_err(&pdev->dev, "resource stuart not defined\n"); - return PTR_ERR(stuart); - } - - dev = alloc_irdadev(sizeof(struct pxa_irda)); - if (!dev) { - err = -ENOMEM; - goto err_mem_1; - } - - SET_NETDEV_DEV(dev, &pdev->dev); - si = netdev_priv(dev); - si->dev = &pdev->dev; - si->pdata = pdev->dev.platform_data; - - si->irda_base = ficp; - si->stuart_base = stuart; - si->uart_irq = platform_get_irq(pdev, 0); - si->icp_irq = platform_get_irq(pdev, 1); - - si->sir_clk = devm_clk_get(&pdev->dev, "UARTCLK"); - si->fir_clk = devm_clk_get(&pdev->dev, "FICPCLK"); - if (IS_ERR(si->sir_clk) || IS_ERR(si->fir_clk)) { - err = PTR_ERR(IS_ERR(si->sir_clk) ? si->sir_clk : si->fir_clk); - goto err_mem_4; - } - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (res) - si->drcmr_rx = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (res) - si->drcmr_tx = res->start; - - /* - * Initialise the SIR buffers - */ - err = pxa_irda_init_iobuf(&si->rx_buff, 14384); - if (err) - goto err_mem_4; - err = pxa_irda_init_iobuf(&si->tx_buff, 4000); - if (err) - goto err_mem_5; - - if (gpio_is_valid(si->pdata->gpio_pwdown)) { - err = gpio_request(si->pdata->gpio_pwdown, "IrDA switch"); - if (err) - goto err_startup; - err = gpio_direction_output(si->pdata->gpio_pwdown, - !si->pdata->gpio_pwdown_inverted); - if (err) { - gpio_free(si->pdata->gpio_pwdown); - goto err_startup; - } - } - - if (si->pdata->startup) { - err = si->pdata->startup(si->dev); - if (err) - goto err_startup; - } - - if (gpio_is_valid(si->pdata->gpio_pwdown) && si->pdata->startup) - dev_warn(si->dev, "gpio_pwdown and startup() both defined!\n"); - - dev->netdev_ops = &pxa_irda_netdev_ops; - - irda_init_max_qos_capabilies(&si->qos); - - baudrate_mask = 0; - if (si->pdata->transceiver_cap & IR_SIRMODE) - baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - if (si->pdata->transceiver_cap & IR_FIRMODE) - baudrate_mask |= IR_4000000 << 8; - - si->qos.baud_rate.bits &= baudrate_mask; - si->qos.min_turn_time.bits = 7; /* 1ms or more */ - - irda_qos_bits_to_value(&si->qos); - - err = register_netdev(dev); - - if (err == 0) - platform_set_drvdata(pdev, dev); - - if (err) { - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); -err_startup: - kfree(si->tx_buff.head); -err_mem_5: - kfree(si->rx_buff.head); -err_mem_4: - free_netdev(dev); - } -err_mem_1: - return err; -} - -static int pxa_irda_remove(struct platform_device *_dev) -{ - struct net_device *dev = platform_get_drvdata(_dev); - - if (dev) { - struct pxa_irda *si = netdev_priv(dev); - unregister_netdev(dev); - if (gpio_is_valid(si->pdata->gpio_pwdown)) - gpio_free(si->pdata->gpio_pwdown); - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); - kfree(si->tx_buff.head); - kfree(si->rx_buff.head); - free_netdev(dev); - } - - return 0; -} - -static struct platform_driver pxa_ir_driver = { - .driver = { - .name = "pxa2xx-ir", - }, - .probe = pxa_irda_probe, - .remove = pxa_irda_remove, - .suspend = pxa_irda_suspend, - .resume = pxa_irda_resume, -}; - -module_platform_driver(pxa_ir_driver); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pxa2xx-ir"); diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c deleted file mode 100644 index b6e44ff4e373..000000000000 --- a/drivers/net/irda/sa1100_ir.c +++ /dev/null @@ -1,1150 +0,0 @@ -/* - * linux/drivers/net/irda/sa1100_ir.c - * - * Copyright (C) 2000-2001 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Infra-red driver for the StrongARM SA1100 embedded microprocessor - * - * Note that we don't have to worry about the SA1111's DMA bugs in here, - * so we use the straight forward dma_map_* functions with a null pointer. - * - * This driver takes one kernel command line parameter, sa1100ir=, with - * the following options: - * max_rate:baudrate - set the maximum baud rate - * power_level:level - set the transmitter power level - * tx_lpm:0|1 - set transmit low power mode - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -static int power_level = 3; -static int tx_lpm; -static int max_rate = 4000000; - -struct sa1100_buf { - struct device *dev; - struct sk_buff *skb; - struct scatterlist sg; - struct dma_chan *chan; - dma_cookie_t cookie; -}; - -struct sa1100_irda { - unsigned char utcr4; - unsigned char power; - unsigned char open; - - int speed; - int newspeed; - - struct sa1100_buf dma_rx; - struct sa1100_buf dma_tx; - - struct device *dev; - struct irda_platform_data *pdata; - struct irlap_cb *irlap; - struct qos_info qos; - - iobuff_t tx_buff; - iobuff_t rx_buff; - - int (*tx_start)(struct sk_buff *, struct net_device *, struct sa1100_irda *); - irqreturn_t (*irq)(struct net_device *, struct sa1100_irda *); -}; - -static int sa1100_irda_set_speed(struct sa1100_irda *, int); - -#define IS_FIR(si) ((si)->speed >= 4000000) - -#define HPSIR_MAX_RXLEN 2047 - -static struct dma_slave_config sa1100_irda_sir_tx = { - .direction = DMA_TO_DEVICE, - .dst_addr = __PREG(Ser2UTDR), - .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .dst_maxburst = 4, -}; - -static struct dma_slave_config sa1100_irda_fir_rx = { - .direction = DMA_FROM_DEVICE, - .src_addr = __PREG(Ser2HSDR), - .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .src_maxburst = 8, -}; - -static struct dma_slave_config sa1100_irda_fir_tx = { - .direction = DMA_TO_DEVICE, - .dst_addr = __PREG(Ser2HSDR), - .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .dst_maxburst = 8, -}; - -static unsigned sa1100_irda_dma_xferred(struct sa1100_buf *buf) -{ - struct dma_chan *chan = buf->chan; - struct dma_tx_state state; - enum dma_status status; - - status = chan->device->device_tx_status(chan, buf->cookie, &state); - if (status != DMA_PAUSED) - return 0; - - return sg_dma_len(&buf->sg) - state.residue; -} - -static int sa1100_irda_dma_request(struct device *dev, struct sa1100_buf *buf, - const char *name, struct dma_slave_config *cfg) -{ - dma_cap_mask_t m; - int ret; - - dma_cap_zero(m); - dma_cap_set(DMA_SLAVE, m); - - buf->chan = dma_request_channel(m, sa11x0_dma_filter_fn, (void *)name); - if (!buf->chan) { - dev_err(dev, "unable to request DMA channel for %s\n", - name); - return -ENOENT; - } - - ret = dmaengine_slave_config(buf->chan, cfg); - if (ret) - dev_warn(dev, "DMA slave_config for %s returned %d\n", - name, ret); - - buf->dev = buf->chan->device->dev; - - return 0; -} - -static void sa1100_irda_dma_start(struct sa1100_buf *buf, - enum dma_transfer_direction dir, dma_async_tx_callback cb, void *cb_p) -{ - struct dma_async_tx_descriptor *desc; - struct dma_chan *chan = buf->chan; - - desc = dmaengine_prep_slave_sg(chan, &buf->sg, 1, dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (desc) { - desc->callback = cb; - desc->callback_param = cb_p; - buf->cookie = dmaengine_submit(desc); - dma_async_issue_pending(chan); - } -} - -/* - * Allocate and map the receive buffer, unless it is already allocated. - */ -static int sa1100_irda_rx_alloc(struct sa1100_irda *si) -{ - if (si->dma_rx.skb) - return 0; - - si->dma_rx.skb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); - if (!si->dma_rx.skb) { - printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n"); - return -ENOMEM; - } - - /* - * Align any IP headers that may be contained - * within the frame. - */ - skb_reserve(si->dma_rx.skb, 1); - - sg_set_buf(&si->dma_rx.sg, si->dma_rx.skb->data, HPSIR_MAX_RXLEN); - if (dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE) == 0) { - dev_kfree_skb_any(si->dma_rx.skb); - return -ENOMEM; - } - - return 0; -} - -/* - * We want to get here as soon as possible, and get the receiver setup. - * We use the existing buffer. - */ -static void sa1100_irda_rx_dma_start(struct sa1100_irda *si) -{ - if (!si->dma_rx.skb) { - printk(KERN_ERR "sa1100_ir: rx buffer went missing\n"); - return; - } - - /* - * First empty receive FIFO - */ - Ser2HSCR0 = HSCR0_HSSP; - - /* - * Enable the DMA, receiver and receive interrupt. - */ - dmaengine_terminate_all(si->dma_rx.chan); - sa1100_irda_dma_start(&si->dma_rx, DMA_DEV_TO_MEM, NULL, NULL); - - Ser2HSCR0 = HSCR0_HSSP | HSCR0_RXE; -} - -static void sa1100_irda_check_speed(struct sa1100_irda *si) -{ - if (si->newspeed) { - sa1100_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } -} - -/* - * HP-SIR format support. - */ -static void sa1100_irda_sirtxdma_irq(void *id) -{ - struct net_device *dev = id; - struct sa1100_irda *si = netdev_priv(dev); - - dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE); - dev_kfree_skb(si->dma_tx.skb); - si->dma_tx.skb = NULL; - - dev->stats.tx_packets++; - dev->stats.tx_bytes += sg_dma_len(&si->dma_tx.sg); - - /* We need to ensure that the transmitter has finished. */ - do - rmb(); - while (Ser2UTSR1 & UTSR1_TBY); - - /* - * Ok, we've finished transmitting. Now enable the receiver. - * Sometimes we get a receive IRQ immediately after a transmit... - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; - - sa1100_irda_check_speed(si); - - /* I'm hungry! */ - netif_wake_queue(dev); -} - -static int sa1100_irda_sir_tx_start(struct sk_buff *skb, struct net_device *dev, - struct sa1100_irda *si) -{ - si->tx_buff.data = si->tx_buff.head; - si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, - si->tx_buff.truesize); - - si->dma_tx.skb = skb; - sg_set_buf(&si->dma_tx.sg, si->tx_buff.data, si->tx_buff.len); - if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { - si->dma_tx.skb = NULL; - netif_wake_queue(dev); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - - sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_sirtxdma_irq, dev); - - /* - * The mean turn-around time is enforced by XBOF padding, - * so we don't have to do anything special here. - */ - Ser2UTCR3 = UTCR3_TXE; - - return NETDEV_TX_OK; -} - -static irqreturn_t sa1100_irda_sir_irq(struct net_device *dev, struct sa1100_irda *si) -{ - int status; - - status = Ser2UTSR0; - - /* - * Deal with any receive errors first. The bytes in error may be - * the only bytes in the receive FIFO, so we do this first. - */ - while (status & UTSR0_EIF) { - int stat, data; - - stat = Ser2UTSR1; - data = Ser2UTDR; - - if (stat & (UTSR1_FRE | UTSR1_ROR)) { - dev->stats.rx_errors++; - if (stat & UTSR1_FRE) - dev->stats.rx_frame_errors++; - if (stat & UTSR1_ROR) - dev->stats.rx_fifo_errors++; - } else - async_unwrap_char(dev, &dev->stats, &si->rx_buff, data); - - status = Ser2UTSR0; - } - - /* - * We must clear certain bits. - */ - Ser2UTSR0 = status & (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - - if (status & UTSR0_RFS) { - /* - * There are at least 4 bytes in the FIFO. Read 3 bytes - * and leave the rest to the block below. - */ - async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); - async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); - async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); - } - - if (status & (UTSR0_RFS | UTSR0_RID)) { - /* - * Fifo contains more than 1 character. - */ - do { - async_unwrap_char(dev, &dev->stats, &si->rx_buff, - Ser2UTDR); - } while (Ser2UTSR1 & UTSR1_RNE); - - } - - return IRQ_HANDLED; -} - -/* - * FIR format support. - */ -static void sa1100_irda_firtxdma_irq(void *id) -{ - struct net_device *dev = id; - struct sa1100_irda *si = netdev_priv(dev); - struct sk_buff *skb; - - /* - * Wait for the transmission to complete. Unfortunately, - * the hardware doesn't give us an interrupt to indicate - * "end of frame". - */ - do - rmb(); - while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY); - - /* - * Clear the transmit underrun bit. - */ - Ser2HSSR0 = HSSR0_TUR; - - /* - * Do we need to change speed? Note that we're lazy - * here - we don't free the old dma_rx.skb. We don't need - * to allocate a buffer either. - */ - sa1100_irda_check_speed(si); - - /* - * Start reception. This disables the transmitter for - * us. This will be using the existing RX buffer. - */ - sa1100_irda_rx_dma_start(si); - - /* Account and free the packet. */ - skb = si->dma_tx.skb; - if (skb) { - dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, - DMA_TO_DEVICE); - dev->stats.tx_packets ++; - dev->stats.tx_bytes += skb->len; - dev_kfree_skb_irq(skb); - si->dma_tx.skb = NULL; - } - - /* - * Make sure that the TX queue is available for sending - * (for retries). TX has priority over RX at all times. - */ - netif_wake_queue(dev); -} - -static int sa1100_irda_fir_tx_start(struct sk_buff *skb, struct net_device *dev, - struct sa1100_irda *si) -{ - int mtt = irda_get_mtt(skb); - - si->dma_tx.skb = skb; - sg_set_buf(&si->dma_tx.sg, skb->data, skb->len); - if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { - si->dma_tx.skb = NULL; - netif_wake_queue(dev); - dev->stats.tx_dropped++; - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_firtxdma_irq, dev); - - /* - * If we have a mean turn-around time, impose the specified - * specified delay. We could shorten this by timing from - * the point we received the packet. - */ - if (mtt) - udelay(mtt); - - Ser2HSCR0 = HSCR0_HSSP | HSCR0_TXE; - - return NETDEV_TX_OK; -} - -static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev) -{ - struct sk_buff *skb = si->dma_rx.skb; - unsigned int len, stat, data; - - if (!skb) { - printk(KERN_ERR "sa1100_ir: SKB is NULL!\n"); - return; - } - - /* - * Get the current data position. - */ - len = sa1100_irda_dma_xferred(&si->dma_rx); - if (len > HPSIR_MAX_RXLEN) - len = HPSIR_MAX_RXLEN; - dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); - - do { - /* - * Read Status, and then Data. - */ - stat = Ser2HSSR1; - rmb(); - data = Ser2HSDR; - - if (stat & (HSSR1_CRE | HSSR1_ROR)) { - dev->stats.rx_errors++; - if (stat & HSSR1_CRE) - dev->stats.rx_crc_errors++; - if (stat & HSSR1_ROR) - dev->stats.rx_frame_errors++; - } else - skb->data[len++] = data; - - /* - * If we hit the end of frame, there's - * no point in continuing. - */ - if (stat & HSSR1_EOF) - break; - } while (Ser2HSSR0 & HSSR0_EIF); - - if (stat & HSSR1_EOF) { - si->dma_rx.skb = NULL; - - skb_put(skb, len); - skb->dev = dev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - - /* - * Before we pass the buffer up, allocate a new one. - */ - sa1100_irda_rx_alloc(si); - - netif_rx(skb); - } else { - /* - * Remap the buffer - it was previously mapped, and we - * hope that this succeeds. - */ - dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); - } -} - -/* - * We only have to handle RX events here; transmit events go via the TX - * DMA handler. We disable RX, process, and the restart RX. - */ -static irqreturn_t sa1100_irda_fir_irq(struct net_device *dev, struct sa1100_irda *si) -{ - /* - * Stop RX DMA - */ - dmaengine_pause(si->dma_rx.chan); - - /* - * Framing error - we throw away the packet completely. - * Clearing RXE flushes the error conditions and data - * from the fifo. - */ - if (Ser2HSSR0 & (HSSR0_FRE | HSSR0_RAB)) { - dev->stats.rx_errors++; - - if (Ser2HSSR0 & HSSR0_FRE) - dev->stats.rx_frame_errors++; - - /* - * Clear out the DMA... - */ - Ser2HSCR0 = HSCR0_HSSP; - - /* - * Clear selected status bits now, so we - * don't miss them next time around. - */ - Ser2HSSR0 = HSSR0_FRE | HSSR0_RAB; - } - - /* - * Deal with any receive errors. The any of the lowest - * 8 bytes in the FIFO may contain an error. We must read - * them one by one. The "error" could even be the end of - * packet! - */ - if (Ser2HSSR0 & HSSR0_EIF) - sa1100_irda_fir_error(si, dev); - - /* - * No matter what happens, we must restart reception. - */ - sa1100_irda_rx_dma_start(si); - - return IRQ_HANDLED; -} - -/* - * Set the IrDA communications speed. - */ -static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) -{ - unsigned long flags; - int brd, ret = -EINVAL; - - switch (speed) { - case 9600: case 19200: case 38400: - case 57600: case 115200: - brd = 3686400 / (16 * speed) - 1; - - /* Stop the receive DMA, and configure transmit. */ - if (IS_FIR(si)) { - dmaengine_terminate_all(si->dma_rx.chan); - dmaengine_slave_config(si->dma_tx.chan, - &sa1100_irda_sir_tx); - } - - local_irq_save(flags); - - Ser2UTCR3 = 0; - Ser2HSCR0 = HSCR0_UART; - - Ser2UTCR1 = brd >> 8; - Ser2UTCR2 = brd; - - /* - * Clear status register - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; - - if (si->pdata->set_speed) - si->pdata->set_speed(si->dev, speed); - - si->speed = speed; - si->tx_start = sa1100_irda_sir_tx_start; - si->irq = sa1100_irda_sir_irq; - - local_irq_restore(flags); - ret = 0; - break; - - case 4000000: - if (!IS_FIR(si)) - dmaengine_slave_config(si->dma_tx.chan, - &sa1100_irda_fir_tx); - - local_irq_save(flags); - - Ser2HSSR0 = 0xff; - Ser2HSCR0 = HSCR0_HSSP; - Ser2UTCR3 = 0; - - si->speed = speed; - si->tx_start = sa1100_irda_fir_tx_start; - si->irq = sa1100_irda_fir_irq; - - if (si->pdata->set_speed) - si->pdata->set_speed(si->dev, speed); - - sa1100_irda_rx_alloc(si); - sa1100_irda_rx_dma_start(si); - - local_irq_restore(flags); - - break; - - default: - break; - } - - return ret; -} - -/* - * Control the power state of the IrDA transmitter. - * State: - * 0 - off - * 1 - short range, lowest power - * 2 - medium range, medium power - * 3 - maximum range, high power - * - * Currently, only assabet is known to support this. - */ -static int -__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state) -{ - int ret = 0; - if (si->pdata->set_power) - ret = si->pdata->set_power(si->dev, state); - return ret; -} - -static inline int -sa1100_set_power(struct sa1100_irda *si, unsigned int state) -{ - int ret; - - ret = __sa1100_irda_set_power(si, state); - if (ret == 0) - si->power = state; - - return ret; -} - -static irqreturn_t sa1100_irda_irq(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct sa1100_irda *si = netdev_priv(dev); - - return si->irq(dev, si); -} - -static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct sa1100_irda *si = netdev_priv(dev); - int speed = irda_get_next_speed(skb); - - /* - * Does this packet contain a request to change the interface - * speed? If so, remember it until we complete the transmission - * of this frame. - */ - if (speed != si->speed && speed != -1) - si->newspeed = speed; - - /* If this is an empty frame, we can bypass a lot. */ - if (skb->len == 0) { - sa1100_irda_check_speed(si); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - netif_stop_queue(dev); - - /* We must not already have a skb to transmit... */ - BUG_ON(si->dma_tx.skb); - - return si->tx_start(skb, dev, si); -} - -static int -sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) -{ - struct if_irda_req *rq = (struct if_irda_req *)ifreq; - struct sa1100_irda *si = netdev_priv(dev); - int ret = -EOPNOTSUPP; - - switch (cmd) { - case SIOCSBANDWIDTH: - if (capable(CAP_NET_ADMIN)) { - /* - * We are unable to set the speed if the - * device is not running. - */ - if (si->open) { - ret = sa1100_irda_set_speed(si, - rq->ifr_baudrate); - } else { - printk("sa1100_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n"); - ret = 0; - } - } - break; - - case SIOCSMEDIABUSY: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - irda_device_set_media_busy(dev, TRUE); - ret = 0; - } - break; - - case SIOCGRECEIVING: - rq->ifr_receiving = IS_FIR(si) ? 0 - : si->rx_buff.state != OUTSIDE_FRAME; - break; - - default: - break; - } - - return ret; -} - -static int sa1100_irda_startup(struct sa1100_irda *si) -{ - int ret; - - /* - * Ensure that the ports for this device are setup correctly. - */ - if (si->pdata->startup) { - ret = si->pdata->startup(si->dev); - if (ret) - return ret; - } - - /* - * Configure PPC for IRDA - we want to drive TXD2 low. - * We also want to drive this pin low during sleep. - */ - PPSR &= ~PPC_TXD2; - PSDR &= ~PPC_TXD2; - PPDR |= PPC_TXD2; - - /* - * Enable HP-SIR modulation, and ensure that the port is disabled. - */ - Ser2UTCR3 = 0; - Ser2HSCR0 = HSCR0_UART; - Ser2UTCR4 = si->utcr4; - Ser2UTCR0 = UTCR0_8BitData; - Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL; - - /* - * Clear status register - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - - ret = sa1100_irda_set_speed(si, si->speed = 9600); - if (ret) { - Ser2UTCR3 = 0; - Ser2HSCR0 = 0; - - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); - } - - return ret; -} - -static void sa1100_irda_shutdown(struct sa1100_irda *si) -{ - /* - * Stop all DMA activity. - */ - dmaengine_terminate_all(si->dma_rx.chan); - dmaengine_terminate_all(si->dma_tx.chan); - - /* Disable the port. */ - Ser2UTCR3 = 0; - Ser2HSCR0 = 0; - - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); -} - -static int sa1100_irda_start(struct net_device *dev) -{ - struct sa1100_irda *si = netdev_priv(dev); - int err; - - si->speed = 9600; - - err = sa1100_irda_dma_request(si->dev, &si->dma_rx, "Ser2ICPRc", - &sa1100_irda_fir_rx); - if (err) - goto err_rx_dma; - - err = sa1100_irda_dma_request(si->dev, &si->dma_tx, "Ser2ICPTr", - &sa1100_irda_sir_tx); - if (err) - goto err_tx_dma; - - /* - * Setup the serial port for the specified speed. - */ - err = sa1100_irda_startup(si); - if (err) - goto err_startup; - - /* - * Open a new IrLAP layer instance. - */ - si->irlap = irlap_open(dev, &si->qos, "sa1100"); - err = -ENOMEM; - if (!si->irlap) - goto err_irlap; - - err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev); - if (err) - goto err_irq; - - /* - * Now enable the interrupt and start the queue - */ - si->open = 1; - sa1100_set_power(si, power_level); /* low power mode */ - - netif_start_queue(dev); - return 0; - -err_irq: - irlap_close(si->irlap); -err_irlap: - si->open = 0; - sa1100_irda_shutdown(si); -err_startup: - dma_release_channel(si->dma_tx.chan); -err_tx_dma: - dma_release_channel(si->dma_rx.chan); -err_rx_dma: - return err; -} - -static int sa1100_irda_stop(struct net_device *dev) -{ - struct sa1100_irda *si = netdev_priv(dev); - struct sk_buff *skb; - - netif_stop_queue(dev); - - si->open = 0; - sa1100_irda_shutdown(si); - - /* - * If we have been doing any DMA activity, make sure we - * tidy that up cleanly. - */ - skb = si->dma_rx.skb; - if (skb) { - dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, - DMA_FROM_DEVICE); - dev_kfree_skb(skb); - si->dma_rx.skb = NULL; - } - - skb = si->dma_tx.skb; - if (skb) { - dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, - DMA_TO_DEVICE); - dev_kfree_skb(skb); - si->dma_tx.skb = NULL; - } - - /* Stop IrLAP */ - if (si->irlap) { - irlap_close(si->irlap); - si->irlap = NULL; - } - - /* - * Free resources - */ - dma_release_channel(si->dma_tx.chan); - dma_release_channel(si->dma_rx.chan); - free_irq(dev->irq, dev); - - sa1100_set_power(si, 0); - - return 0; -} - -static int sa1100_irda_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL | GFP_DMA); - if (io->head != NULL) { - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - } - return io->head ? 0 : -ENOMEM; -} - -static const struct net_device_ops sa1100_irda_netdev_ops = { - .ndo_open = sa1100_irda_start, - .ndo_stop = sa1100_irda_stop, - .ndo_start_xmit = sa1100_irda_hard_xmit, - .ndo_do_ioctl = sa1100_irda_ioctl, -}; - -static int sa1100_irda_probe(struct platform_device *pdev) -{ - struct net_device *dev; - struct sa1100_irda *si; - unsigned int baudrate_mask; - int err, irq; - - if (!pdev->dev.platform_data) - return -EINVAL; - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return irq < 0 ? irq : -ENXIO; - - err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_1; - err = request_mem_region(__PREG(Ser2HSCR0), 0x1c, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_2; - err = request_mem_region(__PREG(Ser2HSCR2), 0x04, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_3; - - dev = alloc_irdadev(sizeof(struct sa1100_irda)); - if (!dev) { - err = -ENOMEM; - goto err_mem_4; - } - - SET_NETDEV_DEV(dev, &pdev->dev); - - si = netdev_priv(dev); - si->dev = &pdev->dev; - si->pdata = pdev->dev.platform_data; - - sg_init_table(&si->dma_rx.sg, 1); - sg_init_table(&si->dma_tx.sg, 1); - - /* - * Initialise the HP-SIR buffers - */ - err = sa1100_irda_init_iobuf(&si->rx_buff, 14384); - if (err) - goto err_mem_5; - err = sa1100_irda_init_iobuf(&si->tx_buff, IRDA_SIR_MAX_FRAME); - if (err) - goto err_mem_5; - - dev->netdev_ops = &sa1100_irda_netdev_ops; - dev->irq = irq; - - irda_init_max_qos_capabilies(&si->qos); - - /* - * We support original IRDA up to 115k2. (we don't currently - * support 4Mbps). Min Turn Time set to 1ms or greater. - */ - baudrate_mask = IR_9600; - - switch (max_rate) { - case 4000000: baudrate_mask |= IR_4000000 << 8; - case 115200: baudrate_mask |= IR_115200; - case 57600: baudrate_mask |= IR_57600; - case 38400: baudrate_mask |= IR_38400; - case 19200: baudrate_mask |= IR_19200; - } - - si->qos.baud_rate.bits &= baudrate_mask; - si->qos.min_turn_time.bits = 7; - - irda_qos_bits_to_value(&si->qos); - - si->utcr4 = UTCR4_HPSIR; - if (tx_lpm) - si->utcr4 |= UTCR4_Z1_6us; - - /* - * Initially enable HP-SIR modulation, and ensure that the port - * is disabled. - */ - Ser2UTCR3 = 0; - Ser2UTCR4 = si->utcr4; - Ser2HSCR0 = HSCR0_UART; - - err = register_netdev(dev); - if (err == 0) - platform_set_drvdata(pdev, dev); - - if (err) { - err_mem_5: - kfree(si->tx_buff.head); - kfree(si->rx_buff.head); - free_netdev(dev); - err_mem_4: - release_mem_region(__PREG(Ser2HSCR2), 0x04); - err_mem_3: - release_mem_region(__PREG(Ser2HSCR0), 0x1c); - err_mem_2: - release_mem_region(__PREG(Ser2UTCR0), 0x24); - } - err_mem_1: - return err; -} - -static int sa1100_irda_remove(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - - if (dev) { - struct sa1100_irda *si = netdev_priv(dev); - unregister_netdev(dev); - kfree(si->tx_buff.head); - kfree(si->rx_buff.head); - free_netdev(dev); - } - - release_mem_region(__PREG(Ser2HSCR2), 0x04); - release_mem_region(__PREG(Ser2HSCR0), 0x1c); - release_mem_region(__PREG(Ser2UTCR0), 0x24); - - return 0; -} - -#ifdef CONFIG_PM -/* - * Suspend the IrDA interface. - */ -static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct sa1100_irda *si; - - if (!dev) - return 0; - - si = netdev_priv(dev); - if (si->open) { - /* - * Stop the transmit queue - */ - netif_device_detach(dev); - disable_irq(dev->irq); - sa1100_irda_shutdown(si); - __sa1100_irda_set_power(si, 0); - } - - return 0; -} - -/* - * Resume the IrDA interface. - */ -static int sa1100_irda_resume(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct sa1100_irda *si; - - if (!dev) - return 0; - - si = netdev_priv(dev); - if (si->open) { - /* - * If we missed a speed change, initialise at the new speed - * directly. It is debatable whether this is actually - * required, but in the interests of continuing from where - * we left off it is desirable. The converse argument is - * that we should re-negotiate at 9600 baud again. - */ - if (si->newspeed) { - si->speed = si->newspeed; - si->newspeed = 0; - } - - sa1100_irda_startup(si); - __sa1100_irda_set_power(si, si->power); - enable_irq(dev->irq); - - /* - * This automatically wakes up the queue - */ - netif_device_attach(dev); - } - - return 0; -} -#else -#define sa1100_irda_suspend NULL -#define sa1100_irda_resume NULL -#endif - -static struct platform_driver sa1100ir_driver = { - .probe = sa1100_irda_probe, - .remove = sa1100_irda_remove, - .suspend = sa1100_irda_suspend, - .resume = sa1100_irda_resume, - .driver = { - .name = "sa11x0-ir", - }, -}; - -static int __init sa1100_irda_init(void) -{ - /* - * Limit power level a sensible range. - */ - if (power_level < 1) - power_level = 1; - if (power_level > 3) - power_level = 3; - - return platform_driver_register(&sa1100ir_driver); -} - -static void __exit sa1100_irda_exit(void) -{ - platform_driver_unregister(&sa1100ir_driver); -} - -module_init(sa1100_irda_init); -module_exit(sa1100_irda_exit); -module_param(power_level, int, 0); -module_param(tx_lpm, int, 0); -module_param(max_rate, int, 0); - -MODULE_AUTHOR("Russell King "); -MODULE_DESCRIPTION("StrongARM SA1100 IrDA driver"); -MODULE_LICENSE("GPL"); -MODULE_PARM_DESC(power_level, "IrDA power level, 1 (low) to 3 (high)"); -MODULE_PARM_DESC(tx_lpm, "Enable transmitter low power (1.6us) mode"); -MODULE_PARM_DESC(max_rate, "Maximum baud rate (4000000, 115200, 57600, 38400, 19200, 9600)"); -MODULE_ALIAS("platform:sa11x0-ir"); diff --git a/drivers/net/irda/sh_sir.c b/drivers/net/irda/sh_sir.c deleted file mode 100644 index fede6864c737..000000000000 --- a/drivers/net/irda/sh_sir.c +++ /dev/null @@ -1,810 +0,0 @@ -/* - * SuperH IrDA Driver - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on bfin_sir.c - * Copyright 2006-2009 Analog Devices Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "sh_sir" - -#define RX_PHASE (1 << 0) -#define TX_PHASE (1 << 1) -#define TX_COMP_PHASE (1 << 2) /* tx complete */ -#define NONE_PHASE (1 << 31) - -#define IRIF_RINTCLR 0x0016 /* DMA rx interrupt source clear */ -#define IRIF_TINTCLR 0x0018 /* DMA tx interrupt source clear */ -#define IRIF_SIR0 0x0020 /* IrDA-SIR10 control */ -#define IRIF_SIR1 0x0022 /* IrDA-SIR10 baudrate error correction */ -#define IRIF_SIR2 0x0024 /* IrDA-SIR10 baudrate count */ -#define IRIF_SIR3 0x0026 /* IrDA-SIR10 status */ -#define IRIF_SIR_FRM 0x0028 /* Hardware frame processing set */ -#define IRIF_SIR_EOF 0x002A /* EOF value */ -#define IRIF_SIR_FLG 0x002C /* Flag clear */ -#define IRIF_UART_STS2 0x002E /* UART status 2 */ -#define IRIF_UART0 0x0030 /* UART control */ -#define IRIF_UART1 0x0032 /* UART status */ -#define IRIF_UART2 0x0034 /* UART mode */ -#define IRIF_UART3 0x0036 /* UART transmit data */ -#define IRIF_UART4 0x0038 /* UART receive data */ -#define IRIF_UART5 0x003A /* UART interrupt mask */ -#define IRIF_UART6 0x003C /* UART baud rate error correction */ -#define IRIF_UART7 0x003E /* UART baud rate count set */ -#define IRIF_CRC0 0x0040 /* CRC engine control */ -#define IRIF_CRC1 0x0042 /* CRC engine input data */ -#define IRIF_CRC2 0x0044 /* CRC engine calculation */ -#define IRIF_CRC3 0x0046 /* CRC engine output data 1 */ -#define IRIF_CRC4 0x0048 /* CRC engine output data 2 */ - -/* IRIF_SIR0 */ -#define IRTPW (1 << 1) /* transmit pulse width select */ -#define IRERRC (1 << 0) /* Clear receive pulse width error */ - -/* IRIF_SIR3 */ -#define IRERR (1 << 0) /* received pulse width Error */ - -/* IRIF_SIR_FRM */ -#define EOFD (1 << 9) /* EOF detection flag */ -#define FRER (1 << 8) /* Frame Error bit */ -#define FRP (1 << 0) /* Frame processing set */ - -/* IRIF_UART_STS2 */ -#define IRSME (1 << 6) /* Receive Sum Error flag */ -#define IROVE (1 << 5) /* Receive Overrun Error flag */ -#define IRFRE (1 << 4) /* Receive Framing Error flag */ -#define IRPRE (1 << 3) /* Receive Parity Error flag */ - -/* IRIF_UART0_*/ -#define TBEC (1 << 2) /* Transmit Data Clear */ -#define RIE (1 << 1) /* Receive Enable */ -#define TIE (1 << 0) /* Transmit Enable */ - -/* IRIF_UART1 */ -#define URSME (1 << 6) /* Receive Sum Error Flag */ -#define UROVE (1 << 5) /* Receive Overrun Error Flag */ -#define URFRE (1 << 4) /* Receive Framing Error Flag */ -#define URPRE (1 << 3) /* Receive Parity Error Flag */ -#define RBF (1 << 2) /* Receive Buffer Full Flag */ -#define TSBE (1 << 1) /* Transmit Shift Buffer Empty Flag */ -#define TBE (1 << 0) /* Transmit Buffer Empty flag */ -#define TBCOMP (TSBE | TBE) - -/* IRIF_UART5 */ -#define RSEIM (1 << 6) /* Receive Sum Error Flag IRQ Mask */ -#define RBFIM (1 << 2) /* Receive Buffer Full Flag IRQ Mask */ -#define TSBEIM (1 << 1) /* Transmit Shift Buffer Empty Flag IRQ Mask */ -#define TBEIM (1 << 0) /* Transmit Buffer Empty Flag IRQ Mask */ -#define RX_MASK (RSEIM | RBFIM) - -/* IRIF_CRC0 */ -#define CRC_RST (1 << 15) /* CRC Engine Reset */ -#define CRC_CT_MASK 0x0FFF - -/************************************************************************ - - - structure - - -************************************************************************/ -struct sh_sir_self { - void __iomem *membase; - unsigned int irq; - struct clk *clk; - - struct net_device *ndev; - - struct irlap_cb *irlap; - struct qos_info qos; - - iobuff_t tx_buff; - iobuff_t rx_buff; -}; - -/************************************************************************ - - - common function - - -************************************************************************/ -static void sh_sir_write(struct sh_sir_self *self, u32 offset, u16 data) -{ - iowrite16(data, self->membase + offset); -} - -static u16 sh_sir_read(struct sh_sir_self *self, u32 offset) -{ - return ioread16(self->membase + offset); -} - -static void sh_sir_update_bits(struct sh_sir_self *self, u32 offset, - u16 mask, u16 data) -{ - u16 old, new; - - old = sh_sir_read(self, offset); - new = (old & ~mask) | data; - if (old != new) - sh_sir_write(self, offset, new); -} - -/************************************************************************ - - - CRC function - - -************************************************************************/ -static void sh_sir_crc_reset(struct sh_sir_self *self) -{ - sh_sir_write(self, IRIF_CRC0, CRC_RST); -} - -static void sh_sir_crc_add(struct sh_sir_self *self, u8 data) -{ - sh_sir_write(self, IRIF_CRC1, (u16)data); -} - -static u16 sh_sir_crc_cnt(struct sh_sir_self *self) -{ - return CRC_CT_MASK & sh_sir_read(self, IRIF_CRC0); -} - -static u16 sh_sir_crc_out(struct sh_sir_self *self) -{ - return sh_sir_read(self, IRIF_CRC4); -} - -static int sh_sir_crc_init(struct sh_sir_self *self) -{ - struct device *dev = &self->ndev->dev; - int ret = -EIO; - u16 val; - - sh_sir_crc_reset(self); - - sh_sir_crc_add(self, 0xCC); - sh_sir_crc_add(self, 0xF5); - sh_sir_crc_add(self, 0xF1); - sh_sir_crc_add(self, 0xA7); - - val = sh_sir_crc_cnt(self); - if (4 != val) { - dev_err(dev, "CRC count error %x\n", val); - goto crc_init_out; - } - - val = sh_sir_crc_out(self); - if (0x51DF != val) { - dev_err(dev, "CRC result error%x\n", val); - goto crc_init_out; - } - - ret = 0; - -crc_init_out: - - sh_sir_crc_reset(self); - return ret; -} - -/************************************************************************ - - - baud rate functions - - -************************************************************************/ -#define SCLK_BASE 1843200 /* 1.8432MHz */ - -static u32 sh_sir_find_sclk(struct clk *irda_clk) -{ - struct cpufreq_frequency_table *freq_table = irda_clk->freq_table; - struct cpufreq_frequency_table *pos; - struct clk *pclk = clk_get(NULL, "peripheral_clk"); - u32 limit, min = 0xffffffff, tmp; - int index = 0; - - limit = clk_get_rate(pclk); - clk_put(pclk); - - /* IrDA can not set over peripheral_clk */ - cpufreq_for_each_valid_entry(pos, freq_table) { - u32 freq = pos->frequency; - - /* IrDA should not over peripheral_clk */ - if (freq > limit) - continue; - - tmp = freq % SCLK_BASE; - if (tmp < min) { - min = tmp; - index = pos - freq_table; - } - } - - return freq_table[index].frequency; -} - -#define ERR_ROUNDING(a) ((a + 5000) / 10000) -static int sh_sir_set_baudrate(struct sh_sir_self *self, u32 baudrate) -{ - struct clk *clk; - struct device *dev = &self->ndev->dev; - u32 rate; - u16 uabca, uabc; - u16 irbca, irbc; - u32 min, rerr, tmp; - int i; - - /* Baud Rate Error Correction x 10000 */ - u32 rate_err_array[] = { - 0, 625, 1250, 1875, - 2500, 3125, 3750, 4375, - 5000, 5625, 6250, 6875, - 7500, 8125, 8750, 9375, - }; - - /* - * FIXME - * - * it support 9600 only now - */ - switch (baudrate) { - case 9600: - break; - default: - dev_err(dev, "un-supported baudrate %d\n", baudrate); - return -EIO; - } - - clk = clk_get(NULL, "irda_clk"); - if (IS_ERR(clk)) { - dev_err(dev, "can not get irda_clk\n"); - return -EIO; - } - - clk_set_rate(clk, sh_sir_find_sclk(clk)); - rate = clk_get_rate(clk); - clk_put(clk); - - dev_dbg(dev, "selected sclk = %d\n", rate); - - /* - * CALCULATION - * - * 1843200 = system rate / (irbca + (irbc + 1)) - */ - - irbc = rate / SCLK_BASE; - - tmp = rate - (SCLK_BASE * irbc); - tmp *= 10000; - - rerr = tmp / SCLK_BASE; - - min = 0xffffffff; - irbca = 0; - for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) { - tmp = abs(rate_err_array[i] - rerr); - if (min > tmp) { - min = tmp; - irbca = i; - } - } - - tmp = rate / (irbc + ERR_ROUNDING(rate_err_array[irbca])); - if ((SCLK_BASE / 100) < abs(tmp - SCLK_BASE)) - dev_warn(dev, "IrDA freq error margin over %d\n", tmp); - - dev_dbg(dev, "target = %d, result = %d, infrared = %d.%d\n", - SCLK_BASE, tmp, irbc, rate_err_array[irbca]); - - irbca = (irbca & 0xF) << 4; - irbc = (irbc - 1) & 0xF; - - if (!irbc) { - dev_err(dev, "sh_sir can not set 0 in IRIF_SIR2\n"); - return -EIO; - } - - sh_sir_write(self, IRIF_SIR0, IRTPW | IRERRC); - sh_sir_write(self, IRIF_SIR1, irbca); - sh_sir_write(self, IRIF_SIR2, irbc); - - /* - * CALCULATION - * - * BaudRate[bps] = system rate / (uabca + (uabc + 1) x 16) - */ - - uabc = rate / baudrate; - uabc = (uabc / 16) - 1; - uabc = (uabc + 1) * 16; - - tmp = rate - (uabc * baudrate); - tmp *= 10000; - - rerr = tmp / baudrate; - - min = 0xffffffff; - uabca = 0; - for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) { - tmp = abs(rate_err_array[i] - rerr); - if (min > tmp) { - min = tmp; - uabca = i; - } - } - - tmp = rate / (uabc + ERR_ROUNDING(rate_err_array[uabca])); - if ((baudrate / 100) < abs(tmp - baudrate)) - dev_warn(dev, "UART freq error margin over %d\n", tmp); - - dev_dbg(dev, "target = %d, result = %d, uart = %d.%d\n", - baudrate, tmp, - uabc, rate_err_array[uabca]); - - uabca = (uabca & 0xF) << 4; - uabc = (uabc / 16) - 1; - - sh_sir_write(self, IRIF_UART6, uabca); - sh_sir_write(self, IRIF_UART7, uabc); - - return 0; -} - -/************************************************************************ - - - iobuf function - - -************************************************************************/ -static int __sh_sir_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL); - if (!io->head) - return -ENOMEM; - - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - - return 0; -} - -static void sh_sir_remove_iobuf(struct sh_sir_self *self) -{ - kfree(self->rx_buff.head); - kfree(self->tx_buff.head); - - self->rx_buff.head = NULL; - self->tx_buff.head = NULL; -} - -static int sh_sir_init_iobuf(struct sh_sir_self *self, int rxsize, int txsize) -{ - int err = -ENOMEM; - - if (self->rx_buff.head || - self->tx_buff.head) { - dev_err(&self->ndev->dev, "iobuff has already existed."); - return err; - } - - err = __sh_sir_init_iobuf(&self->rx_buff, rxsize); - if (err) - goto iobuf_err; - - err = __sh_sir_init_iobuf(&self->tx_buff, txsize); - -iobuf_err: - if (err) - sh_sir_remove_iobuf(self); - - return err; -} - -/************************************************************************ - - - status function - - -************************************************************************/ -static void sh_sir_clear_all_err(struct sh_sir_self *self) -{ - /* Clear error flag for receive pulse width */ - sh_sir_update_bits(self, IRIF_SIR0, IRERRC, IRERRC); - - /* Clear frame / EOF error flag */ - sh_sir_write(self, IRIF_SIR_FLG, 0xffff); - - /* Clear all status error */ - sh_sir_write(self, IRIF_UART_STS2, 0); -} - -static void sh_sir_set_phase(struct sh_sir_self *self, int phase) -{ - u16 uart5 = 0; - u16 uart0 = 0; - - switch (phase) { - case TX_PHASE: - uart5 = TBEIM; - uart0 = TBEC | TIE; - break; - case TX_COMP_PHASE: - uart5 = TSBEIM; - uart0 = TIE; - break; - case RX_PHASE: - uart5 = RX_MASK; - uart0 = RIE; - break; - default: - break; - } - - sh_sir_write(self, IRIF_UART5, uart5); - sh_sir_write(self, IRIF_UART0, uart0); -} - -static int sh_sir_is_which_phase(struct sh_sir_self *self) -{ - u16 val = sh_sir_read(self, IRIF_UART5); - - if (val & TBEIM) - return TX_PHASE; - - if (val & TSBEIM) - return TX_COMP_PHASE; - - if (val & RX_MASK) - return RX_PHASE; - - return NONE_PHASE; -} - -static void sh_sir_tx(struct sh_sir_self *self, int phase) -{ - switch (phase) { - case TX_PHASE: - if (0 >= self->tx_buff.len) { - sh_sir_set_phase(self, TX_COMP_PHASE); - } else { - sh_sir_write(self, IRIF_UART3, self->tx_buff.data[0]); - self->tx_buff.len--; - self->tx_buff.data++; - } - break; - case TX_COMP_PHASE: - sh_sir_set_phase(self, RX_PHASE); - netif_wake_queue(self->ndev); - break; - default: - dev_err(&self->ndev->dev, "should not happen\n"); - break; - } -} - -static int sh_sir_read_data(struct sh_sir_self *self) -{ - u16 val = 0; - int timeout = 1024; - - while (timeout--) { - val = sh_sir_read(self, IRIF_UART1); - - /* data get */ - if (val & RBF) { - if (val & (URSME | UROVE | URFRE | URPRE)) - break; - - return (int)sh_sir_read(self, IRIF_UART4); - } - - udelay(1); - } - - dev_err(&self->ndev->dev, "UART1 %04x : STATUS %04x\n", - val, sh_sir_read(self, IRIF_UART_STS2)); - - /* read data register for clear error */ - sh_sir_read(self, IRIF_UART4); - - return -1; -} - -static void sh_sir_rx(struct sh_sir_self *self) -{ - int timeout = 1024; - int data; - - while (timeout--) { - data = sh_sir_read_data(self); - if (data < 0) - break; - - async_unwrap_char(self->ndev, &self->ndev->stats, - &self->rx_buff, (u8)data); - - if (EOFD & sh_sir_read(self, IRIF_SIR_FRM)) - continue; - - break; - } -} - -static irqreturn_t sh_sir_irq(int irq, void *dev_id) -{ - struct sh_sir_self *self = dev_id; - struct device *dev = &self->ndev->dev; - int phase = sh_sir_is_which_phase(self); - - switch (phase) { - case TX_COMP_PHASE: - case TX_PHASE: - sh_sir_tx(self, phase); - break; - case RX_PHASE: - if (sh_sir_read(self, IRIF_SIR3)) - dev_err(dev, "rcv pulse width error occurred\n"); - - sh_sir_rx(self); - sh_sir_clear_all_err(self); - break; - default: - dev_err(dev, "unknown interrupt\n"); - } - - return IRQ_HANDLED; -} - -/************************************************************************ - - - net_device_ops function - - -************************************************************************/ -static int sh_sir_hard_xmit(struct sk_buff *skb, struct net_device *ndev) -{ - struct sh_sir_self *self = netdev_priv(ndev); - int speed = irda_get_next_speed(skb); - - if ((0 < speed) && - (9600 != speed)) { - dev_err(&ndev->dev, "support 9600 only (%d)\n", speed); - return -EIO; - } - - netif_stop_queue(ndev); - - self->tx_buff.data = self->tx_buff.head; - self->tx_buff.len = 0; - if (skb->len) - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - sh_sir_set_phase(self, TX_PHASE); - dev_kfree_skb(skb); - - return 0; -} - -static int sh_sir_ioctl(struct net_device *ndev, struct ifreq *ifreq, int cmd) -{ - /* - * FIXME - * - * This function is needed for irda framework. - * But nothing to do now - */ - return 0; -} - -static struct net_device_stats *sh_sir_stats(struct net_device *ndev) -{ - struct sh_sir_self *self = netdev_priv(ndev); - - return &self->ndev->stats; -} - -static int sh_sir_open(struct net_device *ndev) -{ - struct sh_sir_self *self = netdev_priv(ndev); - int err; - - clk_enable(self->clk); - err = sh_sir_crc_init(self); - if (err) - goto open_err; - - sh_sir_set_baudrate(self, 9600); - - self->irlap = irlap_open(ndev, &self->qos, DRIVER_NAME); - if (!self->irlap) { - err = -ENODEV; - goto open_err; - } - - /* - * Now enable the interrupt then start the queue - */ - sh_sir_update_bits(self, IRIF_SIR_FRM, FRP, FRP); - sh_sir_read(self, IRIF_UART1); /* flag clear */ - sh_sir_read(self, IRIF_UART4); /* flag clear */ - sh_sir_set_phase(self, RX_PHASE); - - netif_start_queue(ndev); - - dev_info(&self->ndev->dev, "opened\n"); - - return 0; - -open_err: - clk_disable(self->clk); - - return err; -} - -static int sh_sir_stop(struct net_device *ndev) -{ - struct sh_sir_self *self = netdev_priv(ndev); - - /* Stop IrLAP */ - if (self->irlap) { - irlap_close(self->irlap); - self->irlap = NULL; - } - - netif_stop_queue(ndev); - - dev_info(&ndev->dev, "stopped\n"); - - return 0; -} - -static const struct net_device_ops sh_sir_ndo = { - .ndo_open = sh_sir_open, - .ndo_stop = sh_sir_stop, - .ndo_start_xmit = sh_sir_hard_xmit, - .ndo_do_ioctl = sh_sir_ioctl, - .ndo_get_stats = sh_sir_stats, -}; - -/************************************************************************ - - - platform_driver function - - -************************************************************************/ -static int sh_sir_probe(struct platform_device *pdev) -{ - struct net_device *ndev; - struct sh_sir_self *self; - struct resource *res; - char clk_name[8]; - int irq; - int err = -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || irq < 0) { - dev_err(&pdev->dev, "Not enough platform resources.\n"); - goto exit; - } - - ndev = alloc_irdadev(sizeof(*self)); - if (!ndev) - goto exit; - - self = netdev_priv(ndev); - self->membase = ioremap_nocache(res->start, resource_size(res)); - if (!self->membase) { - err = -ENXIO; - dev_err(&pdev->dev, "Unable to ioremap.\n"); - goto err_mem_1; - } - - err = sh_sir_init_iobuf(self, IRDA_SKB_MAX_MTU, IRDA_SIR_MAX_FRAME); - if (err) - goto err_mem_2; - - snprintf(clk_name, sizeof(clk_name), "irda%d", pdev->id); - self->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(self->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - err = -ENODEV; - goto err_mem_3; - } - - irda_init_max_qos_capabilies(&self->qos); - - ndev->netdev_ops = &sh_sir_ndo; - ndev->irq = irq; - - self->ndev = ndev; - self->qos.baud_rate.bits &= IR_9600; /* FIXME */ - self->qos.min_turn_time.bits = 1; /* 10 ms or more */ - - irda_qos_bits_to_value(&self->qos); - - err = register_netdev(ndev); - if (err) - goto err_mem_4; - - platform_set_drvdata(pdev, ndev); - err = devm_request_irq(&pdev->dev, irq, sh_sir_irq, 0, "sh_sir", self); - if (err) { - dev_warn(&pdev->dev, "Unable to attach sh_sir interrupt\n"); - goto err_mem_4; - } - - dev_info(&pdev->dev, "SuperH IrDA probed\n"); - - goto exit; - -err_mem_4: - clk_put(self->clk); -err_mem_3: - sh_sir_remove_iobuf(self); -err_mem_2: - iounmap(self->membase); -err_mem_1: - free_netdev(ndev); -exit: - return err; -} - -static int sh_sir_remove(struct platform_device *pdev) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct sh_sir_self *self = netdev_priv(ndev); - - if (!self) - return 0; - - unregister_netdev(ndev); - clk_put(self->clk); - sh_sir_remove_iobuf(self); - iounmap(self->membase); - free_netdev(ndev); - - return 0; -} - -static struct platform_driver sh_sir_driver = { - .probe = sh_sir_probe, - .remove = sh_sir_remove, - .driver = { - .name = DRIVER_NAME, - }, -}; - -module_platform_driver(sh_sir_driver); - -MODULE_AUTHOR("Kuninori Morimoto "); -MODULE_DESCRIPTION("SuperH IrDA driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/sir-dev.h b/drivers/net/irda/sir-dev.h deleted file mode 100644 index f50b9c1c0639..000000000000 --- a/drivers/net/irda/sir-dev.h +++ /dev/null @@ -1,191 +0,0 @@ -/********************************************************************* - * - * sir.h: include file for irda-sir device abstraction layer - * - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#ifndef IRDA_SIR_H -#define IRDA_SIR_H - -#include -#include - -#include -#include // iobuff_t - -struct sir_fsm { - struct semaphore sem; - struct delayed_work work; - unsigned state, substate; - int param; - int result; -}; - -#define SIRDEV_STATE_WAIT_TX_COMPLETE 0x0100 - -/* substates for wait_tx_complete */ -#define SIRDEV_STATE_WAIT_XMIT 0x0101 -#define SIRDEV_STATE_WAIT_UNTIL_SENT 0x0102 -#define SIRDEV_STATE_TX_DONE 0x0103 - -#define SIRDEV_STATE_DONGLE_OPEN 0x0300 - -/* 0x0301-0x03ff reserved for individual dongle substates */ - -#define SIRDEV_STATE_DONGLE_CLOSE 0x0400 - -/* 0x0401-0x04ff reserved for individual dongle substates */ - -#define SIRDEV_STATE_SET_DTR_RTS 0x0500 - -#define SIRDEV_STATE_SET_SPEED 0x0700 -#define SIRDEV_STATE_DONGLE_CHECK 0x0800 -#define SIRDEV_STATE_DONGLE_RESET 0x0900 - -/* 0x0901-0x09ff reserved for individual dongle substates */ - -#define SIRDEV_STATE_DONGLE_SPEED 0x0a00 -/* 0x0a01-0x0aff reserved for individual dongle substates */ - -#define SIRDEV_STATE_PORT_SPEED 0x0b00 -#define SIRDEV_STATE_DONE 0x0c00 -#define SIRDEV_STATE_ERROR 0x0d00 -#define SIRDEV_STATE_COMPLETE 0x0e00 - -#define SIRDEV_STATE_DEAD 0xffff - - -struct sir_dev; - -struct dongle_driver { - - struct module *owner; - - const char *driver_name; - - IRDA_DONGLE type; - - int (*open)(struct sir_dev *dev); - int (*close)(struct sir_dev *dev); - int (*reset)(struct sir_dev *dev); - int (*set_speed)(struct sir_dev *dev, unsigned speed); - - struct list_head dongle_list; -}; - -struct sir_driver { - - struct module *owner; - - const char *driver_name; - - int qos_mtt_bits; - - int (*chars_in_buffer)(struct sir_dev *dev); - void (*wait_until_sent)(struct sir_dev *dev); - int (*set_speed)(struct sir_dev *dev, unsigned speed); - int (*set_dtr_rts)(struct sir_dev *dev, int dtr, int rts); - - int (*do_write)(struct sir_dev *dev, const unsigned char *ptr, size_t len); - - int (*start_dev)(struct sir_dev *dev); - int (*stop_dev)(struct sir_dev *dev); -}; - - -/* exported */ - -int irda_register_dongle(struct dongle_driver *new); -int irda_unregister_dongle(struct dongle_driver *drv); - -struct sir_dev *sirdev_get_instance(const struct sir_driver *drv, - const char *name); -int sirdev_put_instance(struct sir_dev *self); - -int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type); -void sirdev_write_complete(struct sir_dev *dev); -int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count); - -/* low level helpers for SIR device/dongle setup */ -int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len); -int sirdev_raw_read(struct sir_dev *dev, char *buf, int len); -int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts); - -/* not exported */ - -int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type); -int sirdev_put_dongle(struct sir_dev *self); - -void sirdev_enable_rx(struct sir_dev *dev); -int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param); - -/* inline helpers */ - -static inline int sirdev_schedule_speed(struct sir_dev *dev, unsigned speed) -{ - return sirdev_schedule_request(dev, SIRDEV_STATE_SET_SPEED, speed); -} - -static inline int sirdev_schedule_dongle_open(struct sir_dev *dev, int dongle_id) -{ - return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_OPEN, dongle_id); -} - -static inline int sirdev_schedule_dongle_close(struct sir_dev *dev) -{ - return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_CLOSE, 0); -} - -static inline int sirdev_schedule_dtr_rts(struct sir_dev *dev, int dtr, int rts) -{ - int dtrrts; - - dtrrts = ((dtr) ? 0x02 : 0x00) | ((rts) ? 0x01 : 0x00); - return sirdev_schedule_request(dev, SIRDEV_STATE_SET_DTR_RTS, dtrrts); -} - -#if 0 -static inline int sirdev_schedule_mode(struct sir_dev *dev, int mode) -{ - return sirdev_schedule_request(dev, SIRDEV_STATE_SET_MODE, mode); -} -#endif - - -struct sir_dev { - struct net_device *netdev; - - struct irlap_cb *irlap; - - struct qos_info qos; - - char hwname[32]; - - struct sir_fsm fsm; - atomic_t enable_rx; - int raw_tx; - spinlock_t tx_lock; - - u32 new_speed; - u32 flags; - - unsigned speed; - - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - struct sk_buff *tx_skb; - - const struct dongle_driver * dongle_drv; - const struct sir_driver * drv; - void *priv; - -}; - -#endif /* IRDA_SIR_H */ diff --git a/drivers/net/irda/sir_dev.c b/drivers/net/irda/sir_dev.c deleted file mode 100644 index 6af26a7d787c..000000000000 --- a/drivers/net/irda/sir_dev.c +++ /dev/null @@ -1,987 +0,0 @@ -/********************************************************************* - * - * sir_dev.c: irda sir network device - * - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "sir-dev.h" - - -static struct workqueue_struct *irda_sir_wq; - -/* STATE MACHINE */ - -/* substate handler of the config-fsm to handle the cases where we want - * to wait for transmit completion before changing the port configuration - */ - -static int sirdev_tx_complete_fsm(struct sir_dev *dev) -{ - struct sir_fsm *fsm = &dev->fsm; - unsigned next_state, delay; - unsigned bytes_left; - - do { - next_state = fsm->substate; /* default: stay in current substate */ - delay = 0; - - switch(fsm->substate) { - - case SIRDEV_STATE_WAIT_XMIT: - if (dev->drv->chars_in_buffer) - bytes_left = dev->drv->chars_in_buffer(dev); - else - bytes_left = 0; - if (!bytes_left) { - next_state = SIRDEV_STATE_WAIT_UNTIL_SENT; - break; - } - - if (dev->speed > 115200) - delay = (bytes_left*8*10000) / (dev->speed/100); - else if (dev->speed > 0) - delay = (bytes_left*10*10000) / (dev->speed/100); - else - delay = 0; - /* expected delay (usec) until remaining bytes are sent */ - if (delay < 100) { - udelay(delay); - delay = 0; - break; - } - /* sleep some longer delay (msec) */ - delay = (delay+999) / 1000; - break; - - case SIRDEV_STATE_WAIT_UNTIL_SENT: - /* block until underlaying hardware buffer are empty */ - if (dev->drv->wait_until_sent) - dev->drv->wait_until_sent(dev); - next_state = SIRDEV_STATE_TX_DONE; - break; - - case SIRDEV_STATE_TX_DONE: - return 0; - - default: - net_err_ratelimited("%s - undefined state\n", __func__); - return -EINVAL; - } - fsm->substate = next_state; - } while (delay == 0); - return delay; -} - -/* - * Function sirdev_config_fsm - * - * State machine to handle the configuration of the device (and attached dongle, if any). - * This handler is scheduled for execution in kIrDAd context, so we can sleep. - * however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too - * long. Instead, for longer delays we start a timer to reschedule us later. - * On entry, fsm->sem is always locked and the netdev xmit queue stopped. - * Both must be unlocked/restarted on completion - but only on final exit. - */ - -static void sirdev_config_fsm(struct work_struct *work) -{ - struct sir_dev *dev = container_of(work, struct sir_dev, fsm.work.work); - struct sir_fsm *fsm = &dev->fsm; - int next_state; - int ret = -1; - unsigned delay; - - pr_debug("%s(), <%ld>\n", __func__, jiffies); - - do { - pr_debug("%s - state=0x%04x / substate=0x%04x\n", - __func__, fsm->state, fsm->substate); - - next_state = fsm->state; - delay = 0; - - switch(fsm->state) { - - case SIRDEV_STATE_DONGLE_OPEN: - if (dev->dongle_drv != NULL) { - ret = sirdev_put_dongle(dev); - if (ret) { - fsm->result = -EINVAL; - next_state = SIRDEV_STATE_ERROR; - break; - } - } - - /* Initialize dongle */ - ret = sirdev_get_dongle(dev, fsm->param); - if (ret) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - - /* Dongles are powered through the modem control lines which - * were just set during open. Before resetting, let's wait for - * the power to stabilize. This is what some dongle drivers did - * in open before, while others didn't - should be safe anyway. - */ - - delay = 50; - fsm->substate = SIRDEV_STATE_DONGLE_RESET; - next_state = SIRDEV_STATE_DONGLE_RESET; - - fsm->param = 9600; - - break; - - case SIRDEV_STATE_DONGLE_CLOSE: - /* shouldn't we just treat this as success=? */ - if (dev->dongle_drv == NULL) { - fsm->result = -EINVAL; - next_state = SIRDEV_STATE_ERROR; - break; - } - - ret = sirdev_put_dongle(dev); - if (ret) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - next_state = SIRDEV_STATE_DONE; - break; - - case SIRDEV_STATE_SET_DTR_RTS: - ret = sirdev_set_dtr_rts(dev, - (fsm->param&0x02) ? TRUE : FALSE, - (fsm->param&0x01) ? TRUE : FALSE); - next_state = SIRDEV_STATE_DONE; - break; - - case SIRDEV_STATE_SET_SPEED: - fsm->substate = SIRDEV_STATE_WAIT_XMIT; - next_state = SIRDEV_STATE_DONGLE_CHECK; - break; - - case SIRDEV_STATE_DONGLE_CHECK: - ret = sirdev_tx_complete_fsm(dev); - if (ret < 0) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - if ((delay=ret) != 0) - break; - - if (dev->dongle_drv) { - fsm->substate = SIRDEV_STATE_DONGLE_RESET; - next_state = SIRDEV_STATE_DONGLE_RESET; - } - else { - dev->speed = fsm->param; - next_state = SIRDEV_STATE_PORT_SPEED; - } - break; - - case SIRDEV_STATE_DONGLE_RESET: - if (dev->dongle_drv->reset) { - ret = dev->dongle_drv->reset(dev); - if (ret < 0) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - } - else - ret = 0; - if ((delay=ret) == 0) { - /* set serial port according to dongle default speed */ - if (dev->drv->set_speed) - dev->drv->set_speed(dev, dev->speed); - fsm->substate = SIRDEV_STATE_DONGLE_SPEED; - next_state = SIRDEV_STATE_DONGLE_SPEED; - } - break; - - case SIRDEV_STATE_DONGLE_SPEED: - if (dev->dongle_drv->set_speed) { - ret = dev->dongle_drv->set_speed(dev, fsm->param); - if (ret < 0) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - } - else - ret = 0; - if ((delay=ret) == 0) - next_state = SIRDEV_STATE_PORT_SPEED; - break; - - case SIRDEV_STATE_PORT_SPEED: - /* Finally we are ready to change the serial port speed */ - if (dev->drv->set_speed) - dev->drv->set_speed(dev, dev->speed); - dev->new_speed = 0; - next_state = SIRDEV_STATE_DONE; - break; - - case SIRDEV_STATE_DONE: - /* Signal network layer so it can send more frames */ - netif_wake_queue(dev->netdev); - next_state = SIRDEV_STATE_COMPLETE; - break; - - default: - net_err_ratelimited("%s - undefined state\n", __func__); - fsm->result = -EINVAL; - /* fall thru */ - - case SIRDEV_STATE_ERROR: - net_err_ratelimited("%s - error: %d\n", - __func__, fsm->result); - -#if 0 /* don't enable this before we have netdev->tx_timeout to recover */ - netif_stop_queue(dev->netdev); -#else - netif_wake_queue(dev->netdev); -#endif - /* fall thru */ - - case SIRDEV_STATE_COMPLETE: - /* config change finished, so we are not busy any longer */ - sirdev_enable_rx(dev); - up(&fsm->sem); - return; - } - fsm->state = next_state; - } while(!delay); - - queue_delayed_work(irda_sir_wq, &fsm->work, msecs_to_jiffies(delay)); -} - -/* schedule some device configuration task for execution by kIrDAd - * on behalf of the above state machine. - * can be called from process or interrupt/tasklet context. - */ - -int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param) -{ - struct sir_fsm *fsm = &dev->fsm; - - pr_debug("%s - state=0x%04x / param=%u\n", __func__, - initial_state, param); - - if (down_trylock(&fsm->sem)) { - if (in_interrupt() || in_atomic() || irqs_disabled()) { - pr_debug("%s(), state machine busy!\n", __func__); - return -EWOULDBLOCK; - } else - down(&fsm->sem); - } - - if (fsm->state == SIRDEV_STATE_DEAD) { - /* race with sirdev_close should never happen */ - net_err_ratelimited("%s(), instance staled!\n", __func__); - up(&fsm->sem); - return -ESTALE; /* or better EPIPE? */ - } - - netif_stop_queue(dev->netdev); - atomic_set(&dev->enable_rx, 0); - - fsm->state = initial_state; - fsm->param = param; - fsm->result = 0; - - INIT_DELAYED_WORK(&fsm->work, sirdev_config_fsm); - queue_delayed_work(irda_sir_wq, &fsm->work, 0); - return 0; -} - - -/***************************************************************************/ - -void sirdev_enable_rx(struct sir_dev *dev) -{ - if (unlikely(atomic_read(&dev->enable_rx))) - return; - - /* flush rx-buffer - should also help in case of problems with echo cancelation */ - dev->rx_buff.data = dev->rx_buff.head; - dev->rx_buff.len = 0; - dev->rx_buff.in_frame = FALSE; - dev->rx_buff.state = OUTSIDE_FRAME; - atomic_set(&dev->enable_rx, 1); -} - -static int sirdev_is_receiving(struct sir_dev *dev) -{ - if (!atomic_read(&dev->enable_rx)) - return 0; - - return dev->rx_buff.state != OUTSIDE_FRAME; -} - -int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type) -{ - int err; - - pr_debug("%s : requesting dongle %d.\n", __func__, type); - - err = sirdev_schedule_dongle_open(dev, type); - if (unlikely(err)) - return err; - down(&dev->fsm.sem); /* block until config change completed */ - err = dev->fsm.result; - up(&dev->fsm.sem); - return err; -} -EXPORT_SYMBOL(sirdev_set_dongle); - -/* used by dongle drivers for dongle programming */ - -int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len) -{ - unsigned long flags; - int ret; - - if (unlikely(len > dev->tx_buff.truesize)) - return -ENOSPC; - - spin_lock_irqsave(&dev->tx_lock, flags); /* serialize with other tx operations */ - while (dev->tx_buff.len > 0) { /* wait until tx idle */ - spin_unlock_irqrestore(&dev->tx_lock, flags); - msleep(10); - spin_lock_irqsave(&dev->tx_lock, flags); - } - - dev->tx_buff.data = dev->tx_buff.head; - memcpy(dev->tx_buff.data, buf, len); - dev->tx_buff.len = len; - - ret = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); - if (ret > 0) { - pr_debug("%s(), raw-tx started\n", __func__); - - dev->tx_buff.data += ret; - dev->tx_buff.len -= ret; - dev->raw_tx = 1; - ret = len; /* all data is going to be sent */ - } - spin_unlock_irqrestore(&dev->tx_lock, flags); - return ret; -} -EXPORT_SYMBOL(sirdev_raw_write); - -/* seems some dongle drivers may need this */ - -int sirdev_raw_read(struct sir_dev *dev, char *buf, int len) -{ - int count; - - if (atomic_read(&dev->enable_rx)) - return -EIO; /* fail if we expect irda-frames */ - - count = (len < dev->rx_buff.len) ? len : dev->rx_buff.len; - - if (count > 0) { - memcpy(buf, dev->rx_buff.data, count); - dev->rx_buff.data += count; - dev->rx_buff.len -= count; - } - - /* remaining stuff gets flushed when re-enabling normal rx */ - - return count; -} -EXPORT_SYMBOL(sirdev_raw_read); - -int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) -{ - int ret = -ENXIO; - if (dev->drv->set_dtr_rts) - ret = dev->drv->set_dtr_rts(dev, dtr, rts); - return ret; -} -EXPORT_SYMBOL(sirdev_set_dtr_rts); - -/**********************************************************************/ - -/* called from client driver - likely with bh-context - to indicate - * it made some progress with transmission. Hence we send the next - * chunk, if any, or complete the skb otherwise - */ - -void sirdev_write_complete(struct sir_dev *dev) -{ - unsigned long flags; - struct sk_buff *skb; - int actual = 0; - int err; - - spin_lock_irqsave(&dev->tx_lock, flags); - - pr_debug("%s() - dev->tx_buff.len = %d\n", - __func__, dev->tx_buff.len); - - if (likely(dev->tx_buff.len > 0)) { - /* Write data left in transmit buffer */ - actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); - - if (likely(actual>0)) { - dev->tx_buff.data += actual; - dev->tx_buff.len -= actual; - } - else if (unlikely(actual<0)) { - /* could be dropped later when we have tx_timeout to recover */ - net_err_ratelimited("%s: drv->do_write failed (%d)\n", - __func__, actual); - if ((skb=dev->tx_skb) != NULL) { - dev->tx_skb = NULL; - dev_kfree_skb_any(skb); - dev->netdev->stats.tx_errors++; - dev->netdev->stats.tx_dropped++; - } - dev->tx_buff.len = 0; - } - if (dev->tx_buff.len > 0) - goto done; /* more data to send later */ - } - - if (unlikely(dev->raw_tx != 0)) { - /* in raw mode we are just done now after the buffer was sent - * completely. Since this was requested by some dongle driver - * running under the control of the irda-thread we must take - * care here not to re-enable the queue. The queue will be - * restarted when the irda-thread has completed the request. - */ - - pr_debug("%s(), raw-tx done\n", __func__); - dev->raw_tx = 0; - goto done; /* no post-frame handling in raw mode */ - } - - /* we have finished now sending this skb. - * update statistics and free the skb. - * finally we check and trigger a pending speed change, if any. - * if not we switch to rx mode and wake the queue for further - * packets. - * note the scheduled speed request blocks until the lower - * client driver and the corresponding hardware has really - * finished sending all data (xmit fifo drained f.e.) - * before the speed change gets finally done and the queue - * re-activated. - */ - - pr_debug("%s(), finished with frame!\n", __func__); - - if ((skb=dev->tx_skb) != NULL) { - dev->tx_skb = NULL; - dev->netdev->stats.tx_packets++; - dev->netdev->stats.tx_bytes += skb->len; - dev_kfree_skb_any(skb); - } - - if (unlikely(dev->new_speed > 0)) { - pr_debug("%s(), Changing speed!\n", __func__); - err = sirdev_schedule_speed(dev, dev->new_speed); - if (unlikely(err)) { - /* should never happen - * forget the speed change and hope the stack recovers - */ - net_err_ratelimited("%s - schedule speed change failed: %d\n", - __func__, err); - netif_wake_queue(dev->netdev); - } - /* else: success - * speed change in progress now - * on completion dev->new_speed gets cleared, - * rx-reenabled and the queue restarted - */ - } - else { - sirdev_enable_rx(dev); - netif_wake_queue(dev->netdev); - } - -done: - spin_unlock_irqrestore(&dev->tx_lock, flags); -} -EXPORT_SYMBOL(sirdev_write_complete); - -/* called from client driver - likely with bh-context - to give us - * some more received bytes. We put them into the rx-buffer, - * normally unwrapping and building LAP-skb's (unless rx disabled) - */ - -int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count) -{ - if (!dev || !dev->netdev) { - net_warn_ratelimited("%s(), not ready yet!\n", __func__); - return -1; - } - - if (!dev->irlap) { - net_warn_ratelimited("%s - too early: %p / %zd!\n", - __func__, cp, count); - return -1; - } - - if (cp==NULL) { - /* error already at lower level receive - * just update stats and set media busy - */ - irda_device_set_media_busy(dev->netdev, TRUE); - dev->netdev->stats.rx_dropped++; - pr_debug("%s; rx-drop: %zd\n", __func__, count); - return 0; - } - - /* Read the characters into the buffer */ - if (likely(atomic_read(&dev->enable_rx))) { - while (count--) - /* Unwrap and destuff one byte */ - async_unwrap_char(dev->netdev, &dev->netdev->stats, - &dev->rx_buff, *cp++); - } else { - while (count--) { - /* rx not enabled: save the raw bytes and never - * trigger any netif_rx. The received bytes are flushed - * later when we re-enable rx but might be read meanwhile - * by the dongle driver. - */ - dev->rx_buff.data[dev->rx_buff.len++] = *cp++; - - /* What should we do when the buffer is full? */ - if (unlikely(dev->rx_buff.len == dev->rx_buff.truesize)) - dev->rx_buff.len = 0; - } - } - - return 0; -} -EXPORT_SYMBOL(sirdev_receive); - -/**********************************************************************/ - -/* callbacks from network layer */ - -static netdev_tx_t sirdev_hard_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - struct sir_dev *dev = netdev_priv(ndev); - unsigned long flags; - int actual = 0; - int err; - s32 speed; - - IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); - - netif_stop_queue(ndev); - - pr_debug("%s(), skb->len = %d\n", __func__, skb->len); - - speed = irda_get_next_speed(skb); - if ((speed != dev->speed) && (speed != -1)) { - if (!skb->len) { - err = sirdev_schedule_speed(dev, speed); - if (unlikely(err == -EWOULDBLOCK)) { - /* Failed to initiate the speed change, likely the fsm - * is still busy (pretty unlikely, but...) - * We refuse to accept the skb and return with the queue - * stopped so the network layer will retry after the - * fsm completes and wakes the queue. - */ - return NETDEV_TX_BUSY; - } - else if (unlikely(err)) { - /* other fatal error - forget the speed change and - * hope the stack will recover somehow - */ - netif_start_queue(ndev); - } - /* else: success - * speed change in progress now - * on completion the queue gets restarted - */ - - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } else - dev->new_speed = speed; - } - - /* Init tx buffer*/ - dev->tx_buff.data = dev->tx_buff.head; - - /* Check problems */ - if(spin_is_locked(&dev->tx_lock)) { - pr_debug("%s(), write not completed\n", __func__); - } - - /* serialize with write completion */ - spin_lock_irqsave(&dev->tx_lock, flags); - - /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ - dev->tx_buff.len = async_wrap_skb(skb, dev->tx_buff.data, dev->tx_buff.truesize); - - /* transmission will start now - disable receive. - * if we are just in the middle of an incoming frame, - * treat it as collision. probably it's a good idea to - * reset the rx_buf OUTSIDE_FRAME in this case too? - */ - atomic_set(&dev->enable_rx, 0); - if (unlikely(sirdev_is_receiving(dev))) - dev->netdev->stats.collisions++; - - actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); - - if (likely(actual > 0)) { - dev->tx_skb = skb; - dev->tx_buff.data += actual; - dev->tx_buff.len -= actual; - } - else if (unlikely(actual < 0)) { - /* could be dropped later when we have tx_timeout to recover */ - net_err_ratelimited("%s: drv->do_write failed (%d)\n", - __func__, actual); - dev_kfree_skb_any(skb); - dev->netdev->stats.tx_errors++; - dev->netdev->stats.tx_dropped++; - netif_wake_queue(ndev); - } - spin_unlock_irqrestore(&dev->tx_lock, flags); - - return NETDEV_TX_OK; -} - -/* called from network layer with rtnl hold */ - -static int sirdev_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct sir_dev *dev = netdev_priv(ndev); - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, ndev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - ret = sirdev_schedule_speed(dev, irq->ifr_baudrate); - /* cannot sleep here for completion - * we are called from network layer with rtnl hold - */ - break; - - case SIOCSDONGLE: /* Set dongle */ - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - ret = sirdev_schedule_dongle_open(dev, irq->ifr_dongle); - /* cannot sleep here for completion - * we are called from network layer with rtnl hold - */ - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - irda_device_set_media_busy(dev->netdev, TRUE); - break; - - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = sirdev_is_receiving(dev); - break; - - case SIOCSDTRRTS: - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - ret = sirdev_schedule_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); - /* cannot sleep here for completion - * we are called from network layer with rtnl hold - */ - break; - - case SIOCSMODE: -#if 0 - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - ret = sirdev_schedule_mode(dev, irq->ifr_mode); - /* cannot sleep here for completion - * we are called from network layer with rtnl hold - */ - break; -#endif - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -/* ----------------------------------------------------------------------------- */ - -#define SIRBUF_ALLOCSIZE 4269 /* worst case size of a wrapped IrLAP frame */ - -static int sirdev_alloc_buffers(struct sir_dev *dev) -{ - dev->tx_buff.truesize = SIRBUF_ALLOCSIZE; - dev->rx_buff.truesize = IRDA_SKB_MAX_MTU; - - /* Bootstrap ZeroCopy Rx */ - dev->rx_buff.skb = __netdev_alloc_skb(dev->netdev, dev->rx_buff.truesize, - GFP_KERNEL); - if (dev->rx_buff.skb == NULL) - return -ENOMEM; - skb_reserve(dev->rx_buff.skb, 1); - dev->rx_buff.head = dev->rx_buff.skb->data; - - dev->tx_buff.head = kmalloc(dev->tx_buff.truesize, GFP_KERNEL); - if (dev->tx_buff.head == NULL) { - kfree_skb(dev->rx_buff.skb); - dev->rx_buff.skb = NULL; - dev->rx_buff.head = NULL; - return -ENOMEM; - } - - dev->tx_buff.data = dev->tx_buff.head; - dev->rx_buff.data = dev->rx_buff.head; - dev->tx_buff.len = 0; - dev->rx_buff.len = 0; - - dev->rx_buff.in_frame = FALSE; - dev->rx_buff.state = OUTSIDE_FRAME; - return 0; -}; - -static void sirdev_free_buffers(struct sir_dev *dev) -{ - kfree_skb(dev->rx_buff.skb); - kfree(dev->tx_buff.head); - dev->rx_buff.head = dev->tx_buff.head = NULL; - dev->rx_buff.skb = NULL; -} - -static int sirdev_open(struct net_device *ndev) -{ - struct sir_dev *dev = netdev_priv(ndev); - const struct sir_driver *drv = dev->drv; - - if (!drv) - return -ENODEV; - - /* increase the reference count of the driver module before doing serious stuff */ - if (!try_module_get(drv->owner)) - return -ESTALE; - - if (sirdev_alloc_buffers(dev)) - goto errout_dec; - - if (!dev->drv->start_dev || dev->drv->start_dev(dev)) - goto errout_free; - - sirdev_enable_rx(dev); - dev->raw_tx = 0; - - netif_start_queue(ndev); - dev->irlap = irlap_open(ndev, &dev->qos, dev->hwname); - if (!dev->irlap) - goto errout_stop; - - netif_wake_queue(ndev); - - pr_debug("%s - done, speed = %d\n", __func__, dev->speed); - - return 0; - -errout_stop: - atomic_set(&dev->enable_rx, 0); - if (dev->drv->stop_dev) - dev->drv->stop_dev(dev); -errout_free: - sirdev_free_buffers(dev); -errout_dec: - module_put(drv->owner); - return -EAGAIN; -} - -static int sirdev_close(struct net_device *ndev) -{ - struct sir_dev *dev = netdev_priv(ndev); - const struct sir_driver *drv; - -/* pr_debug("%s\n", __func__); */ - - netif_stop_queue(ndev); - - down(&dev->fsm.sem); /* block on pending config completion */ - - atomic_set(&dev->enable_rx, 0); - - if (unlikely(!dev->irlap)) - goto out; - irlap_close(dev->irlap); - dev->irlap = NULL; - - drv = dev->drv; - if (unlikely(!drv || !dev->priv)) - goto out; - - if (drv->stop_dev) - drv->stop_dev(dev); - - sirdev_free_buffers(dev); - module_put(drv->owner); - -out: - dev->speed = 0; - up(&dev->fsm.sem); - return 0; -} - -static const struct net_device_ops sirdev_ops = { - .ndo_start_xmit = sirdev_hard_xmit, - .ndo_open = sirdev_open, - .ndo_stop = sirdev_close, - .ndo_do_ioctl = sirdev_ioctl, -}; -/* ----------------------------------------------------------------------------- */ - -struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name) -{ - struct net_device *ndev; - struct sir_dev *dev; - - pr_debug("%s - %s\n", __func__, name); - - /* instead of adding tests to protect against drv->do_write==NULL - * at several places we refuse to create a sir_dev instance for - * drivers which don't implement do_write. - */ - if (!drv || !drv->do_write) - return NULL; - - /* - * Allocate new instance of the device - */ - ndev = alloc_irdadev(sizeof(*dev)); - if (ndev == NULL) { - net_err_ratelimited("%s - Can't allocate memory for IrDA control block!\n", - __func__); - goto out; - } - dev = netdev_priv(ndev); - - irda_init_max_qos_capabilies(&dev->qos); - dev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - dev->qos.min_turn_time.bits = drv->qos_mtt_bits; - irda_qos_bits_to_value(&dev->qos); - - strncpy(dev->hwname, name, sizeof(dev->hwname)-1); - - atomic_set(&dev->enable_rx, 0); - dev->tx_skb = NULL; - - spin_lock_init(&dev->tx_lock); - sema_init(&dev->fsm.sem, 1); - - dev->drv = drv; - dev->netdev = ndev; - - /* Override the network functions we need to use */ - ndev->netdev_ops = &sirdev_ops; - - if (register_netdev(ndev)) { - net_err_ratelimited("%s(), register_netdev() failed!\n", - __func__); - goto out_freenetdev; - } - - return dev; - -out_freenetdev: - free_netdev(ndev); -out: - return NULL; -} -EXPORT_SYMBOL(sirdev_get_instance); - -int sirdev_put_instance(struct sir_dev *dev) -{ - int err = 0; - - pr_debug("%s\n", __func__); - - atomic_set(&dev->enable_rx, 0); - - netif_carrier_off(dev->netdev); - netif_device_detach(dev->netdev); - - if (dev->dongle_drv) - err = sirdev_schedule_dongle_close(dev); - if (err) - net_err_ratelimited("%s - error %d\n", __func__, err); - - sirdev_close(dev->netdev); - - down(&dev->fsm.sem); - dev->fsm.state = SIRDEV_STATE_DEAD; /* mark staled */ - dev->dongle_drv = NULL; - dev->priv = NULL; - up(&dev->fsm.sem); - - /* Remove netdevice */ - unregister_netdev(dev->netdev); - - free_netdev(dev->netdev); - - return 0; -} -EXPORT_SYMBOL(sirdev_put_instance); - -static int __init sir_wq_init(void) -{ - irda_sir_wq = create_singlethread_workqueue("irda_sir_wq"); - if (!irda_sir_wq) - return -ENOMEM; - return 0; -} - -static void __exit sir_wq_exit(void) -{ - destroy_workqueue(irda_sir_wq); -} - -module_init(sir_wq_init); -module_exit(sir_wq_exit); - -MODULE_AUTHOR("Martin Diehl "); -MODULE_DESCRIPTION("IrDA SIR core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/sir_dongle.c b/drivers/net/irda/sir_dongle.c deleted file mode 100644 index 7436f73ff1bb..000000000000 --- a/drivers/net/irda/sir_dongle.c +++ /dev/null @@ -1,133 +0,0 @@ -/********************************************************************* - * - * sir_dongle.c: manager for serial dongle protocol drivers - * - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include - -#include "sir-dev.h" - -/************************************************************************** - * - * dongle registration and attachment - * - */ - -static LIST_HEAD(dongle_list); /* list of registered dongle drivers */ -static DEFINE_MUTEX(dongle_list_lock); /* protects the list */ - -int irda_register_dongle(struct dongle_driver *new) -{ - struct list_head *entry; - struct dongle_driver *drv; - - pr_debug("%s : registering dongle \"%s\" (%d).\n", - __func__, new->driver_name, new->type); - - mutex_lock(&dongle_list_lock); - list_for_each(entry, &dongle_list) { - drv = list_entry(entry, struct dongle_driver, dongle_list); - if (new->type == drv->type) { - mutex_unlock(&dongle_list_lock); - return -EEXIST; - } - } - list_add(&new->dongle_list, &dongle_list); - mutex_unlock(&dongle_list_lock); - return 0; -} -EXPORT_SYMBOL(irda_register_dongle); - -int irda_unregister_dongle(struct dongle_driver *drv) -{ - mutex_lock(&dongle_list_lock); - list_del(&drv->dongle_list); - mutex_unlock(&dongle_list_lock); - return 0; -} -EXPORT_SYMBOL(irda_unregister_dongle); - -int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type) -{ - struct list_head *entry; - const struct dongle_driver *drv = NULL; - int err = -EINVAL; - - request_module("irda-dongle-%d", type); - - if (dev->dongle_drv != NULL) - return -EBUSY; - - /* serialize access to the list of registered dongles */ - mutex_lock(&dongle_list_lock); - - list_for_each(entry, &dongle_list) { - drv = list_entry(entry, struct dongle_driver, dongle_list); - if (drv->type == type) - break; - else - drv = NULL; - } - - if (!drv) { - err = -ENODEV; - goto out_unlock; /* no such dongle */ - } - - /* handling of SMP races with dongle module removal - three cases: - * 1) dongle driver was already unregistered - then we haven't found the - * requested dongle above and are already out here - * 2) the module is already marked deleted but the driver is still - * registered - then the try_module_get() below will fail - * 3) the try_module_get() below succeeds before the module is marked - * deleted - then sys_delete_module() fails and prevents the removal - * because the module is in use. - */ - - if (!try_module_get(drv->owner)) { - err = -ESTALE; - goto out_unlock; /* rmmod already pending */ - } - dev->dongle_drv = drv; - - if (!drv->open || (err=drv->open(dev))!=0) - goto out_reject; /* failed to open driver */ - - mutex_unlock(&dongle_list_lock); - return 0; - -out_reject: - dev->dongle_drv = NULL; - module_put(drv->owner); -out_unlock: - mutex_unlock(&dongle_list_lock); - return err; -} - -int sirdev_put_dongle(struct sir_dev *dev) -{ - const struct dongle_driver *drv = dev->dongle_drv; - - if (drv) { - if (drv->close) - drv->close(dev); /* close this dongle instance */ - - dev->dongle_drv = NULL; /* unlink the dongle driver */ - module_put(drv->owner);/* decrement driver's module refcount */ - } - - return 0; -} diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c deleted file mode 100644 index 19a55cba6beb..000000000000 --- a/drivers/net/irda/smsc-ircc2.c +++ /dev/null @@ -1,3026 +0,0 @@ -/********************************************************************* - * - * Description: Driver for the SMC Infrared Communications Controller - * Author: Daniele Peri (peri@csai.unipa.it) - * Created at: - * Modified at: - * Modified by: - * - * Copyright (c) 2002 Daniele Peri - * All Rights Reserved. - * Copyright (c) 2002 Jean Tourrilhes - * Copyright (c) 2006 Linus Walleij - * - * - * Based on smc-ircc.c: - * - * Copyright (c) 2001 Stefani Seibold - * Copyright (c) 1999-2001 Dag Brattli - * Copyright (c) 1998-1999 Thomas Davis, - * - * and irport.c: - * - * Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved. - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#ifdef CONFIG_PCI -#include -#endif - -#include -#include -#include - -#include "smsc-ircc2.h" -#include "smsc-sio.h" - - -MODULE_AUTHOR("Daniele Peri "); -MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver"); -MODULE_LICENSE("GPL"); - -static bool smsc_nopnp = true; -module_param_named(nopnp, smsc_nopnp, bool, 0); -MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings, defaults to true"); - -#define DMA_INVAL 255 -static int ircc_dma = DMA_INVAL; -module_param_hw(ircc_dma, int, dma, 0); -MODULE_PARM_DESC(ircc_dma, "DMA channel"); - -#define IRQ_INVAL 255 -static int ircc_irq = IRQ_INVAL; -module_param_hw(ircc_irq, int, irq, 0); -MODULE_PARM_DESC(ircc_irq, "IRQ line"); - -static int ircc_fir; -module_param_hw(ircc_fir, int, ioport, 0); -MODULE_PARM_DESC(ircc_fir, "FIR Base Address"); - -static int ircc_sir; -module_param_hw(ircc_sir, int, ioport, 0); -MODULE_PARM_DESC(ircc_sir, "SIR Base Address"); - -static int ircc_cfg; -module_param_hw(ircc_cfg, int, ioport, 0); -MODULE_PARM_DESC(ircc_cfg, "Configuration register base address"); - -static int ircc_transceiver; -module_param(ircc_transceiver, int, 0); -MODULE_PARM_DESC(ircc_transceiver, "Transceiver type"); - -/* Types */ - -#ifdef CONFIG_PCI -struct smsc_ircc_subsystem_configuration { - unsigned short vendor; /* PCI vendor ID */ - unsigned short device; /* PCI vendor ID */ - unsigned short subvendor; /* PCI subsystem vendor ID */ - unsigned short subdevice; /* PCI subsystem device ID */ - unsigned short sir_io; /* I/O port for SIR */ - unsigned short fir_io; /* I/O port for FIR */ - unsigned char fir_irq; /* FIR IRQ */ - unsigned char fir_dma; /* FIR DMA */ - unsigned short cfg_base; /* I/O port for chip configuration */ - int (*preconfigure)(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); /* Preconfig function */ - const char *name; /* name shown as info */ -}; -#endif - -struct smsc_transceiver { - char *name; - void (*set_for_speed)(int fir_base, u32 speed); - int (*probe)(int fir_base); -}; - -struct smsc_chip { - char *name; - #if 0 - u8 type; - #endif - u16 flags; - u8 devid; - u8 rev; -}; - -struct smsc_chip_address { - unsigned int cfg_base; - unsigned int type; -}; - -/* Private data for each instance */ -struct smsc_ircc_cb { - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - struct qos_info qos; /* QoS capabilities for this device */ - - spinlock_t lock; /* For serializing operations */ - - __u32 new_speed; - __u32 flags; /* Interface flags */ - - int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */ - int tx_len; /* Number of frames in tx_buff */ - - int transceiver; - struct platform_device *pldev; -}; - -/* Constants */ - -#define SMSC_IRCC2_DRIVER_NAME "smsc-ircc2" - -#define SMSC_IRCC2_C_IRDA_FALLBACK_SPEED 9600 -#define SMSC_IRCC2_C_DEFAULT_TRANSCEIVER 1 -#define SMSC_IRCC2_C_NET_TIMEOUT 0 -#define SMSC_IRCC2_C_SIR_STOP 0 - -static const char *driver_name = SMSC_IRCC2_DRIVER_NAME; - -/* Prototypes */ - -static int smsc_ircc_open(unsigned int firbase, unsigned int sirbase, u8 dma, u8 irq); -static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base); -static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq); -static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self); -static void smsc_ircc_init_chip(struct smsc_ircc_cb *self); -static int __exit smsc_ircc_close(struct smsc_ircc_cb *self); -static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self); -static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self); -static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self); -static netdev_tx_t smsc_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev); -static netdev_tx_t smsc_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev); -static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int bofs); -static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self); -static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed); -static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, u32 speed); -static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id); -static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev); -static void smsc_ircc_sir_start(struct smsc_ircc_cb *self); -#if SMSC_IRCC2_C_SIR_STOP -static void smsc_ircc_sir_stop(struct smsc_ircc_cb *self); -#endif -static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self); -static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len); -static int smsc_ircc_net_open(struct net_device *dev); -static int smsc_ircc_net_close(struct net_device *dev); -static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -#if SMSC_IRCC2_C_NET_TIMEOUT -static void smsc_ircc_timeout(struct net_device *dev); -#endif -static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self); -static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self); -static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed); -static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self); - -/* Probing */ -static int __init smsc_ircc_look_for_chips(void); -static const struct smsc_chip * __init smsc_ircc_probe(unsigned short cfg_base, u8 reg, const struct smsc_chip *chip, char *type); -static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned short cfg_base, char *type); -static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type); -static int __init smsc_superio_fdc(unsigned short cfg_base); -static int __init smsc_superio_lpc(unsigned short cfg_base); -#ifdef CONFIG_PCI -static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuration *conf); -static int __init preconfigure_through_82801(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); -static void __init preconfigure_ali_port(struct pci_dev *dev, - unsigned short port); -static int __init preconfigure_through_ali(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); -static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg, - unsigned short ircc_fir, - unsigned short ircc_sir, - unsigned char ircc_dma, - unsigned char ircc_irq); -#endif - -/* Transceivers specific functions */ - -static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed); -static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base); -static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed); -static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base); -static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed); -static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base); - -/* Power Management */ - -static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state); -static int smsc_ircc_resume(struct platform_device *dev); - -static struct platform_driver smsc_ircc_driver = { - .suspend = smsc_ircc_suspend, - .resume = smsc_ircc_resume, - .driver = { - .name = SMSC_IRCC2_DRIVER_NAME, - }, -}; - -/* Transceivers for SMSC-ircc */ - -static struct smsc_transceiver smsc_transceivers[] = -{ - { "Toshiba Satellite 1800 (GP data pin select)", smsc_ircc_set_transceiver_toshiba_sat1800, smsc_ircc_probe_transceiver_toshiba_sat1800 }, - { "Fast pin select", smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select, smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select }, - { "ATC IRMode", smsc_ircc_set_transceiver_smsc_ircc_atc, smsc_ircc_probe_transceiver_smsc_ircc_atc }, - { NULL, NULL } -}; -#define SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS (ARRAY_SIZE(smsc_transceivers) - 1) - -/* SMC SuperIO chipsets definitions */ - -#define KEY55_1 0 /* SuperIO Configuration mode with Key <0x55> */ -#define KEY55_2 1 /* SuperIO Configuration mode with Key <0x55,0x55> */ -#define NoIRDA 2 /* SuperIO Chip has no IRDA Port */ -#define SIR 0 /* SuperIO Chip has only slow IRDA */ -#define FIR 4 /* SuperIO Chip has fast IRDA */ -#define SERx4 8 /* SuperIO Chip supports 115,2 KBaud * 4=460,8 KBaud */ - -static struct smsc_chip __initdata fdc_chips_flat[] = -{ - /* Base address 0x3f0 or 0x370 */ - { "37C44", KEY55_1|NoIRDA, 0x00, 0x00 }, /* This chip cannot be detected */ - { "37C665GT", KEY55_2|NoIRDA, 0x65, 0x01 }, - { "37C665GT", KEY55_2|NoIRDA, 0x66, 0x01 }, - { "37C669", KEY55_2|SIR|SERx4, 0x03, 0x02 }, - { "37C669", KEY55_2|SIR|SERx4, 0x04, 0x02 }, /* ID? */ - { "37C78", KEY55_2|NoIRDA, 0x78, 0x00 }, - { "37N769", KEY55_1|FIR|SERx4, 0x28, 0x00 }, - { "37N869", KEY55_1|FIR|SERx4, 0x29, 0x00 }, - { NULL } -}; - -static struct smsc_chip __initdata fdc_chips_paged[] = -{ - /* Base address 0x3f0 or 0x370 */ - { "37B72X", KEY55_1|SIR|SERx4, 0x4c, 0x00 }, - { "37B77X", KEY55_1|SIR|SERx4, 0x43, 0x00 }, - { "37B78X", KEY55_1|SIR|SERx4, 0x44, 0x00 }, - { "37B80X", KEY55_1|SIR|SERx4, 0x42, 0x00 }, - { "37C67X", KEY55_1|FIR|SERx4, 0x40, 0x00 }, - { "37C93X", KEY55_2|SIR|SERx4, 0x02, 0x01 }, - { "37C93XAPM", KEY55_1|SIR|SERx4, 0x30, 0x01 }, - { "37C93XFR", KEY55_2|FIR|SERx4, 0x03, 0x01 }, - { "37M707", KEY55_1|SIR|SERx4, 0x42, 0x00 }, - { "37M81X", KEY55_1|SIR|SERx4, 0x4d, 0x00 }, - { "37N958FR", KEY55_1|FIR|SERx4, 0x09, 0x04 }, - { "37N971", KEY55_1|FIR|SERx4, 0x0a, 0x00 }, - { "37N972", KEY55_1|FIR|SERx4, 0x0b, 0x00 }, - { NULL } -}; - -static struct smsc_chip __initdata lpc_chips_flat[] = -{ - /* Base address 0x2E or 0x4E */ - { "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 }, - { "47N227", KEY55_1|FIR|SERx4, 0x7a, 0x00 }, - { "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 }, - { NULL } -}; - -static struct smsc_chip __initdata lpc_chips_paged[] = -{ - /* Base address 0x2E or 0x4E */ - { "47B27X", KEY55_1|SIR|SERx4, 0x51, 0x00 }, - { "47B37X", KEY55_1|SIR|SERx4, 0x52, 0x00 }, - { "47M10X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, - { "47M120", KEY55_1|NoIRDA|SERx4, 0x5c, 0x00 }, - { "47M13X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, - { "47M14X", KEY55_1|SIR|SERx4, 0x5f, 0x00 }, - { "47N252", KEY55_1|FIR|SERx4, 0x0e, 0x00 }, - { "47S42X", KEY55_1|SIR|SERx4, 0x57, 0x00 }, - { NULL } -}; - -#define SMSCSIO_TYPE_FDC 1 -#define SMSCSIO_TYPE_LPC 2 -#define SMSCSIO_TYPE_FLAT 4 -#define SMSCSIO_TYPE_PAGED 8 - -static struct smsc_chip_address __initdata possible_addresses[] = -{ - { 0x3f0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0x370, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0xe0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0x2e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0x4e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0, 0 } -}; - -/* Globals */ - -static struct smsc_ircc_cb *dev_self[] = { NULL, NULL }; -static unsigned short dev_count; - -static inline void register_bank(int iobase, int bank) -{ - outb(((inb(iobase + IRCC_MASTER) & 0xf0) | (bank & 0x07)), - iobase + IRCC_MASTER); -} - -/* PNP hotplug support */ -static const struct pnp_device_id smsc_ircc_pnp_table[] = { - { .id = "SMCf010", .driver_data = 0 }, - /* and presumably others */ - { } -}; -MODULE_DEVICE_TABLE(pnp, smsc_ircc_pnp_table); - -static int pnp_driver_registered; - -#ifdef CONFIG_PNP -static int smsc_ircc_pnp_probe(struct pnp_dev *dev, - const struct pnp_device_id *dev_id) -{ - unsigned int firbase, sirbase; - u8 dma, irq; - - if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && - pnp_dma_valid(dev, 0) && pnp_irq_valid(dev, 0))) - return -EINVAL; - - sirbase = pnp_port_start(dev, 0); - firbase = pnp_port_start(dev, 1); - dma = pnp_dma(dev, 0); - irq = pnp_irq(dev, 0); - - if (smsc_ircc_open(firbase, sirbase, dma, irq)) - return -ENODEV; - - return 0; -} - -static struct pnp_driver smsc_ircc_pnp_driver = { - .name = "smsc-ircc2", - .id_table = smsc_ircc_pnp_table, - .probe = smsc_ircc_pnp_probe, -}; -#else /* CONFIG_PNP */ -static struct pnp_driver smsc_ircc_pnp_driver; -#endif - -/******************************************************************************* - * - * - * SMSC-ircc stuff - * - * - *******************************************************************************/ - -static int __init smsc_ircc_legacy_probe(void) -{ - int ret = 0; - -#ifdef CONFIG_PCI - if (smsc_ircc_preconfigure_subsystems(ircc_cfg, ircc_fir, ircc_sir, ircc_dma, ircc_irq) < 0) { - /* Ignore errors from preconfiguration */ - net_err_ratelimited("%s, Preconfiguration failed !\n", - driver_name); - } -#endif - - if (ircc_fir > 0 && ircc_sir > 0) { - net_info_ratelimited(" Overriding FIR address 0x%04x\n", - ircc_fir); - net_info_ratelimited(" Overriding SIR address 0x%04x\n", - ircc_sir); - - if (smsc_ircc_open(ircc_fir, ircc_sir, ircc_dma, ircc_irq)) - ret = -ENODEV; - } else { - ret = -ENODEV; - - /* try user provided configuration register base address */ - if (ircc_cfg > 0) { - net_info_ratelimited(" Overriding configuration address 0x%04x\n", - ircc_cfg); - if (!smsc_superio_fdc(ircc_cfg)) - ret = 0; - if (!smsc_superio_lpc(ircc_cfg)) - ret = 0; - } - - if (smsc_ircc_look_for_chips() > 0) - ret = 0; - } - return ret; -} - -/* - * Function smsc_ircc_init () - * - * Initialize chip. Just try to find out how many chips we are dealing with - * and where they are - */ -static int __init smsc_ircc_init(void) -{ - int ret; - - pr_debug("%s\n", __func__); - - ret = platform_driver_register(&smsc_ircc_driver); - if (ret) { - net_err_ratelimited("%s, Can't register driver!\n", - driver_name); - return ret; - } - - dev_count = 0; - - if (smsc_nopnp || !pnp_platform_devices || - ircc_cfg || ircc_fir || ircc_sir || - ircc_dma != DMA_INVAL || ircc_irq != IRQ_INVAL) { - ret = smsc_ircc_legacy_probe(); - } else { - if (pnp_register_driver(&smsc_ircc_pnp_driver) == 0) - pnp_driver_registered = 1; - } - - if (ret) { - if (pnp_driver_registered) - pnp_unregister_driver(&smsc_ircc_pnp_driver); - platform_driver_unregister(&smsc_ircc_driver); - } - - return ret; -} - -static netdev_tx_t smsc_ircc_net_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct smsc_ircc_cb *self = netdev_priv(dev); - - if (self->io.speed > 115200) - return smsc_ircc_hard_xmit_fir(skb, dev); - else - return smsc_ircc_hard_xmit_sir(skb, dev); -} - -static const struct net_device_ops smsc_ircc_netdev_ops = { - .ndo_open = smsc_ircc_net_open, - .ndo_stop = smsc_ircc_net_close, - .ndo_do_ioctl = smsc_ircc_net_ioctl, - .ndo_start_xmit = smsc_ircc_net_xmit, -#if SMSC_IRCC2_C_NET_TIMEOUT - .ndo_tx_timeout = smsc_ircc_timeout, -#endif -}; - -/* - * Function smsc_ircc_open (firbase, sirbase, dma, irq) - * - * Try to open driver instance - * - */ -static int smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq) -{ - struct smsc_ircc_cb *self; - struct net_device *dev; - int err; - - pr_debug("%s\n", __func__); - - err = smsc_ircc_present(fir_base, sir_base); - if (err) - goto err_out; - - err = -ENOMEM; - if (dev_count >= ARRAY_SIZE(dev_self)) { - net_warn_ratelimited("%s(), too many devices!\n", __func__); - goto err_out1; - } - - /* - * Allocate new instance of the driver - */ - dev = alloc_irdadev(sizeof(struct smsc_ircc_cb)); - if (!dev) { - net_warn_ratelimited("%s() can't allocate net device\n", - __func__); - goto err_out1; - } - -#if SMSC_IRCC2_C_NET_TIMEOUT - dev->watchdog_timeo = HZ * 2; /* Allow enough time for speed change */ -#endif - dev->netdev_ops = &smsc_ircc_netdev_ops; - - self = netdev_priv(dev); - self->netdev = dev; - - /* Make ifconfig display some details */ - dev->base_addr = self->io.fir_base = fir_base; - dev->irq = self->io.irq = irq; - - /* Need to store self somewhere */ - dev_self[dev_count] = self; - spin_lock_init(&self->lock); - - self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE; - self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE; - - self->rx_buff.head = - dma_zalloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (self->rx_buff.head == NULL) - goto err_out2; - - self->tx_buff.head = - dma_zalloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (self->tx_buff.head == NULL) - goto err_out3; - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - - smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq); - smsc_ircc_setup_qos(self); - smsc_ircc_init_chip(self); - - if (ircc_transceiver > 0 && - ircc_transceiver < SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS) - self->transceiver = ircc_transceiver; - else - smsc_ircc_probe_transceiver(self); - - err = register_netdev(self->netdev); - if (err) { - net_err_ratelimited("%s, Network device registration failed!\n", - driver_name); - goto err_out4; - } - - self->pldev = platform_device_register_simple(SMSC_IRCC2_DRIVER_NAME, - dev_count, NULL, 0); - if (IS_ERR(self->pldev)) { - err = PTR_ERR(self->pldev); - goto err_out5; - } - platform_set_drvdata(self->pldev, self); - - net_info_ratelimited("IrDA: Registered device %s\n", dev->name); - dev_count++; - - return 0; - - err_out5: - unregister_netdev(self->netdev); - - err_out4: - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - err_out3: - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - err_out2: - free_netdev(self->netdev); - dev_self[dev_count] = NULL; - err_out1: - release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); - release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); - err_out: - return err; -} - -/* - * Function smsc_ircc_present(fir_base, sir_base) - * - * Check the smsc-ircc chip presence - * - */ -static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base) -{ - unsigned char low, high, chip, config, dma, irq, version; - - if (!request_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT, - driver_name)) { - net_warn_ratelimited("%s: can't get fir_base of 0x%03x\n", - __func__, fir_base); - goto out1; - } - - if (!request_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT, - driver_name)) { - net_warn_ratelimited("%s: can't get sir_base of 0x%03x\n", - __func__, sir_base); - goto out2; - } - - register_bank(fir_base, 3); - - high = inb(fir_base + IRCC_ID_HIGH); - low = inb(fir_base + IRCC_ID_LOW); - chip = inb(fir_base + IRCC_CHIP_ID); - version = inb(fir_base + IRCC_VERSION); - config = inb(fir_base + IRCC_INTERFACE); - dma = config & IRCC_INTERFACE_DMA_MASK; - irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; - - if (high != 0x10 || low != 0xb8 || (chip != 0xf1 && chip != 0xf2)) { - net_warn_ratelimited("%s(), addr 0x%04x - no device found!\n", - __func__, fir_base); - goto out3; - } - net_info_ratelimited("SMsC IrDA Controller found\n IrCC version %d.%d, firport 0x%03x, sirport 0x%03x dma=%d, irq=%d\n", - chip & 0x0f, version, - fir_base, sir_base, dma, irq); - - return 0; - - out3: - release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); - out2: - release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); - out1: - return -ENODEV; -} - -/* - * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq) - * - * Setup I/O - * - */ -static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, - unsigned int fir_base, unsigned int sir_base, - u8 dma, u8 irq) -{ - unsigned char config, chip_dma, chip_irq; - - register_bank(fir_base, 3); - config = inb(fir_base + IRCC_INTERFACE); - chip_dma = config & IRCC_INTERFACE_DMA_MASK; - chip_irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; - - self->io.fir_base = fir_base; - self->io.sir_base = sir_base; - self->io.fir_ext = SMSC_IRCC2_FIR_CHIP_IO_EXTENT; - self->io.sir_ext = SMSC_IRCC2_SIR_CHIP_IO_EXTENT; - self->io.fifo_size = SMSC_IRCC2_FIFO_SIZE; - self->io.speed = SMSC_IRCC2_C_IRDA_FALLBACK_SPEED; - - if (irq != IRQ_INVAL) { - if (irq != chip_irq) - net_info_ratelimited("%s, Overriding IRQ - chip says %d, using %d\n", - driver_name, chip_irq, irq); - self->io.irq = irq; - } else - self->io.irq = chip_irq; - - if (dma != DMA_INVAL) { - if (dma != chip_dma) - net_info_ratelimited("%s, Overriding DMA - chip says %d, using %d\n", - driver_name, chip_dma, dma); - self->io.dma = dma; - } else - self->io.dma = chip_dma; - -} - -/* - * Function smsc_ircc_setup_qos(self) - * - * Setup qos - * - */ -static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self) -{ - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| - IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); - - self->qos.min_turn_time.bits = SMSC_IRCC2_MIN_TURN_TIME; - self->qos.window_size.bits = SMSC_IRCC2_WINDOW_SIZE; - irda_qos_bits_to_value(&self->qos); -} - -/* - * Function smsc_ircc_init_chip(self) - * - * Init chip - * - */ -static void smsc_ircc_init_chip(struct smsc_ircc_cb *self) -{ - int iobase = self->io.fir_base; - - register_bank(iobase, 0); - outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER); - outb(0x00, iobase + IRCC_MASTER); - - register_bank(iobase, 1); - outb(((inb(iobase + IRCC_SCE_CFGA) & 0x87) | IRCC_CFGA_IRDA_SIR_A), - iobase + IRCC_SCE_CFGA); - -#ifdef smsc_669 /* Uses pin 88/89 for Rx/Tx */ - outb(((inb(iobase + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), - iobase + IRCC_SCE_CFGB); -#else - outb(((inb(iobase + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), - iobase + IRCC_SCE_CFGB); -#endif - (void) inb(iobase + IRCC_FIFO_THRESHOLD); - outb(SMSC_IRCC2_FIFO_THRESHOLD, iobase + IRCC_FIFO_THRESHOLD); - - register_bank(iobase, 4); - outb((inb(iobase + IRCC_CONTROL) & 0x30), iobase + IRCC_CONTROL); - - register_bank(iobase, 0); - outb(0, iobase + IRCC_LCR_A); - - smsc_ircc_set_sir_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED); - - /* Power on device */ - outb(0x00, iobase + IRCC_MASTER); -} - -/* - * Function smsc_ircc_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct smsc_ircc_cb *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else { - /* Make sure we are the only one touching - * self->io.speed and the hardware - Jean II */ - spin_lock_irqsave(&self->lock, flags); - smsc_ircc_change_speed(self, irq->ifr_baudrate); - spin_unlock_irqrestore(&self->lock, flags); - } - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = smsc_ircc_is_receiving(self); - break; - #if 0 - case SIOCSDTRRTS: - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - smsc_ircc_sir_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); - break; - #endif - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -#if SMSC_IRCC2_C_NET_TIMEOUT -/* - * Function smsc_ircc_timeout (struct net_device *dev) - * - * The networking timeout management. - * - */ - -static void smsc_ircc_timeout(struct net_device *dev) -{ - struct smsc_ircc_cb *self = netdev_priv(dev); - unsigned long flags; - - net_warn_ratelimited("%s: transmit timed out, changing speed to: %d\n", - dev->name, self->io.speed); - spin_lock_irqsave(&self->lock, flags); - smsc_ircc_sir_start(self); - smsc_ircc_change_speed(self, self->io.speed); - netif_trans_update(dev); /* prevent tx timeout */ - netif_wake_queue(dev); - spin_unlock_irqrestore(&self->lock, flags); -} -#endif - -/* - * Function smsc_ircc_hard_xmit_sir (struct sk_buff *skb, struct net_device *dev) - * - * Transmits the current frame until FIFO is full, then - * waits until the next transmit interrupt, and continues until the - * frame is transmitted. - */ -static netdev_tx_t smsc_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev) -{ - struct smsc_ircc_cb *self; - unsigned long flags; - s32 speed; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - - netif_stop_queue(dev); - - /* Make sure test of self->io.speed & speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if (speed != self->io.speed && speed != -1) { - /* Check for empty frame */ - if (!skb->len) { - /* - * We send frames one by one in SIR mode (no - * pipelining), so at this point, if we were sending - * a previous frame, we just received the interrupt - * telling us it is finished (UART_IIR_THRI). - * Therefore, waiting for the transmitter to really - * finish draining the fifo won't take too long. - * And the interrupt handler is not expected to run. - * - Jean II */ - smsc_ircc_sir_wait_hw_transmitter_finish(self); - smsc_ircc_change_speed(self, speed); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - self->new_speed = speed; - } - - /* Init tx buffer */ - self->tx_buff.data = self->tx_buff.head; - - /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - dev->stats.tx_bytes += self->tx_buff.len; - - /* Turn on transmit finished interrupt. Will fire immediately! */ - outb(UART_IER_THRI, self->io.sir_base + UART_IER); - - spin_unlock_irqrestore(&self->lock, flags); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* - * Function smsc_ircc_set_fir_speed (self, baud) - * - * Change the speed of the device - * - */ -static void smsc_ircc_set_fir_speed(struct smsc_ircc_cb *self, u32 speed) -{ - int fir_base, ir_mode, ctrl, fast; - - IRDA_ASSERT(self != NULL, return;); - fir_base = self->io.fir_base; - - self->io.speed = speed; - - switch (speed) { - default: - case 576000: - ir_mode = IRCC_CFGA_IRDA_HDLC; - ctrl = IRCC_CRC; - fast = 0; - pr_debug("%s(), handling baud of 576000\n", __func__); - break; - case 1152000: - ir_mode = IRCC_CFGA_IRDA_HDLC; - ctrl = IRCC_1152 | IRCC_CRC; - fast = IRCC_LCR_A_FAST | IRCC_LCR_A_GP_DATA; - pr_debug("%s(), handling baud of 1152000\n", - __func__); - break; - case 4000000: - ir_mode = IRCC_CFGA_IRDA_4PPM; - ctrl = IRCC_CRC; - fast = IRCC_LCR_A_FAST; - pr_debug("%s(), handling baud of 4000000\n", - __func__); - break; - } - #if 0 - Now in tranceiver! - /* This causes an interrupt */ - register_bank(fir_base, 0); - outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast, fir_base + IRCC_LCR_A); - #endif - - register_bank(fir_base, 1); - outb(((inb(fir_base + IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | ir_mode), fir_base + IRCC_SCE_CFGA); - - register_bank(fir_base, 4); - outb((inb(fir_base + IRCC_CONTROL) & 0x30) | ctrl, fir_base + IRCC_CONTROL); -} - -/* - * Function smsc_ircc_fir_start(self) - * - * Change the speed of the device - * - */ -static void smsc_ircc_fir_start(struct smsc_ircc_cb *self) -{ - struct net_device *dev; - int fir_base; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return;); - dev = self->netdev; - IRDA_ASSERT(dev != NULL, return;); - - fir_base = self->io.fir_base; - - /* Reset everything */ - - /* Clear FIFO */ - outb(inb(fir_base + IRCC_LCR_A) | IRCC_LCR_A_FIFO_RESET, fir_base + IRCC_LCR_A); - - /* Enable interrupt */ - /*outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base + IRCC_IER);*/ - - register_bank(fir_base, 1); - - /* Select the TX/RX interface */ -#ifdef SMSC_669 /* Uses pin 88/89 for Rx/Tx */ - outb(((inb(fir_base + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), - fir_base + IRCC_SCE_CFGB); -#else - outb(((inb(fir_base + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), - fir_base + IRCC_SCE_CFGB); -#endif - (void) inb(fir_base + IRCC_FIFO_THRESHOLD); - - /* Enable SCE interrupts */ - outb(0, fir_base + IRCC_MASTER); - register_bank(fir_base, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, fir_base + IRCC_IER); - outb(IRCC_MASTER_INT_EN, fir_base + IRCC_MASTER); -} - -/* - * Function smsc_ircc_fir_stop(self, baud) - * - * Change the speed of the device - * - */ -static void smsc_ircc_fir_stop(struct smsc_ircc_cb *self) -{ - int fir_base; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return;); - - fir_base = self->io.fir_base; - register_bank(fir_base, 0); - /*outb(IRCC_MASTER_RESET, fir_base + IRCC_MASTER);*/ - outb(inb(fir_base + IRCC_LCR_B) & IRCC_LCR_B_SIP_ENABLE, fir_base + IRCC_LCR_B); -} - - -/* - * Function smsc_ircc_change_speed(self, baud) - * - * Change the speed of the device - * - * This function *must* be called with spinlock held, because it may - * be called from the irq handler. - Jean II - */ -static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed) -{ - struct net_device *dev; - int last_speed_was_sir; - - pr_debug("%s() changing speed to: %d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return;); - dev = self->netdev; - - last_speed_was_sir = self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED; - - #if 0 - /* Temp Hack */ - speed= 1152000; - self->io.speed = speed; - last_speed_was_sir = 0; - smsc_ircc_fir_start(self); - #endif - - if (self->io.speed == 0) - smsc_ircc_sir_start(self); - - #if 0 - if (!last_speed_was_sir) speed = self->io.speed; - #endif - - if (self->io.speed != speed) - smsc_ircc_set_transceiver_for_speed(self, speed); - - self->io.speed = speed; - - if (speed <= SMSC_IRCC2_MAX_SIR_SPEED) { - if (!last_speed_was_sir) { - smsc_ircc_fir_stop(self); - smsc_ircc_sir_start(self); - } - smsc_ircc_set_sir_speed(self, speed); - } else { - if (last_speed_was_sir) { - #if SMSC_IRCC2_C_SIR_STOP - smsc_ircc_sir_stop(self); - #endif - smsc_ircc_fir_start(self); - } - smsc_ircc_set_fir_speed(self, speed); - - #if 0 - self->tx_buff.len = 10; - self->tx_buff.data = self->tx_buff.head; - - smsc_ircc_dma_xmit(self, 4000); - #endif - /* Be ready for incoming frames */ - smsc_ircc_dma_receive(self); - } - - netif_wake_queue(dev); -} - -/* - * Function smsc_ircc_set_sir_speed (self, speed) - * - * Set speed of IrDA port to specified baudrate - * - */ -static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, __u32 speed) -{ - int iobase; - int fcr; /* FIFO control reg */ - int lcr; /* Line control reg */ - int divisor; - - pr_debug("%s(), Setting speed to: %d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return;); - iobase = self->io.sir_base; - - /* Update accounting for new speed */ - self->io.speed = speed; - - /* Turn off interrupts */ - outb(0, iobase + UART_IER); - - divisor = SMSC_IRCC2_MAX_SIR_SPEED / speed; - - fcr = UART_FCR_ENABLE_FIFO; - - /* - * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and - * almost 1,7 ms at 19200 bps. At speeds above that we can just forget - * about this timeout since it will always be fast enough. - */ - fcr |= self->io.speed < 38400 ? - UART_FCR_TRIGGER_1 : UART_FCR_TRIGGER_14; - - /* IrDA ports use 8N1 */ - lcr = UART_LCR_WLEN8; - - outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */ - outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */ - outb(divisor >> 8, iobase + UART_DLM); - outb(lcr, iobase + UART_LCR); /* Set 8N1 */ - outb(fcr, iobase + UART_FCR); /* Enable FIFO's */ - - /* Turn on interrups */ - outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); - - pr_debug("%s() speed changed to: %d\n", __func__, speed); -} - - -/* - * Function smsc_ircc_hard_xmit_fir (skb, dev) - * - * Transmit the frame! - * - */ -static netdev_tx_t smsc_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev) -{ - struct smsc_ircc_cb *self; - unsigned long flags; - s32 speed; - int mtt; - - IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - - netif_stop_queue(dev); - - /* Make sure test of self->io.speed & speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if we need to change the speed after this frame */ - speed = irda_get_next_speed(skb); - if (speed != self->io.speed && speed != -1) { - /* Check for empty frame */ - if (!skb->len) { - /* Note : you should make sure that speed changes - * are not going to corrupt any outgoing frame. - * Look at nsc-ircc for the gory details - Jean II */ - smsc_ircc_change_speed(self, speed); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - self->new_speed = speed; - } - - skb_copy_from_linear_data(skb, self->tx_buff.head, skb->len); - - self->tx_buff.len = skb->len; - self->tx_buff.data = self->tx_buff.head; - - mtt = irda_get_mtt(skb); - if (mtt) { - int bofs; - - /* - * Compute how many BOFs (STA or PA's) we need to waste the - * min turn time given the speed of the link. - */ - bofs = mtt * (self->io.speed / 1000) / 8000; - if (bofs > 4095) - bofs = 4095; - - smsc_ircc_dma_xmit(self, bofs); - } else { - /* Transmit frame */ - smsc_ircc_dma_xmit(self, 0); - } - - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* - * Function smsc_ircc_dma_xmit (self, bofs) - * - * Transmit data using DMA - * - */ -static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int bofs) -{ - int iobase = self->io.fir_base; - u8 ctrl; - - pr_debug("%s\n", __func__); -#if 1 - /* Disable Rx */ - register_bank(iobase, 0); - outb(0x00, iobase + IRCC_LCR_B); -#endif - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - iobase + IRCC_SCE_CFGB); - - self->io.direction = IO_XMIT; - - /* Set BOF additional count for generating the min turn time */ - register_bank(iobase, 4); - outb(bofs & 0xff, iobase + IRCC_BOF_COUNT_LO); - ctrl = inb(iobase + IRCC_CONTROL) & 0xf0; - outb(ctrl | ((bofs >> 8) & 0x0f), iobase + IRCC_BOF_COUNT_HI); - - /* Set max Tx frame size */ - outb(self->tx_buff.len >> 8, iobase + IRCC_TX_SIZE_HI); - outb(self->tx_buff.len & 0xff, iobase + IRCC_TX_SIZE_LO); - - /*outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR);*/ - - /* Enable burst mode chip Tx DMA */ - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | - IRCC_CFGB_DMA_BURST, iobase + IRCC_SCE_CFGB); - - /* Setup DMA controller (must be done after enabling chip DMA) */ - irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, - DMA_TX_MODE); - - /* Enable interrupt */ - - register_bank(iobase, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER); - outb(IRCC_MASTER_INT_EN, iobase + IRCC_MASTER); - - /* Enable transmit */ - outb(IRCC_LCR_B_SCE_TRANSMIT | IRCC_LCR_B_SIP_ENABLE, iobase + IRCC_LCR_B); -} - -/* - * Function smsc_ircc_dma_xmit_complete (self) - * - * The transfer of a frame in finished. This function will only be called - * by the interrupt handler - * - */ -static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self) -{ - int iobase = self->io.fir_base; - - pr_debug("%s\n", __func__); -#if 0 - /* Disable Tx */ - register_bank(iobase, 0); - outb(0x00, iobase + IRCC_LCR_B); -#endif - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - iobase + IRCC_SCE_CFGB); - - /* Check for underrun! */ - register_bank(iobase, 0); - if (inb(iobase + IRCC_LSR) & IRCC_LSR_UNDERRUN) { - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - - /* Reset error condition */ - register_bank(iobase, 0); - outb(IRCC_MASTER_ERROR_RESET, iobase + IRCC_MASTER); - outb(0x00, iobase + IRCC_MASTER); - } else { - self->netdev->stats.tx_packets++; - self->netdev->stats.tx_bytes += self->tx_buff.len; - } - - /* Check if it's time to change the speed */ - if (self->new_speed) { - smsc_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - netif_wake_queue(self->netdev); -} - -/* - * Function smsc_ircc_dma_receive(self) - * - * Get ready for receiving a frame. The device will initiate a DMA - * if it starts to receive a frame. - * - */ -static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self) -{ - int iobase = self->io.fir_base; -#if 0 - /* Turn off chip DMA */ - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - iobase + IRCC_SCE_CFGB); -#endif - - /* Disable Tx */ - register_bank(iobase, 0); - outb(0x00, iobase + IRCC_LCR_B); - - /* Turn off chip DMA */ - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - iobase + IRCC_SCE_CFGB); - - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - - /* Set max Rx frame size */ - register_bank(iobase, 4); - outb((2050 >> 8) & 0x0f, iobase + IRCC_RX_SIZE_HI); - outb(2050 & 0xff, iobase + IRCC_RX_SIZE_LO); - - /* Setup DMA controller */ - irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, - DMA_RX_MODE); - - /* Enable burst mode chip Rx DMA */ - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | - IRCC_CFGB_DMA_BURST, iobase + IRCC_SCE_CFGB); - - /* Enable interrupt */ - register_bank(iobase, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER); - outb(IRCC_MASTER_INT_EN, iobase + IRCC_MASTER); - - /* Enable receiver */ - register_bank(iobase, 0); - outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE, - iobase + IRCC_LCR_B); - - return 0; -} - -/* - * Function smsc_ircc_dma_receive_complete(self) - * - * Finished with receiving frames - * - */ -static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self) -{ - struct sk_buff *skb; - int len, msgcnt, lsr; - int iobase = self->io.fir_base; - - register_bank(iobase, 0); - - pr_debug("%s\n", __func__); -#if 0 - /* Disable Rx */ - register_bank(iobase, 0); - outb(0x00, iobase + IRCC_LCR_B); -#endif - register_bank(iobase, 0); - outb(inb(iobase + IRCC_LSAR) & ~IRCC_LSAR_ADDRESS_MASK, iobase + IRCC_LSAR); - lsr= inb(iobase + IRCC_LSR); - msgcnt = inb(iobase + IRCC_LCR_B) & 0x08; - - pr_debug("%s: dma count = %d\n", __func__, - get_dma_residue(self->io.dma)); - - len = self->rx_buff.truesize - get_dma_residue(self->io.dma); - - /* Look for errors */ - if (lsr & (IRCC_LSR_FRAME_ERROR | IRCC_LSR_CRC_ERROR | IRCC_LSR_SIZE_ERROR)) { - self->netdev->stats.rx_errors++; - if (lsr & IRCC_LSR_FRAME_ERROR) - self->netdev->stats.rx_frame_errors++; - if (lsr & IRCC_LSR_CRC_ERROR) - self->netdev->stats.rx_crc_errors++; - if (lsr & IRCC_LSR_SIZE_ERROR) - self->netdev->stats.rx_length_errors++; - if (lsr & (IRCC_LSR_UNDERRUN | IRCC_LSR_OVERRUN)) - self->netdev->stats.rx_length_errors++; - return; - } - - /* Remove CRC */ - len -= self->io.speed < 4000000 ? 2 : 4; - - if (len < 2 || len > 2050) { - net_warn_ratelimited("%s(), bogus len=%d\n", __func__, len); - return; - } - pr_debug("%s: msgcnt = %d, len=%d\n", __func__, msgcnt, len); - - skb = dev_alloc_skb(len + 1); - if (!skb) - return; - - /* Make sure IP header gets aligned */ - skb_reserve(skb, 1); - - skb_put_data(skb, self->rx_buff.data, len); - self->netdev->stats.rx_packets++; - self->netdev->stats.rx_bytes += len; - - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); -} - -/* - * Function smsc_ircc_sir_receive (self) - * - * Receive one frame from the infrared port - * - */ -static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self) -{ - int boguscount = 0; - int iobase; - - IRDA_ASSERT(self != NULL, return;); - - iobase = self->io.sir_base; - - /* - * Receive all characters in Rx FIFO, unwrap and unstuff them. - * async_unwrap_char will deliver all found frames - */ - do { - async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, - inb(iobase + UART_RX)); - - /* Make sure we don't stay here to long */ - if (boguscount++ > 32) { - pr_debug("%s(), breaking!\n", __func__); - break; - } - } while (inb(iobase + UART_LSR) & UART_LSR_DR); -} - - -/* - * Function smsc_ircc_interrupt (irq, dev_id, regs) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t smsc_ircc_interrupt(int dummy, void *dev_id) -{ - struct net_device *dev = dev_id; - struct smsc_ircc_cb *self = netdev_priv(dev); - int iobase, iir, lcra, lsr; - irqreturn_t ret = IRQ_NONE; - - /* Serialise the interrupt handler in various CPUs, stop Tx path */ - spin_lock(&self->lock); - - /* Check if we should use the SIR interrupt handler */ - if (self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED) { - ret = smsc_ircc_interrupt_sir(dev); - goto irq_ret_unlock; - } - - iobase = self->io.fir_base; - - register_bank(iobase, 0); - iir = inb(iobase + IRCC_IIR); - if (iir == 0) - goto irq_ret_unlock; - ret = IRQ_HANDLED; - - /* Disable interrupts */ - outb(0, iobase + IRCC_IER); - lcra = inb(iobase + IRCC_LCR_A); - lsr = inb(iobase + IRCC_LSR); - - pr_debug("%s(), iir = 0x%02x\n", __func__, iir); - - if (iir & IRCC_IIR_EOM) { - if (self->io.direction == IO_RECV) - smsc_ircc_dma_receive_complete(self); - else - smsc_ircc_dma_xmit_complete(self); - - smsc_ircc_dma_receive(self); - } - - if (iir & IRCC_IIR_ACTIVE_FRAME) { - /*printk(KERN_WARNING "%s(): Active Frame\n", __func__);*/ - } - - /* Enable interrupts again */ - - register_bank(iobase, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER); - - irq_ret_unlock: - spin_unlock(&self->lock); - - return ret; -} - -/* - * Function irport_interrupt_sir (irq, dev_id) - * - * Interrupt handler for SIR modes - */ -static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev) -{ - struct smsc_ircc_cb *self = netdev_priv(dev); - int boguscount = 0; - int iobase; - int iir, lsr; - - /* Already locked coming here in smsc_ircc_interrupt() */ - /*spin_lock(&self->lock);*/ - - iobase = self->io.sir_base; - - iir = inb(iobase + UART_IIR) & UART_IIR_ID; - if (iir == 0) - return IRQ_NONE; - while (iir) { - /* Clear interrupt */ - lsr = inb(iobase + UART_LSR); - - pr_debug("%s(), iir=%02x, lsr=%02x, iobase=%#x\n", - __func__, iir, lsr, iobase); - - switch (iir) { - case UART_IIR_RLSI: - pr_debug("%s(), RLSI\n", __func__); - break; - case UART_IIR_RDI: - /* Receive interrupt */ - smsc_ircc_sir_receive(self); - break; - case UART_IIR_THRI: - if (lsr & UART_LSR_THRE) - /* Transmitter ready for data */ - smsc_ircc_sir_write_wakeup(self); - break; - default: - pr_debug("%s(), unhandled IIR=%#x\n", - __func__, iir); - break; - } - - /* Make sure we don't stay here to long */ - if (boguscount++ > 100) - break; - - iir = inb(iobase + UART_IIR) & UART_IIR_ID; - } - /*spin_unlock(&self->lock);*/ - return IRQ_HANDLED; -} - - -#if 0 /* unused */ -/* - * Function ircc_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int ircc_is_receiving(struct smsc_ircc_cb *self) -{ - int status = FALSE; - /* int iobase; */ - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return FALSE;); - - pr_debug("%s: dma count = %d\n", __func__, - get_dma_residue(self->io.dma)); - - status = (self->rx_buff.state != OUTSIDE_FRAME); - - return status; -} -#endif /* unused */ - -static int smsc_ircc_request_irq(struct smsc_ircc_cb *self) -{ - int error; - - error = request_irq(self->io.irq, smsc_ircc_interrupt, 0, - self->netdev->name, self->netdev); - if (error) - pr_debug("%s(), unable to allocate irq=%d, err=%d\n", - __func__, self->io.irq, error); - - return error; -} - -static void smsc_ircc_start_interrupts(struct smsc_ircc_cb *self) -{ - unsigned long flags; - - spin_lock_irqsave(&self->lock, flags); - - self->io.speed = 0; - smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED); - - spin_unlock_irqrestore(&self->lock, flags); -} - -static void smsc_ircc_stop_interrupts(struct smsc_ircc_cb *self) -{ - int iobase = self->io.fir_base; - unsigned long flags; - - spin_lock_irqsave(&self->lock, flags); - - register_bank(iobase, 0); - outb(0, iobase + IRCC_IER); - outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER); - outb(0x00, iobase + IRCC_MASTER); - - spin_unlock_irqrestore(&self->lock, flags); -} - - -/* - * Function smsc_ircc_net_open (dev) - * - * Start the device - * - */ -static int smsc_ircc_net_open(struct net_device *dev) -{ - struct smsc_ircc_cb *self; - char hwname[16]; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - if (self->io.suspended) { - pr_debug("%s(), device is suspended\n", __func__); - return -EAGAIN; - } - - if (request_irq(self->io.irq, smsc_ircc_interrupt, 0, dev->name, - (void *) dev)) { - pr_debug("%s(), unable to allocate irq=%d\n", - __func__, self->io.irq); - return -EAGAIN; - } - - smsc_ircc_start_interrupts(self); - - /* Give self a hardware name */ - /* It would be cool to offer the chip revision here - Jean II */ - sprintf(hwname, "SMSC @ 0x%03x", self->io.fir_base); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open(dev, &self->qos, hwname); - - /* - * Always allocate the DMA channel after the IRQ, - * and clean up on failure. - */ - if (request_dma(self->io.dma, dev->name)) { - smsc_ircc_net_close(dev); - - net_warn_ratelimited("%s(), unable to allocate DMA=%d\n", - __func__, self->io.dma); - return -EAGAIN; - } - - netif_start_queue(dev); - - return 0; -} - -/* - * Function smsc_ircc_net_close (dev) - * - * Stop the device - * - */ -static int smsc_ircc_net_close(struct net_device *dev) -{ - struct smsc_ircc_cb *self; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - smsc_ircc_stop_interrupts(self); - - /* if we are called from smsc_ircc_resume we don't have IRQ reserved */ - if (!self->io.suspended) - free_irq(self->io.irq, dev); - - disable_dma(self->io.dma); - free_dma(self->io.dma); - - return 0; -} - -static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state) -{ - struct smsc_ircc_cb *self = platform_get_drvdata(dev); - - if (!self->io.suspended) { - pr_debug("%s, Suspending\n", driver_name); - - rtnl_lock(); - if (netif_running(self->netdev)) { - netif_device_detach(self->netdev); - smsc_ircc_stop_interrupts(self); - free_irq(self->io.irq, self->netdev); - disable_dma(self->io.dma); - } - self->io.suspended = 1; - rtnl_unlock(); - } - - return 0; -} - -static int smsc_ircc_resume(struct platform_device *dev) -{ - struct smsc_ircc_cb *self = platform_get_drvdata(dev); - - if (self->io.suspended) { - pr_debug("%s, Waking up\n", driver_name); - - rtnl_lock(); - smsc_ircc_init_chip(self); - if (netif_running(self->netdev)) { - if (smsc_ircc_request_irq(self)) { - /* - * Don't fail resume process, just kill this - * network interface - */ - unregister_netdevice(self->netdev); - } else { - enable_dma(self->io.dma); - smsc_ircc_start_interrupts(self); - netif_device_attach(self->netdev); - } - } - self->io.suspended = 0; - rtnl_unlock(); - } - return 0; -} - -/* - * Function smsc_ircc_close (self) - * - * Close driver instance - * - */ -static int __exit smsc_ircc_close(struct smsc_ircc_cb *self) -{ - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return -1;); - - platform_device_unregister(self->pldev); - - /* Remove netdevice */ - unregister_netdev(self->netdev); - - smsc_ircc_stop_interrupts(self); - - /* Release the PORTS that this driver is using */ - pr_debug("%s(), releasing 0x%03x\n", __func__, - self->io.fir_base); - - release_region(self->io.fir_base, self->io.fir_ext); - - pr_debug("%s(), releasing 0x%03x\n", __func__, - self->io.sir_base); - - release_region(self->io.sir_base, self->io.sir_ext); - - if (self->tx_buff.head) - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - - if (self->rx_buff.head) - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - free_netdev(self->netdev); - - return 0; -} - -static void __exit smsc_ircc_cleanup(void) -{ - int i; - - pr_debug("%s\n", __func__); - - for (i = 0; i < 2; i++) { - if (dev_self[i]) - smsc_ircc_close(dev_self[i]); - } - - if (pnp_driver_registered) - pnp_unregister_driver(&smsc_ircc_pnp_driver); - - platform_driver_unregister(&smsc_ircc_driver); -} - -/* - * Start SIR operations - * - * This function *must* be called with spinlock held, because it may - * be called from the irq handler (via smsc_ircc_change_speed()). - Jean II - */ -static void smsc_ircc_sir_start(struct smsc_ircc_cb *self) -{ - struct net_device *dev; - int fir_base, sir_base; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return;); - dev = self->netdev; - IRDA_ASSERT(dev != NULL, return;); - - fir_base = self->io.fir_base; - sir_base = self->io.sir_base; - - /* Reset everything */ - outb(IRCC_MASTER_RESET, fir_base + IRCC_MASTER); - - #if SMSC_IRCC2_C_SIR_STOP - /*smsc_ircc_sir_stop(self);*/ - #endif - - register_bank(fir_base, 1); - outb(((inb(fir_base + IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | IRCC_CFGA_IRDA_SIR_A), fir_base + IRCC_SCE_CFGA); - - /* Initialize UART */ - outb(UART_LCR_WLEN8, sir_base + UART_LCR); /* Reset DLAB */ - outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), sir_base + UART_MCR); - - /* Turn on interrups */ - outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, sir_base + UART_IER); - - pr_debug("%s() - exit\n", __func__); - - outb(0x00, fir_base + IRCC_MASTER); -} - -#if SMSC_IRCC2_C_SIR_STOP -void smsc_ircc_sir_stop(struct smsc_ircc_cb *self) -{ - int iobase; - - pr_debug("%s\n", __func__); - iobase = self->io.sir_base; - - /* Reset UART */ - outb(0, iobase + UART_MCR); - - /* Turn off interrupts */ - outb(0, iobase + UART_IER); -} -#endif - -/* - * Function smsc_sir_write_wakeup (self) - * - * Called by the SIR interrupt handler when there's room for more data. - * If we have more packets to send, we send them here. - * - */ -static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self) -{ - int actual = 0; - int iobase; - int fcr; - - IRDA_ASSERT(self != NULL, return;); - - pr_debug("%s\n", __func__); - - iobase = self->io.sir_base; - - /* Finished with frame? */ - if (self->tx_buff.len > 0) { - /* Write data left in transmit buffer */ - actual = smsc_ircc_sir_write(iobase, self->io.fifo_size, - self->tx_buff.data, self->tx_buff.len); - self->tx_buff.data += actual; - self->tx_buff.len -= actual; - } else { - - /*if (self->tx_buff.len ==0) {*/ - - /* - * Now serial buffer is almost free & we can start - * transmission of another packet. But first we must check - * if we need to change the speed of the hardware - */ - if (self->new_speed) { - pr_debug("%s(), Changing speed to %d.\n", - __func__, self->new_speed); - smsc_ircc_sir_wait_hw_transmitter_finish(self); - smsc_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - } else { - /* Tell network layer that we want more frames */ - netif_wake_queue(self->netdev); - } - self->netdev->stats.tx_packets++; - - if (self->io.speed <= 115200) { - /* - * Reset Rx FIFO to make sure that all reflected transmit data - * is discarded. This is needed for half duplex operation - */ - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR; - fcr |= self->io.speed < 38400 ? - UART_FCR_TRIGGER_1 : UART_FCR_TRIGGER_14; - - outb(fcr, iobase + UART_FCR); - - /* Turn on receive interrupts */ - outb(UART_IER_RDI, iobase + UART_IER); - } - } -} - -/* - * Function smsc_ircc_sir_write (iobase, fifo_size, buf, len) - * - * Fill Tx FIFO with transmit data - * - */ -static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len) -{ - int actual = 0; - - /* Tx FIFO should be empty! */ - if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) { - net_warn_ratelimited("%s(), failed, fifo not empty!\n", - __func__); - return 0; - } - - /* Fill FIFO with current frame */ - while (fifo_size-- > 0 && actual < len) { - /* Transmit next byte */ - outb(buf[actual], iobase + UART_TX); - actual++; - } - return actual; -} - -/* - * Function smsc_ircc_is_receiving (self) - * - * Returns true is we are currently receiving data - * - */ -static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self) -{ - return self->rx_buff.state != OUTSIDE_FRAME; -} - - -/* - * Function smsc_ircc_probe_transceiver(self) - * - * Tries to find the used Transceiver - * - */ -static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self) -{ - unsigned int i; - - IRDA_ASSERT(self != NULL, return;); - - for (i = 0; smsc_transceivers[i].name != NULL; i++) - if (smsc_transceivers[i].probe(self->io.fir_base)) { - net_info_ratelimited(" %s transceiver found\n", - smsc_transceivers[i].name); - self->transceiver= i + 1; - return; - } - - net_info_ratelimited("No transceiver found. Defaulting to %s\n", - smsc_transceivers[SMSC_IRCC2_C_DEFAULT_TRANSCEIVER].name); - - self->transceiver = SMSC_IRCC2_C_DEFAULT_TRANSCEIVER; -} - - -/* - * Function smsc_ircc_set_transceiver_for_speed(self, speed) - * - * Set the transceiver according to the speed - * - */ -static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed) -{ - unsigned int trx; - - trx = self->transceiver; - if (trx > 0) - smsc_transceivers[trx - 1].set_for_speed(self->io.fir_base, speed); -} - -/* - * Function smsc_ircc_wait_hw_transmitter_finish () - * - * Wait for the real end of HW transmission - * - * The UART is a strict FIFO, and we get called only when we have finished - * pushing data to the FIFO, so the maximum amount of time we must wait - * is only for the FIFO to drain out. - * - * We use a simple calibrated loop. We may need to adjust the loop - * delay (udelay) to balance I/O traffic and latency. And we also need to - * adjust the maximum timeout. - * It would probably be better to wait for the proper interrupt, - * but it doesn't seem to be available. - * - * We can't use jiffies or kernel timers because : - * 1) We are called from the interrupt handler, which disable softirqs, - * so jiffies won't be increased - * 2) Jiffies granularity is usually very coarse (10ms), and we don't - * want to wait that long to detect stuck hardware. - * Jean II - */ - -static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self) -{ - int iobase = self->io.sir_base; - int count = SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US; - - /* Calibrated busy loop */ - while (count-- > 0 && !(inb(iobase + UART_LSR) & UART_LSR_TEMT)) - udelay(1); - - if (count < 0) - pr_debug("%s(): stuck transmitter\n", __func__); -} - - -/* PROBING - * - * REVISIT we can be told about the device by PNP, and should use that info - * instead of probing hardware and creating a platform_device ... - */ - -static int __init smsc_ircc_look_for_chips(void) -{ - struct smsc_chip_address *address; - char *type; - unsigned int cfg_base, found; - - found = 0; - address = possible_addresses; - - while (address->cfg_base) { - cfg_base = address->cfg_base; - - /*printk(KERN_WARNING "%s(): probing: 0x%02x for: 0x%02x\n", __func__, cfg_base, address->type);*/ - - if (address->type & SMSCSIO_TYPE_FDC) { - type = "FDC"; - if (address->type & SMSCSIO_TYPE_FLAT) - if (!smsc_superio_flat(fdc_chips_flat, cfg_base, type)) - found++; - - if (address->type & SMSCSIO_TYPE_PAGED) - if (!smsc_superio_paged(fdc_chips_paged, cfg_base, type)) - found++; - } - if (address->type & SMSCSIO_TYPE_LPC) { - type = "LPC"; - if (address->type & SMSCSIO_TYPE_FLAT) - if (!smsc_superio_flat(lpc_chips_flat, cfg_base, type)) - found++; - - if (address->type & SMSCSIO_TYPE_PAGED) - if (!smsc_superio_paged(lpc_chips_paged, cfg_base, type)) - found++; - } - address++; - } - return found; -} - -/* - * Function smsc_superio_flat (chip, base, type) - * - * Try to get configuration of a smc SuperIO chip with flat register model - * - */ -static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned short cfgbase, char *type) -{ - unsigned short firbase, sirbase; - u8 mode, dma, irq; - int ret = -ENODEV; - - pr_debug("%s\n", __func__); - - if (smsc_ircc_probe(cfgbase, SMSCSIOFLAT_DEVICEID_REG, chips, type) == NULL) - return ret; - - outb(SMSCSIOFLAT_UARTMODE0C_REG, cfgbase); - mode = inb(cfgbase + 1); - - /*printk(KERN_WARNING "%s(): mode: 0x%02x\n", __func__, mode);*/ - - if (!(mode & SMSCSIOFLAT_UART2MODE_VAL_IRDA)) - net_warn_ratelimited("%s(): IrDA not enabled\n", __func__); - - outb(SMSCSIOFLAT_UART2BASEADDR_REG, cfgbase); - sirbase = inb(cfgbase + 1) << 2; - - /* FIR iobase */ - outb(SMSCSIOFLAT_FIRBASEADDR_REG, cfgbase); - firbase = inb(cfgbase + 1) << 3; - - /* DMA */ - outb(SMSCSIOFLAT_FIRDMASELECT_REG, cfgbase); - dma = inb(cfgbase + 1) & SMSCSIOFLAT_FIRDMASELECT_MASK; - - /* IRQ */ - outb(SMSCSIOFLAT_UARTIRQSELECT_REG, cfgbase); - irq = inb(cfgbase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK; - - net_info_ratelimited("%s(): fir: 0x%02x, sir: 0x%02x, dma: %02d, irq: %d, mode: 0x%02x\n", - __func__, firbase, sirbase, dma, irq, mode); - - if (firbase && smsc_ircc_open(firbase, sirbase, dma, irq) == 0) - ret = 0; - - /* Exit configuration */ - outb(SMSCSIO_CFGEXITKEY, cfgbase); - - return ret; -} - -/* - * Function smsc_superio_paged (chip, base, type) - * - * Try to get configuration of a smc SuperIO chip with paged register model - * - */ -static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type) -{ - unsigned short fir_io, sir_io; - int ret = -ENODEV; - - pr_debug("%s\n", __func__); - - if (smsc_ircc_probe(cfg_base, 0x20, chips, type) == NULL) - return ret; - - /* Select logical device (UART2) */ - outb(0x07, cfg_base); - outb(0x05, cfg_base + 1); - - /* SIR iobase */ - outb(0x60, cfg_base); - sir_io = inb(cfg_base + 1) << 8; - outb(0x61, cfg_base); - sir_io |= inb(cfg_base + 1); - - /* Read FIR base */ - outb(0x62, cfg_base); - fir_io = inb(cfg_base + 1) << 8; - outb(0x63, cfg_base); - fir_io |= inb(cfg_base + 1); - outb(0x2b, cfg_base); /* ??? */ - - if (fir_io && smsc_ircc_open(fir_io, sir_io, ircc_dma, ircc_irq) == 0) - ret = 0; - - /* Exit configuration */ - outb(SMSCSIO_CFGEXITKEY, cfg_base); - - return ret; -} - - -static int __init smsc_access(unsigned short cfg_base, unsigned char reg) -{ - pr_debug("%s\n", __func__); - - outb(reg, cfg_base); - return inb(cfg_base) != reg ? -1 : 0; -} - -static const struct smsc_chip * __init smsc_ircc_probe(unsigned short cfg_base, u8 reg, const struct smsc_chip *chip, char *type) -{ - u8 devid, xdevid, rev; - - pr_debug("%s\n", __func__); - - /* Leave configuration */ - - outb(SMSCSIO_CFGEXITKEY, cfg_base); - - if (inb(cfg_base) == SMSCSIO_CFGEXITKEY) /* not a smc superio chip */ - return NULL; - - outb(reg, cfg_base); - - xdevid = inb(cfg_base + 1); - - /* Enter configuration */ - - outb(SMSCSIO_CFGACCESSKEY, cfg_base); - - #if 0 - if (smsc_access(cfg_base,0x55)) /* send second key and check */ - return NULL; - #endif - - /* probe device ID */ - - if (smsc_access(cfg_base, reg)) - return NULL; - - devid = inb(cfg_base + 1); - - if (devid == 0 || devid == 0xff) /* typical values for unused port */ - return NULL; - - /* probe revision ID */ - - if (smsc_access(cfg_base, reg + 1)) - return NULL; - - rev = inb(cfg_base + 1); - - if (rev >= 128) /* i think this will make no sense */ - return NULL; - - if (devid == xdevid) /* protection against false positives */ - return NULL; - - /* Check for expected device ID; are there others? */ - - while (chip->devid != devid) { - - chip++; - - if (chip->name == NULL) - return NULL; - } - - net_info_ratelimited("found SMC SuperIO Chip (devid=0x%02x rev=%02X base=0x%04x): %s%s\n", - devid, rev, cfg_base, type, chip->name); - - if (chip->rev > rev) { - net_info_ratelimited("Revision higher than expected\n"); - return NULL; - } - - if (chip->flags & NoIRDA) - net_info_ratelimited("chipset does not support IRDA\n"); - - return chip; -} - -static int __init smsc_superio_fdc(unsigned short cfg_base) -{ - int ret = -1; - - if (!request_region(cfg_base, 2, driver_name)) { - net_warn_ratelimited("%s: can't get cfg_base of 0x%03x\n", - __func__, cfg_base); - } else { - if (!smsc_superio_flat(fdc_chips_flat, cfg_base, "FDC") || - !smsc_superio_paged(fdc_chips_paged, cfg_base, "FDC")) - ret = 0; - - release_region(cfg_base, 2); - } - - return ret; -} - -static int __init smsc_superio_lpc(unsigned short cfg_base) -{ - int ret = -1; - - if (!request_region(cfg_base, 2, driver_name)) { - net_warn_ratelimited("%s: can't get cfg_base of 0x%03x\n", - __func__, cfg_base); - } else { - if (!smsc_superio_flat(lpc_chips_flat, cfg_base, "LPC") || - !smsc_superio_paged(lpc_chips_paged, cfg_base, "LPC")) - ret = 0; - - release_region(cfg_base, 2); - } - return ret; -} - -/* - * Look for some specific subsystem setups that need - * pre-configuration not properly done by the BIOS (especially laptops) - * This code is based in part on smcinit.c, tosh1800-smcinit.c - * and tosh2450-smcinit.c. The table lists the device entries - * for ISA bridges with an LPC (Low Pin Count) controller which - * handles the communication with the SMSC device. After the LPC - * controller is initialized through PCI, the SMSC device is initialized - * through a dedicated port in the ISA port-mapped I/O area, this latter - * area is used to configure the SMSC device with default - * SIR and FIR I/O ports, DMA and IRQ. Different vendors have - * used different sets of parameters and different control port - * addresses making a subsystem device table necessary. - */ -#ifdef CONFIG_PCI -static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __initdata = { - /* - * Subsystems needing entries: - * 0x10b9:0x1533 0x103c:0x0850 HP nx9010 family - * 0x10b9:0x1533 0x0e11:0x005a Compaq nc4000 family - * 0x8086:0x24cc 0x0e11:0x002a HP nx9000 family - */ - { - /* Guessed entry */ - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ - .device = 0x24cc, - .subvendor = 0x103c, - .subdevice = 0x08bc, - .sir_io = 0x02f8, - .fir_io = 0x0130, - .fir_irq = 0x05, - .fir_dma = 0x03, - .cfg_base = 0x004e, - .preconfigure = preconfigure_through_82801, - .name = "HP nx5000 family", - }, - { - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ - .device = 0x24cc, - .subvendor = 0x103c, - .subdevice = 0x088c, - /* Quite certain these are the same for nc8000 as for nc6000 */ - .sir_io = 0x02f8, - .fir_io = 0x0130, - .fir_irq = 0x05, - .fir_dma = 0x03, - .cfg_base = 0x004e, - .preconfigure = preconfigure_through_82801, - .name = "HP nc8000 family", - }, - { - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ - .device = 0x24cc, - .subvendor = 0x103c, - .subdevice = 0x0890, - .sir_io = 0x02f8, - .fir_io = 0x0130, - .fir_irq = 0x05, - .fir_dma = 0x03, - .cfg_base = 0x004e, - .preconfigure = preconfigure_through_82801, - .name = "HP nc6000 family", - }, - { - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ - .device = 0x24cc, - .subvendor = 0x0e11, - .subdevice = 0x0860, - /* I assume these are the same for x1000 as for the others */ - .sir_io = 0x02e8, - .fir_io = 0x02f8, - .fir_irq = 0x07, - .fir_dma = 0x03, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_82801, - .name = "Compaq x1000 family", - }, - { - /* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */ - .vendor = PCI_VENDOR_ID_INTEL, - .device = 0x24c0, - .subvendor = 0x1179, - .subdevice = 0xffff, /* 0xffff is "any" */ - .sir_io = 0x03f8, - .fir_io = 0x0130, - .fir_irq = 0x07, - .fir_dma = 0x01, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_82801, - .name = "Toshiba laptop with Intel 82801DB/DBL LPC bridge", - }, - { - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801CAM ISA bridge */ - .device = 0x248c, - .subvendor = 0x1179, - .subdevice = 0xffff, /* 0xffff is "any" */ - .sir_io = 0x03f8, - .fir_io = 0x0130, - .fir_irq = 0x03, - .fir_dma = 0x03, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_82801, - .name = "Toshiba laptop with Intel 82801CAM ISA bridge", - }, - { - /* 82801DBM (ICH4-M) LPC Interface Bridge */ - .vendor = PCI_VENDOR_ID_INTEL, - .device = 0x24cc, - .subvendor = 0x1179, - .subdevice = 0xffff, /* 0xffff is "any" */ - .sir_io = 0x03f8, - .fir_io = 0x0130, - .fir_irq = 0x03, - .fir_dma = 0x03, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_82801, - .name = "Toshiba laptop with Intel 8281DBM LPC bridge", - }, - { - /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */ - .vendor = PCI_VENDOR_ID_AL, - .device = 0x1533, - .subvendor = 0x1179, - .subdevice = 0xffff, /* 0xffff is "any" */ - .sir_io = 0x02e8, - .fir_io = 0x02f8, - .fir_irq = 0x07, - .fir_dma = 0x03, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_ali, - .name = "Toshiba laptop with ALi ISA bridge", - }, - { } // Terminator -}; - - -/* - * This sets up the basic SMSC parameters - * (FIR port, SIR port, FIR DMA, FIR IRQ) - * through the chip configuration port. - */ -static int __init preconfigure_smsc_chip(struct - smsc_ircc_subsystem_configuration - *conf) -{ - unsigned short iobase = conf->cfg_base; - unsigned char tmpbyte; - - outb(LPC47N227_CFGACCESSKEY, iobase); // enter configuration state - outb(SMSCSIOFLAT_DEVICEID_REG, iobase); // set for device ID - tmpbyte = inb(iobase +1); // Read device ID - pr_debug("Detected Chip id: 0x%02x, setting up registers...\n", - tmpbyte); - - /* Disable UART1 and set up SIR I/O port */ - outb(0x24, iobase); // select CR24 - UART1 base addr - outb(0x00, iobase + 1); // disable UART1 - outb(SMSCSIOFLAT_UART2BASEADDR_REG, iobase); // select CR25 - UART2 base addr - outb( (conf->sir_io >> 2), iobase + 1); // bits 2-9 of 0x3f8 - tmpbyte = inb(iobase + 1); - if (tmpbyte != (conf->sir_io >> 2) ) { - net_warn_ratelimited("ERROR: could not configure SIR ioport\n"); - net_warn_ratelimited("Try to supply ircc_cfg argument\n"); - return -ENXIO; - } - - /* Set up FIR IRQ channel for UART2 */ - outb(SMSCSIOFLAT_UARTIRQSELECT_REG, iobase); // select CR28 - UART1,2 IRQ select - tmpbyte = inb(iobase + 1); - tmpbyte &= SMSCSIOFLAT_UART1IRQSELECT_MASK; // Do not touch the UART1 portion - tmpbyte |= (conf->fir_irq & SMSCSIOFLAT_UART2IRQSELECT_MASK); - outb(tmpbyte, iobase + 1); - tmpbyte = inb(iobase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK; - if (tmpbyte != conf->fir_irq) { - net_warn_ratelimited("ERROR: could not configure FIR IRQ channel\n"); - return -ENXIO; - } - - /* Set up FIR I/O port */ - outb(SMSCSIOFLAT_FIRBASEADDR_REG, iobase); // CR2B - SCE (FIR) base addr - outb((conf->fir_io >> 3), iobase + 1); - tmpbyte = inb(iobase + 1); - if (tmpbyte != (conf->fir_io >> 3) ) { - net_warn_ratelimited("ERROR: could not configure FIR I/O port\n"); - return -ENXIO; - } - - /* Set up FIR DMA channel */ - outb(SMSCSIOFLAT_FIRDMASELECT_REG, iobase); // CR2C - SCE (FIR) DMA select - outb((conf->fir_dma & LPC47N227_FIRDMASELECT_MASK), iobase + 1); // DMA - tmpbyte = inb(iobase + 1) & LPC47N227_FIRDMASELECT_MASK; - if (tmpbyte != (conf->fir_dma & LPC47N227_FIRDMASELECT_MASK)) { - net_warn_ratelimited("ERROR: could not configure FIR DMA channel\n"); - return -ENXIO; - } - - outb(SMSCSIOFLAT_UARTMODE0C_REG, iobase); // CR0C - UART mode - tmpbyte = inb(iobase + 1); - tmpbyte &= ~SMSCSIOFLAT_UART2MODE_MASK | - SMSCSIOFLAT_UART2MODE_VAL_IRDA; - outb(tmpbyte, iobase + 1); // enable IrDA (HPSIR) mode, high speed - - outb(LPC47N227_APMBOOTDRIVE_REG, iobase); // CR07 - Auto Pwr Mgt/boot drive sel - tmpbyte = inb(iobase + 1); - outb(tmpbyte | LPC47N227_UART2AUTOPWRDOWN_MASK, iobase + 1); // enable UART2 autopower down - - /* This one was not part of tosh1800 */ - outb(0x0a, iobase); // CR0a - ecp fifo / ir mux - tmpbyte = inb(iobase + 1); - outb(tmpbyte | 0x40, iobase + 1); // send active device to ir port - - outb(LPC47N227_UART12POWER_REG, iobase); // CR02 - UART 1,2 power - tmpbyte = inb(iobase + 1); - outb(tmpbyte | LPC47N227_UART2POWERDOWN_MASK, iobase + 1); // UART2 power up mode, UART1 power down - - outb(LPC47N227_FDCPOWERVALIDCONF_REG, iobase); // CR00 - FDC Power/valid config cycle - tmpbyte = inb(iobase + 1); - outb(tmpbyte | LPC47N227_VALID_MASK, iobase + 1); // valid config cycle done - - outb(LPC47N227_CFGEXITKEY, iobase); // Exit configuration - - return 0; -} - -/* 82801CAM generic registers */ -#define VID 0x00 -#define DID 0x02 -#define PIRQ_A_D_ROUT 0x60 -#define SIRQ_CNTL 0x64 -#define PIRQ_E_H_ROUT 0x68 -#define PCI_DMA_C 0x90 -/* LPC-specific registers */ -#define COM_DEC 0xe0 -#define GEN1_DEC 0xe4 -#define LPC_EN 0xe6 -#define GEN2_DEC 0xec -/* - * Sets up the I/O range using the 82801CAM ISA bridge, 82801DBM LPC bridge - * or Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge. - * They all work the same way! - */ -static int __init preconfigure_through_82801(struct pci_dev *dev, - struct - smsc_ircc_subsystem_configuration - *conf) -{ - unsigned short tmpword; - unsigned char tmpbyte; - - net_info_ratelimited("Setting up Intel 82801 controller and SMSC device\n"); - /* - * Select the range for the COMA COM port (SIR) - * Register COM_DEC: - * Bit 7: reserved - * Bit 6-4, COMB decode range - * Bit 3: reserved - * Bit 2-0, COMA decode range - * - * Decode ranges: - * 000 = 0x3f8-0x3ff (COM1) - * 001 = 0x2f8-0x2ff (COM2) - * 010 = 0x220-0x227 - * 011 = 0x228-0x22f - * 100 = 0x238-0x23f - * 101 = 0x2e8-0x2ef (COM4) - * 110 = 0x338-0x33f - * 111 = 0x3e8-0x3ef (COM3) - */ - pci_read_config_byte(dev, COM_DEC, &tmpbyte); - tmpbyte &= 0xf8; /* mask COMA bits */ - switch(conf->sir_io) { - case 0x3f8: - tmpbyte |= 0x00; - break; - case 0x2f8: - tmpbyte |= 0x01; - break; - case 0x220: - tmpbyte |= 0x02; - break; - case 0x228: - tmpbyte |= 0x03; - break; - case 0x238: - tmpbyte |= 0x04; - break; - case 0x2e8: - tmpbyte |= 0x05; - break; - case 0x338: - tmpbyte |= 0x06; - break; - case 0x3e8: - tmpbyte |= 0x07; - break; - default: - tmpbyte |= 0x01; /* COM2 default */ - } - pr_debug("COM_DEC (write): 0x%02x\n", tmpbyte); - pci_write_config_byte(dev, COM_DEC, tmpbyte); - - /* Enable Low Pin Count interface */ - pci_read_config_word(dev, LPC_EN, &tmpword); - /* These seem to be set up at all times, - * just make sure it is properly set. - */ - switch(conf->cfg_base) { - case 0x04e: - tmpword |= 0x2000; - break; - case 0x02e: - tmpword |= 0x1000; - break; - case 0x062: - tmpword |= 0x0800; - break; - case 0x060: - tmpword |= 0x0400; - break; - default: - net_warn_ratelimited("Uncommon I/O base address: 0x%04x\n", - conf->cfg_base); - break; - } - tmpword &= 0xfffd; /* disable LPC COMB */ - tmpword |= 0x0001; /* set bit 0 : enable LPC COMA addr range (GEN2) */ - pr_debug("LPC_EN (write): 0x%04x\n", tmpword); - pci_write_config_word(dev, LPC_EN, tmpword); - - /* - * Configure LPC DMA channel - * PCI_DMA_C bits: - * Bit 15-14: DMA channel 7 select - * Bit 13-12: DMA channel 6 select - * Bit 11-10: DMA channel 5 select - * Bit 9-8: Reserved - * Bit 7-6: DMA channel 3 select - * Bit 5-4: DMA channel 2 select - * Bit 3-2: DMA channel 1 select - * Bit 1-0: DMA channel 0 select - * 00 = Reserved value - * 01 = PC/PCI DMA - * 10 = Reserved value - * 11 = LPC I/F DMA - */ - pci_read_config_word(dev, PCI_DMA_C, &tmpword); - switch(conf->fir_dma) { - case 0x07: - tmpword |= 0xc000; - break; - case 0x06: - tmpword |= 0x3000; - break; - case 0x05: - tmpword |= 0x0c00; - break; - case 0x03: - tmpword |= 0x00c0; - break; - case 0x02: - tmpword |= 0x0030; - break; - case 0x01: - tmpword |= 0x000c; - break; - case 0x00: - tmpword |= 0x0003; - break; - default: - break; /* do not change settings */ - } - pr_debug("PCI_DMA_C (write): 0x%04x\n", tmpword); - pci_write_config_word(dev, PCI_DMA_C, tmpword); - - /* - * GEN2_DEC bits: - * Bit 15-4: Generic I/O range - * Bit 3-1: reserved (read as 0) - * Bit 0: enable GEN2 range on LPC I/F - */ - tmpword = conf->fir_io & 0xfff8; - tmpword |= 0x0001; - pr_debug("GEN2_DEC (write): 0x%04x\n", tmpword); - pci_write_config_word(dev, GEN2_DEC, tmpword); - - /* Pre-configure chip */ - return preconfigure_smsc_chip(conf); -} - -/* - * Pre-configure a certain port on the ALi 1533 bridge. - * This is based on reverse-engineering since ALi does not - * provide any data sheet for the 1533 chip. - */ -static void __init preconfigure_ali_port(struct pci_dev *dev, - unsigned short port) -{ - unsigned char reg; - /* These bits obviously control the different ports */ - unsigned char mask; - unsigned char tmpbyte; - - switch(port) { - case 0x0130: - case 0x0178: - reg = 0xb0; - mask = 0x80; - break; - case 0x03f8: - reg = 0xb4; - mask = 0x80; - break; - case 0x02f8: - reg = 0xb4; - mask = 0x30; - break; - case 0x02e8: - reg = 0xb4; - mask = 0x08; - break; - default: - net_err_ratelimited("Failed to configure unsupported port on ALi 1533 bridge: 0x%04x\n", - port); - return; - } - - pci_read_config_byte(dev, reg, &tmpbyte); - /* Turn on the right bits */ - tmpbyte |= mask; - pci_write_config_byte(dev, reg, tmpbyte); - net_info_ratelimited("Activated ALi 1533 ISA bridge port 0x%04x\n", - port); -} - -static int __init preconfigure_through_ali(struct pci_dev *dev, - struct - smsc_ircc_subsystem_configuration - *conf) -{ - /* Configure the two ports on the ALi 1533 */ - preconfigure_ali_port(dev, conf->sir_io); - preconfigure_ali_port(dev, conf->fir_io); - - /* Pre-configure chip */ - return preconfigure_smsc_chip(conf); -} - -static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg, - unsigned short ircc_fir, - unsigned short ircc_sir, - unsigned char ircc_dma, - unsigned char ircc_irq) -{ - struct pci_dev *dev = NULL; - unsigned short ss_vendor = 0x0000; - unsigned short ss_device = 0x0000; - int ret = 0; - - for_each_pci_dev(dev) { - struct smsc_ircc_subsystem_configuration *conf; - - /* - * Cache the subsystem vendor/device: - * some manufacturers fail to set this for all components, - * so we save it in case there is just 0x0000 0x0000 on the - * device we want to check. - */ - if (dev->subsystem_vendor != 0x0000U) { - ss_vendor = dev->subsystem_vendor; - ss_device = dev->subsystem_device; - } - conf = subsystem_configurations; - for( ; conf->subvendor; conf++) { - if(conf->vendor == dev->vendor && - conf->device == dev->device && - conf->subvendor == ss_vendor && - /* Sometimes these are cached values */ - (conf->subdevice == ss_device || - conf->subdevice == 0xffff)) { - struct smsc_ircc_subsystem_configuration - tmpconf; - - memcpy(&tmpconf, conf, - sizeof(struct smsc_ircc_subsystem_configuration)); - - /* - * Override the default values with anything - * passed in as parameter - */ - if (ircc_cfg != 0) - tmpconf.cfg_base = ircc_cfg; - if (ircc_fir != 0) - tmpconf.fir_io = ircc_fir; - if (ircc_sir != 0) - tmpconf.sir_io = ircc_sir; - if (ircc_dma != DMA_INVAL) - tmpconf.fir_dma = ircc_dma; - if (ircc_irq != IRQ_INVAL) - tmpconf.fir_irq = ircc_irq; - - net_info_ratelimited("Detected unconfigured %s SMSC IrDA chip, pre-configuring device\n", - conf->name); - if (conf->preconfigure) - ret = conf->preconfigure(dev, &tmpconf); - else - ret = -ENODEV; - } - } - } - - return ret; -} -#endif // CONFIG_PCI - -/************************************************ - * - * Transceivers specific functions - * - ************************************************/ - - -/* - * Function smsc_ircc_set_transceiver_smsc_ircc_atc(fir_base, speed) - * - * Program transceiver through smsc-ircc ATC circuitry - * - */ - -static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed) -{ - unsigned long jiffies_now, jiffies_timeout; - u8 val; - - jiffies_now = jiffies; - jiffies_timeout = jiffies + SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES; - - /* ATC */ - register_bank(fir_base, 4); - outb((inb(fir_base + IRCC_ATC) & IRCC_ATC_MASK) | IRCC_ATC_nPROGREADY|IRCC_ATC_ENABLE, - fir_base + IRCC_ATC); - - while ((val = (inb(fir_base + IRCC_ATC) & IRCC_ATC_nPROGREADY)) && - !time_after(jiffies, jiffies_timeout)) - /* empty */; - - if (val) - net_warn_ratelimited("%s(): ATC: 0x%02x\n", - __func__, inb(fir_base + IRCC_ATC)); -} - -/* - * Function smsc_ircc_probe_transceiver_smsc_ircc_atc(fir_base) - * - * Probe transceiver smsc-ircc ATC circuitry - * - */ - -static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base) -{ - return 0; -} - -/* - * Function smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(self, speed) - * - * Set transceiver - * - */ - -static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed) -{ - u8 fast_mode; - - switch (speed) { - default: - case 576000 : - fast_mode = 0; - break; - case 1152000 : - case 4000000 : - fast_mode = IRCC_LCR_A_FAST; - break; - } - register_bank(fir_base, 0); - outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast_mode, fir_base + IRCC_LCR_A); -} - -/* - * Function smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(fir_base) - * - * Probe transceiver - * - */ - -static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base) -{ - return 0; -} - -/* - * Function smsc_ircc_set_transceiver_toshiba_sat1800(fir_base, speed) - * - * Set transceiver - * - */ - -static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed) -{ - u8 fast_mode; - - switch (speed) { - default: - case 576000 : - fast_mode = 0; - break; - case 1152000 : - case 4000000 : - fast_mode = /*IRCC_LCR_A_FAST |*/ IRCC_LCR_A_GP_DATA; - break; - - } - /* This causes an interrupt */ - register_bank(fir_base, 0); - outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast_mode, fir_base + IRCC_LCR_A); -} - -/* - * Function smsc_ircc_probe_transceiver_toshiba_sat1800(fir_base) - * - * Probe transceiver - * - */ - -static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base) -{ - return 0; -} - - -module_init(smsc_ircc_init); -module_exit(smsc_ircc_cleanup); diff --git a/drivers/net/irda/smsc-ircc2.h b/drivers/net/irda/smsc-ircc2.h deleted file mode 100644 index 4829fa22cb29..000000000000 --- a/drivers/net/irda/smsc-ircc2.h +++ /dev/null @@ -1,191 +0,0 @@ -/********************************************************************* - * - * Description: Definitions for the SMC IrCC chipset - * Status: Experimental. - * Author: Daniele Peri (peri@csai.unipa.it) - * - * Copyright (c) 2002 Daniele Peri - * All Rights Reserved. - * - * Based on smc-ircc.h: - * - * Copyright (c) 1999-2000, Dag Brattli - * Copyright (c) 1998-1999, Thomas Davis (tadavis@jps.net> - * All Rights Reserved - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef SMSC_IRCC2_H -#define SMSC_IRCC2_H - -/* DMA modes needed */ -#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ -#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ - -/* Master Control Register */ -#define IRCC_MASTER 0x07 -#define IRCC_MASTER_POWERDOWN 0x80 -#define IRCC_MASTER_RESET 0x40 -#define IRCC_MASTER_INT_EN 0x20 -#define IRCC_MASTER_ERROR_RESET 0x10 - -/* Register block 0 */ - -/* Interrupt Identification */ -#define IRCC_IIR 0x01 -#define IRCC_IIR_ACTIVE_FRAME 0x80 -#define IRCC_IIR_EOM 0x40 -#define IRCC_IIR_RAW_MODE 0x20 -#define IRCC_IIR_FIFO 0x10 - -/* Interrupt Enable */ -#define IRCC_IER 0x02 -#define IRCC_IER_ACTIVE_FRAME 0x80 -#define IRCC_IER_EOM 0x40 -#define IRCC_IER_RAW_MODE 0x20 -#define IRCC_IER_FIFO 0x10 - -/* Line Status Register */ -#define IRCC_LSR 0x03 -#define IRCC_LSR_UNDERRUN 0x80 -#define IRCC_LSR_OVERRUN 0x40 -#define IRCC_LSR_FRAME_ERROR 0x20 -#define IRCC_LSR_SIZE_ERROR 0x10 -#define IRCC_LSR_CRC_ERROR 0x80 -#define IRCC_LSR_FRAME_ABORT 0x40 - -/* Line Status Address Register */ -#define IRCC_LSAR 0x03 -#define IRCC_LSAR_ADDRESS_MASK 0x07 - -/* Line Control Register A */ -#define IRCC_LCR_A 0x04 -#define IRCC_LCR_A_FIFO_RESET 0x80 -#define IRCC_LCR_A_FAST 0x40 -#define IRCC_LCR_A_GP_DATA 0x20 -#define IRCC_LCR_A_RAW_TX 0x10 -#define IRCC_LCR_A_RAW_RX 0x08 -#define IRCC_LCR_A_ABORT 0x04 -#define IRCC_LCR_A_DATA_DONE 0x02 - -/* Line Control Register B */ -#define IRCC_LCR_B 0x05 -#define IRCC_LCR_B_SCE_DISABLED 0x00 -#define IRCC_LCR_B_SCE_TRANSMIT 0x40 -#define IRCC_LCR_B_SCE_RECEIVE 0x80 -#define IRCC_LCR_B_SCE_UNDEFINED 0xc0 -#define IRCC_LCR_B_SIP_ENABLE 0x20 -#define IRCC_LCR_B_BRICK_WALL 0x10 - -/* Bus Status Register */ -#define IRCC_BSR 0x06 -#define IRCC_BSR_NOT_EMPTY 0x80 -#define IRCC_BSR_FIFO_FULL 0x40 -#define IRCC_BSR_TIMEOUT 0x20 - -/* Register block 1 */ - -#define IRCC_FIFO_THRESHOLD 0x02 - -#define IRCC_SCE_CFGA 0x00 -#define IRCC_CFGA_AUX_IR 0x80 -#define IRCC_CFGA_HALF_DUPLEX 0x04 -#define IRCC_CFGA_TX_POLARITY 0x02 -#define IRCC_CFGA_RX_POLARITY 0x01 - -#define IRCC_CFGA_COM 0x00 -#define IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK 0x87 -#define IRCC_CFGA_IRDA_SIR_A 0x08 -#define IRCC_CFGA_ASK_SIR 0x10 -#define IRCC_CFGA_IRDA_SIR_B 0x18 -#define IRCC_CFGA_IRDA_HDLC 0x20 -#define IRCC_CFGA_IRDA_4PPM 0x28 -#define IRCC_CFGA_CONSUMER 0x30 -#define IRCC_CFGA_RAW_IR 0x38 -#define IRCC_CFGA_OTHER 0x40 - -#define IRCC_IR_HDLC 0x04 -#define IRCC_IR_4PPM 0x01 -#define IRCC_IR_CONSUMER 0x02 - -#define IRCC_SCE_CFGB 0x01 -#define IRCC_CFGB_LOOPBACK 0x20 -#define IRCC_CFGB_LPBCK_TX_CRC 0x10 -#define IRCC_CFGB_NOWAIT 0x08 -#define IRCC_CFGB_STRING_MOVE 0x04 -#define IRCC_CFGB_DMA_BURST 0x02 -#define IRCC_CFGB_DMA_ENABLE 0x01 - -#define IRCC_CFGB_MUX_COM 0x00 -#define IRCC_CFGB_MUX_IR 0x40 -#define IRCC_CFGB_MUX_AUX 0x80 -#define IRCC_CFGB_MUX_INACTIVE 0xc0 - -/* Register block 3 - Identification Registers! */ -#define IRCC_ID_HIGH 0x00 /* 0x10 */ -#define IRCC_ID_LOW 0x01 /* 0xB8 */ -#define IRCC_CHIP_ID 0x02 /* 0xF1 */ -#define IRCC_VERSION 0x03 /* 0x01 */ -#define IRCC_INTERFACE 0x04 /* low 4 = DMA, high 4 = IRQ */ -#define IRCC_INTERFACE_DMA_MASK 0x0F /* low 4 = DMA, high 4 = IRQ */ -#define IRCC_INTERFACE_IRQ_MASK 0xF0 /* low 4 = DMA, high 4 = IRQ */ - -/* Register block 4 - IrDA */ -#define IRCC_CONTROL 0x00 -#define IRCC_BOF_COUNT_LO 0x01 /* Low byte */ -#define IRCC_BOF_COUNT_HI 0x00 /* High nibble (bit 0-3) */ -#define IRCC_BRICKWALL_CNT_LO 0x02 /* Low byte */ -#define IRCC_BRICKWALL_CNT_HI 0x03 /* High nibble (bit 4-7) */ -#define IRCC_TX_SIZE_LO 0x04 /* Low byte */ -#define IRCC_TX_SIZE_HI 0x03 /* High nibble (bit 0-3) */ -#define IRCC_RX_SIZE_HI 0x05 /* High nibble (bit 0-3) */ -#define IRCC_RX_SIZE_LO 0x06 /* Low byte */ - -#define IRCC_1152 0x80 -#define IRCC_CRC 0x40 - -/* Register block 5 - IrDA */ -#define IRCC_ATC 0x00 -#define IRCC_ATC_nPROGREADY 0x80 -#define IRCC_ATC_SPEED 0x40 -#define IRCC_ATC_ENABLE 0x20 -#define IRCC_ATC_MASK 0xE0 - - -#define IRCC_IRHALFDUPLEX_TIMEOUT 0x01 - -#define IRCC_SCE_TX_DELAY_TIMER 0x02 - -/* - * Other definitions - */ - -#define SMSC_IRCC2_MAX_SIR_SPEED 115200 -#define SMSC_IRCC2_FIR_CHIP_IO_EXTENT 8 -#define SMSC_IRCC2_SIR_CHIP_IO_EXTENT 8 -#define SMSC_IRCC2_FIFO_SIZE 16 -#define SMSC_IRCC2_FIFO_THRESHOLD 64 -/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ -#define SMSC_IRCC2_RX_BUFF_TRUESIZE 14384 -#define SMSC_IRCC2_TX_BUFF_TRUESIZE 14384 -#define SMSC_IRCC2_MIN_TURN_TIME 0x07 -#define SMSC_IRCC2_WINDOW_SIZE 0x07 -/* Maximum wait for hw transmitter to finish */ -#define SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US 1000 /* 1 ms */ -/* Maximum wait for ATC transceiver programming to finish */ -#define SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES 1 -#endif /* SMSC_IRCC2_H */ diff --git a/drivers/net/irda/smsc-sio.h b/drivers/net/irda/smsc-sio.h deleted file mode 100644 index 59e20e653ebe..000000000000 --- a/drivers/net/irda/smsc-sio.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef SMSC_SIO_H -#define SMSC_SIO_H - -/****************************************** - Keys. They should work with every SMsC SIO - ******************************************/ - -#define SMSCSIO_CFGACCESSKEY 0x55 -#define SMSCSIO_CFGEXITKEY 0xaa - -/***************************** - * Generic SIO Flat (!?) * - *****************************/ - -/* Register 0x0d */ -#define SMSCSIOFLAT_DEVICEID_REG 0x0d - -/* Register 0x0c */ -#define SMSCSIOFLAT_UARTMODE0C_REG 0x0c -#define SMSCSIOFLAT_UART2MODE_MASK 0x38 -#define SMSCSIOFLAT_UART2MODE_VAL_COM 0x00 -#define SMSCSIOFLAT_UART2MODE_VAL_IRDA 0x08 -#define SMSCSIOFLAT_UART2MODE_VAL_ASKIR 0x10 - -/* Register 0x25 */ -#define SMSCSIOFLAT_UART2BASEADDR_REG 0x25 - -/* Register 0x2b */ -#define SMSCSIOFLAT_FIRBASEADDR_REG 0x2b - -/* Register 0x2c */ -#define SMSCSIOFLAT_FIRDMASELECT_REG 0x2c -#define SMSCSIOFLAT_FIRDMASELECT_MASK 0x0f - -/* Register 0x28 */ -#define SMSCSIOFLAT_UARTIRQSELECT_REG 0x28 -#define SMSCSIOFLAT_UART2IRQSELECT_MASK 0x0f -#define SMSCSIOFLAT_UART1IRQSELECT_MASK 0xf0 -#define SMSCSIOFLAT_UARTIRQSELECT_VAL_NONE 0x00 - - -/********************* - * LPC47N227 * - *********************/ - -#define LPC47N227_CFGACCESSKEY 0x55 -#define LPC47N227_CFGEXITKEY 0xaa - -/* Register 0x00 */ -#define LPC47N227_FDCPOWERVALIDCONF_REG 0x00 -#define LPC47N227_FDCPOWER_MASK 0x08 -#define LPC47N227_VALID_MASK 0x80 - -/* Register 0x02 */ -#define LPC47N227_UART12POWER_REG 0x02 -#define LPC47N227_UART1POWERDOWN_MASK 0x08 -#define LPC47N227_UART2POWERDOWN_MASK 0x80 - -/* Register 0x07 */ -#define LPC47N227_APMBOOTDRIVE_REG 0x07 -#define LPC47N227_PARPORT2AUTOPWRDOWN_MASK 0x10 /* auto power down on if set */ -#define LPC47N227_UART2AUTOPWRDOWN_MASK 0x20 /* auto power down on if set */ -#define LPC47N227_UART1AUTOPWRDOWN_MASK 0x40 /* auto power down on if set */ - -/* Register 0x0c */ -#define LPC47N227_UARTMODE0C_REG 0x0c -#define LPC47N227_UART2MODE_MASK 0x38 -#define LPC47N227_UART2MODE_VAL_COM 0x00 -#define LPC47N227_UART2MODE_VAL_IRDA 0x08 -#define LPC47N227_UART2MODE_VAL_ASKIR 0x10 - -/* Register 0x0d */ -#define LPC47N227_DEVICEID_REG 0x0d -#define LPC47N227_DEVICEID_DEFVAL 0x5a - -/* Register 0x0e */ -#define LPC47N227_REVISIONID_REG 0x0e - -/* Register 0x25 */ -#define LPC47N227_UART2BASEADDR_REG 0x25 - -/* Register 0x28 */ -#define LPC47N227_UARTIRQSELECT_REG 0x28 -#define LPC47N227_UART2IRQSELECT_MASK 0x0f -#define LPC47N227_UART1IRQSELECT_MASK 0xf0 -#define LPC47N227_UARTIRQSELECT_VAL_NONE 0x00 - -/* Register 0x2b */ -#define LPC47N227_FIRBASEADDR_REG 0x2b - -/* Register 0x2c */ -#define LPC47N227_FIRDMASELECT_REG 0x2c -#define LPC47N227_FIRDMASELECT_MASK 0x0f -#define LPC47N227_FIRDMASELECT_VAL_DMA1 0x01 /* 47n227 has three dma channels */ -#define LPC47N227_FIRDMASELECT_VAL_DMA2 0x02 -#define LPC47N227_FIRDMASELECT_VAL_DMA3 0x03 -#define LPC47N227_FIRDMASELECT_VAL_NONE 0x0f - - -#endif diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c deleted file mode 100644 index ee2cb70b688d..000000000000 --- a/drivers/net/irda/stir4200.c +++ /dev/null @@ -1,1134 +0,0 @@ -/***************************************************************************** -* -* Filename: stir4200.c -* Version: 0.4 -* Description: Irda SigmaTel USB Dongle -* Status: Experimental -* Author: Stephen Hemminger -* -* Based on earlier driver by Paul Stewart -* -* Copyright (C) 2000, Roman Weissgaerber -* Copyright (C) 2001, Dag Brattli -* Copyright (C) 2001, Jean Tourrilhes -* Copyright (C) 2004, Stephen Hemminger -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * This dongle does no framing, and requires polling to receive the - * data. The STIr4200 has bulk in and out endpoints just like - * usr-irda devices, but the data it sends and receives is raw; like - * irtty, it needs to call the wrap and unwrap functions to add and - * remove SOF/BOF and escape characters to/from the frame. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Stephen Hemminger "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for SigmaTel STIr4200"); -MODULE_LICENSE("GPL"); - -static int qos_mtt_bits = 0x07; /* 1 ms or more */ -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); - -static int rx_sensitivity = 1; /* FIR 0..4, SIR 0..6 */ -module_param(rx_sensitivity, int, 0); -MODULE_PARM_DESC(rx_sensitivity, "Set Receiver sensitivity (0-6, 0 is most sensitive)"); - -static int tx_power = 0; /* 0 = highest ... 3 = lowest */ -module_param(tx_power, int, 0); -MODULE_PARM_DESC(tx_power, "Set Transmitter power (0-3, 0 is highest power)"); - -#define STIR_IRDA_HEADER 4 -#define CTRL_TIMEOUT 100 /* milliseconds */ -#define TRANSMIT_TIMEOUT 200 /* milliseconds */ -#define STIR_FIFO_SIZE 4096 -#define FIFO_REGS_SIZE 3 - -enum FirChars { - FIR_CE = 0x7d, - FIR_XBOF = 0x7f, - FIR_EOF = 0x7e, -}; - -enum StirRequests { - REQ_WRITE_REG = 0x00, - REQ_READ_REG = 0x01, - REQ_READ_ROM = 0x02, - REQ_WRITE_SINGLE = 0x03, -}; - -/* Register offsets */ -enum StirRegs { - REG_RSVD=0, - REG_MODE, - REG_PDCLK, - REG_CTRL1, - REG_CTRL2, - REG_FIFOCTL, - REG_FIFOLSB, - REG_FIFOMSB, - REG_DPLL, - REG_IRDIG, - REG_TEST=15, -}; - -enum StirModeMask { - MODE_FIR = 0x80, - MODE_SIR = 0x20, - MODE_ASK = 0x10, - MODE_FASTRX = 0x08, - MODE_FFRSTEN = 0x04, - MODE_NRESET = 0x02, - MODE_2400 = 0x01, -}; - -enum StirPdclkMask { - PDCLK_4000000 = 0x02, - PDCLK_115200 = 0x09, - PDCLK_57600 = 0x13, - PDCLK_38400 = 0x1D, - PDCLK_19200 = 0x3B, - PDCLK_9600 = 0x77, - PDCLK_2400 = 0xDF, -}; - -enum StirCtrl1Mask { - CTRL1_SDMODE = 0x80, - CTRL1_RXSLOW = 0x40, - CTRL1_TXPWD = 0x10, - CTRL1_RXPWD = 0x08, - CTRL1_SRESET = 0x01, -}; - -enum StirCtrl2Mask { - CTRL2_SPWIDTH = 0x08, - CTRL2_REVID = 0x03, -}; - -enum StirFifoCtlMask { - FIFOCTL_DIR = 0x10, - FIFOCTL_CLR = 0x08, - FIFOCTL_EMPTY = 0x04, -}; - -enum StirDiagMask { - IRDIG_RXHIGH = 0x80, - IRDIG_RXLOW = 0x40, -}; - -enum StirTestMask { - TEST_PLLDOWN = 0x80, - TEST_LOOPIR = 0x40, - TEST_LOOPUSB = 0x20, - TEST_TSTENA = 0x10, - TEST_TSTOSC = 0x0F, -}; - -struct stir_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - struct qos_info qos; - unsigned speed; /* Current speed */ - - struct task_struct *thread; /* transmit thread */ - - struct sk_buff *tx_pending; - void *io_buf; /* transmit/receive buffer */ - __u8 *fifo_status; - - iobuff_t rx_buff; /* receive unwrap state machine */ - ktime_t rx_time; - int receiving; - struct urb *rx_urb; -}; - - -/* These are the currently known USB ids */ -static const struct usb_device_id dongles[] = { - /* SigmaTel, Inc, STIr4200 IrDA/USB Bridge */ - { USB_DEVICE(0x066f, 0x4200) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, dongles); - -/* Send control message to set dongle register */ -static int write_reg(struct stir_cb *stir, __u16 reg, __u8 value) -{ - struct usb_device *dev = stir->usbdev; - - pr_debug("%s: write reg %d = 0x%x\n", - stir->netdev->name, reg, value); - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - REQ_WRITE_SINGLE, - USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE, - value, reg, NULL, 0, - CTRL_TIMEOUT); -} - -/* Send control message to read multiple registers */ -static inline int read_reg(struct stir_cb *stir, __u16 reg, - __u8 *data, __u16 count) -{ - struct usb_device *dev = stir->usbdev; - - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - REQ_READ_REG, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, reg, data, count, - CTRL_TIMEOUT); -} - -static inline int isfir(u32 speed) -{ - return speed == 4000000; -} - -/* - * Prepare a FIR IrDA frame for transmission to the USB dongle. The - * FIR transmit frame is documented in the datasheet. It consists of - * a two byte 0x55 0xAA sequence, two little-endian length bytes, a - * sequence of exactly 16 XBOF bytes of 0x7E, two BOF bytes of 0x7E, - * then the data escaped as follows: - * - * 0x7D -> 0x7D 0x5D - * 0x7E -> 0x7D 0x5E - * 0x7F -> 0x7D 0x5F - * - * Then, 4 bytes of little endian (stuffed) FCS follow, then two - * trailing EOF bytes of 0x7E. - */ -static inline __u8 *stuff_fir(__u8 *p, __u8 c) -{ - switch(c) { - case 0x7d: - case 0x7e: - case 0x7f: - *p++ = 0x7d; - c ^= IRDA_TRANS; - /* fall through */ - default: - *p++ = c; - } - return p; -} - -/* Take raw data in skb and put it wrapped into buf */ -static unsigned wrap_fir_skb(const struct sk_buff *skb, __u8 *buf) -{ - __u8 *ptr = buf; - __u32 fcs = ~(crc32_le(~0, skb->data, skb->len)); - __u16 wraplen; - int i; - - /* Header */ - buf[0] = 0x55; - buf[1] = 0xAA; - - ptr = buf + STIR_IRDA_HEADER; - memset(ptr, 0x7f, 16); - ptr += 16; - - /* BOF */ - *ptr++ = 0x7e; - *ptr++ = 0x7e; - - /* Address / Control / Information */ - for (i = 0; i < skb->len; i++) - ptr = stuff_fir(ptr, skb->data[i]); - - /* FCS */ - ptr = stuff_fir(ptr, fcs & 0xff); - ptr = stuff_fir(ptr, (fcs >> 8) & 0xff); - ptr = stuff_fir(ptr, (fcs >> 16) & 0xff); - ptr = stuff_fir(ptr, (fcs >> 24) & 0xff); - - /* EOFs */ - *ptr++ = 0x7e; - *ptr++ = 0x7e; - - /* Total length, minus the header */ - wraplen = (ptr - buf) - STIR_IRDA_HEADER; - buf[2] = wraplen & 0xff; - buf[3] = (wraplen >> 8) & 0xff; - - return wraplen + STIR_IRDA_HEADER; -} - -static unsigned wrap_sir_skb(struct sk_buff *skb, __u8 *buf) -{ - __u16 wraplen; - - wraplen = async_wrap_skb(skb, buf + STIR_IRDA_HEADER, - STIR_FIFO_SIZE - STIR_IRDA_HEADER); - buf[0] = 0x55; - buf[1] = 0xAA; - buf[2] = wraplen & 0xff; - buf[3] = (wraplen >> 8) & 0xff; - - return wraplen + STIR_IRDA_HEADER; -} - -/* - * Frame is fully formed in the rx_buff so check crc - * and pass up to irlap - * setup for next receive - */ -static void fir_eof(struct stir_cb *stir) -{ - iobuff_t *rx_buff = &stir->rx_buff; - int len = rx_buff->len - 4; - struct sk_buff *skb, *nskb; - __u32 fcs; - - if (unlikely(len <= 0)) { - pr_debug("%s: short frame len %d\n", - stir->netdev->name, len); - - ++stir->netdev->stats.rx_errors; - ++stir->netdev->stats.rx_length_errors; - return; - } - - fcs = ~(crc32_le(~0, rx_buff->data, len)); - if (fcs != get_unaligned_le32(rx_buff->data + len)) { - pr_debug("crc error calc 0x%x len %d\n", fcs, len); - stir->netdev->stats.rx_errors++; - stir->netdev->stats.rx_crc_errors++; - return; - } - - /* if frame is short then just copy it */ - if (len < IRDA_RX_COPY_THRESHOLD) { - nskb = dev_alloc_skb(len + 1); - if (unlikely(!nskb)) { - ++stir->netdev->stats.rx_dropped; - return; - } - skb_reserve(nskb, 1); - skb = nskb; - skb_copy_to_linear_data(nskb, rx_buff->data, len); - } else { - nskb = dev_alloc_skb(rx_buff->truesize); - if (unlikely(!nskb)) { - ++stir->netdev->stats.rx_dropped; - return; - } - skb_reserve(nskb, 1); - skb = rx_buff->skb; - rx_buff->skb = nskb; - rx_buff->head = nskb->data; - } - - skb_put(skb, len); - - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - skb->dev = stir->netdev; - - netif_rx(skb); - - stir->netdev->stats.rx_packets++; - stir->netdev->stats.rx_bytes += len; - - rx_buff->data = rx_buff->head; - rx_buff->len = 0; -} - -/* Unwrap FIR stuffed data and bump it to IrLAP */ -static void stir_fir_chars(struct stir_cb *stir, - const __u8 *bytes, int len) -{ - iobuff_t *rx_buff = &stir->rx_buff; - int i; - - for (i = 0; i < len; i++) { - __u8 byte = bytes[i]; - - switch(rx_buff->state) { - case OUTSIDE_FRAME: - /* ignore garbage till start of frame */ - if (unlikely(byte != FIR_EOF)) - continue; - /* Now receiving frame */ - rx_buff->state = BEGIN_FRAME; - - /* Time to initialize receive buffer */ - rx_buff->data = rx_buff->head; - rx_buff->len = 0; - continue; - - case LINK_ESCAPE: - if (byte == FIR_EOF) { - pr_debug("%s: got EOF after escape\n", - stir->netdev->name); - goto frame_error; - } - rx_buff->state = INSIDE_FRAME; - byte ^= IRDA_TRANS; - break; - - case BEGIN_FRAME: - /* ignore multiple BOF/EOF */ - if (byte == FIR_EOF) - continue; - rx_buff->state = INSIDE_FRAME; - rx_buff->in_frame = TRUE; - - /* fall through */ - case INSIDE_FRAME: - switch(byte) { - case FIR_CE: - rx_buff->state = LINK_ESCAPE; - continue; - case FIR_XBOF: - /* 0x7f is not used in this framing */ - pr_debug("%s: got XBOF without escape\n", - stir->netdev->name); - goto frame_error; - case FIR_EOF: - rx_buff->state = OUTSIDE_FRAME; - rx_buff->in_frame = FALSE; - fir_eof(stir); - continue; - } - break; - } - - /* add byte to rx buffer */ - if (unlikely(rx_buff->len >= rx_buff->truesize)) { - pr_debug("%s: fir frame exceeds %d\n", - stir->netdev->name, rx_buff->truesize); - ++stir->netdev->stats.rx_over_errors; - goto error_recovery; - } - - rx_buff->data[rx_buff->len++] = byte; - continue; - - frame_error: - ++stir->netdev->stats.rx_frame_errors; - - error_recovery: - ++stir->netdev->stats.rx_errors; - rx_buff->state = OUTSIDE_FRAME; - rx_buff->in_frame = FALSE; - } -} - -/* Unwrap SIR stuffed data and bump it up to IrLAP */ -static void stir_sir_chars(struct stir_cb *stir, - const __u8 *bytes, int len) -{ - int i; - - for (i = 0; i < len; i++) - async_unwrap_char(stir->netdev, &stir->netdev->stats, - &stir->rx_buff, bytes[i]); -} - -static inline void unwrap_chars(struct stir_cb *stir, - const __u8 *bytes, int length) -{ - if (isfir(stir->speed)) - stir_fir_chars(stir, bytes, length); - else - stir_sir_chars(stir, bytes, length); -} - -/* Mode parameters for each speed */ -static const struct { - unsigned speed; - __u8 pdclk; -} stir_modes[] = { - { 2400, PDCLK_2400 }, - { 9600, PDCLK_9600 }, - { 19200, PDCLK_19200 }, - { 38400, PDCLK_38400 }, - { 57600, PDCLK_57600 }, - { 115200, PDCLK_115200 }, - { 4000000, PDCLK_4000000 }, -}; - - -/* - * Setup chip for speed. - * Called at startup to initialize the chip - * and on speed changes. - * - * Note: Write multiple registers doesn't appear to work - */ -static int change_speed(struct stir_cb *stir, unsigned speed) -{ - int i, err; - __u8 mode; - - for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) { - if (speed == stir_modes[i].speed) - goto found; - } - - dev_warn(&stir->netdev->dev, "invalid speed %d\n", speed); - return -EINVAL; - - found: - pr_debug("speed change from %d to %d\n", stir->speed, speed); - - /* Reset modulator */ - err = write_reg(stir, REG_CTRL1, CTRL1_SRESET); - if (err) - goto out; - - /* Undocumented magic to tweak the DPLL */ - err = write_reg(stir, REG_DPLL, 0x15); - if (err) - goto out; - - /* Set clock */ - err = write_reg(stir, REG_PDCLK, stir_modes[i].pdclk); - if (err) - goto out; - - mode = MODE_NRESET | MODE_FASTRX; - if (isfir(speed)) - mode |= MODE_FIR | MODE_FFRSTEN; - else - mode |= MODE_SIR; - - if (speed == 2400) - mode |= MODE_2400; - - err = write_reg(stir, REG_MODE, mode); - if (err) - goto out; - - /* This resets TEMIC style transceiver if any. */ - err = write_reg(stir, REG_CTRL1, - CTRL1_SDMODE | (tx_power & 3) << 1); - if (err) - goto out; - - err = write_reg(stir, REG_CTRL1, (tx_power & 3) << 1); - if (err) - goto out; - - /* Reset sensitivity */ - err = write_reg(stir, REG_CTRL2, (rx_sensitivity & 7) << 5); - out: - stir->speed = speed; - return err; -} - -/* - * Called from net/core when new frame is available. - */ -static netdev_tx_t stir_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct stir_cb *stir = netdev_priv(netdev); - - netif_stop_queue(netdev); - - /* the IRDA wrapping routines don't deal with non linear skb */ - SKB_LINEAR_ASSERT(skb); - - skb = xchg(&stir->tx_pending, skb); - wake_up_process(stir->thread); - - /* this should never happen unless stop/wakeup problem */ - if (unlikely(skb)) { - WARN_ON(1); - dev_kfree_skb(skb); - } - - return NETDEV_TX_OK; -} - -/* - * Wait for the transmit FIFO to have space for next data - * - * If space < 0 then wait till FIFO completely drains. - * FYI: can take up to 13 seconds at 2400baud. - */ -static int fifo_txwait(struct stir_cb *stir, int space) -{ - int err; - unsigned long count, status; - unsigned long prev_count = 0x1fff; - - /* Read FIFO status and count */ - for (;; prev_count = count) { - err = read_reg(stir, REG_FIFOCTL, stir->fifo_status, - FIFO_REGS_SIZE); - if (unlikely(err != FIFO_REGS_SIZE)) { - dev_warn(&stir->netdev->dev, - "FIFO register read error: %d\n", err); - - return err; - } - - status = stir->fifo_status[0]; - count = (unsigned)(stir->fifo_status[2] & 0x1f) << 8 - | stir->fifo_status[1]; - - pr_debug("fifo status 0x%lx count %lu\n", status, count); - - /* is fifo receiving already, or empty */ - if (!(status & FIFOCTL_DIR) || - (status & FIFOCTL_EMPTY)) - return 0; - - if (signal_pending(current)) - return -EINTR; - - /* shutting down? */ - if (!netif_running(stir->netdev) || - !netif_device_present(stir->netdev)) - return -ESHUTDOWN; - - /* only waiting for some space */ - if (space >= 0 && STIR_FIFO_SIZE - 4 > space + count) - return 0; - - /* queue confused */ - if (prev_count < count) - break; - - /* estimate transfer time for remaining chars */ - msleep((count * 8000) / stir->speed); - } - - err = write_reg(stir, REG_FIFOCTL, FIFOCTL_CLR); - if (err) - return err; - err = write_reg(stir, REG_FIFOCTL, 0); - if (err) - return err; - - return 0; -} - - -/* Wait for turnaround delay before starting transmit. */ -static void turnaround_delay(const struct stir_cb *stir, long us) -{ - long ticks; - - if (us <= 0) - return; - - us -= ktime_us_delta(ktime_get(), stir->rx_time); - - if (us < 10) - return; - - ticks = us / (1000000 / HZ); - if (ticks > 0) - schedule_timeout_interruptible(1 + ticks); - else - udelay(us); -} - -/* - * Start receiver by submitting a request to the receive pipe. - * If nothing is available it will return after rx_interval. - */ -static int receive_start(struct stir_cb *stir) -{ - /* reset state */ - stir->receiving = 1; - - stir->rx_buff.in_frame = FALSE; - stir->rx_buff.state = OUTSIDE_FRAME; - - stir->rx_urb->status = 0; - return usb_submit_urb(stir->rx_urb, GFP_KERNEL); -} - -/* Stop all pending receive Urb's */ -static void receive_stop(struct stir_cb *stir) -{ - stir->receiving = 0; - usb_kill_urb(stir->rx_urb); - - if (stir->rx_buff.in_frame) - stir->netdev->stats.collisions++; -} -/* - * Wrap data in socket buffer and send it. - */ -static void stir_send(struct stir_cb *stir, struct sk_buff *skb) -{ - unsigned wraplen; - int first_frame = 0; - - /* if receiving, need to turnaround */ - if (stir->receiving) { - receive_stop(stir); - turnaround_delay(stir, irda_get_mtt(skb)); - first_frame = 1; - } - - if (isfir(stir->speed)) - wraplen = wrap_fir_skb(skb, stir->io_buf); - else - wraplen = wrap_sir_skb(skb, stir->io_buf); - - /* check for space available in fifo */ - if (!first_frame) - fifo_txwait(stir, wraplen); - - stir->netdev->stats.tx_packets++; - stir->netdev->stats.tx_bytes += skb->len; - netif_trans_update(stir->netdev); - pr_debug("send %d (%d)\n", skb->len, wraplen); - - if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1), - stir->io_buf, wraplen, - NULL, TRANSMIT_TIMEOUT)) - stir->netdev->stats.tx_errors++; -} - -/* - * Transmit state machine thread - */ -static int stir_transmit_thread(void *arg) -{ - struct stir_cb *stir = arg; - struct net_device *dev = stir->netdev; - struct sk_buff *skb; - - while (!kthread_should_stop()) { -#ifdef CONFIG_PM - /* if suspending, then power off and wait */ - if (unlikely(freezing(current))) { - if (stir->receiving) - receive_stop(stir); - else - fifo_txwait(stir, -1); - - write_reg(stir, REG_CTRL1, CTRL1_TXPWD|CTRL1_RXPWD); - - try_to_freeze(); - - if (change_speed(stir, stir->speed)) - break; - } -#endif - - /* if something to send? */ - skb = xchg(&stir->tx_pending, NULL); - if (skb) { - unsigned new_speed = irda_get_next_speed(skb); - netif_wake_queue(dev); - - if (skb->len > 0) - stir_send(stir, skb); - dev_kfree_skb(skb); - - if ((new_speed != -1) && (stir->speed != new_speed)) { - if (fifo_txwait(stir, -1) || - change_speed(stir, new_speed)) - break; - } - continue; - } - - /* nothing to send? start receiving */ - if (!stir->receiving && - irda_device_txqueue_empty(dev)) { - /* Wait otherwise chip gets confused. */ - if (fifo_txwait(stir, -1)) - break; - - if (unlikely(receive_start(stir))) { - if (net_ratelimit()) - dev_info(&dev->dev, - "%s: receive usb submit failed\n", - stir->netdev->name); - stir->receiving = 0; - msleep(10); - continue; - } - } - - /* sleep if nothing to send */ - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - - } - return 0; -} - - -/* - * USB bulk receive completion callback. - * Wakes up every ms (usb round trip) with wrapped - * data. - */ -static void stir_rcv_irq(struct urb *urb) -{ - struct stir_cb *stir = urb->context; - int err; - - /* in process of stopping, just drop data */ - if (!netif_running(stir->netdev)) - return; - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) - return; - - if (urb->actual_length > 0) { - pr_debug("receive %d\n", urb->actual_length); - unwrap_chars(stir, urb->transfer_buffer, - urb->actual_length); - - stir->rx_time = ktime_get(); - } - - /* kernel thread is stopping receiver don't resubmit */ - if (!stir->receiving) - return; - - /* resubmit existing urb */ - err = usb_submit_urb(urb, GFP_ATOMIC); - - /* in case of error, the kernel thread will restart us */ - if (err) { - dev_warn(&stir->netdev->dev, "usb receive submit error: %d\n", - err); - stir->receiving = 0; - wake_up_process(stir->thread); - } -} - -/* - * Function stir_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - */ -static int stir_net_open(struct net_device *netdev) -{ - struct stir_cb *stir = netdev_priv(netdev); - int err; - char hwname[16]; - - err = usb_clear_halt(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1)); - if (err) - goto err_out1; - err = usb_clear_halt(stir->usbdev, usb_rcvbulkpipe(stir->usbdev, 2)); - if (err) - goto err_out1; - - err = change_speed(stir, 9600); - if (err) - goto err_out1; - - err = -ENOMEM; - - /* Initialize for SIR/FIR to copy data directly into skb. */ - stir->receiving = 0; - stir->rx_buff.truesize = IRDA_SKB_MAX_MTU; - stir->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!stir->rx_buff.skb) - goto err_out1; - - skb_reserve(stir->rx_buff.skb, 1); - stir->rx_buff.head = stir->rx_buff.skb->data; - stir->rx_time = ktime_get(); - - stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!stir->rx_urb) - goto err_out2; - - stir->io_buf = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL); - if (!stir->io_buf) - goto err_out3; - - usb_fill_bulk_urb(stir->rx_urb, stir->usbdev, - usb_rcvbulkpipe(stir->usbdev, 2), - stir->io_buf, STIR_FIFO_SIZE, - stir_rcv_irq, stir); - - stir->fifo_status = kmalloc(FIFO_REGS_SIZE, GFP_KERNEL); - if (!stir->fifo_status) - goto err_out4; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - * Note : will send immediately a speed change... - */ - sprintf(hwname, "usb#%d", stir->usbdev->devnum); - stir->irlap = irlap_open(netdev, &stir->qos, hwname); - if (!stir->irlap) { - dev_err(&stir->usbdev->dev, "irlap_open failed\n"); - goto err_out5; - } - - /** Start kernel thread for transmit. */ - stir->thread = kthread_run(stir_transmit_thread, stir, - "%s", stir->netdev->name); - if (IS_ERR(stir->thread)) { - err = PTR_ERR(stir->thread); - dev_err(&stir->usbdev->dev, "unable to start kernel thread\n"); - goto err_out6; - } - - netif_start_queue(netdev); - - return 0; - - err_out6: - irlap_close(stir->irlap); - err_out5: - kfree(stir->fifo_status); - err_out4: - kfree(stir->io_buf); - err_out3: - usb_free_urb(stir->rx_urb); - err_out2: - kfree_skb(stir->rx_buff.skb); - err_out1: - return err; -} - -/* - * Function stir_net_close (stir) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int stir_net_close(struct net_device *netdev) -{ - struct stir_cb *stir = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - /* Kill transmit thread */ - kthread_stop(stir->thread); - kfree(stir->fifo_status); - - /* Mop up receive urb's */ - usb_kill_urb(stir->rx_urb); - - kfree(stir->io_buf); - usb_free_urb(stir->rx_urb); - kfree_skb(stir->rx_buff.skb); - - /* Stop and remove instance of IrLAP */ - if (stir->irlap) - irlap_close(stir->irlap); - - stir->irlap = NULL; - - return 0; -} - -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int stir_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct stir_cb *stir = netdev_priv(netdev); - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the device is still there */ - if (netif_device_present(stir->netdev)) - ret = change_speed(stir, irq->ifr_baudrate); - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the IrDA stack is still there */ - if (netif_running(stir->netdev)) - irda_device_set_media_busy(stir->netdev, TRUE); - break; - - case SIOCGRECEIVING: - /* Only approximately true */ - irq->ifr_receiving = stir->receiving; - break; - - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -static const struct net_device_ops stir_netdev_ops = { - .ndo_open = stir_net_open, - .ndo_stop = stir_net_close, - .ndo_start_xmit = stir_hard_xmit, - .ndo_do_ioctl = stir_net_ioctl, -}; - -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - * Note : it might be worth protecting this function by a global - * spinlock... Or not, because maybe USB already deal with that... - */ -static int stir_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct stir_cb *stir = NULL; - struct net_device *net; - int ret = -ENOMEM; - - /* Allocate network device container. */ - net = alloc_irdadev(sizeof(*stir)); - if(!net) - goto err_out1; - - SET_NETDEV_DEV(net, &intf->dev); - stir = netdev_priv(net); - stir->netdev = net; - stir->usbdev = dev; - - ret = usb_reset_configuration(dev); - if (ret != 0) { - dev_err(&intf->dev, "usb reset configuration failed\n"); - goto err_out2; - } - - printk(KERN_INFO "SigmaTel STIr4200 IRDA/USB found at address %d, " - "Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&stir->qos); - - /* That's the Rx capability. */ - stir->qos.baud_rate.bits &= IR_2400 | IR_9600 | IR_19200 | - IR_38400 | IR_57600 | IR_115200 | - (IR_4000000 << 8); - stir->qos.min_turn_time.bits &= qos_mtt_bits; - irda_qos_bits_to_value(&stir->qos); - - /* Override the network functions we need to use */ - net->netdev_ops = &stir_netdev_ops; - - ret = register_netdev(net); - if (ret != 0) - goto err_out2; - - dev_info(&intf->dev, "IrDA: Registered SigmaTel device %s\n", - net->name); - - usb_set_intfdata(intf, stir); - - return 0; - -err_out2: - free_netdev(net); -err_out1: - return ret; -} - -/* - * The current device is removed, the USB layer tell us to shut it down... - */ -static void stir_disconnect(struct usb_interface *intf) -{ - struct stir_cb *stir = usb_get_intfdata(intf); - - if (!stir) - return; - - unregister_netdev(stir->netdev); - free_netdev(stir->netdev); - - usb_set_intfdata(intf, NULL); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int stir_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct stir_cb *stir = usb_get_intfdata(intf); - - netif_device_detach(stir->netdev); - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int stir_resume(struct usb_interface *intf) -{ - struct stir_cb *stir = usb_get_intfdata(intf); - - netif_device_attach(stir->netdev); - - /* receiver restarted when send thread wakes up */ - return 0; -} -#endif - -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "stir4200", - .probe = stir_probe, - .disconnect = stir_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = stir_suspend, - .resume = stir_resume, -#endif -}; - -module_usb_driver(irda_driver); diff --git a/drivers/net/irda/tekram-sir.c b/drivers/net/irda/tekram-sir.c deleted file mode 100644 index 9dcf0c103b9d..000000000000 --- a/drivers/net/irda/tekram-sir.c +++ /dev/null @@ -1,225 +0,0 @@ -/********************************************************************* - * - * Filename: tekram.c - * Version: 1.3 - * Description: Implementation of the Tekram IrMate IR-210B dongle - * Status: Experimental. - * Author: Dag Brattli - * Created at: Wed Oct 21 20:02:35 1998 - * Modified at: Sun Oct 27 22:02:38 2002 - * Modified by: Martin Diehl - * - * Copyright (c) 1998-1999 Dag Brattli, - * Copyright (c) 2002 Martin Diehl, - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int tekram_delay = 150; /* default is 150 ms */ -module_param(tekram_delay, int, 0); -MODULE_PARM_DESC(tekram_delay, "tekram dongle write complete delay"); - -static int tekram_open(struct sir_dev *); -static int tekram_close(struct sir_dev *); -static int tekram_change_speed(struct sir_dev *, unsigned); -static int tekram_reset(struct sir_dev *); - -#define TEKRAM_115200 0x00 -#define TEKRAM_57600 0x01 -#define TEKRAM_38400 0x02 -#define TEKRAM_19200 0x03 -#define TEKRAM_9600 0x04 - -#define TEKRAM_PW 0x10 /* Pulse select bit */ - -static struct dongle_driver tekram = { - .owner = THIS_MODULE, - .driver_name = "Tekram IR-210B", - .type = IRDA_TEKRAM_DONGLE, - .open = tekram_open, - .close = tekram_close, - .reset = tekram_reset, - .set_speed = tekram_change_speed, -}; - -static int __init tekram_sir_init(void) -{ - if (tekram_delay < 1 || tekram_delay > 500) - tekram_delay = 200; - pr_debug("%s - using %d ms delay\n", - tekram.driver_name, tekram_delay); - return irda_register_dongle(&tekram); -} - -static void __exit tekram_sir_cleanup(void) -{ - irda_unregister_dongle(&tekram); -} - -static int tekram_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int tekram_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function tekram_change_speed (dev, state, speed) - * - * Set the speed for the Tekram IRMate 210 type dongle. Warning, this - * function must be called with a process context! - * - * Algorithm - * 1. clear DTR - * 2. set RTS, and wait at least 7 us - * 3. send Control Byte to the IR-210 through TXD to set new baud rate - * wait until the stop bit of Control Byte is sent (for 9600 baud rate, - * it takes about 100 msec) - * - * [oops, why 100 msec? sending 1 byte (10 bits) takes 1.05 msec - * - is this probably to compensate for delays in tty layer?] - * - * 5. clear RTS (return to NORMAL Operation) - * 6. wait at least 50 us, new setting (baud rate, etc) takes effect here - * after - */ - -#define TEKRAM_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) - -static int tekram_change_speed(struct sir_dev *dev, unsigned speed) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 byte; - static int ret = 0; - - switch(state) { - case SIRDEV_STATE_DONGLE_SPEED: - - switch (speed) { - default: - speed = 9600; - ret = -EINVAL; - /* fall thru */ - case 9600: - byte = TEKRAM_PW|TEKRAM_9600; - break; - case 19200: - byte = TEKRAM_PW|TEKRAM_19200; - break; - case 38400: - byte = TEKRAM_PW|TEKRAM_38400; - break; - case 57600: - byte = TEKRAM_PW|TEKRAM_57600; - break; - case 115200: - byte = TEKRAM_115200; - break; - } - - /* Set DTR, Clear RTS */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - - /* Wait at least 7us */ - udelay(14); - - /* Write control byte */ - sirdev_raw_write(dev, &byte, 1); - - dev->speed = speed; - - state = TEKRAM_STATE_WAIT_SPEED; - delay = tekram_delay; - break; - - case TEKRAM_STATE_WAIT_SPEED: - /* Set DTR, Set RTS */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - udelay(50); - break; - - default: - net_err_ratelimited("%s - undefined state %d\n", - __func__, state); - ret = -EINVAL; - break; - } - - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -/* - * Function tekram_reset (driver) - * - * This function resets the tekram dongle. Warning, this function - * must be called with a process context!! - * - * Algorithm: - * 0. Clear RTS and DTR, and wait 50 ms (power off the IR-210 ) - * 1. clear RTS - * 2. set DTR, and wait at least 1 ms - * 3. clear DTR to SPACE state, wait at least 50 us for further - * operation - */ - -static int tekram_reset(struct sir_dev *dev) -{ - /* Clear DTR, Set RTS */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - /* Should sleep 1 ms */ - msleep(1); - - /* Set DTR, Set RTS */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Wait at least 50 us */ - udelay(75); - - dev->speed = 9600; - - return 0; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-0"); /* IRDA_TEKRAM_DONGLE */ - -module_init(tekram_sir_init); -module_exit(tekram_sir_cleanup); diff --git a/drivers/net/irda/toim3232-sir.c b/drivers/net/irda/toim3232-sir.c deleted file mode 100644 index b977d6d33e74..000000000000 --- a/drivers/net/irda/toim3232-sir.c +++ /dev/null @@ -1,358 +0,0 @@ -/********************************************************************* - * - * Filename: toim3232-sir.c - * Version: 1.0 - * Description: Implementation of dongles based on the Vishay/Temic - * TOIM3232 SIR Endec chipset. Currently only the - * IRWave IR320ST-2 is tested, although it should work - * with any TOIM3232 or TOIM4232 chipset based RS232 - * dongle with minimal modification. - * Based heavily on the Tekram driver (tekram.c), - * with thanks to Dag Brattli and Martin Diehl. - * Status: Experimental. - * Author: David Basden - * Created at: Thu Feb 09 23:47:32 2006 - * - * Copyright (c) 2006 David Basden. - * Copyright (c) 1998-1999 Dag Brattli, - * Copyright (c) 2002 Martin Diehl, - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -/* - * This driver has currently only been tested on the IRWave IR320ST-2 - * - * PROTOCOL: - * - * The protocol for talking to the TOIM3232 is quite easy, and is - * designed to interface with RS232 with only level convertors. The - * BR/~D line on the chip is brought high to signal 'command mode', - * where a command byte is sent to select the baudrate of the RS232 - * interface and the pulse length of the IRDA output. When BR/~D - * is brought low, the dongle then changes to the selected baudrate, - * and the RS232 interface is used for data until BR/~D is brought - * high again. The initial speed for the TOIMx323 after RESET is - * 9600 baud. The baudrate for command-mode is the last selected - * baud-rate, or 9600 after a RESET. - * - * The dongle I have (below) adds some extra hardware on the front end, - * but this is mostly directed towards pariasitic power from the RS232 - * line rather than changing very much about how to communicate with - * the TOIM3232. - * - * The protocol to talk to the TOIM4232 chipset seems to be almost - * identical to the TOIM3232 (and the 4232 datasheet is more detailed) - * so this code will probably work on that as well, although I haven't - * tested it on that hardware. - * - * Target dongle variations that might be common: - * - * DTR and RTS function: - * The data sheet for the 4232 has a sample implementation that hooks the - * DTR and RTS lines to the RESET and BaudRate/~Data lines of the - * chip (through line-converters). Given both DTR and RTS would have to - * be held low in normal operation, and the TOIMx232 requires +5V to - * signal ground, most dongle designers would almost certainly choose - * an implementation that kept at least one of DTR or RTS high in - * normal operation to provide power to the dongle, but will likely - * vary between designs. - * - * User specified command bits: - * There are two user-controllable output lines from the TOIMx232 that - * can be set low or high by setting the appropriate bits in the - * high-nibble of the command byte (when setting speed and pulse length). - * These might be used to switch on and off added hardware or extra - * dongle features. - * - * - * Target hardware: IRWave IR320ST-2 - * - * The IRWave IR320ST-2 is a simple dongle based on the Vishay/Temic - * TOIM3232 SIR Endec and the Vishay/Temic TFDS4500 SIR IRDA transceiver. - * It uses a hex inverter and some discrete components to buffer and - * line convert the RS232 down to 5V. - * - * The dongle is powered through a voltage regulator, fed by a large - * capacitor. To switch the dongle on, DTR is brought high to charge - * the capacitor and drive the voltage regulator. DTR isn't associated - * with any control lines on the TOIM3232. Parisitic power is also taken - * from the RTS, TD and RD lines when brought high, but through resistors. - * When DTR is low, the circuit might lose power even with RTS high. - * - * RTS is inverted and attached to the BR/~D input pin. When RTS - * is high, BR/~D is low, and the TOIM3232 is in the normal 'data' mode. - * RTS is brought low, BR/~D is high, and the TOIM3232 is in 'command - * mode'. - * - * For some unknown reason, the RESET line isn't actually connected - * to anything. This means to reset the dongle to get it to a known - * state (9600 baud) you must drop DTR and RTS low, wait for the power - * capacitor to discharge, and then bring DTR (and RTS for data mode) - * high again, and wait for the capacitor to charge, the power supply - * to stabilise, and the oscillator clock to stabilise. - * - * Fortunately, if the current baudrate is known, the chipset can - * easily change speed by entering command mode without having to - * reset the dongle first. - * - * Major Components: - * - * - Vishay/Temic TOIM3232 SIR Endec to change RS232 pulse timings - * to IRDA pulse timings - * - 3.6864MHz crystal to drive TOIM3232 clock oscillator - * - DM74lS04M Inverting Hex line buffer for RS232 input buffering - * and level conversion - * - PJ2951AC 150mA voltage regulator - * - Vishay/Temic TFDS4500 SIR IRDA front-end transceiver - * - */ - -#include -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int toim3232delay = 150; /* default is 150 ms */ -module_param(toim3232delay, int, 0); -MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay"); - -static int toim3232_open(struct sir_dev *); -static int toim3232_close(struct sir_dev *); -static int toim3232_change_speed(struct sir_dev *, unsigned); -static int toim3232_reset(struct sir_dev *); - -#define TOIM3232_115200 0x00 -#define TOIM3232_57600 0x01 -#define TOIM3232_38400 0x02 -#define TOIM3232_19200 0x03 -#define TOIM3232_9600 0x06 -#define TOIM3232_2400 0x0A - -#define TOIM3232_PW 0x10 /* Pulse select bit */ - -static struct dongle_driver toim3232 = { - .owner = THIS_MODULE, - .driver_name = "Vishay TOIM3232", - .type = IRDA_TOIM3232_DONGLE, - .open = toim3232_open, - .close = toim3232_close, - .reset = toim3232_reset, - .set_speed = toim3232_change_speed, -}; - -static int __init toim3232_sir_init(void) -{ - if (toim3232delay < 1 || toim3232delay > 500) - toim3232delay = 200; - pr_debug("%s - using %d ms delay\n", - toim3232.driver_name, toim3232delay); - return irda_register_dongle(&toim3232); -} - -static void __exit toim3232_sir_cleanup(void) -{ - irda_unregister_dongle(&toim3232); -} - -static int toim3232_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Pull the lines high to start with. - * - * For the IR320ST-2, we need to charge the main supply capacitor to - * switch the device on. We keep DTR high throughout to do this. - * When RTS, TD and RD are high, they will also trickle-charge the - * cap. RTS is high for data transmission, and low for baud rate select. - * -- DGB - */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* The TOI3232 supports many speeds between 1200bps and 115000bps. - * We really only care about those supported by the IRDA spec, but - * 38400 seems to be implemented in many places */ - qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - - /* From the tekram driver. Not sure what a reasonable value is -- DGB */ - qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int toim3232_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function toim3232change_speed (dev, state, speed) - * - * Set the speed for the TOIM3232 based dongle. Warning, this - * function must be called with a process context! - * - * Algorithm - * 1. keep DTR high but clear RTS to bring into baud programming mode - * 2. wait at least 7us to enter programming mode - * 3. send control word to set baud rate and timing - * 4. wait at least 1us - * 5. bring RTS high to enter DATA mode (RS232 is passed through to transceiver) - * 6. should take effect immediately (although probably worth waiting) - */ - -#define TOIM3232_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) - -static int toim3232_change_speed(struct sir_dev *dev, unsigned speed) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 byte; - static int ret = 0; - - switch(state) { - case SIRDEV_STATE_DONGLE_SPEED: - - /* Figure out what we are going to send as a control byte */ - switch (speed) { - case 2400: - byte = TOIM3232_PW|TOIM3232_2400; - break; - default: - speed = 9600; - ret = -EINVAL; - /* fall thru */ - case 9600: - byte = TOIM3232_PW|TOIM3232_9600; - break; - case 19200: - byte = TOIM3232_PW|TOIM3232_19200; - break; - case 38400: - byte = TOIM3232_PW|TOIM3232_38400; - break; - case 57600: - byte = TOIM3232_PW|TOIM3232_57600; - break; - case 115200: - byte = TOIM3232_115200; - break; - } - - /* Set DTR, Clear RTS: Go into baud programming mode */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - - /* Wait at least 7us */ - udelay(14); - - /* Write control byte */ - sirdev_raw_write(dev, &byte, 1); - - dev->speed = speed; - - state = TOIM3232_STATE_WAIT_SPEED; - delay = toim3232delay; - break; - - case TOIM3232_STATE_WAIT_SPEED: - /* Have transmitted control byte * Wait for 'at least 1us' */ - udelay(14); - - /* Set DTR, Set RTS: Go into normal data mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Wait (TODO: check this is needed) */ - udelay(50); - break; - - default: - printk(KERN_ERR "%s - undefined state %d\n", __func__, state); - ret = -EINVAL; - break; - } - - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -/* - * Function toim3232reset (driver) - * - * This function resets the toim3232 dongle. Warning, this function - * must be called with a process context!! - * - * What we should do is: - * 0. Pull RESET high - * 1. Wait for at least 7us - * 2. Pull RESET low - * 3. Wait for at least 7us - * 4. Pull BR/~D high - * 5. Wait for at least 7us - * 6. Send control byte to set baud rate - * 7. Wait at least 1us after stop bit - * 8. Pull BR/~D low - * 9. Should then be in data mode - * - * Because the IR320ST-2 doesn't have the RESET line connected for some reason, - * we'll have to do something else. - * - * The default speed after a RESET is 9600, so lets try just bringing it up in - * data mode after switching it off, waiting for the supply capacitor to - * discharge, and then switch it back on. This isn't actually pulling RESET - * high, but it seems to have the same effect. - * - * This behaviour will probably work on dongles that have the RESET line connected, - * but if not, add a flag for the IR320ST-2, and implment the above-listed proper - * behaviour. - * - * RTS is inverted and then fed to BR/~D, so to put it in programming mode, we - * need to have pull RTS low - */ - -static int toim3232_reset(struct sir_dev *dev) -{ - /* Switch off both DTR and RTS to switch off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - /* Should sleep a while. This might be evil doing it this way.*/ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(50)); - - /* Set DTR, Set RTS (data mode) */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Wait at least 10 ms for power to stabilize again */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(10)); - - /* Speed should now be 9600 */ - dev->speed = 9600; - - return 0; -} - -MODULE_AUTHOR("David Basden "); -MODULE_DESCRIPTION("Vishay/Temic TOIM3232 based dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-12"); /* IRDA_TOIM3232_DONGLE */ - -module_init(toim3232_sir_init); -module_exit(toim3232_sir_cleanup); diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c deleted file mode 100644 index ca4442a9d631..000000000000 --- a/drivers/net/irda/via-ircc.c +++ /dev/null @@ -1,1593 +0,0 @@ -/******************************************************************** - Filename: via-ircc.c - Version: 1.0 - Description: Driver for the VIA VT8231/VT8233 IrDA chipsets - Author: VIA Technologies,inc - Date : 08/06/2003 - -Copyright (c) 1998-2003 VIA Technologies, Inc. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 2, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTIES OR REPRESENTATIONS; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, see . - -F01 Oct/02/02: Modify code for V0.11(move out back to back transfer) -F02 Oct/28/02: Add SB device ID for 3147 and 3177. - Comment : - jul/09/2002 : only implement two kind of dongle currently. - Oct/02/2002 : work on VT8231 and VT8233 . - Aug/06/2003 : change driver format to pci driver . - -2004-02-16: -- Removed unneeded 'legacy' pci stuff. -- Make sure SIR mode is set (hw_init()) before calling mode-dependent stuff. -- On speed change from core, don't send SIR frame with new speed. - Use current speed and change speeds later. -- Make module-param dongle_id actually work. -- New dongle_id 17 (0x11): TDFS4500. Single-ended SIR only. - Tested with home-grown PCB on EPIA boards. -- Code cleanup. - - ********************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include "via-ircc.h" - -#define VIA_MODULE_NAME "via-ircc" -#define CHIP_IO_EXTENT 0x40 - -static char *driver_name = VIA_MODULE_NAME; - -/* Module parameters */ -static int qos_mtt_bits = 0x07; /* 1 ms or more */ -static int dongle_id = 0; /* default: probe */ - -/* We can't guess the type of connected dongle, user *must* supply it. */ -module_param(dongle_id, int, 0); - -/* Some prototypes */ -static int via_ircc_open(struct pci_dev *pdev, chipio_t *info, - unsigned int id); -static int via_ircc_dma_receive(struct via_ircc_cb *self); -static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, - int iobase); -static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev); -static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev); -static void via_hw_init(struct via_ircc_cb *self); -static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 baud); -static irqreturn_t via_ircc_interrupt(int irq, void *dev_id); -static int via_ircc_is_receiving(struct via_ircc_cb *self); -static int via_ircc_read_dongle_id(int iobase); - -static int via_ircc_net_open(struct net_device *dev); -static int via_ircc_net_close(struct net_device *dev); -static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, - int cmd); -static void via_ircc_change_dongle_speed(int iobase, int speed, - int dongle_id); -static int RxTimerHandler(struct via_ircc_cb *self, int iobase); -static void hwreset(struct via_ircc_cb *self); -static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase); -static int upload_rxdata(struct via_ircc_cb *self, int iobase); -static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id); -static void via_remove_one(struct pci_dev *pdev); - -/* FIXME : Should use udelay() instead, even if we are x86 only - Jean II */ -static void iodelay(int udelay) -{ - u8 data; - int i; - - for (i = 0; i < udelay; i++) { - data = inb(0x80); - } -} - -static const struct pci_device_id via_pci_tbl[] = { - { PCI_VENDOR_ID_VIA, 0x8231, PCI_ANY_ID, PCI_ANY_ID,0,0,0 }, - { PCI_VENDOR_ID_VIA, 0x3109, PCI_ANY_ID, PCI_ANY_ID,0,0,1 }, - { PCI_VENDOR_ID_VIA, 0x3074, PCI_ANY_ID, PCI_ANY_ID,0,0,2 }, - { PCI_VENDOR_ID_VIA, 0x3147, PCI_ANY_ID, PCI_ANY_ID,0,0,3 }, - { PCI_VENDOR_ID_VIA, 0x3177, PCI_ANY_ID, PCI_ANY_ID,0,0,4 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci,via_pci_tbl); - - -static struct pci_driver via_driver = { - .name = VIA_MODULE_NAME, - .id_table = via_pci_tbl, - .probe = via_init_one, - .remove = via_remove_one, -}; - - -/* - * Function via_ircc_init () - * - * Initialize chip. Just find out chip type and resource. - */ -static int __init via_ircc_init(void) -{ - int rc; - - rc = pci_register_driver(&via_driver); - if (rc < 0) { - pr_debug("%s(): error rc = %d, returning -ENODEV...\n", - __func__, rc); - return -ENODEV; - } - return 0; -} - -static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id) -{ - int rc; - u8 temp,oldPCI_40,oldPCI_44,bTmp,bTmp1; - u16 Chipset,FirDRQ1,FirDRQ0,FirIRQ,FirIOBase; - chipio_t info; - - pr_debug("%s(): Device ID=(0X%X)\n", __func__, id->device); - - rc = pci_enable_device (pcidev); - if (rc) { - pr_debug("%s(): error rc = %d\n", __func__, rc); - return -ENODEV; - } - - // South Bridge exist - if ( ReadLPCReg(0x20) != 0x3C ) - Chipset=0x3096; - else - Chipset=0x3076; - - if (Chipset==0x3076) { - pr_debug("%s(): Chipset = 3076\n", __func__); - - WriteLPCReg(7,0x0c ); - temp=ReadLPCReg(0x30);//check if BIOS Enable Fir - if((temp&0x01)==1) { // BIOS close or no FIR - WriteLPCReg(0x1d, 0x82 ); - WriteLPCReg(0x23,0x18); - temp=ReadLPCReg(0xF0); - if((temp&0x01)==0) { - temp=(ReadLPCReg(0x74)&0x03); //DMA - FirDRQ0=temp + 4; - temp=(ReadLPCReg(0x74)&0x0C) >> 2; - FirDRQ1=temp + 4; - } else { - temp=(ReadLPCReg(0x74)&0x0C) >> 2; //DMA - FirDRQ0=temp + 4; - FirDRQ1=FirDRQ0; - } - FirIRQ=(ReadLPCReg(0x70)&0x0f); //IRQ - FirIOBase=ReadLPCReg(0x60 ) << 8; //IO Space :high byte - FirIOBase=FirIOBase| ReadLPCReg(0x61) ; //low byte - FirIOBase=FirIOBase ; - info.fir_base=FirIOBase; - info.irq=FirIRQ; - info.dma=FirDRQ1; - info.dma2=FirDRQ0; - pci_read_config_byte(pcidev,0x40,&bTmp); - pci_write_config_byte(pcidev,0x40,((bTmp | 0x08) & 0xfe)); - pci_read_config_byte(pcidev,0x42,&bTmp); - pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0)); - pci_write_config_byte(pcidev,0x5a,0xc0); - WriteLPCReg(0x28, 0x70 ); - rc = via_ircc_open(pcidev, &info, 0x3076); - } else - rc = -ENODEV; //IR not turn on - } else { //Not VT1211 - pr_debug("%s(): Chipset = 3096\n", __func__); - - pci_read_config_byte(pcidev,0x67,&bTmp);//check if BIOS Enable Fir - if((bTmp&0x01)==1) { // BIOS enable FIR - //Enable Double DMA clock - pci_read_config_byte(pcidev,0x42,&oldPCI_40); - pci_write_config_byte(pcidev,0x42,oldPCI_40 | 0x80); - pci_read_config_byte(pcidev,0x40,&oldPCI_40); - pci_write_config_byte(pcidev,0x40,oldPCI_40 & 0xf7); - pci_read_config_byte(pcidev,0x44,&oldPCI_44); - pci_write_config_byte(pcidev,0x44,0x4e); - //---------- read configuration from Function0 of south bridge - if((bTmp&0x02)==0) { - pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA - FirDRQ0 = (bTmp1 & 0x30) >> 4; - pci_read_config_byte(pcidev,0x44,&bTmp1); - FirDRQ1 = (bTmp1 & 0xc0) >> 6; - } else { - pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA - FirDRQ0 = (bTmp1 & 0x30) >> 4 ; - FirDRQ1=0; - } - pci_read_config_byte(pcidev,0x47,&bTmp1); //IRQ - FirIRQ = bTmp1 & 0x0f; - - pci_read_config_byte(pcidev,0x69,&bTmp); - FirIOBase = bTmp << 8;//hight byte - pci_read_config_byte(pcidev,0x68,&bTmp); - FirIOBase = (FirIOBase | bTmp ) & 0xfff0; - //------------------------- - info.fir_base=FirIOBase; - info.irq=FirIRQ; - info.dma=FirDRQ1; - info.dma2=FirDRQ0; - rc = via_ircc_open(pcidev, &info, 0x3096); - } else - rc = -ENODEV; //IR not turn on !!!!! - }//Not VT1211 - - pr_debug("%s(): End - rc = %d\n", __func__, rc); - return rc; -} - -static void __exit via_ircc_cleanup(void) -{ - /* Cleanup all instances of the driver */ - pci_unregister_driver (&via_driver); -} - -static const struct net_device_ops via_ircc_sir_ops = { - .ndo_start_xmit = via_ircc_hard_xmit_sir, - .ndo_open = via_ircc_net_open, - .ndo_stop = via_ircc_net_close, - .ndo_do_ioctl = via_ircc_net_ioctl, -}; -static const struct net_device_ops via_ircc_fir_ops = { - .ndo_start_xmit = via_ircc_hard_xmit_fir, - .ndo_open = via_ircc_net_open, - .ndo_stop = via_ircc_net_close, - .ndo_do_ioctl = via_ircc_net_ioctl, -}; - -/* - * Function via_ircc_open(pdev, iobase, irq) - * - * Open driver instance - * - */ -static int via_ircc_open(struct pci_dev *pdev, chipio_t *info, unsigned int id) -{ - struct net_device *dev; - struct via_ircc_cb *self; - int err; - - /* Allocate new instance of the driver */ - dev = alloc_irdadev(sizeof(struct via_ircc_cb)); - if (dev == NULL) - return -ENOMEM; - - self = netdev_priv(dev); - self->netdev = dev; - spin_lock_init(&self->lock); - - pci_set_drvdata(pdev, self); - - /* Initialize Resource */ - self->io.cfg_base = info->cfg_base; - self->io.fir_base = info->fir_base; - self->io.irq = info->irq; - self->io.fir_ext = CHIP_IO_EXTENT; - self->io.dma = info->dma; - self->io.dma2 = info->dma2; - self->io.fifo_size = 32; - self->chip_id = id; - self->st_fifo.len = 0; - self->RxDataReady = 0; - - /* Reserve the ioports that we need */ - if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) { - pr_debug("%s(), can't get iobase of 0x%03x\n", - __func__, self->io.fir_base); - err = -ENODEV; - goto err_out1; - } - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* Check if user has supplied the dongle id or not */ - if (!dongle_id) - dongle_id = via_ircc_read_dongle_id(self->io.fir_base); - self->io.dongle_id = dongle_id; - - /* The only value we must override it the baudrate */ - /* Maximum speeds and capabilities are dongle-dependent. */ - switch( self->io.dongle_id ){ - case 0x0d: - self->qos.baud_rate.bits = - IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 | - IR_576000 | IR_1152000 | (IR_4000000 << 8); - break; - default: - self->qos.baud_rate.bits = - IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200; - break; - } - - /* Following was used for testing: - * - * self->qos.baud_rate.bits = IR_9600; - * - * Is is no good, as it prohibits (error-prone) speed-changes. - */ - - self->qos.min_turn_time.bits = qos_mtt_bits; - irda_qos_bits_to_value(&self->qos); - - /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ - self->rx_buff.truesize = 14384 + 2048; - self->tx_buff.truesize = 14384 + 2048; - - /* Allocate memory if needed */ - self->rx_buff.head = - dma_zalloc_coherent(&pdev->dev, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (self->rx_buff.head == NULL) { - err = -ENOMEM; - goto err_out2; - } - - self->tx_buff.head = - dma_zalloc_coherent(&pdev->dev, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (self->tx_buff.head == NULL) { - err = -ENOMEM; - goto err_out3; - } - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Tx queue info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Override the network functions we need to use */ - dev->netdev_ops = &via_ircc_sir_ops; - - err = register_netdev(dev); - if (err) - goto err_out4; - - net_info_ratelimited("IrDA: Registered device %s (via-ircc)\n", - dev->name); - - /* Initialise the hardware.. - */ - self->io.speed = 9600; - via_hw_init(self); - return 0; - err_out4: - dma_free_coherent(&pdev->dev, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - err_out3: - dma_free_coherent(&pdev->dev, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - err_out2: - release_region(self->io.fir_base, self->io.fir_ext); - err_out1: - free_netdev(dev); - return err; -} - -/* - * Function via_remove_one(pdev) - * - * Close driver instance - * - */ -static void via_remove_one(struct pci_dev *pdev) -{ - struct via_ircc_cb *self = pci_get_drvdata(pdev); - int iobase; - - iobase = self->io.fir_base; - - ResetChip(iobase, 5); //hardware reset. - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Release the PORT that this driver is using */ - pr_debug("%s(), Releasing Region %03x\n", - __func__, self->io.fir_base); - release_region(self->io.fir_base, self->io.fir_ext); - if (self->tx_buff.head) - dma_free_coherent(&pdev->dev, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - if (self->rx_buff.head) - dma_free_coherent(&pdev->dev, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - free_netdev(self->netdev); - - pci_disable_device(pdev); -} - -/* - * Function via_hw_init(self) - * - * Returns non-negative on success. - * - * Formerly via_ircc_setup - */ -static void via_hw_init(struct via_ircc_cb *self) -{ - int iobase = self->io.fir_base; - - SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095 - // FIFO Init - EnRXFIFOReadyInt(iobase, OFF); - EnRXFIFOHalfLevelInt(iobase, OFF); - EnTXFIFOHalfLevelInt(iobase, OFF); - EnTXFIFOUnderrunEOMInt(iobase, ON); - EnTXFIFOReadyInt(iobase, OFF); - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); - - if (ReadLPCReg(0x20) == 0x3c) - WriteLPCReg(0xF0, 0); // for VT1211 - /* Int Init */ - EnRXSpecInt(iobase, ON); - - /* The following is basically hwreset */ - /* If this is the case, why not just call hwreset() ? Jean II */ - ResetChip(iobase, 5); - EnableDMA(iobase, OFF); - EnableTX(iobase, OFF); - EnableRX(iobase, OFF); - EnRXDMA(iobase, OFF); - EnTXDMA(iobase, OFF); - RXStart(iobase, OFF); - TXStart(iobase, OFF); - InitCard(iobase); - CommonInit(iobase); - SIRFilter(iobase, ON); - SetSIR(iobase, ON); - CRC16(iobase, ON); - EnTXCRC(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x00); - SetBaudRate(iobase, 9600); - SetPulseWidth(iobase, 12); - SetSendPreambleCount(iobase, 0); - - self->io.speed = 9600; - self->st_fifo.len = 0; - - via_ircc_change_dongle_speed(iobase, self->io.speed, - self->io.dongle_id); - - WriteReg(iobase, I_ST_CT_0, 0x80); -} - -/* - * Function via_ircc_read_dongle_id (void) - * - */ -static int via_ircc_read_dongle_id(int iobase) -{ - net_err_ratelimited("via-ircc: dongle probing not supported, please specify dongle_id module parameter\n"); - return 9; /* Default to IBM */ -} - -/* - * Function via_ircc_change_dongle_speed (iobase, speed, dongle_id) - * Change speed of the attach dongle - * only implement two type of dongle currently. - */ -static void via_ircc_change_dongle_speed(int iobase, int speed, - int dongle_id) -{ - u8 mode = 0; - - /* speed is unused, as we use IsSIROn()/IsMIROn() */ - speed = speed; - - pr_debug("%s(): change_dongle_speed to %d for 0x%x, %d\n", - __func__, speed, iobase, dongle_id); - - switch (dongle_id) { - - /* Note: The dongle_id's listed here are derived from - * nsc-ircc.c */ - - case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - UseOneRX(iobase, ON); // use one RX pin RX1,RX2 - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); - - EnRX2(iobase, ON); //sir to rx2 - EnGPIOtoRX2(iobase, OFF); - - if (IsSIROn(iobase)) { //sir - // Mode select Off - SlowIRRXLowActive(iobase, ON); - udelay(1000); - SlowIRRXLowActive(iobase, OFF); - } else { - if (IsMIROn(iobase)) { //mir - // Mode select On - SlowIRRXLowActive(iobase, OFF); - udelay(20); - } else { // fir - if (IsFIROn(iobase)) { //fir - // Mode select On - SlowIRRXLowActive(iobase, OFF); - udelay(20); - } - } - } - break; - - case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - UseOneRX(iobase, ON); //use ONE RX....RX1 - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); // invert RX pin - - EnRX2(iobase, ON); - EnGPIOtoRX2(iobase, OFF); - if (IsSIROn(iobase)) { //sir - // Mode select On - SlowIRRXLowActive(iobase, ON); - udelay(20); - // Mode select Off - SlowIRRXLowActive(iobase, OFF); - } - if (IsMIROn(iobase)) { //mir - // Mode select On - SlowIRRXLowActive(iobase, OFF); - udelay(20); - // Mode select Off - SlowIRRXLowActive(iobase, ON); - } else { // fir - if (IsFIROn(iobase)) { //fir - // Mode select On - SlowIRRXLowActive(iobase, OFF); - // TX On - WriteTX(iobase, ON); - udelay(20); - // Mode select OFF - SlowIRRXLowActive(iobase, ON); - udelay(20); - // TX Off - WriteTX(iobase, OFF); - } - } - break; - - case 0x0d: - UseOneRX(iobase, OFF); // use two RX pin RX1,RX2 - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); - SlowIRRXLowActive(iobase, OFF); - if (IsSIROn(iobase)) { //sir - EnGPIOtoRX2(iobase, OFF); - WriteGIO(iobase, OFF); - EnRX2(iobase, OFF); //sir to rx2 - } else { // fir mir - EnGPIOtoRX2(iobase, OFF); - WriteGIO(iobase, OFF); - EnRX2(iobase, OFF); //fir to rx - } - break; - - case 0x11: /* Temic TFDS4500 */ - - pr_debug("%s: Temic TFDS4500: One RX pin, TX normal, RX inverted\n", - __func__); - - UseOneRX(iobase, ON); //use ONE RX....RX1 - InvertTX(iobase, OFF); - InvertRX(iobase, ON); // invert RX pin - - EnRX2(iobase, ON); //sir to rx2 - EnGPIOtoRX2(iobase, OFF); - - if( IsSIROn(iobase) ){ //sir - - // Mode select On - SlowIRRXLowActive(iobase, ON); - udelay(20); - // Mode select Off - SlowIRRXLowActive(iobase, OFF); - - } else{ - pr_debug("%s: Warning: TFDS4500 not running in SIR mode !\n", - __func__); - } - break; - - case 0x0ff: /* Vishay */ - if (IsSIROn(iobase)) - mode = 0; - else if (IsMIROn(iobase)) - mode = 1; - else if (IsFIROn(iobase)) - mode = 2; - else if (IsVFIROn(iobase)) - mode = 5; //VFIR-16 - SI_SetMode(iobase, mode); - break; - - default: - net_err_ratelimited("%s: Error: dongle_id %d unsupported !\n", - __func__, dongle_id); - } -} - -/* - * Function via_ircc_change_speed (self, baud) - * - * Change the speed of the device - * - */ -static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 speed) -{ - struct net_device *dev = self->netdev; - u16 iobase; - u8 value = 0, bTmp; - - iobase = self->io.fir_base; - /* Update accounting for new speed */ - self->io.speed = speed; - pr_debug("%s: change_speed to %d bps.\n", __func__, speed); - - WriteReg(iobase, I_ST_CT_0, 0x0); - - /* Controller mode sellection */ - switch (speed) { - case 2400: - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: - value = (115200/speed)-1; - SetSIR(iobase, ON); - CRC16(iobase, ON); - break; - case 576000: - /* FIXME: this can't be right, as it's the same as 115200, - * and 576000 is MIR, not SIR. */ - value = 0; - SetSIR(iobase, ON); - CRC16(iobase, ON); - break; - case 1152000: - value = 0; - SetMIR(iobase, ON); - /* FIXME: CRC ??? */ - break; - case 4000000: - value = 0; - SetFIR(iobase, ON); - SetPulseWidth(iobase, 0); - SetSendPreambleCount(iobase, 14); - CRC16(iobase, OFF); - EnTXCRC(iobase, ON); - break; - case 16000000: - value = 0; - SetVFIR(iobase, ON); - /* FIXME: CRC ??? */ - break; - default: - value = 0; - break; - } - - /* Set baudrate to 0x19[2..7] */ - bTmp = (ReadReg(iobase, I_CF_H_1) & 0x03); - bTmp |= value << 2; - WriteReg(iobase, I_CF_H_1, bTmp); - - /* Some dongles may need to be informed about speed changes. */ - via_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); - - /* Set FIFO size to 64 */ - SetFIFO(iobase, 64); - - /* Enable IR */ - WriteReg(iobase, I_ST_CT_0, 0x80); - - // EnTXFIFOHalfLevelInt(iobase,ON); - - /* Enable some interrupts so we can receive frames */ - //EnAllInt(iobase,ON); - - if (IsSIROn(iobase)) { - SIRFilter(iobase, ON); - SIRRecvAny(iobase, ON); - } else { - SIRFilter(iobase, OFF); - SIRRecvAny(iobase, OFF); - } - - if (speed > 115200) { - /* Install FIR xmit handler */ - dev->netdev_ops = &via_ircc_fir_ops; - via_ircc_dma_receive(self); - } else { - /* Install SIR xmit handler */ - dev->netdev_ops = &via_ircc_sir_ops; - } - netif_wake_queue(dev); -} - -/* - * Function via_ircc_hard_xmit (skb, dev) - * - * Transmit the frame! - * - */ -static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev) -{ - struct via_ircc_cb *self; - unsigned long flags; - u16 iobase; - __u32 speed; - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - iobase = self->io.fir_base; - - netif_stop_queue(dev); - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame */ - if (!skb->len) { - via_ircc_change_speed(self, speed); - netif_trans_update(dev); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - InitCard(iobase); - CommonInit(iobase); - SIRFilter(iobase, ON); - SetSIR(iobase, ON); - CRC16(iobase, ON); - EnTXCRC(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x00); - - spin_lock_irqsave(&self->lock, flags); - self->tx_buff.data = self->tx_buff.head; - self->tx_buff.len = - async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - dev->stats.tx_bytes += self->tx_buff.len; - /* Send this frame with old speed */ - SetBaudRate(iobase, self->io.speed); - SetPulseWidth(iobase, 12); - SetSendPreambleCount(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x80); - - EnableTX(iobase, ON); - EnableRX(iobase, OFF); - - ResetChip(iobase, 0); - ResetChip(iobase, 1); - ResetChip(iobase, 2); - ResetChip(iobase, 3); - ResetChip(iobase, 4); - - EnAllInt(iobase, ON); - EnTXDMA(iobase, ON); - EnRXDMA(iobase, OFF); - - irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, - DMA_TX_MODE); - - SetSendByte(iobase, self->tx_buff.len); - RXStart(iobase, OFF); - TXStart(iobase, ON); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev) -{ - struct via_ircc_cb *self; - u16 iobase; - __u32 speed; - unsigned long flags; - - self = netdev_priv(dev); - iobase = self->io.fir_base; - - if (self->st_fifo.len) - return NETDEV_TX_OK; - if (self->chip_id == 0x3076) - iodelay(1500); - else - udelay(1500); - netif_stop_queue(dev); - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - if (!skb->len) { - via_ircc_change_speed(self, speed); - netif_trans_update(dev); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - spin_lock_irqsave(&self->lock, flags); - self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; - self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; - - self->tx_fifo.tail += skb->len; - dev->stats.tx_bytes += skb->len; - skb_copy_from_linear_data(skb, - self->tx_fifo.queue[self->tx_fifo.free].start, skb->len); - self->tx_fifo.len++; - self->tx_fifo.free++; -//F01 if (self->tx_fifo.len == 1) { - via_ircc_dma_xmit(self, iobase); -//F01 } -//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) netif_wake_queue(self->netdev); - netif_trans_update(dev); - dev_kfree_skb(skb); - spin_unlock_irqrestore(&self->lock, flags); - return NETDEV_TX_OK; - -} - -static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase) -{ - EnTXDMA(iobase, OFF); - self->io.direction = IO_XMIT; - EnPhys(iobase, ON); - EnableTX(iobase, ON); - EnableRX(iobase, OFF); - ResetChip(iobase, 0); - ResetChip(iobase, 1); - ResetChip(iobase, 2); - ResetChip(iobase, 3); - ResetChip(iobase, 4); - EnAllInt(iobase, ON); - EnTXDMA(iobase, ON); - EnRXDMA(iobase, OFF); - irda_setup_dma(self->io.dma, - ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - - self->tx_buff.head) + self->tx_buff_dma, - self->tx_fifo.queue[self->tx_fifo.ptr].len, DMA_TX_MODE); - pr_debug("%s: tx_fifo.ptr=%x,len=%x,tx_fifo.len=%x..\n", - __func__, self->tx_fifo.ptr, - self->tx_fifo.queue[self->tx_fifo.ptr].len, - self->tx_fifo.len); - - SetSendByte(iobase, self->tx_fifo.queue[self->tx_fifo.ptr].len); - RXStart(iobase, OFF); - TXStart(iobase, ON); - return 0; - -} - -/* - * Function via_ircc_dma_xmit_complete (self) - * - * The transfer of a frame in finished. This function will only be called - * by the interrupt handler - * - */ -static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self) -{ - int iobase; - u8 Tx_status; - - iobase = self->io.fir_base; - /* Disable DMA */ -// DisableDmaChannel(self->io.dma); - /* Check for underrun! */ - /* Clear bit, by writing 1 into it */ - Tx_status = GetTXStatus(iobase); - if (Tx_status & 0x08) { - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - hwreset(self); - /* how to clear underrun? */ - } else { - self->netdev->stats.tx_packets++; - ResetChip(iobase, 3); - ResetChip(iobase, 4); - } - /* Check if we need to change the speed */ - if (self->new_speed) { - via_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - /* Finished with this frame, so prepare for next */ - if (IsFIROn(iobase)) { - if (self->tx_fifo.len) { - self->tx_fifo.len--; - self->tx_fifo.ptr++; - } - } - pr_debug("%s: tx_fifo.len=%x ,tx_fifo.ptr=%x,tx_fifo.free=%x...\n", - __func__, - self->tx_fifo.len, self->tx_fifo.ptr, self->tx_fifo.free); -/* F01_S - // Any frames to be sent back-to-back? - if (self->tx_fifo.len) { - // Not finished yet! - via_ircc_dma_xmit(self, iobase); - ret = FALSE; - } else { -F01_E*/ - // Reset Tx FIFO info - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; -//F01 } - - // Make sure we have room for more frames -//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) { - // Not busy transmitting anymore - // Tell the network layer, that we can accept more frames - netif_wake_queue(self->netdev); -//F01 } - return TRUE; -} - -/* - * Function via_ircc_dma_receive (self) - * - * Set configuration for receive a frame. - * - */ -static int via_ircc_dma_receive(struct via_ircc_cb *self) -{ - int iobase; - - iobase = self->io.fir_base; - - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - self->RxDataReady = 0; - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - self->st_fifo.len = self->st_fifo.pending_bytes = 0; - self->st_fifo.tail = self->st_fifo.head = 0; - - EnPhys(iobase, ON); - EnableTX(iobase, OFF); - EnableRX(iobase, ON); - - ResetChip(iobase, 0); - ResetChip(iobase, 1); - ResetChip(iobase, 2); - ResetChip(iobase, 3); - ResetChip(iobase, 4); - - EnAllInt(iobase, ON); - EnTXDMA(iobase, OFF); - EnRXDMA(iobase, ON); - irda_setup_dma(self->io.dma2, self->rx_buff_dma, - self->rx_buff.truesize, DMA_RX_MODE); - TXStart(iobase, OFF); - RXStart(iobase, ON); - - return 0; -} - -/* - * Function via_ircc_dma_receive_complete (self) - * - * Controller Finished with receiving frames, - * and this routine is call by ISR - * - */ -static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, - int iobase) -{ - struct st_fifo *st_fifo; - struct sk_buff *skb; - int len, i; - u8 status = 0; - - iobase = self->io.fir_base; - st_fifo = &self->st_fifo; - - if (self->io.speed < 4000000) { //Speed below FIR - len = GetRecvByte(iobase, self); - skb = dev_alloc_skb(len + 1); - if (skb == NULL) - return FALSE; - // Make sure IP header gets aligned - skb_reserve(skb, 1); - skb_put(skb, len - 2); - if (self->chip_id == 0x3076) { - for (i = 0; i < len - 2; i++) - skb->data[i] = self->rx_buff.data[i * 2]; - } else { - if (self->chip_id == 0x3096) { - for (i = 0; i < len - 2; i++) - skb->data[i] = - self->rx_buff.data[i]; - } - } - // Move to next frame - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - return TRUE; - } - - else { //FIR mode - len = GetRecvByte(iobase, self); - if (len == 0) - return TRUE; //interrupt only, data maybe move by RxT - if (((len - 4) < 2) || ((len - 4) > 2048)) { - pr_debug("%s(): Trouble:len=%x,CurCount=%x,LastCount=%x\n", - __func__, len, RxCurCount(iobase, self), - self->RxLastCount); - hwreset(self); - return FALSE; - } - pr_debug("%s(): fifo.len=%x,len=%x,CurCount=%x..\n", - __func__, - st_fifo->len, len - 4, RxCurCount(iobase, self)); - - st_fifo->entries[st_fifo->tail].status = status; - st_fifo->entries[st_fifo->tail].len = len; - st_fifo->pending_bytes += len; - st_fifo->tail++; - st_fifo->len++; - if (st_fifo->tail > MAX_RX_WINDOW) - st_fifo->tail = 0; - self->RxDataReady = 0; - - // It maybe have MAX_RX_WINDOW package receive by - // receive_complete before Timer IRQ -/* F01_S - if (st_fifo->len < (MAX_RX_WINDOW+2 )) { - RXStart(iobase,ON); - SetTimer(iobase,4); - } - else { -F01_E */ - EnableRX(iobase, OFF); - EnRXDMA(iobase, OFF); - RXStart(iobase, OFF); -//F01_S - // Put this entry back in fifo - if (st_fifo->head > MAX_RX_WINDOW) - st_fifo->head = 0; - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->head++; - st_fifo->len--; - - skb = dev_alloc_skb(len + 1 - 4); - /* - * if frame size, data ptr, or skb ptr are wrong, then get next - * entry. - */ - if ((skb == NULL) || (skb->data == NULL) || - (self->rx_buff.data == NULL) || (len < 6)) { - self->netdev->stats.rx_dropped++; - kfree_skb(skb); - return TRUE; - } - skb_reserve(skb, 1); - skb_put(skb, len - 4); - - skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); - pr_debug("%s(): len=%x.rx_buff=%p\n", __func__, - len - 4, self->rx_buff.data); - - // Move to next frame - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - -//F01_E - } //FIR - return TRUE; - -} - -/* - * if frame is received , but no INT ,then use this routine to upload frame. - */ -static int upload_rxdata(struct via_ircc_cb *self, int iobase) -{ - struct sk_buff *skb; - int len; - struct st_fifo *st_fifo; - st_fifo = &self->st_fifo; - - len = GetRecvByte(iobase, self); - - pr_debug("%s(): len=%x\n", __func__, len); - - if ((len - 4) < 2) { - self->netdev->stats.rx_dropped++; - return FALSE; - } - - skb = dev_alloc_skb(len + 1); - if (skb == NULL) { - self->netdev->stats.rx_dropped++; - return FALSE; - } - skb_reserve(skb, 1); - skb_put(skb, len - 4 + 1); - skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4 + 1); - st_fifo->tail++; - st_fifo->len++; - if (st_fifo->tail > MAX_RX_WINDOW) - st_fifo->tail = 0; - // Move to next frame - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - if (st_fifo->len < (MAX_RX_WINDOW + 2)) { - RXStart(iobase, ON); - } else { - EnableRX(iobase, OFF); - EnRXDMA(iobase, OFF); - RXStart(iobase, OFF); - } - return TRUE; -} - -/* - * Implement back to back receive , use this routine to upload data. - */ - -static int RxTimerHandler(struct via_ircc_cb *self, int iobase) -{ - struct st_fifo *st_fifo; - struct sk_buff *skb; - int len; - u8 status; - - st_fifo = &self->st_fifo; - - if (CkRxRecv(iobase, self)) { - // if still receiving ,then return ,don't upload frame - self->RetryCount = 0; - SetTimer(iobase, 20); - self->RxDataReady++; - return FALSE; - } else - self->RetryCount++; - - if ((self->RetryCount >= 1) || - ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize) || - (st_fifo->len >= (MAX_RX_WINDOW))) { - while (st_fifo->len > 0) { //upload frame - // Put this entry back in fifo - if (st_fifo->head > MAX_RX_WINDOW) - st_fifo->head = 0; - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->head++; - st_fifo->len--; - - skb = dev_alloc_skb(len + 1 - 4); - /* - * if frame size, data ptr, or skb ptr are wrong, - * then get next entry. - */ - if ((skb == NULL) || (skb->data == NULL) || - (self->rx_buff.data == NULL) || (len < 6)) { - self->netdev->stats.rx_dropped++; - continue; - } - skb_reserve(skb, 1); - skb_put(skb, len - 4); - skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); - - pr_debug("%s(): len=%x.head=%x\n", __func__, - len - 4, st_fifo->head); - - // Move to next frame - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - } //while - self->RetryCount = 0; - - pr_debug("%s(): End of upload HostStatus=%x,RxStatus=%x\n", - __func__, GetHostStatus(iobase), GetRXStatus(iobase)); - - /* - * if frame is receive complete at this routine ,then upload - * frame. - */ - if ((GetRXStatus(iobase) & 0x10) && - (RxCurCount(iobase, self) != self->RxLastCount)) { - upload_rxdata(self, iobase); - if (irda_device_txqueue_empty(self->netdev)) - via_ircc_dma_receive(self); - } - } // timer detect complete - else - SetTimer(iobase, 4); - return TRUE; - -} - - - -/* - * Function via_ircc_interrupt (irq, dev_id) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t via_ircc_interrupt(int dummy, void *dev_id) -{ - struct net_device *dev = dev_id; - struct via_ircc_cb *self = netdev_priv(dev); - int iobase; - u8 iHostIntType, iRxIntType, iTxIntType; - - iobase = self->io.fir_base; - spin_lock(&self->lock); - iHostIntType = GetHostStatus(iobase); - - pr_debug("%s(): iHostIntType %02x: %s %s %s %02x\n", - __func__, iHostIntType, - (iHostIntType & 0x40) ? "Timer" : "", - (iHostIntType & 0x20) ? "Tx" : "", - (iHostIntType & 0x10) ? "Rx" : "", - (iHostIntType & 0x0e) >> 1); - - if ((iHostIntType & 0x40) != 0) { //Timer Event - self->EventFlag.TimeOut++; - ClearTimerInt(iobase, 1); - if (self->io.direction == IO_XMIT) { - via_ircc_dma_xmit(self, iobase); - } - if (self->io.direction == IO_RECV) { - /* - * frame ready hold too long, must reset. - */ - if (self->RxDataReady > 30) { - hwreset(self); - if (irda_device_txqueue_empty(self->netdev)) { - via_ircc_dma_receive(self); - } - } else { // call this to upload frame. - RxTimerHandler(self, iobase); - } - } //RECV - } //Timer Event - if ((iHostIntType & 0x20) != 0) { //Tx Event - iTxIntType = GetTXStatus(iobase); - - pr_debug("%s(): iTxIntType %02x: %s %s %s %s\n", - __func__, iTxIntType, - (iTxIntType & 0x08) ? "FIFO underr." : "", - (iTxIntType & 0x04) ? "EOM" : "", - (iTxIntType & 0x02) ? "FIFO ready" : "", - (iTxIntType & 0x01) ? "Early EOM" : ""); - - if (iTxIntType & 0x4) { - self->EventFlag.EOMessage++; // read and will auto clean - if (via_ircc_dma_xmit_complete(self)) { - if (irda_device_txqueue_empty - (self->netdev)) { - via_ircc_dma_receive(self); - } - } else { - self->EventFlag.Unknown++; - } - } //EOP - } //Tx Event - //---------------------------------------- - if ((iHostIntType & 0x10) != 0) { //Rx Event - /* Check if DMA has finished */ - iRxIntType = GetRXStatus(iobase); - - pr_debug("%s(): iRxIntType %02x: %s %s %s %s %s %s %s\n", - __func__, iRxIntType, - (iRxIntType & 0x80) ? "PHY err." : "", - (iRxIntType & 0x40) ? "CRC err" : "", - (iRxIntType & 0x20) ? "FIFO overr." : "", - (iRxIntType & 0x10) ? "EOF" : "", - (iRxIntType & 0x08) ? "RxData" : "", - (iRxIntType & 0x02) ? "RxMaxLen" : "", - (iRxIntType & 0x01) ? "SIR bad" : ""); - if (!iRxIntType) - pr_debug("%s(): RxIRQ =0\n", __func__); - - if (iRxIntType & 0x10) { - if (via_ircc_dma_receive_complete(self, iobase)) { -//F01 if(!(IsFIROn(iobase))) via_ircc_dma_receive(self); - via_ircc_dma_receive(self); - } - } // No ERR - else { //ERR - pr_debug("%s(): RxIRQ ERR:iRxIntType=%x,HostIntType=%x,CurCount=%x,RxLastCount=%x_____\n", - __func__, iRxIntType, iHostIntType, - RxCurCount(iobase, self), self->RxLastCount); - - if (iRxIntType & 0x20) { //FIFO OverRun ERR - ResetChip(iobase, 0); - ResetChip(iobase, 1); - } else { //PHY,CRC ERR - - if (iRxIntType != 0x08) - hwreset(self); //F01 - } - via_ircc_dma_receive(self); - } //ERR - - } //Rx Event - spin_unlock(&self->lock); - return IRQ_RETVAL(iHostIntType); -} - -static void hwreset(struct via_ircc_cb *self) -{ - int iobase; - iobase = self->io.fir_base; - - ResetChip(iobase, 5); - EnableDMA(iobase, OFF); - EnableTX(iobase, OFF); - EnableRX(iobase, OFF); - EnRXDMA(iobase, OFF); - EnTXDMA(iobase, OFF); - RXStart(iobase, OFF); - TXStart(iobase, OFF); - InitCard(iobase); - CommonInit(iobase); - SIRFilter(iobase, ON); - SetSIR(iobase, ON); - CRC16(iobase, ON); - EnTXCRC(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x00); - SetBaudRate(iobase, 9600); - SetPulseWidth(iobase, 12); - SetSendPreambleCount(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x80); - - /* Restore speed. */ - via_ircc_change_speed(self, self->io.speed); - - self->st_fifo.len = 0; -} - -/* - * Function via_ircc_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int via_ircc_is_receiving(struct via_ircc_cb *self) -{ - int status = FALSE; - int iobase; - - IRDA_ASSERT(self != NULL, return FALSE;); - - iobase = self->io.fir_base; - if (CkRxRecv(iobase, self)) - status = TRUE; - - pr_debug("%s(): status=%x....\n", __func__, status); - - return status; -} - - -/* - * Function via_ircc_net_open (dev) - * - * Start the device - * - */ -static int via_ircc_net_open(struct net_device *dev) -{ - struct via_ircc_cb *self; - int iobase; - char hwname[32]; - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - dev->stats.rx_packets = 0; - IRDA_ASSERT(self != NULL, return 0;); - iobase = self->io.fir_base; - if (request_irq(self->io.irq, via_ircc_interrupt, 0, dev->name, dev)) { - net_warn_ratelimited("%s, unable to allocate irq=%d\n", - driver_name, self->io.irq); - return -EAGAIN; - } - /* - * Always allocate the DMA channel after the IRQ, and clean up on - * failure. - */ - if (request_dma(self->io.dma, dev->name)) { - net_warn_ratelimited("%s, unable to allocate dma=%d\n", - driver_name, self->io.dma); - free_irq(self->io.irq, dev); - return -EAGAIN; - } - if (self->io.dma2 != self->io.dma) { - if (request_dma(self->io.dma2, dev->name)) { - net_warn_ratelimited("%s, unable to allocate dma2=%d\n", - driver_name, self->io.dma2); - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - return -EAGAIN; - } - } - - - /* turn on interrupts */ - EnAllInt(iobase, ON); - EnInternalLoop(iobase, OFF); - EnExternalLoop(iobase, OFF); - - /* */ - via_ircc_dma_receive(self); - - /* Ready to play! */ - netif_start_queue(dev); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - sprintf(hwname, "VIA @ 0x%x", iobase); - self->irlap = irlap_open(dev, &self->qos, hwname); - - self->RxLastCount = 0; - - return 0; -} - -/* - * Function via_ircc_net_close (dev) - * - * Stop the device - * - */ -static int via_ircc_net_close(struct net_device *dev) -{ - struct via_ircc_cb *self; - int iobase; - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - /* Stop device */ - netif_stop_queue(dev); - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - iobase = self->io.fir_base; - EnTXDMA(iobase, OFF); - EnRXDMA(iobase, OFF); - DisableDmaChannel(self->io.dma); - - /* Disable interrupts */ - EnAllInt(iobase, OFF); - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - if (self->io.dma2 != self->io.dma) - free_dma(self->io.dma2); - - return 0; -} - -/* - * Function via_ircc_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, - int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct via_ircc_cb *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return -1;); - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, - cmd); - /* Disable interrupts & save flags */ - spin_lock_irqsave(&self->lock, flags); - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - via_ircc_change_speed(self, irq->ifr_baudrate); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = via_ircc_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } - out: - spin_unlock_irqrestore(&self->lock, flags); - return ret; -} - -MODULE_AUTHOR("VIA Technologies,inc"); -MODULE_DESCRIPTION("VIA IrDA Device Driver"); -MODULE_LICENSE("GPL"); - -module_init(via_ircc_init); -module_exit(via_ircc_cleanup); diff --git a/drivers/net/irda/via-ircc.h b/drivers/net/irda/via-ircc.h deleted file mode 100644 index ac1525573398..000000000000 --- a/drivers/net/irda/via-ircc.h +++ /dev/null @@ -1,846 +0,0 @@ -/********************************************************************* - * - * Filename: via-ircc.h - * Version: 1.0 - * Description: Driver for the VIA VT8231/VT8233 IrDA chipsets - * Author: VIA Technologies, inc - * Date : 08/06/2003 - -Copyright (c) 1998-2003 VIA Technologies, Inc. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 2, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTIES OR REPRESENTATIONS; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, see . - - * Comment: - * jul/08/2002 : Rx buffer length should use Rx ring ptr. - * Oct/28/2002 : Add SB id for 3147 and 3177. - * jul/09/2002 : only implement two kind of dongle currently. - * Oct/02/2002 : work on VT8231 and VT8233 . - * Aug/06/2003 : change driver format to pci driver . - ********************************************************************/ -#ifndef via_IRCC_H -#define via_IRCC_H -#include -#include -#include -#include - -#define MAX_TX_WINDOW 7 -#define MAX_RX_WINDOW 7 - -struct st_fifo_entry { - int status; - int len; -}; - -struct st_fifo { - struct st_fifo_entry entries[MAX_RX_WINDOW + 2]; - int pending_bytes; - int head; - int tail; - int len; -}; - -struct frame_cb { - void *start; /* Start of frame in DMA mem */ - int len; /* Length of frame in DMA mem */ -}; - -struct tx_fifo { - struct frame_cb queue[MAX_TX_WINDOW + 2]; /* Info about frames in queue */ - int ptr; /* Currently being sent */ - int len; /* Length of queue */ - int free; /* Next free slot */ - void *tail; /* Next free start in DMA mem */ -}; - - -struct eventflag // for keeping track of Interrupt Events -{ - //--------tx part - unsigned char TxFIFOUnderRun; - unsigned char EOMessage; - unsigned char TxFIFOReady; - unsigned char EarlyEOM; - //--------rx part - unsigned char PHYErr; - unsigned char CRCErr; - unsigned char RxFIFOOverRun; - unsigned char EOPacket; - unsigned char RxAvail; - unsigned char TooLargePacket; - unsigned char SIRBad; - //--------unknown - unsigned char Unknown; - //---------- - unsigned char TimeOut; - unsigned char RxDMATC; - unsigned char TxDMATC; -}; - -/* Private data for each instance */ -struct via_ircc_cb { - struct st_fifo st_fifo; /* Info about received frames */ - struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ - - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; /* QoS capabilities for this device */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - __u8 ier; /* Interrupt enable register */ - - spinlock_t lock; /* For serializing operations */ - - __u32 flags; /* Interface flags */ - __u32 new_speed; - int index; /* Instance index */ - - struct eventflag EventFlag; - unsigned int chip_id; /* to remember chip id */ - unsigned int RetryCount; - unsigned int RxDataReady; - unsigned int RxLastCount; -}; - - -//---------I=Infrared, H=Host, M=Misc, T=Tx, R=Rx, ST=Status, -// CF=Config, CT=Control, L=Low, H=High, C=Count -#define I_CF_L_0 0x10 -#define I_CF_H_0 0x11 -#define I_SIR_BOF 0x12 -#define I_SIR_EOF 0x13 -#define I_ST_CT_0 0x15 -#define I_ST_L_1 0x16 -#define I_ST_H_1 0x17 -#define I_CF_L_1 0x18 -#define I_CF_H_1 0x19 -#define I_CF_L_2 0x1a -#define I_CF_H_2 0x1b -#define I_CF_3 0x1e -#define H_CT 0x20 -#define H_ST 0x21 -#define M_CT 0x22 -#define TX_CT_1 0x23 -#define TX_CT_2 0x24 -#define TX_ST 0x25 -#define RX_CT 0x26 -#define RX_ST 0x27 -#define RESET 0x28 -#define P_ADDR 0x29 -#define RX_C_L 0x2a -#define RX_C_H 0x2b -#define RX_P_L 0x2c -#define RX_P_H 0x2d -#define TX_C_L 0x2e -#define TX_C_H 0x2f -#define TIMER 0x32 -#define I_CF_4 0x33 -#define I_T_C_L 0x34 -#define I_T_C_H 0x35 -#define VERSION 0x3f -//------------------------------- -#define StartAddr 0x10 // the first register address -#define EndAddr 0x3f // the last register address -#define GetBit(val,bit) val = (unsigned char) ((val>>bit) & 0x1) - // Returns the bit -#define SetBit(val,bit) val= (unsigned char ) (val | (0x1 << bit)) - // Sets bit to 1 -#define ResetBit(val,bit) val= (unsigned char ) (val & ~(0x1 << bit)) - // Sets bit to 0 - -#define OFF 0 -#define ON 1 -#define DMA_TX_MODE 0x08 -#define DMA_RX_MODE 0x04 - -#define DMA1 0 -#define DMA2 0xc0 -#define MASK1 DMA1+0x0a -#define MASK2 DMA2+0x14 - -#define Clk_bit 0x40 -#define Tx_bit 0x01 -#define Rd_Valid 0x08 -#define RxBit 0x08 - -static void DisableDmaChannel(unsigned int channel) -{ - switch (channel) { // 8 Bit DMA channels DMAC1 - case 0: - outb(4, MASK1); //mask channel 0 - break; - case 1: - outb(5, MASK1); //Mask channel 1 - break; - case 2: - outb(6, MASK1); //Mask channel 2 - break; - case 3: - outb(7, MASK1); //Mask channel 3 - break; - case 5: - outb(5, MASK2); //Mask channel 5 - break; - case 6: - outb(6, MASK2); //Mask channel 6 - break; - case 7: - outb(7, MASK2); //Mask channel 7 - break; - default: - break; - } -} - -static unsigned char ReadLPCReg(int iRegNum) -{ - unsigned char iVal; - - outb(0x87, 0x2e); - outb(0x87, 0x2e); - outb(iRegNum, 0x2e); - iVal = inb(0x2f); - outb(0xaa, 0x2e); - - return iVal; -} - -static void WriteLPCReg(int iRegNum, unsigned char iVal) -{ - - outb(0x87, 0x2e); - outb(0x87, 0x2e); - outb(iRegNum, 0x2e); - outb(iVal, 0x2f); - outb(0xAA, 0x2e); -} - -static __u8 ReadReg(unsigned int BaseAddr, int iRegNum) -{ - return (__u8) inb(BaseAddr + iRegNum); -} - -static void WriteReg(unsigned int BaseAddr, int iRegNum, unsigned char iVal) -{ - outb(iVal, BaseAddr + iRegNum); -} - -static int WriteRegBit(unsigned int BaseAddr, unsigned char RegNum, - unsigned char BitPos, unsigned char value) -{ - __u8 Rtemp, Wtemp; - - if (BitPos > 7) { - return -1; - } - if ((RegNum < StartAddr) || (RegNum > EndAddr)) - return -1; - Rtemp = ReadReg(BaseAddr, RegNum); - if (value == 0) - Wtemp = ResetBit(Rtemp, BitPos); - else { - if (value == 1) - Wtemp = SetBit(Rtemp, BitPos); - else - return -1; - } - WriteReg(BaseAddr, RegNum, Wtemp); - return 0; -} - -static __u8 CheckRegBit(unsigned int BaseAddr, unsigned char RegNum, - unsigned char BitPos) -{ - __u8 temp; - - if (BitPos > 7) - return 0xff; - if ((RegNum < StartAddr) || (RegNum > EndAddr)) { -// printf("what is the register %x!\n",RegNum); - } - temp = ReadReg(BaseAddr, RegNum); - return GetBit(temp, BitPos); -} - -static void SetMaxRxPacketSize(__u16 iobase, __u16 size) -{ - __u16 low, high; - if ((size & 0xe000) == 0) { - low = size & 0x00ff; - high = (size & 0x1f00) >> 8; - WriteReg(iobase, I_CF_L_2, low); - WriteReg(iobase, I_CF_H_2, high); - - } - -} - -//for both Rx and Tx - -static void SetFIFO(__u16 iobase, __u16 value) -{ - switch (value) { - case 128: - WriteRegBit(iobase, 0x11, 0, 0); - WriteRegBit(iobase, 0x11, 7, 1); - break; - case 64: - WriteRegBit(iobase, 0x11, 0, 0); - WriteRegBit(iobase, 0x11, 7, 0); - break; - case 32: - WriteRegBit(iobase, 0x11, 0, 1); - WriteRegBit(iobase, 0x11, 7, 0); - break; - default: - WriteRegBit(iobase, 0x11, 0, 0); - WriteRegBit(iobase, 0x11, 7, 0); - } - -} - -#define CRC16(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,7,val) //0 for 32 CRC -/* -#define SetVFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,5,val) -#define SetFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,6,val) -#define SetMIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,5,val) -#define SetSIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,4,val) -*/ -#define SIRFilter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,3,val) -#define Filter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,2,val) -#define InvertTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,1,val) -#define InvertRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,0,val) -//****************************I_CF_H_0 -#define EnableTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,4,val) -#define EnableRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,3,val) -#define EnableDMA(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,2,val) -#define SIRRecvAny(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,1,val) -#define DiableTrans(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,0,val) -//***************************I_SIR_BOF,I_SIR_EOF -#define SetSIRBOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_BOF,val) -#define SetSIREOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_EOF,val) -#define GetSIRBOF(BaseAddr) ReadReg(BaseAddr,I_SIR_BOF) -#define GetSIREOF(BaseAddr) ReadReg(BaseAddr,I_SIR_EOF) -//*******************I_ST_CT_0 -#define EnPhys(BaseAddr,val) WriteRegBit(BaseAddr,I_ST_CT_0,7,val) -#define IsModeError(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,6) //RO -#define IsVFIROn(BaseAddr) CheckRegBit(BaseAddr,0x14,0) //RO for VT1211 only -#define IsFIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,5) //RO -#define IsMIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,4) //RO -#define IsSIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,3) //RO -#define IsEnableTX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,2) //RO -#define IsEnableRX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,1) //RO -#define Is16CRC(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,0) //RO -//***************************I_CF_3 -#define DisableAdjacentPulseWidth(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,5,val) //1 disable -#define DisablePulseWidthAdjust(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,4,val) //1 disable -#define UseOneRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,1,val) //0 use two RX -#define SlowIRRXLowActive(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,0,val) //0 show RX high=1 in SIR -//***************************H_CT -#define EnAllInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,7,val) -#define TXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,6,val) -#define RXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,5,val) -#define ClearRXInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,4,val) // 1 clear -//*****************H_ST -#define IsRXInt(BaseAddr) CheckRegBit(BaseAddr,H_ST,4) -#define GetIntIndentify(BaseAddr) ((ReadReg(BaseAddr,H_ST)&0xf1) >>1) -#define IsHostBusy(BaseAddr) CheckRegBit(BaseAddr,H_ST,0) -#define GetHostStatus(BaseAddr) ReadReg(BaseAddr,H_ST) //RO -//**************************M_CT -#define EnTXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,7,val) -#define EnRXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,6,val) -#define SwapDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,5,val) -#define EnInternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,4,val) -#define EnExternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,3,val) -//**************************TX_CT_1 -#define EnTXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,4,val) //half empty int (1 half) -#define EnTXFIFOUnderrunEOMInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,5,val) -#define EnTXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,6,val) //int when reach it threshold (setting by bit 4) -//**************************TX_CT_2 -#define ForceUnderrun(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,7,val) // force an underrun int -#define EnTXCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,6,val) //1 for FIR,MIR...0 (not SIR) -#define ForceBADCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,5,val) //force an bad CRC -#define SendSIP(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,4,val) //send indication pulse for prevent SIR disturb -#define ClearEnTX(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,3,val) // opposite to EnTX -//*****************TX_ST -#define GetTXStatus(BaseAddr) ReadReg(BaseAddr,TX_ST) //RO -//**************************RX_CT -#define EnRXSpecInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,0,val) -#define EnRXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,1,val) //enable int when reach it threshold (setting by bit 7) -#define EnRXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,7,val) //enable int when (1) half full...or (0) just not full -//*****************RX_ST -#define GetRXStatus(BaseAddr) ReadReg(BaseAddr,RX_ST) //RO -//***********************P_ADDR -#define SetPacketAddr(BaseAddr,addr) WriteReg(BaseAddr,P_ADDR,addr) -//***********************I_CF_4 -#define EnGPIOtoRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,7,val) -#define EnTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,1,val) -#define ClearTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,0,val) -//***********************I_T_C_L -#define WriteGIO(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,7,val) -#define ReadGIO(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,7) -#define ReadRX(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,3) //RO -#define WriteTX(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,0,val) -//***********************I_T_C_H -#define EnRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_H,7,val) -#define ReadRX2(BaseAddr) CheckRegBit(BaseAddr,I_T_C_H,7) -//**********************Version -#define GetFIRVersion(BaseAddr) ReadReg(BaseAddr,VERSION) - - -static void SetTimer(__u16 iobase, __u8 count) -{ - EnTimerInt(iobase, OFF); - WriteReg(iobase, TIMER, count); - EnTimerInt(iobase, ON); -} - - -static void SetSendByte(__u16 iobase, __u32 count) -{ - __u32 low, high; - - if ((count & 0xf000) == 0) { - low = count & 0x00ff; - high = (count & 0x0f00) >> 8; - WriteReg(iobase, TX_C_L, low); - WriteReg(iobase, TX_C_H, high); - } -} - -static void ResetChip(__u16 iobase, __u8 type) -{ - __u8 value; - - value = (type + 2) << 4; - WriteReg(iobase, RESET, type); -} - -static int CkRxRecv(__u16 iobase, struct via_ircc_cb *self) -{ - __u8 low, high; - __u16 wTmp = 0, wTmp1 = 0, wTmp_new = 0; - - low = ReadReg(iobase, RX_C_L); - high = ReadReg(iobase, RX_C_H); - wTmp1 = high; - wTmp = (wTmp1 << 8) | low; - udelay(10); - low = ReadReg(iobase, RX_C_L); - high = ReadReg(iobase, RX_C_H); - wTmp1 = high; - wTmp_new = (wTmp1 << 8) | low; - if (wTmp_new != wTmp) - return 1; - else - return 0; - -} - -static __u16 RxCurCount(__u16 iobase, struct via_ircc_cb * self) -{ - __u8 low, high; - __u16 wTmp = 0, wTmp1 = 0; - - low = ReadReg(iobase, RX_P_L); - high = ReadReg(iobase, RX_P_H); - wTmp1 = high; - wTmp = (wTmp1 << 8) | low; - return wTmp; -} - -/* This Routine can only use in recevie_complete - * for it will update last count. - */ - -static __u16 GetRecvByte(__u16 iobase, struct via_ircc_cb * self) -{ - __u8 low, high; - __u16 wTmp, wTmp1, ret; - - low = ReadReg(iobase, RX_P_L); - high = ReadReg(iobase, RX_P_H); - wTmp1 = high; - wTmp = (wTmp1 << 8) | low; - - - if (wTmp >= self->RxLastCount) - ret = wTmp - self->RxLastCount; - else - ret = (0x8000 - self->RxLastCount) + wTmp; - self->RxLastCount = wTmp; - -/* RX_P is more actually the RX_C - low=ReadReg(iobase,RX_C_L); - high=ReadReg(iobase,RX_C_H); - - if(!(high&0xe000)) { - temp=(high<<8)+low; - return temp; - } - else return 0; -*/ - return ret; -} - -static void Sdelay(__u16 scale) -{ - __u8 bTmp; - int i, j; - - for (j = 0; j < scale; j++) { - for (i = 0; i < 0x20; i++) { - bTmp = inb(0xeb); - outb(bTmp, 0xeb); - } - } -} - -static void Tdelay(__u16 scale) -{ - __u8 bTmp; - int i, j; - - for (j = 0; j < scale; j++) { - for (i = 0; i < 0x50; i++) { - bTmp = inb(0xeb); - outb(bTmp, 0xeb); - } - } -} - - -static void ActClk(__u16 iobase, __u8 value) -{ - __u8 bTmp; - bTmp = ReadReg(iobase, 0x34); - if (value) - WriteReg(iobase, 0x34, bTmp | Clk_bit); - else - WriteReg(iobase, 0x34, bTmp & ~Clk_bit); -} - -static void ClkTx(__u16 iobase, __u8 Clk, __u8 Tx) -{ - __u8 bTmp; - - bTmp = ReadReg(iobase, 0x34); - if (Clk == 0) - bTmp &= ~Clk_bit; - else { - if (Clk == 1) - bTmp |= Clk_bit; - } - WriteReg(iobase, 0x34, bTmp); - Sdelay(1); - if (Tx == 0) - bTmp &= ~Tx_bit; - else { - if (Tx == 1) - bTmp |= Tx_bit; - } - WriteReg(iobase, 0x34, bTmp); -} - -static void Wr_Byte(__u16 iobase, __u8 data) -{ - __u8 bData = data; -// __u8 btmp; - int i; - - ClkTx(iobase, 0, 1); - - Tdelay(2); - ActClk(iobase, 1); - Tdelay(1); - - for (i = 0; i < 8; i++) { //LDN - - if ((bData >> i) & 0x01) { - ClkTx(iobase, 0, 1); //bit data = 1; - } else { - ClkTx(iobase, 0, 0); //bit data = 1; - } - Tdelay(2); - Sdelay(1); - ActClk(iobase, 1); //clk hi - Tdelay(1); - } -} - -static __u8 Rd_Indx(__u16 iobase, __u8 addr, __u8 index) -{ - __u8 data = 0, bTmp, data_bit; - int i; - - bTmp = addr | (index << 1) | 0; - ClkTx(iobase, 0, 0); - Tdelay(2); - ActClk(iobase, 1); - udelay(1); - Wr_Byte(iobase, bTmp); - Sdelay(1); - ClkTx(iobase, 0, 0); - Tdelay(2); - for (i = 0; i < 10; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(1); - ClkTx(iobase, 0, 1); - Tdelay(1); - bTmp = ReadReg(iobase, 0x34); - if (!(bTmp & Rd_Valid)) - break; - } - if (!(bTmp & Rd_Valid)) { - for (i = 0; i < 8; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - bTmp = ReadReg(iobase, 0x34); - data_bit = 1 << i; - if (bTmp & RxBit) - data |= data_bit; - else - data &= ~data_bit; - Tdelay(2); - } - } else { - for (i = 0; i < 2; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(2); - } - bTmp = ReadReg(iobase, 0x34); - } - for (i = 0; i < 1; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(2); - } - ClkTx(iobase, 0, 0); - Tdelay(1); - for (i = 0; i < 3; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(2); - } - return data; -} - -static void Wr_Indx(__u16 iobase, __u8 addr, __u8 index, __u8 data) -{ - int i; - __u8 bTmp; - - ClkTx(iobase, 0, 0); - udelay(2); - ActClk(iobase, 1); - udelay(1); - bTmp = addr | (index << 1) | 1; - Wr_Byte(iobase, bTmp); - Wr_Byte(iobase, data); - for (i = 0; i < 2; i++) { - ClkTx(iobase, 0, 0); - Tdelay(2); - ActClk(iobase, 1); - Tdelay(1); - } - ActClk(iobase, 0); -} - -static void ResetDongle(__u16 iobase) -{ - int i; - ClkTx(iobase, 0, 0); - Tdelay(1); - for (i = 0; i < 30; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(1); - } - ActClk(iobase, 0); -} - -static void SetSITmode(__u16 iobase) -{ - - __u8 bTmp; - - bTmp = ReadLPCReg(0x28); - WriteLPCReg(0x28, bTmp | 0x10); //select ITMOFF - bTmp = ReadReg(iobase, 0x35); - WriteReg(iobase, 0x35, bTmp | 0x40); // Driver ITMOFF - WriteReg(iobase, 0x28, bTmp | 0x80); // enable All interrupt -} - -static void SI_SetMode(__u16 iobase, int mode) -{ - //__u32 dTmp; - __u8 bTmp; - - WriteLPCReg(0x28, 0x70); // S/W Reset - SetSITmode(iobase); - ResetDongle(iobase); - udelay(10); - Wr_Indx(iobase, 0x40, 0x0, 0x17); //RX ,APEN enable,Normal power - Wr_Indx(iobase, 0x40, 0x1, mode); //Set Mode - Wr_Indx(iobase, 0x40, 0x2, 0xff); //Set power to FIR VFIR > 1m - bTmp = Rd_Indx(iobase, 0x40, 1); -} - -static void InitCard(__u16 iobase) -{ - ResetChip(iobase, 5); - WriteReg(iobase, I_ST_CT_0, 0x00); // open CHIP on - SetSIRBOF(iobase, 0xc0); // hardware default value - SetSIREOF(iobase, 0xc1); -} - -static void CommonInit(__u16 iobase) -{ -// EnTXCRC(iobase,0); - SwapDMA(iobase, OFF); - SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095 - EnRXFIFOReadyInt(iobase, OFF); - EnRXFIFOHalfLevelInt(iobase, OFF); - EnTXFIFOHalfLevelInt(iobase, OFF); - EnTXFIFOUnderrunEOMInt(iobase, ON); -// EnTXFIFOReadyInt(iobase,ON); - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); -// WriteLPCReg(0xF0,0); //(if VT1211 then do this) - if (IsSIROn(iobase)) { - SIRFilter(iobase, ON); - SIRRecvAny(iobase, ON); - } else { - SIRFilter(iobase, OFF); - SIRRecvAny(iobase, OFF); - } - EnRXSpecInt(iobase, ON); - WriteReg(iobase, I_ST_CT_0, 0x80); - EnableDMA(iobase, ON); -} - -static void SetBaudRate(__u16 iobase, __u32 rate) -{ - __u8 value = 11, temp; - - if (IsSIROn(iobase)) { - switch (rate) { - case (__u32) (2400L): - value = 47; - break; - case (__u32) (9600L): - value = 11; - break; - case (__u32) (19200L): - value = 5; - break; - case (__u32) (38400L): - value = 2; - break; - case (__u32) (57600L): - value = 1; - break; - case (__u32) (115200L): - value = 0; - break; - default: - break; - } - } else if (IsMIROn(iobase)) { - value = 0; // will automatically be fixed in 1.152M - } else if (IsFIROn(iobase)) { - value = 0; // will automatically be fixed in 4M - } - temp = (ReadReg(iobase, I_CF_H_1) & 0x03); - temp |= value << 2; - WriteReg(iobase, I_CF_H_1, temp); -} - -static void SetPulseWidth(__u16 iobase, __u8 width) -{ - __u8 temp, temp1, temp2; - - temp = (ReadReg(iobase, I_CF_L_1) & 0x1f); - temp1 = (ReadReg(iobase, I_CF_H_1) & 0xfc); - temp2 = (width & 0x07) << 5; - temp |= temp2; - temp2 = (width & 0x18) >> 3; - temp1 |= temp2; - WriteReg(iobase, I_CF_L_1, temp); - WriteReg(iobase, I_CF_H_1, temp1); -} - -static void SetSendPreambleCount(__u16 iobase, __u8 count) -{ - __u8 temp; - - temp = ReadReg(iobase, I_CF_L_1) & 0xe0; - temp |= count; - WriteReg(iobase, I_CF_L_1, temp); - -} - -static void SetVFIR(__u16 BaseAddr, __u8 val) -{ - __u8 tmp; - - tmp = ReadReg(BaseAddr, I_CF_L_0); - WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); - WriteRegBit(BaseAddr, I_CF_H_0, 5, val); -} - -static void SetFIR(__u16 BaseAddr, __u8 val) -{ - __u8 tmp; - - WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); - tmp = ReadReg(BaseAddr, I_CF_L_0); - WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); - WriteRegBit(BaseAddr, I_CF_L_0, 6, val); -} - -static void SetMIR(__u16 BaseAddr, __u8 val) -{ - __u8 tmp; - - WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); - tmp = ReadReg(BaseAddr, I_CF_L_0); - WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); - WriteRegBit(BaseAddr, I_CF_L_0, 5, val); -} - -static void SetSIR(__u16 BaseAddr, __u8 val) -{ - __u8 tmp; - - WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); - tmp = ReadReg(BaseAddr, I_CF_L_0); - WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); - WriteRegBit(BaseAddr, I_CF_L_0, 4, val); -} - -#endif /* via_IRCC_H */ diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c deleted file mode 100644 index 6638784c082e..000000000000 --- a/drivers/net/irda/vlsi_ir.c +++ /dev/null @@ -1,1872 +0,0 @@ -/********************************************************************* - * - * vlsi_ir.c: VLSI82C147 PCI IrDA controller driver for Linux - * - * Copyright (c) 2001-2003 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include - -#define DRIVER_NAME "vlsi_ir" -#define DRIVER_VERSION "v0.5" -#define DRIVER_DESCRIPTION "IrDA SIR/MIR/FIR driver for VLSI 82C147" -#define DRIVER_AUTHOR "Martin Diehl " - -MODULE_DESCRIPTION(DRIVER_DESCRIPTION); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_LICENSE("GPL"); - -/********************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "vlsi_ir.h" - -/********************************************************/ - -static /* const */ char drivername[] = DRIVER_NAME; - -static const struct pci_device_id vlsi_irda_table[] = { - { - .class = PCI_CLASS_WIRELESS_IRDA << 8, - .class_mask = PCI_CLASS_SUBCLASS_MASK << 8, - .vendor = PCI_VENDOR_ID_VLSI, - .device = PCI_DEVICE_ID_VLSI_82C147, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { /* all zeroes */ } -}; - -MODULE_DEVICE_TABLE(pci, vlsi_irda_table); - -/********************************************************/ - -/* clksrc: which clock source to be used - * 0: auto - try PLL, fallback to 40MHz XCLK - * 1: on-chip 48MHz PLL - * 2: external 48MHz XCLK - * 3: external 40MHz XCLK (HP OB-800) - */ - -static int clksrc = 0; /* default is 0(auto) */ -module_param(clksrc, int, 0); -MODULE_PARM_DESC(clksrc, "clock input source selection"); - -/* ringsize: size of the tx and rx descriptor rings - * independent for tx and rx - * specify as ringsize=tx[,rx] - * allowed values: 4, 8, 16, 32, 64 - * Due to the IrDA 1.x max. allowed window size=7, - * there should be no gain when using rings larger than 8 - */ - -static int ringsize[] = {8,8}; /* default is tx=8 / rx=8 */ -module_param_array(ringsize, int, NULL, 0); -MODULE_PARM_DESC(ringsize, "TX, RX ring descriptor size"); - -/* sirpulse: tuning of the SIR pulse width within IrPHY 1.3 limits - * 0: very short, 1.5us (exception: 6us at 2.4 kbaud) - * 1: nominal 3/16 bittime width - * note: IrDA compliant peer devices should be happy regardless - * which one is used. Primary goal is to save some power - * on the sender's side - at 9.6kbaud for example the short - * pulse width saves more than 90% of the transmitted IR power. - */ - -static int sirpulse = 1; /* default is 3/16 bittime */ -module_param(sirpulse, int, 0); -MODULE_PARM_DESC(sirpulse, "SIR pulse width tuning"); - -/* qos_mtt_bits: encoded min-turn-time value we require the peer device - * to use before transmitting to us. "Type 1" (per-station) - * bitfield according to IrLAP definition (section 6.6.8) - * Don't know which transceiver is used by my OB800 - the - * pretty common HP HDLS-1100 requires 1 msec - so lets use this. - */ - -static int qos_mtt_bits = 0x07; /* default is 1 ms or more */ -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "IrLAP bitfield representing min-turn-time"); - -/********************************************************/ - -static void vlsi_reg_debug(unsigned iobase, const char *s) -{ - int i; - - printk(KERN_DEBUG "%s: ", s); - for (i = 0; i < 0x20; i++) - printk("%02x", (unsigned)inb((iobase+i))); - printk("\n"); -} - -static void vlsi_ring_debug(struct vlsi_ring *r) -{ - struct ring_descr *rd; - unsigned i; - - printk(KERN_DEBUG "%s - ring %p / size %u / mask 0x%04x / len %u / dir %d / hw %p\n", - __func__, r, r->size, r->mask, r->len, r->dir, r->rd[0].hw); - printk(KERN_DEBUG "%s - head = %d / tail = %d\n", __func__, - atomic_read(&r->head) & r->mask, atomic_read(&r->tail) & r->mask); - for (i = 0; i < r->size; i++) { - rd = &r->rd[i]; - printk(KERN_DEBUG "%s - ring descr %u: ", __func__, i); - printk("skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw); - printk(KERN_DEBUG "%s - hw: status=%02x count=%u addr=0x%08x\n", - __func__, (unsigned) rd_get_status(rd), - (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd)); - } -} - -/********************************************************/ - -/* needed regardless of CONFIG_PROC_FS */ -static struct proc_dir_entry *vlsi_proc_root = NULL; - -#ifdef CONFIG_PROC_FS - -static void vlsi_proc_pdev(struct seq_file *seq, struct pci_dev *pdev) -{ - unsigned iobase = pci_resource_start(pdev, 0); - unsigned i; - - seq_printf(seq, "\n%s (vid/did: [%04x:%04x])\n", - pci_name(pdev), (int)pdev->vendor, (int)pdev->device); - seq_printf(seq, "pci-power-state: %u\n", (unsigned) pdev->current_state); - seq_printf(seq, "resources: irq=%u / io=0x%04x / dma_mask=0x%016Lx\n", - pdev->irq, (unsigned)pci_resource_start(pdev, 0), (unsigned long long)pdev->dma_mask); - seq_printf(seq, "hw registers: "); - for (i = 0; i < 0x20; i++) - seq_printf(seq, "%02x", (unsigned)inb((iobase+i))); - seq_printf(seq, "\n"); -} - -static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - u8 byte; - u16 word; - s32 sec, usec; - unsigned iobase = ndev->base_addr; - - seq_printf(seq, "\n%s link state: %s / %s / %s / %s\n", ndev->name, - netif_device_present(ndev) ? "attached" : "detached", - netif_running(ndev) ? "running" : "not running", - netif_carrier_ok(ndev) ? "carrier ok" : "no carrier", - netif_queue_stopped(ndev) ? "queue stopped" : "queue running"); - - if (!netif_running(ndev)) - return; - - seq_printf(seq, "\nhw-state:\n"); - pci_read_config_byte(idev->pdev, VLSI_PCI_IRMISC, &byte); - seq_printf(seq, "IRMISC:%s%s%s uart%s", - (byte&IRMISC_IRRAIL) ? " irrail" : "", - (byte&IRMISC_IRPD) ? " irpd" : "", - (byte&IRMISC_UARTTST) ? " uarttest" : "", - (byte&IRMISC_UARTEN) ? "@" : " disabled\n"); - if (byte&IRMISC_UARTEN) { - seq_printf(seq, "0x%s\n", - (byte&2) ? ((byte&1) ? "3e8" : "2e8") - : ((byte&1) ? "3f8" : "2f8")); - } - pci_read_config_byte(idev->pdev, VLSI_PCI_CLKCTL, &byte); - seq_printf(seq, "CLKCTL: PLL %s%s%s / clock %s / wakeup %s\n", - (byte&CLKCTL_PD_INV) ? "powered" : "down", - (byte&CLKCTL_LOCK) ? " locked" : "", - (byte&CLKCTL_EXTCLK) ? ((byte&CLKCTL_XCKSEL)?" / 40 MHz XCLK":" / 48 MHz XCLK") : "", - (byte&CLKCTL_CLKSTP) ? "stopped" : "running", - (byte&CLKCTL_WAKE) ? "enabled" : "disabled"); - pci_read_config_byte(idev->pdev, VLSI_PCI_MSTRPAGE, &byte); - seq_printf(seq, "MSTRPAGE: 0x%02x\n", (unsigned)byte); - - byte = inb(iobase+VLSI_PIO_IRINTR); - seq_printf(seq, "IRINTR:%s%s%s%s%s%s%s%s\n", - (byte&IRINTR_ACTEN) ? " ACTEN" : "", - (byte&IRINTR_RPKTEN) ? " RPKTEN" : "", - (byte&IRINTR_TPKTEN) ? " TPKTEN" : "", - (byte&IRINTR_OE_EN) ? " OE_EN" : "", - (byte&IRINTR_ACTIVITY) ? " ACTIVITY" : "", - (byte&IRINTR_RPKTINT) ? " RPKTINT" : "", - (byte&IRINTR_TPKTINT) ? " TPKTINT" : "", - (byte&IRINTR_OE_INT) ? " OE_INT" : ""); - word = inw(iobase+VLSI_PIO_RINGPTR); - seq_printf(seq, "RINGPTR: rx=%u / tx=%u\n", RINGPTR_GET_RX(word), RINGPTR_GET_TX(word)); - word = inw(iobase+VLSI_PIO_RINGBASE); - seq_printf(seq, "RINGBASE: busmap=0x%08x\n", - ((unsigned)word << 10)|(MSTRPAGE_VALUE<<24)); - word = inw(iobase+VLSI_PIO_RINGSIZE); - seq_printf(seq, "RINGSIZE: rx=%u / tx=%u\n", RINGSIZE_TO_RXSIZE(word), - RINGSIZE_TO_TXSIZE(word)); - - word = inw(iobase+VLSI_PIO_IRCFG); - seq_printf(seq, "IRCFG:%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (word&IRCFG_LOOP) ? " LOOP" : "", - (word&IRCFG_ENTX) ? " ENTX" : "", - (word&IRCFG_ENRX) ? " ENRX" : "", - (word&IRCFG_MSTR) ? " MSTR" : "", - (word&IRCFG_RXANY) ? " RXANY" : "", - (word&IRCFG_CRC16) ? " CRC16" : "", - (word&IRCFG_FIR) ? " FIR" : "", - (word&IRCFG_MIR) ? " MIR" : "", - (word&IRCFG_SIR) ? " SIR" : "", - (word&IRCFG_SIRFILT) ? " SIRFILT" : "", - (word&IRCFG_SIRTEST) ? " SIRTEST" : "", - (word&IRCFG_TXPOL) ? " TXPOL" : "", - (word&IRCFG_RXPOL) ? " RXPOL" : ""); - word = inw(iobase+VLSI_PIO_IRENABLE); - seq_printf(seq, "IRENABLE:%s%s%s%s%s%s%s%s\n", - (word&IRENABLE_PHYANDCLOCK) ? " PHYANDCLOCK" : "", - (word&IRENABLE_CFGER) ? " CFGERR" : "", - (word&IRENABLE_FIR_ON) ? " FIR_ON" : "", - (word&IRENABLE_MIR_ON) ? " MIR_ON" : "", - (word&IRENABLE_SIR_ON) ? " SIR_ON" : "", - (word&IRENABLE_ENTXST) ? " ENTXST" : "", - (word&IRENABLE_ENRXST) ? " ENRXST" : "", - (word&IRENABLE_CRC16_ON) ? " CRC16_ON" : ""); - word = inw(iobase+VLSI_PIO_PHYCTL); - seq_printf(seq, "PHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n", - (unsigned)PHYCTL_TO_BAUD(word), - (unsigned)PHYCTL_TO_PLSWID(word), - (unsigned)PHYCTL_TO_PREAMB(word)); - word = inw(iobase+VLSI_PIO_NPHYCTL); - seq_printf(seq, "NPHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n", - (unsigned)PHYCTL_TO_BAUD(word), - (unsigned)PHYCTL_TO_PLSWID(word), - (unsigned)PHYCTL_TO_PREAMB(word)); - word = inw(iobase+VLSI_PIO_MAXPKT); - seq_printf(seq, "MAXPKT: max. rx packet size = %u\n", word); - word = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - seq_printf(seq, "RCVBCNT: rx-fifo filling level = %u\n", word); - - seq_printf(seq, "\nsw-state:\n"); - seq_printf(seq, "IrPHY setup: %d baud - %s encoding\n", idev->baud, - (idev->mode==IFF_SIR)?"SIR":((idev->mode==IFF_MIR)?"MIR":"FIR")); - sec = div_s64_rem(ktime_us_delta(ktime_get(), idev->last_rx), - USEC_PER_SEC, &usec); - seq_printf(seq, "last rx: %ul.%06u sec\n", sec, usec); - - seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu", - ndev->stats.rx_packets, ndev->stats.rx_bytes, ndev->stats.rx_errors, - ndev->stats.rx_dropped); - seq_printf(seq, " / overrun=%lu / length=%lu / frame=%lu / crc=%lu\n", - ndev->stats.rx_over_errors, ndev->stats.rx_length_errors, - ndev->stats.rx_frame_errors, ndev->stats.rx_crc_errors); - seq_printf(seq, "TX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu / fifo=%lu\n", - ndev->stats.tx_packets, ndev->stats.tx_bytes, ndev->stats.tx_errors, - ndev->stats.tx_dropped, ndev->stats.tx_fifo_errors); - -} - -static void vlsi_proc_ring(struct seq_file *seq, struct vlsi_ring *r) -{ - struct ring_descr *rd; - unsigned i, j; - int h, t; - - seq_printf(seq, "size %u / mask 0x%04x / len %u / dir %d / hw %p\n", - r->size, r->mask, r->len, r->dir, r->rd[0].hw); - h = atomic_read(&r->head) & r->mask; - t = atomic_read(&r->tail) & r->mask; - seq_printf(seq, "head = %d / tail = %d ", h, t); - if (h == t) - seq_printf(seq, "(empty)\n"); - else { - if (((t+1)&r->mask) == h) - seq_printf(seq, "(full)\n"); - else - seq_printf(seq, "(level = %d)\n", ((unsigned)(t-h) & r->mask)); - rd = &r->rd[h]; - j = (unsigned) rd_get_count(rd); - seq_printf(seq, "current: rd = %d / status = %02x / len = %u\n", - h, (unsigned)rd_get_status(rd), j); - if (j > 0) { - seq_printf(seq, " data: %*ph\n", - min_t(unsigned, j, 20), rd->buf); - } - } - for (i = 0; i < r->size; i++) { - rd = &r->rd[i]; - seq_printf(seq, "> ring descr %u: ", i); - seq_printf(seq, "skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw); - seq_printf(seq, " hw: status=%02x count=%u busaddr=0x%08x\n", - (unsigned) rd_get_status(rd), - (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd)); - } -} - -static int vlsi_seq_show(struct seq_file *seq, void *v) -{ - struct net_device *ndev = seq->private; - vlsi_irda_dev_t *idev = netdev_priv(ndev); - unsigned long flags; - - seq_printf(seq, "\n%s %s\n\n", DRIVER_NAME, DRIVER_VERSION); - seq_printf(seq, "clksrc: %s\n", - (clksrc>=2) ? ((clksrc==3)?"40MHz XCLK":"48MHz XCLK") - : ((clksrc==1)?"48MHz PLL":"autodetect")); - seq_printf(seq, "ringsize: tx=%d / rx=%d\n", - ringsize[0], ringsize[1]); - seq_printf(seq, "sirpulse: %s\n", (sirpulse)?"3/16 bittime":"short"); - seq_printf(seq, "qos_mtt_bits: 0x%02x\n", (unsigned)qos_mtt_bits); - - spin_lock_irqsave(&idev->lock, flags); - if (idev->pdev != NULL) { - vlsi_proc_pdev(seq, idev->pdev); - - if (idev->pdev->current_state == 0) - vlsi_proc_ndev(seq, ndev); - else - seq_printf(seq, "\nPCI controller down - resume_ok = %d\n", - idev->resume_ok); - if (netif_running(ndev) && idev->rx_ring && idev->tx_ring) { - seq_printf(seq, "\n--------- RX ring -----------\n\n"); - vlsi_proc_ring(seq, idev->rx_ring); - seq_printf(seq, "\n--------- TX ring -----------\n\n"); - vlsi_proc_ring(seq, idev->tx_ring); - } - } - seq_printf(seq, "\n"); - spin_unlock_irqrestore(&idev->lock, flags); - - return 0; -} - -static int vlsi_seq_open(struct inode *inode, struct file *file) -{ - return single_open(file, vlsi_seq_show, PDE_DATA(inode)); -} - -static const struct file_operations vlsi_proc_fops = { - .owner = THIS_MODULE, - .open = vlsi_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -#define VLSI_PROC_FOPS (&vlsi_proc_fops) - -#else /* CONFIG_PROC_FS */ -#define VLSI_PROC_FOPS NULL -#endif - -/********************************************************/ - -static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr_hw *hwmap, - unsigned size, unsigned len, int dir) -{ - struct vlsi_ring *r; - struct ring_descr *rd; - unsigned i, j; - dma_addr_t busaddr; - - if (!size || ((size-1)&size)!=0) /* must be >0 and power of 2 */ - return NULL; - - r = kmalloc(sizeof(*r) + size * sizeof(struct ring_descr), GFP_KERNEL); - if (!r) - return NULL; - memset(r, 0, sizeof(*r)); - - r->pdev = pdev; - r->dir = dir; - r->len = len; - r->rd = (struct ring_descr *)(r+1); - r->mask = size - 1; - r->size = size; - atomic_set(&r->head, 0); - atomic_set(&r->tail, 0); - - for (i = 0; i < size; i++) { - rd = r->rd + i; - memset(rd, 0, sizeof(*rd)); - rd->hw = hwmap + i; - rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA); - if (rd->buf) - busaddr = pci_map_single(pdev, rd->buf, len, dir); - if (rd->buf == NULL || pci_dma_mapping_error(pdev, busaddr)) { - if (rd->buf) { - net_err_ratelimited("%s: failed to create PCI-MAP for %p\n", - __func__, rd->buf); - kfree(rd->buf); - rd->buf = NULL; - } - for (j = 0; j < i; j++) { - rd = r->rd + j; - busaddr = rd_get_addr(rd); - rd_set_addr_status(rd, 0, 0); - pci_unmap_single(pdev, busaddr, len, dir); - kfree(rd->buf); - rd->buf = NULL; - } - kfree(r); - return NULL; - } - rd_set_addr_status(rd, busaddr, 0); - /* initially, the dma buffer is owned by the CPU */ - rd->skb = NULL; - } - return r; -} - -static int vlsi_free_ring(struct vlsi_ring *r) -{ - struct ring_descr *rd; - unsigned i; - dma_addr_t busaddr; - - for (i = 0; i < r->size; i++) { - rd = r->rd + i; - if (rd->skb) - dev_kfree_skb_any(rd->skb); - busaddr = rd_get_addr(rd); - rd_set_addr_status(rd, 0, 0); - if (busaddr) - pci_unmap_single(r->pdev, busaddr, r->len, r->dir); - kfree(rd->buf); - } - kfree(r); - return 0; -} - -static int vlsi_create_hwif(vlsi_irda_dev_t *idev) -{ - char *ringarea; - struct ring_descr_hw *hwmap; - - idev->virtaddr = NULL; - idev->busaddr = 0; - - ringarea = pci_zalloc_consistent(idev->pdev, HW_RING_AREA_SIZE, - &idev->busaddr); - if (!ringarea) - goto out; - - hwmap = (struct ring_descr_hw *)ringarea; - idev->rx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[1], - XFER_BUF_SIZE, PCI_DMA_FROMDEVICE); - if (idev->rx_ring == NULL) - goto out_unmap; - - hwmap += MAX_RING_DESCR; - idev->tx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[0], - XFER_BUF_SIZE, PCI_DMA_TODEVICE); - if (idev->tx_ring == NULL) - goto out_free_rx; - - idev->virtaddr = ringarea; - return 0; - -out_free_rx: - vlsi_free_ring(idev->rx_ring); -out_unmap: - idev->rx_ring = idev->tx_ring = NULL; - pci_free_consistent(idev->pdev, HW_RING_AREA_SIZE, ringarea, idev->busaddr); - idev->busaddr = 0; -out: - return -ENOMEM; -} - -static int vlsi_destroy_hwif(vlsi_irda_dev_t *idev) -{ - vlsi_free_ring(idev->rx_ring); - vlsi_free_ring(idev->tx_ring); - idev->rx_ring = idev->tx_ring = NULL; - - if (idev->busaddr) - pci_free_consistent(idev->pdev,HW_RING_AREA_SIZE,idev->virtaddr,idev->busaddr); - - idev->virtaddr = NULL; - idev->busaddr = 0; - - return 0; -} - -/********************************************************/ - -static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd) -{ - u16 status; - int crclen, len = 0; - struct sk_buff *skb; - int ret = 0; - struct net_device *ndev = pci_get_drvdata(r->pdev); - vlsi_irda_dev_t *idev = netdev_priv(ndev); - - pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); - /* dma buffer now owned by the CPU */ - status = rd_get_status(rd); - if (status & RD_RX_ERROR) { - if (status & RD_RX_OVER) - ret |= VLSI_RX_OVER; - if (status & RD_RX_LENGTH) - ret |= VLSI_RX_LENGTH; - if (status & RD_RX_PHYERR) - ret |= VLSI_RX_FRAME; - if (status & RD_RX_CRCERR) - ret |= VLSI_RX_CRC; - goto done; - } - - len = rd_get_count(rd); - crclen = (idev->mode==IFF_FIR) ? sizeof(u32) : sizeof(u16); - len -= crclen; /* remove trailing CRC */ - if (len <= 0) { - pr_debug("%s: strange frame (len=%d)\n", __func__, len); - ret |= VLSI_RX_DROP; - goto done; - } - - if (idev->mode == IFF_SIR) { /* hw checks CRC in MIR, FIR mode */ - - /* rd->buf is a streaming PCI_DMA_FROMDEVICE map. Doing the - * endian-adjustment there just in place will dirty a cache line - * which belongs to the map and thus we must be sure it will - * get flushed before giving the buffer back to hardware. - * vlsi_fill_rx() will do this anyway - but here we rely on. - */ - le16_to_cpus(rd->buf+len); - if (irda_calc_crc16(INIT_FCS,rd->buf,len+crclen) != GOOD_FCS) { - pr_debug("%s: crc error\n", __func__); - ret |= VLSI_RX_CRC; - goto done; - } - } - - if (!rd->skb) { - net_warn_ratelimited("%s: rx packet lost\n", __func__); - ret |= VLSI_RX_DROP; - goto done; - } - - skb = rd->skb; - rd->skb = NULL; - skb->dev = ndev; - skb_put_data(skb, rd->buf, len); - skb_reset_mac_header(skb); - if (in_interrupt()) - netif_rx(skb); - else - netif_rx_ni(skb); - -done: - rd_set_status(rd, 0); - rd_set_count(rd, 0); - /* buffer still owned by CPU */ - - return (ret) ? -ret : len; -} - -static void vlsi_fill_rx(struct vlsi_ring *r) -{ - struct ring_descr *rd; - - for (rd = ring_last(r); rd != NULL; rd = ring_put(r)) { - if (rd_is_active(rd)) { - net_warn_ratelimited("%s: driver bug: rx descr race with hw\n", - __func__); - vlsi_ring_debug(r); - break; - } - if (!rd->skb) { - rd->skb = dev_alloc_skb(IRLAP_SKB_ALLOCSIZE); - if (rd->skb) { - skb_reserve(rd->skb,1); - rd->skb->protocol = htons(ETH_P_IRDA); - } - else - break; /* probably not worth logging? */ - } - /* give dma buffer back to busmaster */ - pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); - rd_activate(rd); - } -} - -static void vlsi_rx_interrupt(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct vlsi_ring *r = idev->rx_ring; - struct ring_descr *rd; - int ret; - - for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { - - if (rd_is_active(rd)) - break; - - ret = vlsi_process_rx(r, rd); - - if (ret < 0) { - ret = -ret; - ndev->stats.rx_errors++; - if (ret & VLSI_RX_DROP) - ndev->stats.rx_dropped++; - if (ret & VLSI_RX_OVER) - ndev->stats.rx_over_errors++; - if (ret & VLSI_RX_LENGTH) - ndev->stats.rx_length_errors++; - if (ret & VLSI_RX_FRAME) - ndev->stats.rx_frame_errors++; - if (ret & VLSI_RX_CRC) - ndev->stats.rx_crc_errors++; - } - else if (ret > 0) { - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += ret; - } - } - - idev->last_rx = ktime_get(); /* remember "now" for later mtt delay */ - - vlsi_fill_rx(r); - - if (ring_first(r) == NULL) { - /* we are in big trouble, if this should ever happen */ - net_err_ratelimited("%s: rx ring exhausted!\n", __func__); - vlsi_ring_debug(r); - } - else - outw(0, ndev->base_addr+VLSI_PIO_PROMPT); -} - -/* caller must have stopped the controller from busmastering */ - -static void vlsi_unarm_rx(vlsi_irda_dev_t *idev) -{ - struct net_device *ndev = pci_get_drvdata(idev->pdev); - struct vlsi_ring *r = idev->rx_ring; - struct ring_descr *rd; - int ret; - - for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { - - ret = 0; - if (rd_is_active(rd)) { - rd_set_status(rd, 0); - if (rd_get_count(rd)) { - pr_debug("%s - dropping rx packet\n", __func__); - ret = -VLSI_RX_DROP; - } - rd_set_count(rd, 0); - pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); - if (rd->skb) { - dev_kfree_skb_any(rd->skb); - rd->skb = NULL; - } - } - else - ret = vlsi_process_rx(r, rd); - - if (ret < 0) { - ret = -ret; - ndev->stats.rx_errors++; - if (ret & VLSI_RX_DROP) - ndev->stats.rx_dropped++; - if (ret & VLSI_RX_OVER) - ndev->stats.rx_over_errors++; - if (ret & VLSI_RX_LENGTH) - ndev->stats.rx_length_errors++; - if (ret & VLSI_RX_FRAME) - ndev->stats.rx_frame_errors++; - if (ret & VLSI_RX_CRC) - ndev->stats.rx_crc_errors++; - } - else if (ret > 0) { - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += ret; - } - } -} - -/********************************************************/ - -static int vlsi_process_tx(struct vlsi_ring *r, struct ring_descr *rd) -{ - u16 status; - int len; - int ret; - - pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); - /* dma buffer now owned by the CPU */ - status = rd_get_status(rd); - if (status & RD_TX_UNDRN) - ret = VLSI_TX_FIFO; - else - ret = 0; - rd_set_status(rd, 0); - - if (rd->skb) { - len = rd->skb->len; - dev_kfree_skb_any(rd->skb); - rd->skb = NULL; - } - else /* tx-skb already freed? - should never happen */ - len = rd_get_count(rd); /* incorrect for SIR! (due to wrapping) */ - - rd_set_count(rd, 0); - /* dma buffer still owned by the CPU */ - - return (ret) ? -ret : len; -} - -static int vlsi_set_baud(vlsi_irda_dev_t *idev, unsigned iobase) -{ - u16 nphyctl; - u16 config; - unsigned mode; - int ret; - int baudrate; - int fifocnt; - - baudrate = idev->new_baud; - pr_debug("%s: %d -> %d\n", __func__, idev->baud, idev->new_baud); - if (baudrate == 4000000) { - mode = IFF_FIR; - config = IRCFG_FIR; - nphyctl = PHYCTL_FIR; - } - else if (baudrate == 1152000) { - mode = IFF_MIR; - config = IRCFG_MIR | IRCFG_CRC16; - nphyctl = PHYCTL_MIR(clksrc==3); - } - else { - mode = IFF_SIR; - config = IRCFG_SIR | IRCFG_SIRFILT | IRCFG_RXANY; - switch(baudrate) { - default: - net_warn_ratelimited("%s: undefined baudrate %d - fallback to 9600!\n", - __func__, baudrate); - baudrate = 9600; - /* fallthru */ - case 2400: - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: - nphyctl = PHYCTL_SIR(baudrate,sirpulse,clksrc==3); - break; - } - } - config |= IRCFG_MSTR | IRCFG_ENRX; - - fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - if (fifocnt != 0) { - pr_debug("%s: rx fifo not empty(%d)\n", __func__, fifocnt); - } - - outw(0, iobase+VLSI_PIO_IRENABLE); - outw(config, iobase+VLSI_PIO_IRCFG); - outw(nphyctl, iobase+VLSI_PIO_NPHYCTL); - wmb(); - outw(IRENABLE_PHYANDCLOCK, iobase+VLSI_PIO_IRENABLE); - mb(); - - udelay(1); /* chip applies IRCFG on next rising edge of its 8MHz clock */ - - /* read back settings for validation */ - - config = inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_MASK; - - if (mode == IFF_FIR) - config ^= IRENABLE_FIR_ON; - else if (mode == IFF_MIR) - config ^= (IRENABLE_MIR_ON|IRENABLE_CRC16_ON); - else - config ^= IRENABLE_SIR_ON; - - if (config != (IRENABLE_PHYANDCLOCK|IRENABLE_ENRXST)) { - net_warn_ratelimited("%s: failed to set %s mode!\n", - __func__, - mode == IFF_SIR ? "SIR" : - mode == IFF_MIR ? "MIR" : "FIR"); - ret = -1; - } - else { - if (inw(iobase+VLSI_PIO_PHYCTL) != nphyctl) { - net_warn_ratelimited("%s: failed to apply baudrate %d\n", - __func__, baudrate); - ret = -1; - } - else { - idev->mode = mode; - idev->baud = baudrate; - idev->new_baud = 0; - ret = 0; - } - } - - if (ret) - vlsi_reg_debug(iobase,__func__); - - return ret; -} - -static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct vlsi_ring *r = idev->tx_ring; - struct ring_descr *rd; - unsigned long flags; - unsigned iobase = ndev->base_addr; - u8 status; - u16 config; - int mtt, diff; - int len, speed; - char *msg = NULL; - - speed = irda_get_next_speed(skb); - spin_lock_irqsave(&idev->lock, flags); - if (speed != -1 && speed != idev->baud) { - netif_stop_queue(ndev); - idev->new_baud = speed; - status = RD_TX_CLRENTX; /* stop tx-ring after this frame */ - } - else - status = 0; - - if (skb->len == 0) { - /* handle zero packets - should be speed change */ - if (status == 0) { - msg = "bogus zero-length packet"; - goto drop_unlock; - } - - /* due to the completely asynch tx operation we might have - * IrLAP racing with the hardware here, f.e. if the controller - * is just sending the last packet with current speed while - * the LAP is already switching the speed using synchronous - * len=0 packet. Immediate execution would lead to hw lockup - * requiring a powercycle to reset. Good candidate to trigger - * this is the final UA:RSP packet after receiving a DISC:CMD - * when getting the LAP down. - * Note that we are not protected by the queue_stop approach - * because the final UA:RSP arrives _without_ request to apply - * new-speed-after-this-packet - hence the driver doesn't know - * this was the last packet and doesn't stop the queue. So the - * forced switch to default speed from LAP gets through as fast - * as only some 10 usec later while the UA:RSP is still processed - * by the hardware and we would get screwed. - */ - - if (ring_first(idev->tx_ring) == NULL) { - /* no race - tx-ring already empty */ - vlsi_set_baud(idev, iobase); - netif_wake_queue(ndev); - } - else - ; - /* keep the speed change pending like it would - * for any len>0 packet. tx completion interrupt - * will apply it when the tx ring becomes empty. - */ - spin_unlock_irqrestore(&idev->lock, flags); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - - /* sanity checks - simply drop the packet */ - - rd = ring_last(r); - if (!rd) { - msg = "ring full, but queue wasn't stopped"; - goto drop_unlock; - } - - if (rd_is_active(rd)) { - msg = "entry still owned by hw"; - goto drop_unlock; - } - - if (!rd->buf) { - msg = "tx ring entry without pci buffer"; - goto drop_unlock; - } - - if (rd->skb) { - msg = "ring entry with old skb still attached"; - goto drop_unlock; - } - - /* no need for serialization or interrupt disable during mtt */ - spin_unlock_irqrestore(&idev->lock, flags); - - if ((mtt = irda_get_mtt(skb)) > 0) { - diff = ktime_us_delta(ktime_get(), idev->last_rx); - if (mtt > diff) - udelay(mtt - diff); - /* must not sleep here - called under netif_tx_lock! */ - } - - /* tx buffer already owned by CPU due to pci_dma_sync_single_for_cpu() - * after subsequent tx-completion - */ - - if (idev->mode == IFF_SIR) { - status |= RD_TX_DISCRC; /* no hw-crc creation */ - len = async_wrap_skb(skb, rd->buf, r->len); - - /* Some rare worst case situation in SIR mode might lead to - * potential buffer overflow. The wrapper detects this, returns - * with a shortened frame (without FCS/EOF) but doesn't provide - * any error indication about the invalid packet which we are - * going to transmit. - * Therefore we log if the buffer got filled to the point, where the - * wrapper would abort, i.e. when there are less than 5 bytes left to - * allow appending the FCS/EOF. - */ - - if (len >= r->len-5) - net_warn_ratelimited("%s: possible buffer overflow with SIR wrapping!\n", - __func__); - } - else { - /* hw deals with MIR/FIR mode wrapping */ - status |= RD_TX_PULSE; /* send 2 us highspeed indication pulse */ - len = skb->len; - if (len > r->len) { - msg = "frame exceeds tx buffer length"; - goto drop; - } - else - skb_copy_from_linear_data(skb, rd->buf, len); - } - - rd->skb = skb; /* remember skb for tx-complete stats */ - - rd_set_count(rd, len); - rd_set_status(rd, status); /* not yet active! */ - - /* give dma buffer back to busmaster-hw (flush caches to make - * CPU-driven changes visible from the pci bus). - */ - - pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); - -/* Switching to TX mode here races with the controller - * which may stop TX at any time when fetching an inactive descriptor - * or one with CLR_ENTX set. So we switch on TX only, if TX was not running - * _after_ the new descriptor was activated on the ring. This ensures - * we will either find TX already stopped or we can be sure, there - * will be a TX-complete interrupt even if the chip stopped doing - * TX just after we found it still running. The ISR will then find - * the non-empty ring and restart TX processing. The enclosing - * spinlock provides the correct serialization to prevent race with isr. - */ - - spin_lock_irqsave(&idev->lock,flags); - - rd_activate(rd); - - if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) { - int fifocnt; - - fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - if (fifocnt != 0) { - pr_debug("%s: rx fifo not empty(%d)\n", - __func__, fifocnt); - } - - config = inw(iobase+VLSI_PIO_IRCFG); - mb(); - outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG); - wmb(); - outw(0, iobase+VLSI_PIO_PROMPT); - } - - if (ring_put(r) == NULL) { - netif_stop_queue(ndev); - pr_debug("%s: tx ring full - queue stopped\n", __func__); - } - spin_unlock_irqrestore(&idev->lock, flags); - - return NETDEV_TX_OK; - -drop_unlock: - spin_unlock_irqrestore(&idev->lock, flags); -drop: - net_warn_ratelimited("%s: dropping packet - %s\n", __func__, msg); - dev_kfree_skb_any(skb); - ndev->stats.tx_errors++; - ndev->stats.tx_dropped++; - /* Don't even think about returning NET_XMIT_DROP (=1) here! - * In fact any retval!=0 causes the packet scheduler to requeue the - * packet for later retry of transmission - which isn't exactly - * what we want after we've just called dev_kfree_skb_any ;-) - */ - return NETDEV_TX_OK; -} - -static void vlsi_tx_interrupt(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct vlsi_ring *r = idev->tx_ring; - struct ring_descr *rd; - unsigned iobase; - int ret; - u16 config; - - for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { - - if (rd_is_active(rd)) - break; - - ret = vlsi_process_tx(r, rd); - - if (ret < 0) { - ret = -ret; - ndev->stats.tx_errors++; - if (ret & VLSI_TX_DROP) - ndev->stats.tx_dropped++; - if (ret & VLSI_TX_FIFO) - ndev->stats.tx_fifo_errors++; - } - else if (ret > 0){ - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += ret; - } - } - - iobase = ndev->base_addr; - - if (idev->new_baud && rd == NULL) /* tx ring empty and speed change pending */ - vlsi_set_baud(idev, iobase); - - config = inw(iobase+VLSI_PIO_IRCFG); - if (rd == NULL) /* tx ring empty: re-enable rx */ - outw((config & ~IRCFG_ENTX) | IRCFG_ENRX, iobase+VLSI_PIO_IRCFG); - - else if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) { - int fifocnt; - - fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - if (fifocnt != 0) { - pr_debug("%s: rx fifo not empty(%d)\n", - __func__, fifocnt); - } - outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG); - } - - outw(0, iobase+VLSI_PIO_PROMPT); - - if (netif_queue_stopped(ndev) && !idev->new_baud) { - netif_wake_queue(ndev); - pr_debug("%s: queue awoken\n", __func__); - } -} - -/* caller must have stopped the controller from busmastering */ - -static void vlsi_unarm_tx(vlsi_irda_dev_t *idev) -{ - struct net_device *ndev = pci_get_drvdata(idev->pdev); - struct vlsi_ring *r = idev->tx_ring; - struct ring_descr *rd; - int ret; - - for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { - - ret = 0; - if (rd_is_active(rd)) { - rd_set_status(rd, 0); - rd_set_count(rd, 0); - pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); - if (rd->skb) { - dev_kfree_skb_any(rd->skb); - rd->skb = NULL; - } - pr_debug("%s - dropping tx packet\n", __func__); - ret = -VLSI_TX_DROP; - } - else - ret = vlsi_process_tx(r, rd); - - if (ret < 0) { - ret = -ret; - ndev->stats.tx_errors++; - if (ret & VLSI_TX_DROP) - ndev->stats.tx_dropped++; - if (ret & VLSI_TX_FIFO) - ndev->stats.tx_fifo_errors++; - } - else if (ret > 0){ - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += ret; - } - } - -} - -/********************************************************/ - -static int vlsi_start_clock(struct pci_dev *pdev) -{ - u8 clkctl, lock; - int i, count; - - if (clksrc < 2) { /* auto or PLL: try PLL */ - clkctl = CLKCTL_PD_INV | CLKCTL_CLKSTP; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - - /* procedure to detect PLL lock synchronisation: - * after 0.5 msec initial delay we expect to find 3 PLL lock - * indications within 10 msec for successful PLL detection. - */ - udelay(500); - count = 0; - for (i = 500; i <= 10000; i += 50) { /* max 10 msec */ - pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &lock); - if (lock&CLKCTL_LOCK) { - if (++count >= 3) - break; - } - udelay(50); - } - if (count < 3) { - if (clksrc == 1) { /* explicitly asked for PLL hence bail out */ - net_err_ratelimited("%s: no PLL or failed to lock!\n", - __func__); - clkctl = CLKCTL_CLKSTP; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - return -1; - } - else /* was: clksrc=0(auto) */ - clksrc = 3; /* fallback to 40MHz XCLK (OB800) */ - - pr_debug("%s: PLL not locked, fallback to clksrc=%d\n", - __func__, clksrc); - } - else - clksrc = 1; /* got successful PLL lock */ - } - - if (clksrc != 1) { - /* we get here if either no PLL detected in auto-mode or - an external clock source was explicitly specified */ - - clkctl = CLKCTL_EXTCLK | CLKCTL_CLKSTP; - if (clksrc == 3) - clkctl |= CLKCTL_XCKSEL; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - - /* no way to test for working XCLK */ - } - else - pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); - - /* ok, now going to connect the chip with the clock source */ - - clkctl &= ~CLKCTL_CLKSTP; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - - return 0; -} - -static void vlsi_stop_clock(struct pci_dev *pdev) -{ - u8 clkctl; - - /* disconnect chip from clock source */ - pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); - clkctl |= CLKCTL_CLKSTP; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - - /* disable all clock sources */ - clkctl &= ~(CLKCTL_EXTCLK | CLKCTL_PD_INV); - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); -} - -/********************************************************/ - -/* writing all-zero to the VLSI PCI IO register area seems to prevent - * some occasional situations where the hardware fails (symptoms are - * what appears as stalled tx/rx state machines, i.e. everything ok for - * receive or transmit but hw makes no progress or is unable to access - * the bus memory locations). - * Best place to call this is immediately after/before the internal clock - * gets started/stopped. - */ - -static inline void vlsi_clear_regs(unsigned iobase) -{ - unsigned i; - const unsigned chip_io_extent = 32; - - for (i = 0; i < chip_io_extent; i += sizeof(u16)) - outw(0, iobase + i); -} - -static int vlsi_init_chip(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - vlsi_irda_dev_t *idev = netdev_priv(ndev); - unsigned iobase; - u16 ptr; - - /* start the clock and clean the registers */ - - if (vlsi_start_clock(pdev)) { - net_err_ratelimited("%s: no valid clock source\n", __func__); - return -1; - } - iobase = ndev->base_addr; - vlsi_clear_regs(iobase); - - outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* w/c pending IRQ, disable all INT */ - - outw(0, iobase+VLSI_PIO_IRENABLE); /* disable IrPHY-interface */ - - /* disable everything, particularly IRCFG_MSTR - (also resetting the RING_PTR) */ - - outw(0, iobase+VLSI_PIO_IRCFG); - wmb(); - - outw(MAX_PACKET_LENGTH, iobase+VLSI_PIO_MAXPKT); /* max possible value=0x0fff */ - - outw(BUS_TO_RINGBASE(idev->busaddr), iobase+VLSI_PIO_RINGBASE); - - outw(TX_RX_TO_RINGSIZE(idev->tx_ring->size, idev->rx_ring->size), - iobase+VLSI_PIO_RINGSIZE); - - ptr = inw(iobase+VLSI_PIO_RINGPTR); - atomic_set(&idev->rx_ring->head, RINGPTR_GET_RX(ptr)); - atomic_set(&idev->rx_ring->tail, RINGPTR_GET_RX(ptr)); - atomic_set(&idev->tx_ring->head, RINGPTR_GET_TX(ptr)); - atomic_set(&idev->tx_ring->tail, RINGPTR_GET_TX(ptr)); - - vlsi_set_baud(idev, iobase); /* idev->new_baud used as provided by caller */ - - outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* just in case - w/c pending IRQ's */ - wmb(); - - /* DO NOT BLINDLY ENABLE IRINTR_ACTEN! - * basically every received pulse fires an ACTIVITY-INT - * leading to >>1000 INT's per second instead of few 10 - */ - - outb(IRINTR_RPKTEN|IRINTR_TPKTEN, iobase+VLSI_PIO_IRINTR); - - return 0; -} - -static int vlsi_start_hw(vlsi_irda_dev_t *idev) -{ - struct pci_dev *pdev = idev->pdev; - struct net_device *ndev = pci_get_drvdata(pdev); - unsigned iobase = ndev->base_addr; - u8 byte; - - /* we don't use the legacy UART, disable its address decoding */ - - pci_read_config_byte(pdev, VLSI_PCI_IRMISC, &byte); - byte &= ~(IRMISC_UARTEN | IRMISC_UARTTST); - pci_write_config_byte(pdev, VLSI_PCI_IRMISC, byte); - - /* enable PCI busmaster access to our 16MB page */ - - pci_write_config_byte(pdev, VLSI_PCI_MSTRPAGE, MSTRPAGE_VALUE); - pci_set_master(pdev); - - if (vlsi_init_chip(pdev) < 0) { - pci_disable_device(pdev); - return -1; - } - - vlsi_fill_rx(idev->rx_ring); - - idev->last_rx = ktime_get(); /* first mtt may start from now on */ - - outw(0, iobase+VLSI_PIO_PROMPT); /* kick hw state machine */ - - return 0; -} - -static int vlsi_stop_hw(vlsi_irda_dev_t *idev) -{ - struct pci_dev *pdev = idev->pdev; - struct net_device *ndev = pci_get_drvdata(pdev); - unsigned iobase = ndev->base_addr; - unsigned long flags; - - spin_lock_irqsave(&idev->lock,flags); - outw(0, iobase+VLSI_PIO_IRENABLE); - outw(0, iobase+VLSI_PIO_IRCFG); /* disable everything */ - - /* disable and w/c irqs */ - outb(0, iobase+VLSI_PIO_IRINTR); - wmb(); - outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); - spin_unlock_irqrestore(&idev->lock,flags); - - vlsi_unarm_tx(idev); - vlsi_unarm_rx(idev); - - vlsi_clear_regs(iobase); - vlsi_stop_clock(pdev); - - pci_disable_device(pdev); - - return 0; -} - -/**************************************************************/ - -static void vlsi_tx_timeout(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - - - vlsi_reg_debug(ndev->base_addr, __func__); - vlsi_ring_debug(idev->tx_ring); - - if (netif_running(ndev)) - netif_stop_queue(ndev); - - vlsi_stop_hw(idev); - - /* now simply restart the whole thing */ - - if (!idev->new_baud) - idev->new_baud = idev->baud; /* keep current baudrate */ - - if (vlsi_start_hw(idev)) - net_err_ratelimited("%s: failed to restart hw - %s(%s) unusable!\n", - __func__, pci_name(idev->pdev), ndev->name); - else - netif_start_queue(ndev); -} - -static int vlsi_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct if_irda_req *irq = (struct if_irda_req *) rq; - unsigned long flags; - u16 fifocnt; - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - spin_lock_irqsave(&idev->lock, flags); - idev->new_baud = irq->ifr_baudrate; - /* when called from userland there might be a minor race window here - * if the stack tries to change speed concurrently - which would be - * pretty strange anyway with the userland having full control... - */ - vlsi_set_baud(idev, ndev->base_addr); - spin_unlock_irqrestore(&idev->lock, flags); - break; - case SIOCSMEDIABUSY: - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - irda_device_set_media_busy(ndev, TRUE); - break; - case SIOCGRECEIVING: - /* the best we can do: check whether there are any bytes in rx fifo. - * The trustable window (in case some data arrives just afterwards) - * may be as short as 1usec or so at 4Mbps. - */ - fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - irq->ifr_receiving = (fifocnt!=0) ? 1 : 0; - break; - default: - net_warn_ratelimited("%s: notsupp - cmd=%04x\n", - __func__, cmd); - ret = -EOPNOTSUPP; - } - - return ret; -} - -/********************************************************/ - -static irqreturn_t vlsi_interrupt(int irq, void *dev_instance) -{ - struct net_device *ndev = dev_instance; - vlsi_irda_dev_t *idev = netdev_priv(ndev); - unsigned iobase; - u8 irintr; - int boguscount = 5; - unsigned long flags; - int handled = 0; - - iobase = ndev->base_addr; - spin_lock_irqsave(&idev->lock,flags); - do { - irintr = inb(iobase+VLSI_PIO_IRINTR); - mb(); - outb(irintr, iobase+VLSI_PIO_IRINTR); /* acknowledge asap */ - - if (!(irintr&=IRINTR_INT_MASK)) /* not our INT - probably shared */ - break; - - handled = 1; - - if (unlikely(!(irintr & ~IRINTR_ACTIVITY))) - break; /* nothing todo if only activity */ - - if (irintr&IRINTR_RPKTINT) - vlsi_rx_interrupt(ndev); - - if (irintr&IRINTR_TPKTINT) - vlsi_tx_interrupt(ndev); - - } while (--boguscount > 0); - spin_unlock_irqrestore(&idev->lock,flags); - - if (boguscount <= 0) - net_info_ratelimited("%s: too much work in interrupt!\n", - __func__); - return IRQ_RETVAL(handled); -} - -/********************************************************/ - -static int vlsi_open(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - int err = -EAGAIN; - char hwname[32]; - - if (pci_request_regions(idev->pdev, drivername)) { - net_warn_ratelimited("%s: io resource busy\n", __func__); - goto errout; - } - ndev->base_addr = pci_resource_start(idev->pdev,0); - ndev->irq = idev->pdev->irq; - - /* under some rare occasions the chip apparently comes up with - * IRQ's pending. We better w/c pending IRQ and disable them all - */ - - outb(IRINTR_INT_MASK, ndev->base_addr+VLSI_PIO_IRINTR); - - if (request_irq(ndev->irq, vlsi_interrupt, IRQF_SHARED, - drivername, ndev)) { - net_warn_ratelimited("%s: couldn't get IRQ: %d\n", - __func__, ndev->irq); - goto errout_io; - } - - if ((err = vlsi_create_hwif(idev)) != 0) - goto errout_irq; - - sprintf(hwname, "VLSI-FIR @ 0x%04x", (unsigned)ndev->base_addr); - idev->irlap = irlap_open(ndev,&idev->qos,hwname); - if (!idev->irlap) - goto errout_free_ring; - - idev->last_rx = ktime_get(); /* first mtt may start from now on */ - - idev->new_baud = 9600; /* start with IrPHY using 9600(SIR) mode */ - - if ((err = vlsi_start_hw(idev)) != 0) - goto errout_close_irlap; - - netif_start_queue(ndev); - - net_info_ratelimited("%s: device %s operational\n", - __func__, ndev->name); - - return 0; - -errout_close_irlap: - irlap_close(idev->irlap); -errout_free_ring: - vlsi_destroy_hwif(idev); -errout_irq: - free_irq(ndev->irq,ndev); -errout_io: - pci_release_regions(idev->pdev); -errout: - return err; -} - -static int vlsi_close(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - - netif_stop_queue(ndev); - - if (idev->irlap) - irlap_close(idev->irlap); - idev->irlap = NULL; - - vlsi_stop_hw(idev); - - vlsi_destroy_hwif(idev); - - free_irq(ndev->irq,ndev); - - pci_release_regions(idev->pdev); - - net_info_ratelimited("%s: device %s stopped\n", __func__, ndev->name); - - return 0; -} - -static const struct net_device_ops vlsi_netdev_ops = { - .ndo_open = vlsi_open, - .ndo_stop = vlsi_close, - .ndo_start_xmit = vlsi_hard_start_xmit, - .ndo_do_ioctl = vlsi_ioctl, - .ndo_tx_timeout = vlsi_tx_timeout, -}; - -static int vlsi_irda_init(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct pci_dev *pdev = idev->pdev; - - ndev->irq = pdev->irq; - ndev->base_addr = pci_resource_start(pdev,0); - - /* PCI busmastering - * see include file for details why we need these 2 masks, in this order! - */ - - if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW) || - pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) { - net_err_ratelimited("%s: aborting due to PCI BM-DMA address limitations\n", - __func__); - return -1; - } - - irda_init_max_qos_capabilies(&idev->qos); - - /* the VLSI82C147 does not support 576000! */ - - idev->qos.baud_rate.bits = IR_2400 | IR_9600 - | IR_19200 | IR_38400 | IR_57600 | IR_115200 - | IR_1152000 | (IR_4000000 << 8); - - idev->qos.min_turn_time.bits = qos_mtt_bits; - - irda_qos_bits_to_value(&idev->qos); - - /* currently no public media definitions for IrDA */ - - ndev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; - ndev->if_port = IF_PORT_UNKNOWN; - - ndev->netdev_ops = &vlsi_netdev_ops; - ndev->watchdog_timeo = 500*HZ/1000; /* max. allowed turn time for IrLAP */ - - SET_NETDEV_DEV(ndev, &pdev->dev); - - return 0; -} - -/**************************************************************/ - -static int -vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net_device *ndev; - vlsi_irda_dev_t *idev; - - if (pci_enable_device(pdev)) - goto out; - else - pdev->current_state = 0; /* hw must be running now */ - - net_info_ratelimited("%s: IrDA PCI controller %s detected\n", - drivername, pci_name(pdev)); - - if ( !pci_resource_start(pdev,0) || - !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) { - net_err_ratelimited("%s: bar 0 invalid", __func__); - goto out_disable; - } - - ndev = alloc_irdadev(sizeof(*idev)); - if (ndev==NULL) { - net_err_ratelimited("%s: Unable to allocate device memory.\n", - __func__); - goto out_disable; - } - - idev = netdev_priv(ndev); - - spin_lock_init(&idev->lock); - mutex_init(&idev->mtx); - mutex_lock(&idev->mtx); - idev->pdev = pdev; - - if (vlsi_irda_init(ndev) < 0) - goto out_freedev; - - if (register_netdev(ndev) < 0) { - net_err_ratelimited("%s: register_netdev failed\n", __func__); - goto out_freedev; - } - - if (vlsi_proc_root != NULL) { - struct proc_dir_entry *ent; - - ent = proc_create_data(ndev->name, S_IFREG|S_IRUGO, - vlsi_proc_root, VLSI_PROC_FOPS, ndev); - if (!ent) { - net_warn_ratelimited("%s: failed to create proc entry\n", - __func__); - } else { - proc_set_size(ent, 0); - } - idev->proc_entry = ent; - } - net_info_ratelimited("%s: registered device %s\n", - drivername, ndev->name); - - pci_set_drvdata(pdev, ndev); - mutex_unlock(&idev->mtx); - - return 0; - -out_freedev: - mutex_unlock(&idev->mtx); - free_netdev(ndev); -out_disable: - pci_disable_device(pdev); -out: - return -ENODEV; -} - -static void vlsi_irda_remove(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - vlsi_irda_dev_t *idev; - - if (!ndev) { - net_err_ratelimited("%s: lost netdevice?\n", drivername); - return; - } - - unregister_netdev(ndev); - - idev = netdev_priv(ndev); - mutex_lock(&idev->mtx); - if (idev->proc_entry) { - remove_proc_entry(ndev->name, vlsi_proc_root); - idev->proc_entry = NULL; - } - mutex_unlock(&idev->mtx); - - free_netdev(ndev); - - net_info_ratelimited("%s: %s removed\n", drivername, pci_name(pdev)); -} - -#ifdef CONFIG_PM - -/* The Controller doesn't provide PCI PM capabilities as defined by PCI specs. - * Some of the Linux PCI-PM code however depends on this, for example in - * pci_set_power_state(). So we have to take care to perform the required - * operations on our own (particularly reflecting the pdev->current_state) - * otherwise we might get cheated by pci-pm. - */ - - -static int vlsi_irda_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - vlsi_irda_dev_t *idev; - - if (!ndev) { - net_err_ratelimited("%s - %s: no netdevice\n", - __func__, pci_name(pdev)); - return 0; - } - idev = netdev_priv(ndev); - mutex_lock(&idev->mtx); - if (pdev->current_state != 0) { /* already suspended */ - if (state.event > pdev->current_state) { /* simply go deeper */ - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - pdev->current_state = state.event; - } - else - net_err_ratelimited("%s - %s: invalid suspend request %u -> %u\n", - __func__, pci_name(pdev), - pdev->current_state, state.event); - mutex_unlock(&idev->mtx); - return 0; - } - - if (netif_running(ndev)) { - netif_device_detach(ndev); - vlsi_stop_hw(idev); - pci_save_state(pdev); - if (!idev->new_baud) - /* remember speed settings to restore on resume */ - idev->new_baud = idev->baud; - } - - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - pdev->current_state = state.event; - idev->resume_ok = 1; - mutex_unlock(&idev->mtx); - return 0; -} - -static int vlsi_irda_resume(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - vlsi_irda_dev_t *idev; - - if (!ndev) { - net_err_ratelimited("%s - %s: no netdevice\n", - __func__, pci_name(pdev)); - return 0; - } - idev = netdev_priv(ndev); - mutex_lock(&idev->mtx); - if (pdev->current_state == 0) { - mutex_unlock(&idev->mtx); - net_warn_ratelimited("%s - %s: already resumed\n", - __func__, pci_name(pdev)); - return 0; - } - - pci_set_power_state(pdev, PCI_D0); - pdev->current_state = PM_EVENT_ON; - - if (!idev->resume_ok) { - /* should be obsolete now - but used to happen due to: - * - pci layer initially setting pdev->current_state = 4 (unknown) - * - pci layer did not walk the save_state-tree (might be APM problem) - * so we could not refuse to suspend from undefined state - * - vlsi_irda_suspend detected invalid state and refused to save - * configuration for resume - but was too late to stop suspending - * - vlsi_irda_resume got screwed when trying to resume from garbage - * - * now we explicitly set pdev->current_state = 0 after enabling the - * device and independently resume_ok should catch any garbage config. - */ - net_warn_ratelimited("%s - hm, nothing to resume?\n", __func__); - mutex_unlock(&idev->mtx); - return 0; - } - - if (netif_running(ndev)) { - pci_restore_state(pdev); - vlsi_start_hw(idev); - netif_device_attach(ndev); - } - idev->resume_ok = 0; - mutex_unlock(&idev->mtx); - return 0; -} - -#endif /* CONFIG_PM */ - -/*********************************************************/ - -static struct pci_driver vlsi_irda_driver = { - .name = drivername, - .id_table = vlsi_irda_table, - .probe = vlsi_irda_probe, - .remove = vlsi_irda_remove, -#ifdef CONFIG_PM - .suspend = vlsi_irda_suspend, - .resume = vlsi_irda_resume, -#endif -}; - -#define PROC_DIR ("driver/" DRIVER_NAME) - -static int __init vlsi_mod_init(void) -{ - int i, ret; - - if (clksrc < 0 || clksrc > 3) { - net_err_ratelimited("%s: invalid clksrc=%d\n", - drivername, clksrc); - return -1; - } - - for (i = 0; i < 2; i++) { - switch(ringsize[i]) { - case 4: - case 8: - case 16: - case 32: - case 64: - break; - default: - net_warn_ratelimited("%s: invalid %s ringsize %d, using default=8\n", - drivername, - i ? "rx" : "tx", - ringsize[i]); - ringsize[i] = 8; - break; - } - } - - sirpulse = !!sirpulse; - - /* proc_mkdir returns NULL if !CONFIG_PROC_FS. - * Failure to create the procfs entry is handled like running - * without procfs - it's not required for the driver to work. - */ - vlsi_proc_root = proc_mkdir(PROC_DIR, NULL); - - ret = pci_register_driver(&vlsi_irda_driver); - - if (ret && vlsi_proc_root) - remove_proc_entry(PROC_DIR, NULL); - return ret; - -} - -static void __exit vlsi_mod_exit(void) -{ - pci_unregister_driver(&vlsi_irda_driver); - if (vlsi_proc_root) - remove_proc_entry(PROC_DIR, NULL); -} - -module_init(vlsi_mod_init); -module_exit(vlsi_mod_exit); diff --git a/drivers/net/irda/vlsi_ir.h b/drivers/net/irda/vlsi_ir.h deleted file mode 100644 index f9db2ce4c5c6..000000000000 --- a/drivers/net/irda/vlsi_ir.h +++ /dev/null @@ -1,757 +0,0 @@ - -/********************************************************************* - * - * vlsi_ir.h: VLSI82C147 PCI IrDA controller driver for Linux - * - * Version: 0.5 - * - * Copyright (c) 2001-2003 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRDA_VLSI_FIR_H -#define IRDA_VLSI_FIR_H - -/* ================================================================ - * compatibility stuff - */ - -/* definitions not present in pci_ids.h */ - -#ifndef PCI_CLASS_WIRELESS_IRDA -#define PCI_CLASS_WIRELESS_IRDA 0x0d00 -#endif - -#ifndef PCI_CLASS_SUBCLASS_MASK -#define PCI_CLASS_SUBCLASS_MASK 0xffff -#endif - -/* ================================================================ */ - -/* non-standard PCI registers */ - -enum vlsi_pci_regs { - VLSI_PCI_CLKCTL = 0x40, /* chip clock input control */ - VLSI_PCI_MSTRPAGE = 0x41, /* addr [31:24] for all busmaster cycles */ - VLSI_PCI_IRMISC = 0x42 /* mainly legacy UART related */ -}; - -/* ------------------------------------------ */ - -/* VLSI_PCI_CLKCTL: Clock Control Register (u8, rw) */ - -/* Three possible clock sources: either on-chip 48MHz PLL or - * external clock applied to EXTCLK pin. External clock may - * be either 48MHz or 40MHz, which is indicated by XCKSEL. - * CLKSTP controls whether the selected clock source gets - * connected to the IrDA block. - * - * On my HP OB-800 the BIOS sets external 40MHz clock as source - * when IrDA enabled and I've never detected any PLL lock success. - * Apparently the 14.3...MHz OSC input required for the PLL to work - * is not connected and the 40MHz EXTCLK is provided externally. - * At least this is what makes the driver working for me. - */ - -enum vlsi_pci_clkctl { - - /* PLL control */ - - CLKCTL_PD_INV = 0x04, /* PD#: inverted power down signal, - * i.e. PLL is powered, if PD_INV set */ - CLKCTL_LOCK = 0x40, /* (ro) set, if PLL is locked */ - - /* clock source selection */ - - CLKCTL_EXTCLK = 0x20, /* set to select external clock input, not PLL */ - CLKCTL_XCKSEL = 0x10, /* set to indicate EXTCLK is 40MHz, not 48MHz */ - - /* IrDA block control */ - - CLKCTL_CLKSTP = 0x80, /* set to disconnect from selected clock source */ - CLKCTL_WAKE = 0x08 /* set to enable wakeup feature: whenever IR activity - * is detected, PD_INV gets set(?) and CLKSTP cleared */ -}; - -/* ------------------------------------------ */ - -/* VLSI_PCI_MSTRPAGE: Master Page Register (u8, rw) and busmastering stuff */ - -#define DMA_MASK_USED_BY_HW 0xffffffff -#define DMA_MASK_MSTRPAGE 0x00ffffff -#define MSTRPAGE_VALUE (DMA_MASK_MSTRPAGE >> 24) - - /* PCI busmastering is somewhat special for this guy - in short: - * - * We select to operate using fixed MSTRPAGE=0, use ISA DMA - * address restrictions to make the PCI BM api aware of this, - * but ensure the hardware is dealing with real 32bit access. - * - * In detail: - * The chip executes normal 32bit busmaster cycles, i.e. - * drives all 32 address lines. These addresses however are - * composed of [0:23] taken from various busaddr-pointers - * and [24:31] taken from the MSTRPAGE register in the VLSI82C147 - * config space. Therefore _all_ busmastering must be - * targeted to/from one single 16MB (busaddr-) superpage! - * The point is to make sure all the allocations for memory - * locations with busmaster access (ring descriptors, buffers) - * are indeed bus-mappable to the same 16MB range (for x86 this - * means they must reside in the same 16MB physical memory address - * range). The only constraint we have which supports "several objects - * mappable to common 16MB range" paradigma, is the old ISA DMA - * restriction to the first 16MB of physical address range. - * Hence the approach here is to enable PCI busmaster support using - * the correct 32bit dma-mask used by the chip. Afterwards the device's - * dma-mask gets restricted to 24bit, which must be honoured somehow by - * all allocations for memory areas to be exposed to the chip ... - * - * Note: - * Don't be surprised to get "Setting latency timer..." messages every - * time when PCI busmastering is enabled for the chip. - * The chip has its PCI latency timer RO fixed at 0 - which is not a - * problem here, because it is never requesting _burst_ transactions. - */ - -/* ------------------------------------------ */ - -/* VLSI_PCIIRMISC: IR Miscellaneous Register (u8, rw) */ - -/* legacy UART emulation - not used by this driver - would require: - * (see below for some register-value definitions) - * - * - IRMISC_UARTEN must be set to enable UART address decoding - * - IRMISC_UARTSEL configured - * - IRCFG_MASTER must be cleared - * - IRCFG_SIR must be set - * - IRENABLE_PHYANDCLOCK must be asserted 0->1 (and hence IRENABLE_SIR_ON) - */ - -enum vlsi_pci_irmisc { - - /* IR transceiver control */ - - IRMISC_IRRAIL = 0x40, /* (ro?) IR rail power indication (and control?) - * 0=3.3V / 1=5V. Probably set during power-on? - * unclear - not touched by driver */ - IRMISC_IRPD = 0x08, /* transceiver power down, if set */ - - /* legacy UART control */ - - IRMISC_UARTTST = 0x80, /* UART test mode - "always write 0" */ - IRMISC_UARTEN = 0x04, /* enable UART address decoding */ - - /* bits [1:0] IRMISC_UARTSEL to select legacy UART address */ - - IRMISC_UARTSEL_3f8 = 0x00, - IRMISC_UARTSEL_2f8 = 0x01, - IRMISC_UARTSEL_3e8 = 0x02, - IRMISC_UARTSEL_2e8 = 0x03 -}; - -/* ================================================================ */ - -/* registers mapped to 32 byte PCI IO space */ - -/* note: better access all registers at the indicated u8/u16 size - * although some of them contain only 1 byte of information. - * some of them (particaluarly PROMPT and IRCFG) ignore - * access when using the wrong addressing mode! - */ - -enum vlsi_pio_regs { - VLSI_PIO_IRINTR = 0x00, /* interrupt enable/request (u8, rw) */ - VLSI_PIO_RINGPTR = 0x02, /* rx/tx ring pointer (u16, ro) */ - VLSI_PIO_RINGBASE = 0x04, /* [23:10] of ring address (u16, rw) */ - VLSI_PIO_RINGSIZE = 0x06, /* rx/tx ring size (u16, rw) */ - VLSI_PIO_PROMPT = 0x08, /* triggers ring processing (u16, wo) */ - /* 0x0a-0x0f: reserved / duplicated UART regs */ - VLSI_PIO_IRCFG = 0x10, /* configuration select (u16, rw) */ - VLSI_PIO_SIRFLAG = 0x12, /* BOF/EOF for filtered SIR (u16, ro) */ - VLSI_PIO_IRENABLE = 0x14, /* enable and status register (u16, rw/ro) */ - VLSI_PIO_PHYCTL = 0x16, /* physical layer current status (u16, ro) */ - VLSI_PIO_NPHYCTL = 0x18, /* next physical layer select (u16, rw) */ - VLSI_PIO_MAXPKT = 0x1a, /* [11:0] max len for packet receive (u16, rw) */ - VLSI_PIO_RCVBCNT = 0x1c /* current receive-FIFO byte count (u16, ro) */ - /* 0x1e-0x1f: reserved / duplicated UART regs */ -}; - -/* ------------------------------------------ */ - -/* VLSI_PIO_IRINTR: Interrupt Register (u8, rw) */ - -/* enable-bits: - * 1 = enable / 0 = disable - * interrupt condition bits: - * set according to corresponding interrupt source - * (regardless of the state of the enable bits) - * enable bit status indicates whether interrupt gets raised - * write-to-clear - * note: RPKTINT and TPKTINT behave different in legacy UART mode (which we don't use :-) - */ - -enum vlsi_pio_irintr { - IRINTR_ACTEN = 0x80, /* activity interrupt enable */ - IRINTR_ACTIVITY = 0x40, /* activity monitor (traffic detected) */ - IRINTR_RPKTEN = 0x20, /* receive packet interrupt enable*/ - IRINTR_RPKTINT = 0x10, /* rx-packet transferred from fifo to memory finished */ - IRINTR_TPKTEN = 0x08, /* transmit packet interrupt enable */ - IRINTR_TPKTINT = 0x04, /* last bit of tx-packet+crc shifted to ir-pulser */ - IRINTR_OE_EN = 0x02, /* UART rx fifo overrun error interrupt enable */ - IRINTR_OE_INT = 0x01 /* UART rx fifo overrun error (read LSR to clear) */ -}; - -/* we use this mask to check whether the (shared PCI) interrupt is ours */ - -#define IRINTR_INT_MASK (IRINTR_ACTIVITY|IRINTR_RPKTINT|IRINTR_TPKTINT) - -/* ------------------------------------------ */ - -/* VLSI_PIO_RINGPTR: Ring Pointer Read-Back Register (u16, ro) */ - -/* _both_ ring pointers are indices relative to the _entire_ rx,tx-ring! - * i.e. the referenced descriptor is located - * at RINGBASE + PTR * sizeof(descr) for rx and tx - * therefore, the tx-pointer has offset MAX_RING_DESCR - */ - -#define MAX_RING_DESCR 64 /* tx, rx rings may contain up to 64 descr each */ - -#define RINGPTR_RX_MASK (MAX_RING_DESCR-1) -#define RINGPTR_TX_MASK ((MAX_RING_DESCR-1)<<8) - -#define RINGPTR_GET_RX(p) ((p)&RINGPTR_RX_MASK) -#define RINGPTR_GET_TX(p) (((p)&RINGPTR_TX_MASK)>>8) - -/* ------------------------------------------ */ - -/* VLSI_PIO_RINGBASE: Ring Pointer Base Address Register (u16, ro) */ - -/* Contains [23:10] part of the ring base (bus-) address - * which must be 1k-alinged. [31:24] is taken from - * VLSI_PCI_MSTRPAGE above. - * The controller initiates non-burst PCI BM cycles to - * fetch and update the descriptors in the ring. - * Once fetched, the descriptor remains cached onchip - * until it gets closed and updated due to the ring - * processing state machine. - * The entire ring area is split in rx and tx areas with each - * area consisting of 64 descriptors of 8 bytes each. - * The rx(tx) ring is located at ringbase+0 (ringbase+64*8). - */ - -#define BUS_TO_RINGBASE(p) (((p)>>10)&0x3fff) - -/* ------------------------------------------ */ - -/* VLSI_PIO_RINGSIZE: Ring Size Register (u16, rw) */ - -/* bit mask to indicate the ring size to be used for rx and tx. - * possible values encoded bits - * 4 0000 - * 8 0001 - * 16 0011 - * 32 0111 - * 64 1111 - * located at [15:12] for tx and [11:8] for rx ([7:0] unused) - * - * note: probably a good idea to have IRCFG_MSTR cleared when writing - * this so the state machines are stopped and the RINGPTR is reset! - */ - -#define SIZE_TO_BITS(num) ((((num)-1)>>2)&0x0f) -#define TX_RX_TO_RINGSIZE(tx,rx) ((SIZE_TO_BITS(tx)<<12)|(SIZE_TO_BITS(rx)<<8)) -#define RINGSIZE_TO_RXSIZE(rs) ((((rs)&0x0f00)>>6)+4) -#define RINGSIZE_TO_TXSIZE(rs) ((((rs)&0xf000)>>10)+4) - - -/* ------------------------------------------ */ - -/* VLSI_PIO_PROMPT: Ring Prompting Register (u16, write-to-start) */ - -/* writing any value kicks the ring processing state machines - * for both tx, rx rings as follows: - * - active rings (currently owning an active descriptor) - * ignore the prompt and continue - * - idle rings fetch the next descr from the ring and start - * their processing - */ - -/* ------------------------------------------ */ - -/* VLSI_PIO_IRCFG: IR Config Register (u16, rw) */ - -/* notes: - * - not more than one SIR/MIR/FIR bit must be set at any time - * - SIR, MIR, FIR and CRC16 select the configuration which will - * be applied on next 0->1 transition of IRENABLE_PHYANDCLOCK (see below). - * - besides allowing the PCI interface to execute busmaster cycles - * and therefore the ring SM to operate, the MSTR bit has side-effects: - * when MSTR is cleared, the RINGPTR's get reset and the legacy UART mode - * (in contrast to busmaster access mode) gets enabled. - * - clearing ENRX or setting ENTX while data is received may stall the - * receive fifo until ENRX reenabled _and_ another packet arrives - * - SIRFILT means the chip performs the required unwrapping of hardware - * headers (XBOF's, BOF/EOF) and un-escaping in the _receive_ direction. - * Only the resulting IrLAP payload is copied to the receive buffers - - * but with the 16bit FCS still encluded. Question remains, whether it - * was already checked or we should do it before passing the packet to IrLAP? - */ - -enum vlsi_pio_ircfg { - IRCFG_LOOP = 0x4000, /* enable loopback test mode */ - IRCFG_ENTX = 0x1000, /* transmit enable */ - IRCFG_ENRX = 0x0800, /* receive enable */ - IRCFG_MSTR = 0x0400, /* master enable */ - IRCFG_RXANY = 0x0200, /* receive any packet */ - IRCFG_CRC16 = 0x0080, /* 16bit (not 32bit) CRC select for MIR/FIR */ - IRCFG_FIR = 0x0040, /* FIR 4PPM encoding mode enable */ - IRCFG_MIR = 0x0020, /* MIR HDLC encoding mode enable */ - IRCFG_SIR = 0x0010, /* SIR encoding mode enable */ - IRCFG_SIRFILT = 0x0008, /* enable SIR decode filter (receiver unwrapping) */ - IRCFG_SIRTEST = 0x0004, /* allow SIR decode filter when not in SIR mode */ - IRCFG_TXPOL = 0x0002, /* invert tx polarity when set */ - IRCFG_RXPOL = 0x0001 /* invert rx polarity when set */ -}; - -/* ------------------------------------------ */ - -/* VLSI_PIO_SIRFLAG: SIR Flag Register (u16, ro) */ - -/* register contains hardcoded BOF=0xc0 at [7:0] and EOF=0xc1 at [15:8] - * which is used for unwrapping received frames in SIR decode-filter mode - */ - -/* ------------------------------------------ */ - -/* VLSI_PIO_IRENABLE: IR Enable Register (u16, rw/ro) */ - -/* notes: - * - IREN acts as gate for latching the configured IR mode information - * from IRCFG and IRPHYCTL when IREN=reset and applying them when - * IREN gets set afterwards. - * - ENTXST reflects IRCFG_ENTX - * - ENRXST = IRCFG_ENRX && (!IRCFG_ENTX || IRCFG_LOOP) - */ - -enum vlsi_pio_irenable { - IRENABLE_PHYANDCLOCK = 0x8000, /* enable IR phy and gate the mode config (rw) */ - IRENABLE_CFGER = 0x4000, /* mode configuration error (ro) */ - IRENABLE_FIR_ON = 0x2000, /* FIR on status (ro) */ - IRENABLE_MIR_ON = 0x1000, /* MIR on status (ro) */ - IRENABLE_SIR_ON = 0x0800, /* SIR on status (ro) */ - IRENABLE_ENTXST = 0x0400, /* transmit enable status (ro) */ - IRENABLE_ENRXST = 0x0200, /* Receive enable status (ro) */ - IRENABLE_CRC16_ON = 0x0100 /* 16bit (not 32bit) CRC enabled status (ro) */ -}; - -#define IRENABLE_MASK 0xff00 /* Read mask */ - -/* ------------------------------------------ */ - -/* VLSI_PIO_PHYCTL: IR Physical Layer Current Control Register (u16, ro) */ - -/* read-back of the currently applied physical layer status. - * applied from VLSI_PIO_NPHYCTL at rising edge of IRENABLE_PHYANDCLOCK - * contents identical to VLSI_PIO_NPHYCTL (see below) - */ - -/* ------------------------------------------ */ - -/* VLSI_PIO_NPHYCTL: IR Physical Layer Next Control Register (u16, rw) */ - -/* latched during IRENABLE_PHYANDCLOCK=0 and applied at 0-1 transition - * - * consists of BAUD[15:10], PLSWID[9:5] and PREAMB[4:0] bits defined as follows: - * - * SIR-mode: BAUD = (115.2kHz / baudrate) - 1 - * PLSWID = (pulsetime * freq / (BAUD+1)) - 1 - * where pulsetime is the requested IrPHY pulse width - * and freq is 8(16)MHz for 40(48)MHz primary input clock - * PREAMB: don't care for SIR - * - * The nominal SIR pulse width is 3/16 bit time so we have PLSWID=12 - * fixed for all SIR speeds at 40MHz input clock (PLSWID=24 at 48MHz). - * IrPHY also allows shorter pulses down to the nominal pulse duration - * at 115.2kbaud (minus some tolerance) which is 1.41 usec. - * Using the expression PLSWID = 12/(BAUD+1)-1 (multiplied by two for 48MHz) - * we get the minimum acceptable PLSWID values according to the VLSI - * specification, which provides 1.5 usec pulse width for all speeds (except - * for 2.4kbaud getting 6usec). This is fine with IrPHY v1.3 specs and - * reduces the transceiver power which drains the battery. At 9.6kbaud for - * example this amounts to more than 90% battery power saving! - * - * MIR-mode: BAUD = 0 - * PLSWID = 9(10) for 40(48) MHz input clock - * to get nominal MIR pulse width - * PREAMB = 1 - * - * FIR-mode: BAUD = 0 - * PLSWID: don't care - * PREAMB = 15 - */ - -#define PHYCTL_BAUD_SHIFT 10 -#define PHYCTL_BAUD_MASK 0xfc00 -#define PHYCTL_PLSWID_SHIFT 5 -#define PHYCTL_PLSWID_MASK 0x03e0 -#define PHYCTL_PREAMB_SHIFT 0 -#define PHYCTL_PREAMB_MASK 0x001f - -#define PHYCTL_TO_BAUD(bwp) (((bwp)&PHYCTL_BAUD_MASK)>>PHYCTL_BAUD_SHIFT) -#define PHYCTL_TO_PLSWID(bwp) (((bwp)&PHYCTL_PLSWID_MASK)>>PHYCTL_PLSWID_SHIFT) -#define PHYCTL_TO_PREAMB(bwp) (((bwp)&PHYCTL_PREAMB_MASK)>>PHYCTL_PREAMB_SHIFT) - -#define BWP_TO_PHYCTL(b,w,p) ((((b)<0) ? (tmp-1) : 0; -} - -#define PHYCTL_SIR(br,ws,cs) BWP_TO_PHYCTL(BAUD_BITS(br),calc_width_bits((br),(ws),(cs)),0) -#define PHYCTL_MIR(cs) BWP_TO_PHYCTL(0,((cs)?9:10),1) -#define PHYCTL_FIR BWP_TO_PHYCTL(0,0,15) - -/* quite ugly, I know. But implementing these calculations here avoids - * having magic numbers in the code and allows some playing with pulsewidths - * without risk to violate the standards. - * FWIW, here is the table for reference: - * - * baudrate BAUD min-PLSWID nom-PLSWID PREAMB - * 2400 47 0(0) 12(24) 0 - * 9600 11 0(0) 12(24) 0 - * 19200 5 1(2) 12(24) 0 - * 38400 2 3(6) 12(24) 0 - * 57600 1 5(10) 12(24) 0 - * 115200 0 11(22) 12(24) 0 - * MIR 0 - 9(10) 1 - * FIR 0 - 0 15 - * - * note: x(y) means x-value for 40MHz / y-value for 48MHz primary input clock - */ - -/* ------------------------------------------ */ - - -/* VLSI_PIO_MAXPKT: Maximum Packet Length register (u16, rw) */ - -/* maximum acceptable length for received packets */ - -/* hw imposed limitation - register uses only [11:0] */ -#define MAX_PACKET_LENGTH 0x0fff - -/* IrLAP I-field (apparently not defined elsewhere) */ -#define IRDA_MTU 2048 - -/* complete packet consists of A(1)+C(1)+I(<=IRDA_MTU) */ -#define IRLAP_SKB_ALLOCSIZE (1+1+IRDA_MTU) - -/* the buffers we use to exchange frames with the hardware need to be - * larger than IRLAP_SKB_ALLOCSIZE because we may have up to 4 bytes FCS - * appended and, in SIR mode, a lot of frame wrapping bytes. The worst - * case appears to be a SIR packet with I-size==IRDA_MTU and all bytes - * requiring to be escaped to provide transparency. Furthermore, the peer - * might ask for quite a number of additional XBOFs: - * up to 115+48 XBOFS 163 - * regular BOF 1 - * A-field 1 - * C-field 1 - * I-field, IRDA_MTU, all escaped 4096 - * FCS (16 bit at SIR, escaped) 4 - * EOF 1 - * AFAICS nothing in IrLAP guarantees A/C field not to need escaping - * (f.e. 0xc0/0xc1 - i.e. BOF/EOF - are legal values there) so in the - * worst case we have 4269 bytes total frame size. - * However, the VLSI uses 12 bits only for all buffer length values, - * which limits the maximum useable buffer size <= 4095. - * Note this is not a limitation in the receive case because we use - * the SIR filtering mode where the hw unwraps the frame and only the - * bare packet+fcs is stored into the buffer - in contrast to the SIR - * tx case where we have to pass frame-wrapped packets to the hw. - * If this would ever become an issue in real life, the only workaround - * I see would be using the legacy UART emulation in SIR mode. - */ - -#define XFER_BUF_SIZE MAX_PACKET_LENGTH - -/* ------------------------------------------ */ - -/* VLSI_PIO_RCVBCNT: Receive Byte Count Register (u16, ro) */ - -/* receive packet counter gets incremented on every non-filtered - * byte which was put in the receive fifo and reset for each - * new packet. Used to decide whether we are just in the middle - * of receiving - */ - -/* better apply the [11:0] mask when reading, as some docs say the - * reserved [15:12] would return 1 when reading - which is wrong AFAICS - */ -#define RCVBCNT_MASK 0x0fff - -/******************************************************************/ - -/* descriptors for rx/tx ring - * - * accessed by hardware - don't change! - * - * the descriptor is owned by hardware, when the ACTIVE status bit - * is set and nothing (besides reading status to test the bit) - * shall be done. The bit gets cleared by hw, when the descriptor - * gets closed. Premature reaping of descriptors owned be the chip - * can be achieved by disabling IRCFG_MSTR - * - * Attention: Writing addr overwrites status! - * - * ### FIXME: depends on endianess (but there ain't no non-i586 ob800 ;-) - */ - -struct ring_descr_hw { - volatile __le16 rd_count; /* tx/rx count [11:0] */ - __le16 reserved; - union { - __le32 addr; /* [23:0] of the buffer's busaddress */ - struct { - u8 addr_res[3]; - volatile u8 status; /* descriptor status */ - } __packed rd_s; - } __packed rd_u; -} __packed; - -#define rd_addr rd_u.addr -#define rd_status rd_u.rd_s.status - -/* ring descriptor status bits */ - -#define RD_ACTIVE 0x80 /* descriptor owned by hw (both TX,RX) */ - -/* TX ring descriptor status */ - -#define RD_TX_DISCRC 0x40 /* do not send CRC (for SIR) */ -#define RD_TX_BADCRC 0x20 /* force a bad CRC */ -#define RD_TX_PULSE 0x10 /* send indication pulse after this frame (MIR/FIR) */ -#define RD_TX_FRCEUND 0x08 /* force underrun */ -#define RD_TX_CLRENTX 0x04 /* clear ENTX after this frame */ -#define RD_TX_UNDRN 0x01 /* TX fifo underrun (probably PCI problem) */ - -/* RX ring descriptor status */ - -#define RD_RX_PHYERR 0x40 /* physical encoding error */ -#define RD_RX_CRCERR 0x20 /* CRC error (MIR/FIR) */ -#define RD_RX_LENGTH 0x10 /* frame exceeds buffer length */ -#define RD_RX_OVER 0x08 /* RX fifo overrun (probably PCI problem) */ -#define RD_RX_SIRBAD 0x04 /* EOF missing: BOF follows BOF (SIR, filtered) */ - -#define RD_RX_ERROR 0x7c /* any error in received frame */ - -/* the memory required to hold the 2 descriptor rings */ -#define HW_RING_AREA_SIZE (2 * MAX_RING_DESCR * sizeof(struct ring_descr_hw)) - -/******************************************************************/ - -/* sw-ring descriptors consists of a bus-mapped transfer buffer with - * associated skb and a pointer to the hw entry descriptor - */ - -struct ring_descr { - struct ring_descr_hw *hw; - struct sk_buff *skb; - void *buf; -}; - -/* wrappers for operations on hw-exposed ring descriptors - * access to the hw-part of the descriptors must use these. - */ - -static inline int rd_is_active(struct ring_descr *rd) -{ - return (rd->hw->rd_status & RD_ACTIVE) != 0; -} - -static inline void rd_activate(struct ring_descr *rd) -{ - rd->hw->rd_status |= RD_ACTIVE; -} - -static inline void rd_set_status(struct ring_descr *rd, u8 s) -{ - rd->hw->rd_status = s; /* may pass ownership to the hardware */ -} - -static inline void rd_set_addr_status(struct ring_descr *rd, dma_addr_t a, u8 s) -{ - /* order is important for two reasons: - * - overlayed: writing addr overwrites status - * - we want to write status last so we have valid address in - * case status has RD_ACTIVE set - */ - - if ((a & ~DMA_MASK_MSTRPAGE)>>24 != MSTRPAGE_VALUE) { - net_err_ratelimited("%s: pci busaddr inconsistency!\n", - __func__); - dump_stack(); - return; - } - - a &= DMA_MASK_MSTRPAGE; /* clear highbyte to make sure we won't write - * to status - just in case MSTRPAGE_VALUE!=0 - */ - rd->hw->rd_addr = cpu_to_le32(a); - wmb(); - rd_set_status(rd, s); /* may pass ownership to the hardware */ -} - -static inline void rd_set_count(struct ring_descr *rd, u16 c) -{ - rd->hw->rd_count = cpu_to_le16(c); -} - -static inline u8 rd_get_status(struct ring_descr *rd) -{ - return rd->hw->rd_status; -} - -static inline dma_addr_t rd_get_addr(struct ring_descr *rd) -{ - dma_addr_t a; - - a = le32_to_cpu(rd->hw->rd_addr); - return (a & DMA_MASK_MSTRPAGE) | (MSTRPAGE_VALUE << 24); -} - -static inline u16 rd_get_count(struct ring_descr *rd) -{ - return le16_to_cpu(rd->hw->rd_count); -} - -/******************************************************************/ - -/* sw descriptor rings for rx, tx: - * - * operations follow producer-consumer paradigm, with the hw - * in the middle doing the processing. - * ring size must be power of two. - * - * producer advances r->tail after inserting for processing - * consumer advances r->head after removing processed rd - * ring is empty if head==tail / full if (tail+1)==head - */ - -struct vlsi_ring { - struct pci_dev *pdev; - int dir; - unsigned len; - unsigned size; - unsigned mask; - atomic_t head, tail; - struct ring_descr *rd; -}; - -/* ring processing helpers */ - -static inline struct ring_descr *ring_last(struct vlsi_ring *r) -{ - int t; - - t = atomic_read(&r->tail) & r->mask; - return (((t+1) & r->mask) == (atomic_read(&r->head) & r->mask)) ? NULL : &r->rd[t]; -} - -static inline struct ring_descr *ring_put(struct vlsi_ring *r) -{ - atomic_inc(&r->tail); - return ring_last(r); -} - -static inline struct ring_descr *ring_first(struct vlsi_ring *r) -{ - int h; - - h = atomic_read(&r->head) & r->mask; - return (h == (atomic_read(&r->tail) & r->mask)) ? NULL : &r->rd[h]; -} - -static inline struct ring_descr *ring_get(struct vlsi_ring *r) -{ - atomic_inc(&r->head); - return ring_first(r); -} - -/******************************************************************/ - -/* our private compound VLSI-PCI-IRDA device information */ - -typedef struct vlsi_irda_dev { - struct pci_dev *pdev; - - struct irlap_cb *irlap; - - struct qos_info qos; - - unsigned mode; - int baud, new_baud; - - dma_addr_t busaddr; - void *virtaddr; - struct vlsi_ring *tx_ring, *rx_ring; - - ktime_t last_rx; - - spinlock_t lock; - struct mutex mtx; - - u8 resume_ok; - struct proc_dir_entry *proc_entry; - -} vlsi_irda_dev_t; - -/********************************************************/ - -/* the remapped error flags we use for returning from frame - * post-processing in vlsi_process_tx/rx() after it was completed - * by the hardware. These functions either return the >=0 number - * of transferred bytes in case of success or the negative (-) - * of the or'ed error flags. - */ - -#define VLSI_TX_DROP 0x0001 -#define VLSI_TX_FIFO 0x0002 - -#define VLSI_RX_DROP 0x0100 -#define VLSI_RX_OVER 0x0200 -#define VLSI_RX_LENGTH 0x0400 -#define VLSI_RX_FRAME 0x0800 -#define VLSI_RX_CRC 0x1000 - -/********************************************************/ - -#endif /* IRDA_VLSI_FIR_H */ - diff --git a/drivers/net/irda/w83977af.h b/drivers/net/irda/w83977af.h deleted file mode 100644 index 04476c2e9121..000000000000 --- a/drivers/net/irda/w83977af.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef W83977AF_H -#define W83977AF_H - -#define W977_EFIO_BASE 0x370 -#define W977_EFIO2_BASE 0x3f0 -#define W977_DEVICE_IR 0x06 - - -/* - * Enter extended function mode - */ -static inline void w977_efm_enter(unsigned int efio) -{ - outb(0x87, efio); - outb(0x87, efio); -} - -/* - * Select a device to configure - */ - -static inline void w977_select_device(__u8 devnum, unsigned int efio) -{ - outb(0x07, efio); - outb(devnum, efio+1); -} - -/* - * Write a byte to a register - */ -static inline void w977_write_reg(__u8 reg, __u8 value, unsigned int efio) -{ - outb(reg, efio); - outb(value, efio+1); -} - -/* - * read a byte from a register - */ -static inline __u8 w977_read_reg(__u8 reg, unsigned int efio) -{ - outb(reg, efio); - return inb(efio+1); -} - -/* - * Exit extended function mode - */ -static inline void w977_efm_exit(unsigned int efio) -{ - outb(0xAA, efio); -} -#endif diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c deleted file mode 100644 index 282b6c9ae05b..000000000000 --- a/drivers/net/irda/w83977af_ir.c +++ /dev/null @@ -1,1285 +0,0 @@ -/********************************************************************* - * - * Filename: w83977af_ir.c - * Version: 1.0 - * Description: FIR driver for the Winbond W83977AF Super I/O chip - * Status: Experimental. - * Author: Paul VanderSpek - * Created at: Wed Nov 4 11:46:16 1998 - * Modified at: Fri Jan 28 12:10:59 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli - * Copyright (c) 1998-1999 Rebel.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Paul VanderSpek nor Rebel.com admit liability nor provide - * warranty for any of this software. This material is provided "AS-IS" - * and at no charge. - * - * If you find bugs in this file, its very likely that the same bug - * will also be in pc87108.c since the implementations are quite - * similar. - * - * Notice that all functions that needs to access the chip in _any_ - * way, must save BSR register on entry, and restore it on exit. - * It is _very_ important to follow this policy! - * - * __u8 bank; - * - * bank = inb( iobase+BSR); - * - * do_your_stuff_here(); - * - * outb( bank, iobase+BSR); - * - ********************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include "w83977af.h" -#include "w83977af_ir.h" - -#define CONFIG_USE_W977_PNP /* Currently needed */ -#define PIO_MAX_SPEED 115200 - -static char *driver_name = "w83977af_ir"; -static int qos_mtt_bits = 0x07; /* 1 ms or more */ - -#define CHIP_IO_EXTENT 8 - -static unsigned int io[] = { 0x180, ~0, ~0, ~0 }; -#ifdef CONFIG_ARCH_NETWINDER /* Adjust to NetWinder differences */ -static unsigned int irq[] = { 6, 0, 0, 0 }; -#else -static unsigned int irq[] = { 11, 0, 0, 0 }; -#endif -static unsigned int dma[] = { 1, 0, 0, 0 }; -static unsigned int efbase[] = { W977_EFIO_BASE, W977_EFIO2_BASE }; -static unsigned int efio = W977_EFIO_BASE; - -static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL}; - -/* Some prototypes */ -static int w83977af_open(int i, unsigned int iobase, unsigned int irq, - unsigned int dma); -static int w83977af_close(struct w83977af_ir *self); -static int w83977af_probe(int iobase, int irq, int dma); -static int w83977af_dma_receive(struct w83977af_ir *self); -static int w83977af_dma_receive_complete(struct w83977af_ir *self); -static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, - struct net_device *dev); -static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size); -static void w83977af_dma_write(struct w83977af_ir *self, int iobase); -static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed); -static int w83977af_is_receiving(struct w83977af_ir *self); - -static int w83977af_net_open(struct net_device *dev); -static int w83977af_net_close(struct net_device *dev); -static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); - -/* - * Function w83977af_init () - * - * Initialize chip. Just try to find out how many chips we are dealing with - * and where they are - */ -static int __init w83977af_init(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dev_self) && io[i] < 2000; i++) { - if (w83977af_open(i, io[i], irq[i], dma[i]) == 0) - return 0; - } - return -ENODEV; -} - -/* - * Function w83977af_cleanup () - * - * Close all configured chips - * - */ -static void __exit w83977af_cleanup(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dev_self); i++) { - if (dev_self[i]) - w83977af_close(dev_self[i]); - } -} - -static const struct net_device_ops w83977_netdev_ops = { - .ndo_open = w83977af_net_open, - .ndo_stop = w83977af_net_close, - .ndo_start_xmit = w83977af_hard_xmit, - .ndo_do_ioctl = w83977af_net_ioctl, -}; - -/* - * Function w83977af_open (iobase, irq) - * - * Open driver instance - * - */ -static int w83977af_open(int i, unsigned int iobase, unsigned int irq, - unsigned int dma) -{ - struct net_device *dev; - struct w83977af_ir *self; - int err; - - /* Lock the port that we need */ - if (!request_region(iobase, CHIP_IO_EXTENT, driver_name)) { - pr_debug("%s: can't get iobase of 0x%03x\n", - __func__, iobase); - return -ENODEV; - } - - if (w83977af_probe(iobase, irq, dma) == -1) { - err = -1; - goto err_out; - } - /* - * Allocate new instance of the driver - */ - dev = alloc_irdadev(sizeof(struct w83977af_ir)); - if (!dev) { - pr_err("IrDA: Can't allocate memory for IrDA control block!\n"); - err = -ENOMEM; - goto err_out; - } - - self = netdev_priv(dev); - spin_lock_init(&self->lock); - - /* Initialize IO */ - self->io.fir_base = iobase; - self->io.irq = irq; - self->io.fir_ext = CHIP_IO_EXTENT; - self->io.dma = dma; - self->io.fifo_size = 32; - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* The only value we must override it the baudrate */ - - /* FIXME: The HP HDLS-1100 does not support 1152000! */ - self->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 | IR_57600 | - IR_115200 | IR_576000 | IR_1152000 | (IR_4000000 << 8); - - /* The HP HDLS-1100 needs 1 ms according to the specs */ - self->qos.min_turn_time.bits = qos_mtt_bits; - irda_qos_bits_to_value(&self->qos); - - /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ - self->rx_buff.truesize = 14384; - self->tx_buff.truesize = 4000; - - /* Allocate memory if needed */ - self->rx_buff.head = - dma_zalloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (!self->rx_buff.head) { - err = -ENOMEM; - goto err_out1; - } - - self->tx_buff.head = - dma_zalloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (!self->tx_buff.head) { - err = -ENOMEM; - goto err_out2; - } - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - self->netdev = dev; - - dev->netdev_ops = &w83977_netdev_ops; - - err = register_netdev(dev); - if (err) { - net_err_ratelimited("%s:, register_netdevice() failed!\n", - __func__); - goto err_out3; - } - net_info_ratelimited("IrDA: Registered device %s\n", dev->name); - - /* Need to store self somewhere */ - dev_self[i] = self; - - return 0; -err_out3: - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); -err_out2: - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); -err_out1: - free_netdev(dev); -err_out: - release_region(iobase, CHIP_IO_EXTENT); - return err; -} - -/* - * Function w83977af_close (self) - * - * Close driver instance - * - */ -static int w83977af_close(struct w83977af_ir *self) -{ - int iobase; - - iobase = self->io.fir_base; - -#ifdef CONFIG_USE_W977_PNP - /* enter PnP configuration mode */ - w977_efm_enter(efio); - - w977_select_device(W977_DEVICE_IR, efio); - - /* Deactivate device */ - w977_write_reg(0x30, 0x00, efio); - - w977_efm_exit(efio); -#endif /* CONFIG_USE_W977_PNP */ - - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Release the PORT that this driver is using */ - pr_debug("%s: Releasing Region %03x\n", __func__, self->io.fir_base); - release_region(self->io.fir_base, self->io.fir_ext); - - if (self->tx_buff.head) - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - - if (self->rx_buff.head) - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - free_netdev(self->netdev); - - return 0; -} - -static int w83977af_probe(int iobase, int irq, int dma) -{ - int version; - int i; - - for (i = 0; i < 2; i++) { -#ifdef CONFIG_USE_W977_PNP - /* Enter PnP configuration mode */ - w977_efm_enter(efbase[i]); - - w977_select_device(W977_DEVICE_IR, efbase[i]); - - /* Configure PnP port, IRQ, and DMA channel */ - w977_write_reg(0x60, (iobase >> 8) & 0xff, efbase[i]); - w977_write_reg(0x61, (iobase) & 0xff, efbase[i]); - - w977_write_reg(0x70, irq, efbase[i]); -#ifdef CONFIG_ARCH_NETWINDER - /* Netwinder uses 1 higher than Linux */ - w977_write_reg(0x74, dma + 1, efbase[i]); -#else - w977_write_reg(0x74, dma, efbase[i]); -#endif /* CONFIG_ARCH_NETWINDER */ - w977_write_reg(0x75, 0x04, efbase[i]);/* Disable Tx DMA */ - - /* Set append hardware CRC, enable IR bank selection */ - w977_write_reg(0xf0, APEDCRC | ENBNKSEL, efbase[i]); - - /* Activate device */ - w977_write_reg(0x30, 0x01, efbase[i]); - - w977_efm_exit(efbase[i]); -#endif /* CONFIG_USE_W977_PNP */ - /* Disable Advanced mode */ - switch_bank(iobase, SET2); - outb(iobase + 2, 0x00); - - /* Turn on UART (global) interrupts */ - switch_bank(iobase, SET0); - outb(HCR_EN_IRQ, iobase + HCR); - - /* Switch to advanced mode */ - switch_bank(iobase, SET2); - outb(inb(iobase + ADCR1) | ADCR1_ADV_SL, iobase + ADCR1); - - /* Set default IR-mode */ - switch_bank(iobase, SET0); - outb(HCR_SIR, iobase + HCR); - - /* Read the Advanced IR ID */ - switch_bank(iobase, SET3); - version = inb(iobase + AUID); - - /* Should be 0x1? */ - if (0x10 == (version & 0xf0)) { - efio = efbase[i]; - - /* Set FIFO size to 32 */ - switch_bank(iobase, SET2); - outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2); - - /* Set FIFO threshold to TX17, RX16 */ - switch_bank(iobase, SET0); - outb(UFR_RXTL | UFR_TXTL | UFR_TXF_RST | UFR_RXF_RST | - UFR_EN_FIFO, iobase + UFR); - - /* Receiver frame length */ - switch_bank(iobase, SET4); - outb(2048 & 0xff, iobase + 6); - outb((2048 >> 8) & 0x1f, iobase + 7); - - /* - * Init HP HSDL-1100 transceiver. - * - * Set IRX_MSL since we have 2 * receive paths IRRX, - * and IRRXH. Clear IRSL0D since we want IRSL0 * to - * be a input pin used for IRRXH - * - * IRRX pin 37 connected to receiver - * IRTX pin 38 connected to transmitter - * FIRRX pin 39 connected to receiver (IRSL0) - * CIRRX pin 40 connected to pin 37 - */ - switch_bank(iobase, SET7); - outb(0x40, iobase + 7); - - net_info_ratelimited("W83977AF (IR) driver loaded. Version: 0x%02x\n", - version); - - return 0; - } else { - /* Try next extented function register address */ - pr_debug("%s: Wrong chip version\n", __func__); - } - } - return -1; -} - -static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) -{ - int ir_mode = HCR_SIR; - int iobase; - __u8 set; - - iobase = self->io.fir_base; - - /* Update accounting for new speed */ - self->io.speed = speed; - - /* Save current bank */ - set = inb(iobase + SSR); - - /* Disable interrupts */ - switch_bank(iobase, SET0); - outb(0, iobase + ICR); - - /* Select Set 2 */ - switch_bank(iobase, SET2); - outb(0x00, iobase + ABHL); - - switch (speed) { - case 9600: outb(0x0c, iobase + ABLL); break; - case 19200: outb(0x06, iobase + ABLL); break; - case 38400: outb(0x03, iobase + ABLL); break; - case 57600: outb(0x02, iobase + ABLL); break; - case 115200: outb(0x01, iobase + ABLL); break; - case 576000: - ir_mode = HCR_MIR_576; - pr_debug("%s: handling baud of 576000\n", __func__); - break; - case 1152000: - ir_mode = HCR_MIR_1152; - pr_debug("%s: handling baud of 1152000\n", __func__); - break; - case 4000000: - ir_mode = HCR_FIR; - pr_debug("%s: handling baud of 4000000\n", __func__); - break; - default: - ir_mode = HCR_FIR; - pr_debug("%s: unknown baud rate of %d\n", __func__, speed); - break; - } - - /* Set speed mode */ - switch_bank(iobase, SET0); - outb(ir_mode, iobase + HCR); - - /* set FIFO size to 32 */ - switch_bank(iobase, SET2); - outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2); - - /* set FIFO threshold to TX17, RX16 */ - switch_bank(iobase, SET0); - outb(0x00, iobase + UFR); /* Reset */ - outb(UFR_EN_FIFO, iobase + UFR); /* First we must enable FIFO */ - outb(0xa7, iobase + UFR); - - netif_wake_queue(self->netdev); - - /* Enable some interrupts so we can receive frames */ - switch_bank(iobase, SET0); - if (speed > PIO_MAX_SPEED) { - outb(ICR_EFSFI, iobase + ICR); - w83977af_dma_receive(self); - } else { - outb(ICR_ERBRI, iobase + ICR); - } - - /* Restore SSR */ - outb(set, iobase + SSR); -} - -/* - * Function w83977af_hard_xmit (skb, dev) - * - * Sets up a DMA transfer to send the current frame. - * - */ -static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct w83977af_ir *self; - __s32 speed; - int iobase; - __u8 set; - int mtt; - - self = netdev_priv(dev); - - iobase = self->io.fir_base; - - pr_debug("%s: %ld, skb->len=%d\n", __func__, jiffies, (int)skb->len); - - /* Lock transmit buffer */ - netif_stop_queue(dev); - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame */ - if (!skb->len) { - w83977af_change_speed(self, speed); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - self->new_speed = speed; - } - - /* Save current set */ - set = inb(iobase + SSR); - - /* Decide if we should use PIO or DMA transfer */ - if (self->io.speed > PIO_MAX_SPEED) { - self->tx_buff.data = self->tx_buff.head; - skb_copy_from_linear_data(skb, self->tx_buff.data, skb->len); - self->tx_buff.len = skb->len; - - mtt = irda_get_mtt(skb); - pr_debug("%s: %ld, mtt=%d\n", __func__, jiffies, mtt); - if (mtt > 1000) - mdelay(mtt / 1000); - else if (mtt) - udelay(mtt); - - /* Enable DMA interrupt */ - switch_bank(iobase, SET0); - outb(ICR_EDMAI, iobase + ICR); - w83977af_dma_write(self, iobase); - } else { - self->tx_buff.data = self->tx_buff.head; - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - /* Add interrupt on tx low level (will fire immediately) */ - switch_bank(iobase, SET0); - outb(ICR_ETXTHI, iobase + ICR); - } - dev_kfree_skb(skb); - - /* Restore set register */ - outb(set, iobase + SSR); - - return NETDEV_TX_OK; -} - -/* - * Function w83977af_dma_write (self, iobase) - * - * Send frame using DMA - * - */ -static void w83977af_dma_write(struct w83977af_ir *self, int iobase) -{ - __u8 set; - - pr_debug("%s: len=%d\n", __func__, self->tx_buff.len); - - /* Save current set */ - set = inb(iobase + SSR); - - /* Disable DMA */ - switch_bank(iobase, SET0); - outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); - - /* Choose transmit DMA channel */ - switch_bank(iobase, SET2); - outb(ADCR1_D_CHSW | /*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase + ADCR1); - irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, - DMA_MODE_WRITE); - self->io.direction = IO_XMIT; - - /* Enable DMA */ - switch_bank(iobase, SET0); - outb(inb(iobase + HCR) | HCR_EN_DMA | HCR_TX_WT, iobase + HCR); - - /* Restore set register */ - outb(set, iobase + SSR); -} - -/* - * Function w83977af_pio_write (iobase, buf, len, fifo_size) - * - * - * - */ -static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) -{ - int actual = 0; - __u8 set; - - /* Save current bank */ - set = inb(iobase + SSR); - - switch_bank(iobase, SET0); - if (!(inb_p(iobase + USR) & USR_TSRE)) { - pr_debug("%s: warning, FIFO not empty yet!\n", __func__); - - fifo_size -= 17; - pr_debug("%s: %d bytes left in tx fifo\n", __func__, fifo_size); - } - - /* Fill FIFO with current frame */ - while ((fifo_size-- > 0) && (actual < len)) { - /* Transmit next byte */ - outb(buf[actual++], iobase + TBR); - } - - pr_debug("%s: fifo_size %d ; %d sent of %d\n", - __func__, fifo_size, actual, len); - - /* Restore bank */ - outb(set, iobase + SSR); - - return actual; -} - -/* - * Function w83977af_dma_xmit_complete (self) - * - * The transfer of a frame in finished. So do the necessary things - * - * - */ -static void w83977af_dma_xmit_complete(struct w83977af_ir *self) -{ - int iobase; - __u8 set; - - pr_debug("%s: %ld\n", __func__, jiffies); - - IRDA_ASSERT(self, return;); - - iobase = self->io.fir_base; - - /* Save current set */ - set = inb(iobase + SSR); - - /* Disable DMA */ - switch_bank(iobase, SET0); - outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); - - /* Check for underrun! */ - if (inb(iobase + AUDR) & AUDR_UNDR) { - pr_debug("%s: Transmit underrun!\n", __func__); - - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - - /* Clear bit, by writing 1 to it */ - outb(AUDR_UNDR, iobase + AUDR); - } else { - self->netdev->stats.tx_packets++; - } - - if (self->new_speed) { - w83977af_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - /* Unlock tx_buff and request another frame */ - /* Tell the network layer, that we want more frames */ - netif_wake_queue(self->netdev); - - /* Restore set */ - outb(set, iobase + SSR); -} - -/* - * Function w83977af_dma_receive (self) - * - * Get ready for receiving a frame. The device will initiate a DMA - * if it starts to receive a frame. - * - */ -static int w83977af_dma_receive(struct w83977af_ir *self) -{ - int iobase; - __u8 set; -#ifdef CONFIG_ARCH_NETWINDER - unsigned long flags; - __u8 hcr; -#endif - IRDA_ASSERT(self, return -1;); - - pr_debug("%s\n", __func__); - - iobase = self->io.fir_base; - - /* Save current set */ - set = inb(iobase + SSR); - - /* Disable DMA */ - switch_bank(iobase, SET0); - outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); - - /* Choose DMA Rx, DMA Fairness, and Advanced mode */ - switch_bank(iobase, SET2); - outb((inb(iobase + ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/ | ADCR1_ADV_SL, - iobase + ADCR1); - - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - -#ifdef CONFIG_ARCH_NETWINDER - spin_lock_irqsave(&self->lock, flags); - - disable_dma(self->io.dma); - clear_dma_ff(self->io.dma); - set_dma_mode(self->io.dma, DMA_MODE_READ); - set_dma_addr(self->io.dma, self->rx_buff_dma); - set_dma_count(self->io.dma, self->rx_buff.truesize); -#else - irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, - DMA_MODE_READ); -#endif - /* - * Reset Rx FIFO. This will also flush the ST_FIFO, it's very - * important that we don't reset the Tx FIFO since it might not - * be finished transmitting yet - */ - switch_bank(iobase, SET0); - outb(UFR_RXTL | UFR_TXTL | UFR_RXF_RST | UFR_EN_FIFO, iobase + UFR); - self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0; - - /* Enable DMA */ - switch_bank(iobase, SET0); -#ifdef CONFIG_ARCH_NETWINDER - hcr = inb(iobase + HCR); - outb(hcr | HCR_EN_DMA, iobase + HCR); - enable_dma(self->io.dma); - spin_unlock_irqrestore(&self->lock, flags); -#else - outb(inb(iobase + HCR) | HCR_EN_DMA, iobase + HCR); -#endif - /* Restore set */ - outb(set, iobase + SSR); - - return 0; -} - -/* - * Function w83977af_receive_complete (self) - * - * Finished with receiving a frame - * - */ -static int w83977af_dma_receive_complete(struct w83977af_ir *self) -{ - struct sk_buff *skb; - struct st_fifo *st_fifo; - int len; - int iobase; - __u8 set; - __u8 status; - - pr_debug("%s\n", __func__); - - st_fifo = &self->st_fifo; - - iobase = self->io.fir_base; - - /* Save current set */ - set = inb(iobase + SSR); - - iobase = self->io.fir_base; - - /* Read status FIFO */ - switch_bank(iobase, SET5); - while ((status = inb(iobase + FS_FO)) & FS_FO_FSFDR) { - st_fifo->entries[st_fifo->tail].status = status; - - st_fifo->entries[st_fifo->tail].len = inb(iobase + RFLFL); - st_fifo->entries[st_fifo->tail].len |= inb(iobase + RFLFH) << 8; - - st_fifo->tail++; - st_fifo->len++; - } - - while (st_fifo->len) { - /* Get first entry */ - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->head++; - st_fifo->len--; - - /* Check for errors */ - if (status & FS_FO_ERR_MSK) { - if (status & FS_FO_LST_FR) { - /* Add number of lost frames to stats */ - self->netdev->stats.rx_errors += len; - } else { - /* Skip frame */ - self->netdev->stats.rx_errors++; - - self->rx_buff.data += len; - - if (status & FS_FO_MX_LEX) - self->netdev->stats.rx_length_errors++; - - if (status & FS_FO_PHY_ERR) - self->netdev->stats.rx_frame_errors++; - - if (status & FS_FO_CRC_ERR) - self->netdev->stats.rx_crc_errors++; - } - /* The errors below can be reported in both cases */ - if (status & FS_FO_RX_OV) - self->netdev->stats.rx_fifo_errors++; - - if (status & FS_FO_FSF_OV) - self->netdev->stats.rx_fifo_errors++; - - } else { - /* Check if we have transferred all data to memory */ - switch_bank(iobase, SET0); - if (inb(iobase + USR) & USR_RDR) - udelay(80); /* Should be enough!? */ - - skb = dev_alloc_skb(len + 1); - if (!skb) { - pr_info("%s: memory squeeze, dropping frame\n", - __func__); - /* Restore set register */ - outb(set, iobase + SSR); - - return FALSE; - } - - /* Align to 20 bytes */ - skb_reserve(skb, 1); - - /* Copy frame without CRC */ - if (self->io.speed < 4000000) { - skb_put(skb, len - 2); - skb_copy_to_linear_data(skb, - self->rx_buff.data, - len - 2); - } else { - skb_put(skb, len - 4); - skb_copy_to_linear_data(skb, - self->rx_buff.data, - len - 4); - } - - /* Move to next frame */ - self->rx_buff.data += len; - self->netdev->stats.rx_packets++; - - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - } - } - /* Restore set register */ - outb(set, iobase + SSR); - - return TRUE; -} - -/* - * Function pc87108_pio_receive (self) - * - * Receive all data in receiver FIFO - * - */ -static void w83977af_pio_receive(struct w83977af_ir *self) -{ - __u8 byte = 0x00; - int iobase; - - IRDA_ASSERT(self, return;); - - iobase = self->io.fir_base; - - /* Receive all characters in Rx FIFO */ - do { - byte = inb(iobase + RBR); - async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, - byte); - } while (inb(iobase + USR) & USR_RDR); /* Data available */ -} - -/* - * Function w83977af_sir_interrupt (self, eir) - * - * Handle SIR interrupt - * - */ -static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) -{ - int actual; - __u8 new_icr = 0; - __u8 set; - int iobase; - - pr_debug("%s: isr=%#x\n", __func__, isr); - - iobase = self->io.fir_base; - /* Transmit FIFO low on data */ - if (isr & ISR_TXTH_I) { - /* Write data left in transmit buffer */ - actual = w83977af_pio_write(self->io.fir_base, - self->tx_buff.data, - self->tx_buff.len, - self->io.fifo_size); - - self->tx_buff.data += actual; - self->tx_buff.len -= actual; - - self->io.direction = IO_XMIT; - - /* Check if finished */ - if (self->tx_buff.len > 0) { - new_icr |= ICR_ETXTHI; - } else { - set = inb(iobase + SSR); - switch_bank(iobase, SET0); - outb(AUDR_SFEND, iobase + AUDR); - outb(set, iobase + SSR); - - self->netdev->stats.tx_packets++; - - /* Feed me more packets */ - netif_wake_queue(self->netdev); - new_icr |= ICR_ETBREI; - } - } - /* Check if transmission has completed */ - if (isr & ISR_TXEMP_I) { - /* Check if we need to change the speed? */ - if (self->new_speed) { - pr_debug("%s: Changing speed!\n", __func__); - w83977af_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - /* Turn around and get ready to receive some data */ - self->io.direction = IO_RECV; - new_icr |= ICR_ERBRI; - } - - /* Rx FIFO threshold or timeout */ - if (isr & ISR_RXTH_I) { - w83977af_pio_receive(self); - - /* Keep receiving */ - new_icr |= ICR_ERBRI; - } - return new_icr; -} - -/* - * Function pc87108_fir_interrupt (self, eir) - * - * Handle MIR/FIR interrupt - * - */ -static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) -{ - __u8 new_icr = 0; - __u8 set; - int iobase; - - iobase = self->io.fir_base; - set = inb(iobase + SSR); - - /* End of frame detected in FIFO */ - if (isr & (ISR_FEND_I | ISR_FSF_I)) { - if (w83977af_dma_receive_complete(self)) { - /* Wait for next status FIFO interrupt */ - new_icr |= ICR_EFSFI; - } else { - /* DMA not finished yet */ - - /* Set timer value, resolution 1 ms */ - switch_bank(iobase, SET4); - outb(0x01, iobase + TMRL); /* 1 ms */ - outb(0x00, iobase + TMRH); - - /* Start timer */ - outb(IR_MSL_EN_TMR, iobase + IR_MSL); - - new_icr |= ICR_ETMRI; - } - } - /* Timer finished */ - if (isr & ISR_TMR_I) { - /* Disable timer */ - switch_bank(iobase, SET4); - outb(0, iobase + IR_MSL); - - /* Clear timer event */ - /* switch_bank(iobase, SET0); */ -/* outb(ASCR_CTE, iobase+ASCR); */ - - /* Check if this is a TX timer interrupt */ - if (self->io.direction == IO_XMIT) { - w83977af_dma_write(self, iobase); - - new_icr |= ICR_EDMAI; - } else { - /* Check if DMA has now finished */ - w83977af_dma_receive_complete(self); - - new_icr |= ICR_EFSFI; - } - } - /* Finished with DMA */ - if (isr & ISR_DMA_I) { - w83977af_dma_xmit_complete(self); - - /* Check if there are more frames to be transmitted */ - /* if (irda_device_txqueue_empty(self)) { */ - - /* Prepare for receive - * - * ** Netwinder Tx DMA likes that we do this anyway ** - */ - w83977af_dma_receive(self); - new_icr = ICR_EFSFI; - /* } */ - } - - /* Restore set */ - outb(set, iobase + SSR); - - return new_icr; -} - -/* - * Function w83977af_interrupt (irq, dev_id, regs) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t w83977af_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct w83977af_ir *self; - __u8 set, icr, isr; - int iobase; - - self = netdev_priv(dev); - - iobase = self->io.fir_base; - - /* Save current bank */ - set = inb(iobase + SSR); - switch_bank(iobase, SET0); - - icr = inb(iobase + ICR); - isr = inb(iobase + ISR) & icr; /* Mask out the interesting ones */ - - outb(0, iobase + ICR); /* Disable interrupts */ - - if (isr) { - /* Dispatch interrupt handler for the current speed */ - if (self->io.speed > PIO_MAX_SPEED) - icr = w83977af_fir_interrupt(self, isr); - else - icr = w83977af_sir_interrupt(self, isr); - } - - outb(icr, iobase + ICR); /* Restore (new) interrupts */ - outb(set, iobase + SSR); /* Restore bank register */ - return IRQ_RETVAL(isr); -} - -/* - * Function w83977af_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int w83977af_is_receiving(struct w83977af_ir *self) -{ - int status = FALSE; - int iobase; - __u8 set; - - IRDA_ASSERT(self, return FALSE;); - - if (self->io.speed > 115200) { - iobase = self->io.fir_base; - - /* Check if rx FIFO is not empty */ - set = inb(iobase + SSR); - switch_bank(iobase, SET2); - if ((inb(iobase + RXFDTH) & 0x3f) != 0) { - /* We are receiving something */ - status = TRUE; - } - outb(set, iobase + SSR); - } else { - status = (self->rx_buff.state != OUTSIDE_FRAME); - } - - return status; -} - -/* - * Function w83977af_net_open (dev) - * - * Start the device - * - */ -static int w83977af_net_open(struct net_device *dev) -{ - struct w83977af_ir *self; - int iobase; - char hwname[32]; - __u8 set; - - IRDA_ASSERT(dev, return -1;); - self = netdev_priv(dev); - - IRDA_ASSERT(self, return 0;); - - iobase = self->io.fir_base; - - if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, - (void *)dev)) { - return -EAGAIN; - } - /* - * Always allocate the DMA channel after the IRQ, - * and clean up on failure. - */ - if (request_dma(self->io.dma, dev->name)) { - free_irq(self->io.irq, dev); - return -EAGAIN; - } - - /* Save current set */ - set = inb(iobase + SSR); - - /* Enable some interrupts so we can receive frames again */ - switch_bank(iobase, SET0); - if (self->io.speed > 115200) { - outb(ICR_EFSFI, iobase + ICR); - w83977af_dma_receive(self); - } else { - outb(ICR_ERBRI, iobase + ICR); - } - - /* Restore bank register */ - outb(set, iobase + SSR); - - /* Ready to play! */ - netif_start_queue(dev); - - /* Give self a hardware name */ - sprintf(hwname, "w83977af @ 0x%03x", self->io.fir_base); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open(dev, &self->qos, hwname); - - return 0; -} - -/* - * Function w83977af_net_close (dev) - * - * Stop the device - * - */ -static int w83977af_net_close(struct net_device *dev) -{ - struct w83977af_ir *self; - int iobase; - __u8 set; - - IRDA_ASSERT(dev, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self, return 0;); - - iobase = self->io.fir_base; - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - disable_dma(self->io.dma); - - /* Save current set */ - set = inb(iobase + SSR); - - /* Disable interrupts */ - switch_bank(iobase, SET0); - outb(0, iobase + ICR); - - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - - /* Restore bank register */ - outb(set, iobase + SSR); - - return 0; -} - -/* - * Function w83977af_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *)rq; - struct w83977af_ir *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT(dev, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self, return -1;); - - pr_debug("%s: %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - spin_lock_irqsave(&self->lock, flags); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - w83977af_change_speed(self, irq->ifr_baudrate); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = w83977af_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } -out: - spin_unlock_irqrestore(&self->lock, flags); - return ret; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver"); -MODULE_LICENSE("GPL"); - -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Mimimum Turn Time"); -module_param_hw_array(io, int, ioport, NULL, 0); -MODULE_PARM_DESC(io, "Base I/O addresses"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "IRQ lines"); - -/* - * Function init_module (void) - * - * - * - */ -module_init(w83977af_init); - -/* - * Function cleanup_module (void) - * - * - * - */ -module_exit(w83977af_cleanup); diff --git a/drivers/net/irda/w83977af_ir.h b/drivers/net/irda/w83977af_ir.h deleted file mode 100644 index fefe9b11e200..000000000000 --- a/drivers/net/irda/w83977af_ir.h +++ /dev/null @@ -1,198 +0,0 @@ -/********************************************************************* - * - * Filename: w83977af_ir.h - * Version: - * Description: - * Status: Experimental. - * Author: Paul VanderSpek - * Created at: Thu Nov 19 13:55:34 1998 - * Modified at: Tue Jan 11 13:08:19 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef W83977AF_IR_H -#define W83977AF_IR_H - -#include -#include - -/* Flags for configuration register CRF0 */ -#define ENBNKSEL 0x01 -#define APEDCRC 0x02 -#define TXW4C 0x04 -#define RXW4C 0x08 - -/* Bank 0 */ -#define RBR 0x00 /* Receiver buffer register */ -#define TBR 0x00 /* Transmitter buffer register */ - -#define ICR 0x01 /* Interrupt configuration register */ -#define ICR_ERBRI 0x01 /* Receiver buffer register interrupt */ -#define ICR_ETBREI 0x02 /* Transeiver empty interrupt */ -#define ICR_EUSRI 0x04//* IR status interrupt */ -#define ICR_EHSRI 0x04 -#define ICR_ETXURI 0x04 /* Tx underrun */ -#define ICR_EDMAI 0x10 /* DMA interrupt */ -#define ICR_ETXTHI 0x20 /* Transmitter threshold interrupt */ -#define ICR_EFSFI 0x40 /* Frame status FIFO interrupt */ -#define ICR_ETMRI 0x80 /* Timer interrupt */ - -#define UFR 0x02 /* FIFO control register */ -#define UFR_EN_FIFO 0x01 /* Enable FIFO's */ -#define UFR_RXF_RST 0x02 /* Reset Rx FIFO */ -#define UFR_TXF_RST 0x04 /* Reset Tx FIFO */ -#define UFR_RXTL 0x80 /* Rx FIFO threshold (set to 16) */ -#define UFR_TXTL 0x20 /* Tx FIFO threshold (set to 17) */ - -#define ISR 0x02 /* Interrupt status register */ -#define ISR_RXTH_I 0x01 /* Receive threshold interrupt */ -#define ISR_TXEMP_I 0x02 /* Transmitter empty interrupt */ -#define ISR_FEND_I 0x04 -#define ISR_DMA_I 0x10 -#define ISR_TXTH_I 0x20 /* Transmitter threshold interrupt */ -#define ISR_FSF_I 0x40 -#define ISR_TMR_I 0x80 /* Timer interrupt */ - -#define UCR 0x03 /* Uart control register */ -#define UCR_DLS8 0x03 /* 8N1 */ - -#define SSR 0x03 /* Sets select register */ -#define SET0 UCR_DLS8 /* Make sure we keep 8N1 */ -#define SET1 (0x80|UCR_DLS8) /* Make sure we keep 8N1 */ -#define SET2 0xE0 -#define SET3 0xE4 -#define SET4 0xE8 -#define SET5 0xEC -#define SET6 0xF0 -#define SET7 0xF4 - -#define HCR 0x04 -#define HCR_MODE_MASK ~(0xD0) -#define HCR_SIR 0x60 -#define HCR_MIR_576 0x20 -#define HCR_MIR_1152 0x80 -#define HCR_FIR 0xA0 -#define HCR_EN_DMA 0x04 -#define HCR_EN_IRQ 0x08 -#define HCR_TX_WT 0x08 - -#define USR 0x05 /* IR status register */ -#define USR_RDR 0x01 /* Receive data ready */ -#define USR_TSRE 0x40 /* Transmitter empty? */ - -#define AUDR 0x07 -#define AUDR_SFEND 0x08 /* Set a frame end */ -#define AUDR_RXBSY 0x20 /* Rx busy */ -#define AUDR_UNDR 0x40 /* Transeiver underrun */ - -/* Set 2 */ -#define ABLL 0x00 /* Advanced baud rate divisor latch (low byte) */ -#define ABHL 0x01 /* Advanced baud rate divisor latch (high byte) */ - -#define ADCR1 0x02 -#define ADCR1_ADV_SL 0x01 -#define ADCR1_D_CHSW 0x08 /* the specs are wrong. its bit 3, not 4 */ -#define ADCR1_DMA_F 0x02 - -#define ADCR2 0x04 -#define ADCR2_TXFS32 0x01 -#define ADCR2_RXFS32 0x04 - -#define RXFDTH 0x07 - -/* Set 3 */ -#define AUID 0x00 - -/* Set 4 */ -#define TMRL 0x00 /* Timer value register (low byte) */ -#define TMRH 0x01 /* Timer value register (high byte) */ - -#define IR_MSL 0x02 /* Infrared mode select */ -#define IR_MSL_EN_TMR 0x01 /* Enable timer */ - -#define TFRLL 0x04 /* Transmitter frame length (low byte) */ -#define TFRLH 0x05 /* Transmitter frame length (high byte) */ -#define RFRLL 0x06 /* Receiver frame length (low byte) */ -#define RFRLH 0x07 /* Receiver frame length (high byte) */ - -/* Set 5 */ - -#define FS_FO 0x05 /* Frame status FIFO */ -#define FS_FO_FSFDR 0x80 /* Frame status FIFO data ready */ -#define FS_FO_LST_FR 0x40 /* Frame lost */ -#define FS_FO_MX_LEX 0x10 /* Max frame len exceeded */ -#define FS_FO_PHY_ERR 0x08 /* Physical layer error */ -#define FS_FO_CRC_ERR 0x04 -#define FS_FO_RX_OV 0x02 /* Receive overrun */ -#define FS_FO_FSF_OV 0x01 /* Frame status FIFO overrun */ -#define FS_FO_ERR_MSK 0x5f /* Error mask */ - -#define RFLFL 0x06 -#define RFLFH 0x07 - -/* Set 6 */ -#define IR_CFG2 0x00 -#define IR_CFG2_DIS_CRC 0x02 - -/* Set 7 */ -#define IRM_CR 0x07 /* Infrared module control register */ -#define IRM_CR_IRX_MSL 0x40 -#define IRM_CR_AF_MNT 0x80 /* Automatic format */ - -/* For storing entries in the status FIFO */ -struct st_fifo_entry { - int status; - int len; -}; - -struct st_fifo { - struct st_fifo_entry entries[10]; - int head; - int tail; - int len; -}; - -/* Private data for each instance */ -struct w83977af_ir { - struct st_fifo st_fifo; - - int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */ - int tx_len; /* Number of frames in tx_buff */ - - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; /* QoS capabilities for this device */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - /* Note : currently locking is *very* incomplete, but this - * will get you started. Check in nsc-ircc.c for a proper - * locking strategy. - Jean II */ - spinlock_t lock; /* For serializing operations */ - - __u32 new_speed; -}; - -static inline void switch_bank( int iobase, int set) -{ - outb(set, iobase+SSR); -} - -#endif diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 509fbacf9a7e..fced929a0e67 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -3,6 +3,7 @@ obj-y += media/ obj-y += typec/ obj-$(CONFIG_IRDA) += irda/net/ +obj-$(CONFIG_IRDA) += irda/drivers/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ obj-$(CONFIG_COMEDI) += comedi/ obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/ diff --git a/drivers/staging/irda/drivers/Kconfig b/drivers/staging/irda/drivers/Kconfig new file mode 100644 index 000000000000..e070e1222733 --- /dev/null +++ b/drivers/staging/irda/drivers/Kconfig @@ -0,0 +1,398 @@ +menu "Infrared-port device drivers" + depends on IRDA!=n + +comment "SIR device drivers" + +config IRTTY_SIR + tristate "IrTTY (uses Linux serial driver)" + depends on IRDA && TTY + help + Say Y here if you want to build support for the IrTTY line + discipline. To compile it as a module, choose M here: the module + will be called irtty-sir. IrTTY makes it possible to use Linux's + own serial driver for all IrDA ports that are 16550 compatible. + Most IrDA chips are 16550 compatible so you should probably say Y + to this option. Using IrTTY will however limit the speed of the + connection to 115200 bps (IrDA SIR mode). + + If unsure, say Y. + +config BFIN_SIR + tristate "Blackfin SIR on UART" + depends on BLACKFIN && IRDA + default n + help + Say Y here if your want to enable SIR function on Blackfin UART + devices. + + To activate this driver you can start irattach like: + "irattach irda0 -s" + + Saying M, it will be built as a module named bfin_sir. + + Note that you need to turn off one of the serial drivers for SIR + to use that UART. + +config BFIN_SIR0 + bool "Blackfin SIR on UART0" + depends on BFIN_SIR && !SERIAL_BFIN_UART0 + +config BFIN_SIR1 + bool "Blackfin SIR on UART1" + depends on BFIN_SIR && !SERIAL_BFIN_UART1 && (!BF531 && !BF532 && !BF533 && !BF561) + +config BFIN_SIR2 + bool "Blackfin SIR on UART2" + depends on BFIN_SIR && !SERIAL_BFIN_UART2 && (BF54x || BF538 || BF539) + +config BFIN_SIR3 + bool "Blackfin SIR on UART3" + depends on BFIN_SIR && !SERIAL_BFIN_UART3 && (BF54x) + +choice + prompt "SIR Mode" + depends on BFIN_SIR + default SIR_BFIN_DMA + +config SIR_BFIN_DMA + bool "DMA mode" + depends on !DMA_UNCACHED_NONE + +config SIR_BFIN_PIO + bool "PIO mode" +endchoice + +config SH_SIR + tristate "SuperH SIR on UART" + depends on IRDA && SUPERH && \ + (CPU_SUBTYPE_SH7722 || CPU_SUBTYPE_SH7723 || \ + CPU_SUBTYPE_SH7724) + default n + help + Say Y here if your want to enable SIR function on SuperH UART + devices. + +comment "Dongle support" + +config DONGLE + bool "Serial dongle support" + depends on IRTTY_SIR + help + Say Y here if you have an infrared device that connects to your + computer's serial port. These devices are called dongles. Then say Y + or M to the driver for your particular dongle below. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about serial dongles. + +config ESI_DONGLE + tristate "ESI JetEye PC dongle" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the Extended Systems + JetEye PC dongle. To compile it as a module, choose M here. The ESI + dongle attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. To activate support for ESI + dongles you will have to start irattach like this: + "irattach -d esi". + +config ACTISYS_DONGLE + tristate "ACTiSYS IR-220L and IR220L+ dongle" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the ACTiSYS IR-220L and + IR220L+ dongles. To compile it as a module, choose M here. The + ACTiSYS dongles attaches to the normal 9-pin serial port connector, + and can currently only be used by IrTTY. To activate support for + ACTiSYS dongles you will have to start irattach like this: + "irattach -d actisys" or "irattach -d actisys+". + +config TEKRAM_DONGLE + tristate "Tekram IrMate 210B dongle" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the Tekram IrMate 210B + dongle. To compile it as a module, choose M here. The Tekram dongle + attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. To activate support for Tekram + dongles you will have to start irattach like this: + "irattach -d tekram". + +config TOIM3232_DONGLE + tristate "TOIM3232 IrDa dongle" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the Vishay/Temic + TOIM3232 and TOIM4232 based dongles. + To compile it as a module, choose M here. + +config LITELINK_DONGLE + tristate "Parallax LiteLink dongle" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the Parallax Litelink + dongle. To compile it as a module, choose M here. The Parallax + dongle attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. To activate support for Parallax + dongles you will have to start irattach like this: + "irattach -d litelink". + +config MA600_DONGLE + tristate "Mobile Action MA600 dongle" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the Mobile Action MA600 + dongle. To compile it as a module, choose M here. The MA600 dongle + attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. The driver should also support + the MA620 USB version of the dongle, if the integrated USB-to-RS232 + converter is supported by usbserial. To activate support for + MA600 dongle you will have to start irattach like this: + "irattach -d ma600". + +config GIRBIL_DONGLE + tristate "Greenwich GIrBIL dongle" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the Greenwich GIrBIL + dongle. If you want to compile it as a module, choose M here. + The Greenwich dongle attaches to the normal 9-pin serial port + connector, and can currently only be used by IrTTY. To activate + support for Greenwich dongles you will have to start irattach + like this: "irattach -d girbil". + +config MCP2120_DONGLE + tristate "Microchip MCP2120" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the Microchip MCP2120 + dongle. If you want to compile it as a module, choose M here. + The MCP2120 dongle attaches to the normal 9-pin serial port + connector, and can currently only be used by IrTTY. To activate + support for MCP2120 dongles you will have to start irattach + like this: "irattach -d mcp2120". + + You must build this dongle yourself. For more information see: + + +config OLD_BELKIN_DONGLE + tristate "Old Belkin dongle" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the Adaptec Airport 1000 + and 2000 dongles. If you want to compile it as a module, choose + M here. Some information is contained in the comments + at the top of . + +config ACT200L_DONGLE + tristate "ACTiSYS IR-200L dongle" + depends on IRTTY_SIR && DONGLE && IRDA + help + Say Y here if you want to build support for the ACTiSYS IR-200L + dongle. If you want to compile it as a module, choose M here. + The ACTiSYS IR-200L dongle attaches to the normal 9-pin serial + port connector, and can currently only be used by IrTTY. + To activate support for ACTiSYS IR-200L dongle you will have to + start irattach like this: "irattach -d act200l". + +config KINGSUN_DONGLE + tristate "KingSun/DonShine DS-620 IrDA-USB dongle" + depends on IRDA && USB + help + Say Y or M here if you want to build support for the KingSun/DonShine + DS-620 IrDA-USB bridge device driver. + + This USB bridge does not conform to the IrDA-USB device class + specification, and therefore needs its own specific driver. This + dongle supports SIR speed only (9600 bps). + + To compile it as a module, choose M here: the module will be called + kingsun-sir. + +config KSDAZZLE_DONGLE + tristate "KingSun Dazzle IrDA-USB dongle" + depends on IRDA && USB + help + Say Y or M here if you want to build support for the KingSun Dazzle + IrDA-USB bridge device driver. + + This USB bridge does not conform to the IrDA-USB device class + specification, and therefore needs its own specific driver. This + dongle supports SIR speeds only (9600 through 115200 bps). + + To compile it as a module, choose M here: the module will be called + ksdazzle-sir. + +config KS959_DONGLE + tristate "KingSun KS-959 IrDA-USB dongle" + depends on IRDA && USB + help + Say Y or M here if you want to build support for the KingSun KS-959 + IrDA-USB bridge device driver. + + This USB bridge does not conform to the IrDA-USB device class + specification, and therefore needs its own specific driver. This + dongle supports SIR speeds only (9600 through 57600 bps). + + To compile it as a module, choose M here: the module will be called + ks959-sir. + +comment "FIR device drivers" + +config USB_IRDA + tristate "IrDA USB dongles" + depends on IRDA && USB + select FW_LOADER + ---help--- + Say Y here if you want to build support for the USB IrDA FIR Dongle + device driver. To compile it as a module, choose M here: the module + will be called irda-usb. IrDA-USB support the various IrDA USB + dongles available and most of their peculiarities. Those dongles + plug in the USB port of your computer, are plug and play, and + support SIR and FIR (4Mbps) speeds. On the other hand, those + dongles tend to be less efficient than a FIR chipset. + + Please note that the driver is still experimental. And of course, + you will need both USB and IrDA support in your kernel... + +config SIGMATEL_FIR + tristate "SigmaTel STIr4200 bridge" + depends on IRDA && USB + select CRC32 + ---help--- + Say Y here if you want to build support for the SigmaTel STIr4200 + USB IrDA FIR bridge device driver. + + USB bridge based on the SigmaTel STIr4200 don't conform to the + IrDA-USB device class specification, and therefore need their + own specific driver. Those dongles support SIR and FIR (4Mbps) + speeds. + + To compile it as a module, choose M here: the module will be called + stir4200. + +config NSC_FIR + tristate "NSC PC87108/PC87338" + depends on IRDA && ISA_DMA_API + help + Say Y here if you want to build support for the NSC PC87108 and + PC87338 IrDA chipsets. This driver supports SIR, + MIR and FIR (4Mbps) speeds. + + To compile it as a module, choose M here: the module will be called + nsc-ircc. + +config WINBOND_FIR + tristate "Winbond W83977AF (IR)" + depends on IRDA && ISA_DMA_API + help + Say Y here if you want to build IrDA support for the Winbond + W83977AF super-io chipset. This driver should be used for the IrDA + chipset in the Corel NetWinder. The driver supports SIR, MIR and + FIR (4Mbps) speeds. + + To compile it as a module, choose M here: the module will be called + w83977af_ir. + +config TOSHIBA_FIR + tristate "Toshiba Type-O IR Port" + depends on IRDA && PCI && !64BIT && VIRT_TO_BUS + help + Say Y here if you want to build support for the Toshiba Type-O IR + and Donau oboe chipsets. These chipsets are used by the Toshiba + Libretto 100/110CT, Tecra 8100, Portege 7020 and many more laptops. + To compile it as a module, choose M here: the module will be called + donauboe. + +config AU1000_FIR + tristate "Alchemy IrDA SIR/FIR" + depends on IRDA && MIPS_ALCHEMY + help + Say Y/M here to build support the IrDA peripheral on the + Alchemy Au1000 and Au1100 SoCs. + Say M to build a module; it will be called au1k_ir.ko + +config SMC_IRCC_FIR + tristate "SMSC IrCC" + depends on IRDA && ISA_DMA_API + help + Say Y here if you want to build support for the SMC Infrared + Communications Controller. It is used in a wide variety of + laptops (Fujitsu, Sony, Compaq and some Toshiba). + To compile it as a module, choose M here: the module will be called + smsc-ircc2.o. + +config ALI_FIR + tristate "ALi M5123 FIR" + depends on IRDA && ISA_DMA_API + help + Say Y here if you want to build support for the ALi M5123 FIR + Controller. The ALi M5123 FIR Controller is embedded in ALi M1543C, + M1535, M1535D, M1535+, M1535D South Bridge. This driver supports + SIR, MIR and FIR (4Mbps) speeds. + + To compile it as a module, choose M here: the module will be called + ali-ircc. + +config VLSI_FIR + tristate "VLSI 82C147 SIR/MIR/FIR" + depends on IRDA && PCI + help + Say Y here if you want to build support for the VLSI 82C147 + PCI-IrDA Controller. This controller is used by the HP OmniBook 800 + and 5500 notebooks. The driver provides support for SIR, MIR and + FIR (4Mbps) speeds. + + To compile it as a module, choose M here: the module will be called + vlsi_ir. + +config SA1100_FIR + tristate "SA1100 Internal IR" + depends on ARCH_SA1100 && IRDA && DMA_SA11X0 + +config VIA_FIR + tristate "VIA VT8231/VT1211 SIR/MIR/FIR" + depends on IRDA && ISA_DMA_API + help + Say Y here if you want to build support for the VIA VT8231 + and VIA VT1211 IrDA controllers, found on the motherboards using + those VIA chipsets. To use this controller, you will need + to plug a specific 5 pins FIR IrDA dongle in the specific + motherboard connector. The driver provides support for SIR, MIR + and FIR (4Mbps) speeds. + + You will need to specify the 'dongle_id' module parameter to + indicate the FIR dongle attached to the controller. + + To compile it as a module, choose M here: the module will be called + via-ircc. + +config PXA_FICP + tristate "Intel PXA2xx Internal FICP" + depends on ARCH_PXA && IRDA + help + Say Y or M here if you want to build support for the PXA2xx + built-in IRDA interface which can support both SIR and FIR. + This driver relies on platform specific helper routines so + available capabilities may vary from one PXA2xx target to + another. + +config MCS_FIR + tristate "MosChip MCS7780 IrDA-USB dongle" + depends on IRDA && USB + select CRC32 + help + Say Y or M here if you want to build support for the MosChip + MCS7780 IrDA-USB bridge device driver. + + USB bridge based on the MosChip MCS7780 don't conform to the + IrDA-USB device class specification, and therefore need their + own specific driver. Those dongles support SIR and FIR (4Mbps) + speeds. + + To compile it as a module, choose M here: the module will be called + mcs7780. + +endmenu + diff --git a/drivers/staging/irda/drivers/Makefile b/drivers/staging/irda/drivers/Makefile new file mode 100644 index 000000000000..4c344433dae5 --- /dev/null +++ b/drivers/staging/irda/drivers/Makefile @@ -0,0 +1,42 @@ +# +# Makefile for the Linux IrDA infrared port device drivers. +# +# 9 Aug 2000, Christoph Hellwig +# Rewritten to use lists instead of if-statements. +# + +# FIR drivers +obj-$(CONFIG_USB_IRDA) += irda-usb.o +obj-$(CONFIG_SIGMATEL_FIR) += stir4200.o +obj-$(CONFIG_NSC_FIR) += nsc-ircc.o +obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o +obj-$(CONFIG_SA1100_FIR) += sa1100_ir.o +obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o +obj-$(CONFIG_SMC_IRCC_FIR) += smsc-ircc2.o +obj-$(CONFIG_ALI_FIR) += ali-ircc.o +obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o +obj-$(CONFIG_VIA_FIR) += via-ircc.o +obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o +obj-$(CONFIG_MCS_FIR) += mcs7780.o +obj-$(CONFIG_AU1000_FIR) += au1k_ir.o +# SIR drivers +obj-$(CONFIG_IRTTY_SIR) += irtty-sir.o sir-dev.o +obj-$(CONFIG_BFIN_SIR) += bfin_sir.o +obj-$(CONFIG_SH_SIR) += sh_sir.o +# dongle drivers for SIR drivers +obj-$(CONFIG_ESI_DONGLE) += esi-sir.o +obj-$(CONFIG_TEKRAM_DONGLE) += tekram-sir.o +obj-$(CONFIG_ACTISYS_DONGLE) += actisys-sir.o +obj-$(CONFIG_LITELINK_DONGLE) += litelink-sir.o +obj-$(CONFIG_GIRBIL_DONGLE) += girbil-sir.o +obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin-sir.o +obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o +obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o +obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o +obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o +obj-$(CONFIG_KINGSUN_DONGLE) += kingsun-sir.o +obj-$(CONFIG_KSDAZZLE_DONGLE) += ksdazzle-sir.o +obj-$(CONFIG_KS959_DONGLE) += ks959-sir.o + +# The SIR helper module +sir-dev-objs := sir_dev.o sir_dongle.o diff --git a/drivers/staging/irda/drivers/act200l-sir.c b/drivers/staging/irda/drivers/act200l-sir.c new file mode 100644 index 000000000000..e8917511e1aa --- /dev/null +++ b/drivers/staging/irda/drivers/act200l-sir.c @@ -0,0 +1,250 @@ +/********************************************************************* + * + * Filename: act200l.c + * Version: 0.8 + * Description: Implementation for the ACTiSYS ACT-IR200L dongle + * Status: Experimental. + * Author: SHIMIZU Takuya + * Created at: Fri Aug 3 17:35:42 2001 + * Modified at: Fri Aug 17 10:22:40 2001 + * Modified by: SHIMIZU Takuya + * + * Copyright (c) 2001 SHIMIZU Takuya, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + ********************************************************************/ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +static int act200l_reset(struct sir_dev *dev); +static int act200l_open(struct sir_dev *dev); +static int act200l_close(struct sir_dev *dev); +static int act200l_change_speed(struct sir_dev *dev, unsigned speed); + +/* Regsiter 0: Control register #1 */ +#define ACT200L_REG0 0x00 +#define ACT200L_TXEN 0x01 /* Enable transmitter */ +#define ACT200L_RXEN 0x02 /* Enable receiver */ + +/* Register 1: Control register #2 */ +#define ACT200L_REG1 0x10 +#define ACT200L_LODB 0x01 /* Load new baud rate count value */ +#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */ + +/* Register 4: Output Power register */ +#define ACT200L_REG4 0x40 +#define ACT200L_OP0 0x01 /* Enable LED1C output */ +#define ACT200L_OP1 0x02 /* Enable LED2C output */ +#define ACT200L_BLKR 0x04 + +/* Register 5: Receive Mode register */ +#define ACT200L_REG5 0x50 +#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */ + +/* Register 6: Receive Sensitivity register #1 */ +#define ACT200L_REG6 0x60 +#define ACT200L_RS0 0x01 /* receive threshold bit 0 */ +#define ACT200L_RS1 0x02 /* receive threshold bit 1 */ + +/* Register 7: Receive Sensitivity register #2 */ +#define ACT200L_REG7 0x70 +#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */ + +/* Register 8,9: Baud Rate Dvider register #1,#2 */ +#define ACT200L_REG8 0x80 +#define ACT200L_REG9 0x90 + +#define ACT200L_2400 0x5f +#define ACT200L_9600 0x17 +#define ACT200L_19200 0x0b +#define ACT200L_38400 0x05 +#define ACT200L_57600 0x03 +#define ACT200L_115200 0x01 + +/* Register 13: Control register #3 */ +#define ACT200L_REG13 0xd0 +#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */ + +/* Register 15: Status register */ +#define ACT200L_REG15 0xf0 + +/* Register 21: Control register #4 */ +#define ACT200L_REG21 0x50 +#define ACT200L_EXCK 0x02 /* Disable clock output driver */ +#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */ + +static struct dongle_driver act200l = { + .owner = THIS_MODULE, + .driver_name = "ACTiSYS ACT-IR200L", + .type = IRDA_ACT200L_DONGLE, + .open = act200l_open, + .close = act200l_close, + .reset = act200l_reset, + .set_speed = act200l_change_speed, +}; + +static int __init act200l_sir_init(void) +{ + return irda_register_dongle(&act200l); +} + +static void __exit act200l_sir_cleanup(void) +{ + irda_unregister_dongle(&act200l); +} + +static int act200l_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + /* Power on the dongle */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Set the speeds we can accept */ + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x03; + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int act200l_close(struct sir_dev *dev) +{ + /* Power off the dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function act200l_change_speed (dev, speed) + * + * Set the speed for the ACTiSYS ACT-IR200L type dongle. + * + */ +static int act200l_change_speed(struct sir_dev *dev, unsigned speed) +{ + u8 control[3]; + int ret = 0; + + /* Clear DTR and set RTS to enter command mode */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + switch (speed) { + default: + ret = -EINVAL; + /* fall through */ + case 9600: + control[0] = ACT200L_REG8 | (ACT200L_9600 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_9600 >> 4) & 0x0f); + break; + case 19200: + control[0] = ACT200L_REG8 | (ACT200L_19200 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_19200 >> 4) & 0x0f); + break; + case 38400: + control[0] = ACT200L_REG8 | (ACT200L_38400 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_38400 >> 4) & 0x0f); + break; + case 57600: + control[0] = ACT200L_REG8 | (ACT200L_57600 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_57600 >> 4) & 0x0f); + break; + case 115200: + control[0] = ACT200L_REG8 | (ACT200L_115200 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f); + break; + } + control[2] = ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE; + + /* Write control bytes */ + sirdev_raw_write(dev, control, 3); + msleep(5); + + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + dev->speed = speed; + return ret; +} + +/* + * Function act200l_reset (driver) + * + * Reset the ACTiSYS ACT-IR200L type dongle. + */ + +#define ACT200L_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1) +#define ACT200L_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2) + +static int act200l_reset(struct sir_dev *dev) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + static const u8 control[9] = { + ACT200L_REG15, + ACT200L_REG13 | ACT200L_SHDW, + ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL, + ACT200L_REG13, + ACT200L_REG7 | ACT200L_ENPOS, + ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1, + ACT200L_REG5 | ACT200L_RWIDL, + ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR, + ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN + }; + int ret = 0; + + switch (state) { + case SIRDEV_STATE_DONGLE_RESET: + /* Reset the dongle : set RTS low for 25 ms */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + state = ACT200L_STATE_WAIT1_RESET; + delay = 50; + break; + + case ACT200L_STATE_WAIT1_RESET: + /* Clear DTR and set RTS to enter command mode */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + udelay(25); /* better wait for some short while */ + + /* Write control bytes */ + sirdev_raw_write(dev, control, sizeof(control)); + state = ACT200L_STATE_WAIT2_RESET; + delay = 15; + break; + + case ACT200L_STATE_WAIT2_RESET: + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + dev->speed = 9600; + break; + default: + net_err_ratelimited("%s(), unknown state %d\n", + __func__, state); + ret = -1; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +MODULE_AUTHOR("SHIMIZU Takuya "); +MODULE_DESCRIPTION("ACTiSYS ACT-IR200L dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-10"); /* IRDA_ACT200L_DONGLE */ + +module_init(act200l_sir_init); +module_exit(act200l_sir_cleanup); diff --git a/drivers/staging/irda/drivers/actisys-sir.c b/drivers/staging/irda/drivers/actisys-sir.c new file mode 100644 index 000000000000..e224b8b99517 --- /dev/null +++ b/drivers/staging/irda/drivers/actisys-sir.c @@ -0,0 +1,245 @@ +/********************************************************************* + * + * Filename: actisys.c + * Version: 1.1 + * Description: Implementation for the ACTiSYS IR-220L and IR-220L+ + * dongles + * Status: Beta. + * Authors: Dag Brattli (initially) + * Jean Tourrilhes (new version) + * Martin Diehl (new version for sir_dev) + * Created at: Wed Oct 21 20:02:35 1998 + * Modified at: Sun Oct 27 22:02:13 2002 + * Modified by: Martin Diehl + * + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 1999 Jean Tourrilhes + * Copyright (c) 2002 Martin Diehl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +/* + * Changelog + * + * 0.8 -> 0.9999 - Jean + * o New initialisation procedure : much safer and correct + * o New procedure the change speed : much faster and simpler + * o Other cleanups & comments + * Thanks to Lichen Wang @ Actisys for his excellent help... + * + * 1.0 -> 1.1 - Martin Diehl + * modified for new sir infrastructure + */ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +/* + * Define the timing of the pulses we send to the dongle (to reset it, and + * to toggle speeds). Basically, the limit here is the propagation speed of + * the signals through the serial port, the dongle being much faster. Any + * serial port support 115 kb/s, so we are sure that pulses 8.5 us wide can + * go through cleanly . If you are on the wild side, you can try to lower + * this value (Actisys recommended me 2 us, and 0 us work for me on a P233!) + */ +#define MIN_DELAY 10 /* 10 us to be on the conservative side */ + +static int actisys_open(struct sir_dev *); +static int actisys_close(struct sir_dev *); +static int actisys_change_speed(struct sir_dev *, unsigned); +static int actisys_reset(struct sir_dev *); + +/* These are the baudrates supported, in the order available */ +/* Note : the 220L doesn't support 38400, but we will fix that below */ +static unsigned baud_rates[] = { 9600, 19200, 57600, 115200, 38400 }; + +#define MAX_SPEEDS ARRAY_SIZE(baud_rates) + +static struct dongle_driver act220l = { + .owner = THIS_MODULE, + .driver_name = "Actisys ACT-220L", + .type = IRDA_ACTISYS_DONGLE, + .open = actisys_open, + .close = actisys_close, + .reset = actisys_reset, + .set_speed = actisys_change_speed, +}; + +static struct dongle_driver act220l_plus = { + .owner = THIS_MODULE, + .driver_name = "Actisys ACT-220L+", + .type = IRDA_ACTISYS_PLUS_DONGLE, + .open = actisys_open, + .close = actisys_close, + .reset = actisys_reset, + .set_speed = actisys_change_speed, +}; + +static int __init actisys_sir_init(void) +{ + int ret; + + /* First, register an Actisys 220L dongle */ + ret = irda_register_dongle(&act220l); + if (ret < 0) + return ret; + + /* Now, register an Actisys 220L+ dongle */ + ret = irda_register_dongle(&act220l_plus); + if (ret < 0) { + irda_unregister_dongle(&act220l); + return ret; + } + return 0; +} + +static void __exit actisys_sir_cleanup(void) +{ + /* We have to remove both dongles */ + irda_unregister_dongle(&act220l_plus); + irda_unregister_dongle(&act220l); +} + +static int actisys_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Set the speeds we can accept */ + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + + /* Remove support for 38400 if this is not a 220L+ dongle */ + if (dev->dongle_drv->type == IRDA_ACTISYS_DONGLE) + qos->baud_rate.bits &= ~IR_38400; + + qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int actisys_close(struct sir_dev *dev) +{ + /* Power off the dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function actisys_change_speed (task) + * + * Change speed of the ACTiSYS IR-220L and IR-220L+ type IrDA dongles. + * To cycle through the available baud rates, pulse RTS low for a few us. + * + * First, we reset the dongle to always start from a known state. + * Then, we cycle through the speeds by pulsing RTS low and then up. + * The dongle allow us to pulse quite fast, se we can set speed in one go, + * which is must faster ( < 100 us) and less complex than what is found + * in some other dongle drivers... + * Note that even if the new speed is the same as the current speed, + * we reassert the speed. This make sure that things are all right, + * and it's fast anyway... + * By the way, this function will work for both type of dongles, + * because the additional speed is at the end of the sequence... + */ +static int actisys_change_speed(struct sir_dev *dev, unsigned speed) +{ + int ret = 0; + int i = 0; + + pr_debug("%s(), speed=%d (was %d)\n", __func__, speed, dev->speed); + + /* dongle was already resetted from irda_request state machine, + * we are in known state (dongle default) + */ + + /* + * Now, we can set the speed requested. Send RTS pulses until we + * reach the target speed + */ + for (i = 0; i < MAX_SPEEDS; i++) { + if (speed == baud_rates[i]) { + dev->speed = speed; + break; + } + /* Set RTS low for 10 us */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + udelay(MIN_DELAY); + + /* Set RTS high for 10 us */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + udelay(MIN_DELAY); + } + + /* Check if life is sweet... */ + if (i >= MAX_SPEEDS) { + actisys_reset(dev); + ret = -EINVAL; /* This should not happen */ + } + + /* Basta lavoro, on se casse d'ici... */ + return ret; +} + +/* + * Function actisys_reset (task) + * + * Reset the Actisys type dongle. Warning, this function must only be + * called with a process context! + * + * We need to do two things in this function : + * o first make sure that the dongle is in a state where it can operate + * o second put the dongle in a know state + * + * The dongle is powered of the RTS and DTR lines. In the dongle, there + * is a big capacitor to accommodate the current spikes. This capacitor + * takes a least 50 ms to be charged. In theory, the Bios set those lines + * up, so by the time we arrive here we should be set. It doesn't hurt + * to be on the conservative side, so we will wait... + * + * Then, we set the speed to 9600 b/s to get in a known state (see in + * change_speed for details). It is needed because the IrDA stack + * has tried to set the speed immediately after our first return, + * so before we can be sure the dongle is up and running. + */ + +static int actisys_reset(struct sir_dev *dev) +{ + /* Reset the dongle : set DTR low for 10 us */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + udelay(MIN_DELAY); + + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + dev->speed = 9600; /* That's the default */ + + return 0; +} + +MODULE_AUTHOR("Dag Brattli - Jean Tourrilhes "); +MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */ +MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */ + +module_init(actisys_sir_init); +module_exit(actisys_sir_cleanup); diff --git a/drivers/staging/irda/drivers/ali-ircc.c b/drivers/staging/irda/drivers/ali-ircc.c new file mode 100644 index 000000000000..35f198d83701 --- /dev/null +++ b/drivers/staging/irda/drivers/ali-ircc.c @@ -0,0 +1,2218 @@ +/********************************************************************* + * + * Filename: ali-ircc.h + * Version: 0.5 + * Description: Driver for the ALI M1535D and M1543C FIR Controller + * Status: Experimental. + * Author: Benjamin Kong + * Created at: 2000/10/16 03:46PM + * Modified at: 2001/1/3 02:55PM + * Modified by: Benjamin Kong + * Modified at: 2003/11/6 and support for ALi south-bridge chipsets M1563 + * Modified by: Clear Zhang + * + * Copyright (c) 2000 Benjamin Kong + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + ********************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "ali-ircc.h" + +#define CHIP_IO_EXTENT 8 +#define BROKEN_DONGLE_ID + +#define ALI_IRCC_DRIVER_NAME "ali-ircc" + +/* Power Management */ +static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state); +static int ali_ircc_resume(struct platform_device *dev); + +static struct platform_driver ali_ircc_driver = { + .suspend = ali_ircc_suspend, + .resume = ali_ircc_resume, + .driver = { + .name = ALI_IRCC_DRIVER_NAME, + }, +}; + +/* Module parameters */ +static int qos_mtt_bits = 0x07; /* 1 ms or more */ + +/* Use BIOS settions by default, but user may supply module parameters */ +static unsigned int io[] = { ~0, ~0, ~0, ~0 }; +static unsigned int irq[] = { 0, 0, 0, 0 }; +static unsigned int dma[] = { 0, 0, 0, 0 }; + +static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info); +static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info); +static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info); + +/* These are the currently known ALi south-bridge chipsets, the only one difference + * is that M1543C doesn't support HP HDSL-3600 + */ +static ali_chip_t chips[] = +{ + { "M1543", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x43, ali_ircc_probe_53, ali_ircc_init_43 }, + { "M1535", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x53, ali_ircc_probe_53, ali_ircc_init_53 }, + { "M1563", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x63, ali_ircc_probe_53, ali_ircc_init_53 }, + { NULL } +}; + +/* Max 4 instances for now */ +static struct ali_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; + +/* Dongle Types */ +static char *dongle_types[] = { + "TFDS6000", + "HP HSDL-3600", + "HP HSDL-1100", + "No dongle connected", +}; + +/* Some prototypes */ +static int ali_ircc_open(int i, chipio_t *info); + +static int ali_ircc_close(struct ali_ircc_cb *self); + +static int ali_ircc_setup(chipio_t *info); +static int ali_ircc_is_receiving(struct ali_ircc_cb *self); +static int ali_ircc_net_open(struct net_device *dev); +static int ali_ircc_net_close(struct net_device *dev); +static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud); + +/* SIR function */ +static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self); +static void ali_ircc_sir_receive(struct ali_ircc_cb *self); +static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self); +static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len); +static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed); + +/* FIR function */ +static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, + struct net_device *dev); +static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 speed); +static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self); +static int ali_ircc_dma_receive(struct ali_ircc_cb *self); +static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self); +static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self); +static void ali_ircc_dma_xmit(struct ali_ircc_cb *self); + +/* My Function */ +static int ali_ircc_read_dongle_id (int i, chipio_t *info); +static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed); + +/* ALi chip function */ +static void SIR2FIR(int iobase); +static void FIR2SIR(int iobase); +static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable); + +/* + * Function ali_ircc_init () + * + * Initialize chip. Find out whay kinds of chips we are dealing with + * and their configuration registers address + */ +static int __init ali_ircc_init(void) +{ + ali_chip_t *chip; + chipio_t info; + int ret; + int cfg, cfg_base; + int reg, revision; + int i = 0; + + ret = platform_driver_register(&ali_ircc_driver); + if (ret) { + net_err_ratelimited("%s, Can't register driver!\n", + ALI_IRCC_DRIVER_NAME); + return ret; + } + + ret = -ENODEV; + + /* Probe for all the ALi chipsets we know about */ + for (chip= chips; chip->name; chip++, i++) + { + pr_debug("%s(), Probing for %s ...\n", __func__, chip->name); + + /* Try all config registers for this chip */ + for (cfg=0; cfg<2; cfg++) + { + cfg_base = chip->cfg[cfg]; + if (!cfg_base) + continue; + + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = io[i]; + info.dma = dma[i]; + info.irq = irq[i]; + + + /* Enter Configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + /* Select Logical Device 5 Registers (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base+1); + + /* Read Chip Identification Register */ + outb(chip->cid_index, cfg_base); + reg = inb(cfg_base+1); + + if (reg == chip->cid_value) + { + pr_debug("%s(), Chip found at 0x%03x\n", + __func__, cfg_base); + + outb(0x1F, cfg_base); + revision = inb(cfg_base+1); + pr_debug("%s(), Found %s chip, revision=%d\n", + __func__, chip->name, revision); + + /* + * If the user supplies the base address, then + * we init the chip, if not we probe the values + * set by the BIOS + */ + if (io[i] < 2000) + { + chip->init(chip, &info); + } + else + { + chip->probe(chip, &info); + } + + if (ali_ircc_open(i, &info) == 0) + ret = 0; + i++; + } + else + { + pr_debug("%s(), No %s chip at 0x%03x\n", + __func__, chip->name, cfg_base); + } + /* Exit configuration */ + outb(0xbb, cfg_base); + } + } + + if (ret) + platform_driver_unregister(&ali_ircc_driver); + + return ret; +} + +/* + * Function ali_ircc_cleanup () + * + * Close all configured chips + * + */ +static void __exit ali_ircc_cleanup(void) +{ + int i; + + for (i=0; i < ARRAY_SIZE(dev_self); i++) { + if (dev_self[i]) + ali_ircc_close(dev_self[i]); + } + + platform_driver_unregister(&ali_ircc_driver); + +} + +static const struct net_device_ops ali_ircc_sir_ops = { + .ndo_open = ali_ircc_net_open, + .ndo_stop = ali_ircc_net_close, + .ndo_start_xmit = ali_ircc_sir_hard_xmit, + .ndo_do_ioctl = ali_ircc_net_ioctl, +}; + +static const struct net_device_ops ali_ircc_fir_ops = { + .ndo_open = ali_ircc_net_open, + .ndo_stop = ali_ircc_net_close, + .ndo_start_xmit = ali_ircc_fir_hard_xmit, + .ndo_do_ioctl = ali_ircc_net_ioctl, +}; + +/* + * Function ali_ircc_open (int i, chipio_t *inf) + * + * Open driver instance + * + */ +static int ali_ircc_open(int i, chipio_t *info) +{ + struct net_device *dev; + struct ali_ircc_cb *self; + int dongle_id; + int err; + + if (i >= ARRAY_SIZE(dev_self)) { + net_err_ratelimited("%s(), maximum number of supported chips reached!\n", + __func__); + return -ENOMEM; + } + + /* Set FIR FIFO and DMA Threshold */ + if ((ali_ircc_setup(info)) == -1) + return -1; + + dev = alloc_irdadev(sizeof(*self)); + if (dev == NULL) { + net_err_ratelimited("%s(), can't allocate memory for control block!\n", + __func__); + return -ENOMEM; + } + + self = netdev_priv(dev); + self->netdev = dev; + spin_lock_init(&self->lock); + + /* Need to store self somewhere */ + dev_self[i] = self; + self->index = i; + + /* Initialize IO */ + self->io.cfg_base = info->cfg_base; /* In ali_ircc_probe_53 assign */ + self->io.fir_base = info->fir_base; /* info->sir_base = info->fir_base */ + self->io.sir_base = info->sir_base; /* ALi SIR and FIR use the same address */ + self->io.irq = info->irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = info->dma; + self->io.fifo_size = 16; /* SIR: 16, FIR: 32 Benjamin 2000/11/1 */ + + /* Reserve the ioports that we need */ + if (!request_region(self->io.fir_base, self->io.fir_ext, + ALI_IRCC_DRIVER_NAME)) { + net_warn_ratelimited("%s(), can't get iobase of 0x%03x\n", + __func__, self->io.fir_base); + err = -ENODEV; + goto err_out1; + } + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* The only value we must override it the baudrate */ + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); // benjamin 2000/11/8 05:27PM + + self->qos.min_turn_time.bits = qos_mtt_bits; + + irda_qos_bits_to_value(&self->qos); + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384; + self->tx_buff.truesize = 14384; + + /* Allocate memory if needed */ + self->rx_buff.head = + dma_zalloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + err = -ENOMEM; + goto err_out2; + } + + self->tx_buff.head = + dma_zalloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + err = -ENOMEM; + goto err_out3; + } + + self->rx_buff.in_frame = FALSE; + self->rx_buff.state = OUTSIDE_FRAME; + self->tx_buff.data = self->tx_buff.head; + self->rx_buff.data = self->rx_buff.head; + + /* Reset Tx queue info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Override the network functions we need to use */ + dev->netdev_ops = &ali_ircc_sir_ops; + + err = register_netdev(dev); + if (err) { + net_err_ratelimited("%s(), register_netdev() failed!\n", + __func__); + goto err_out4; + } + net_info_ratelimited("IrDA: Registered device %s\n", dev->name); + + /* Check dongle id */ + dongle_id = ali_ircc_read_dongle_id(i, info); + net_info_ratelimited("%s(), %s, Found dongle: %s\n", + __func__, ALI_IRCC_DRIVER_NAME, + dongle_types[dongle_id]); + + self->io.dongle_id = dongle_id; + + + return 0; + + err_out4: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + err_out3: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + err_out2: + release_region(self->io.fir_base, self->io.fir_ext); + err_out1: + dev_self[i] = NULL; + free_netdev(dev); + return err; +} + + +/* + * Function ali_ircc_close (self) + * + * Close driver instance + * + */ +static int __exit ali_ircc_close(struct ali_ircc_cb *self) +{ + int iobase; + + IRDA_ASSERT(self != NULL, return -1;); + + iobase = self->io.fir_base; + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the PORT that this driver is using */ + pr_debug("%s(), Releasing Region %03x\n", __func__, self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + + if (self->tx_buff.head) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + dev_self[self->index] = NULL; + free_netdev(self->netdev); + + + return 0; +} + +/* + * Function ali_ircc_init_43 (chip, info) + * + * Initialize the ALi M1543 chip. + */ +static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info) +{ + /* All controller information like I/O address, DMA channel, IRQ + * are set by BIOS + */ + + return 0; +} + +/* + * Function ali_ircc_init_53 (chip, info) + * + * Initialize the ALi M1535 chip. + */ +static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info) +{ + /* All controller information like I/O address, DMA channel, IRQ + * are set by BIOS + */ + + return 0; +} + +/* + * Function ali_ircc_probe_53 (chip, info) + * + * Probes for the ALi M1535D or M1535 + */ +static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int hi, low, reg; + + + /* Enter Configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + /* Select Logical Device 5 Registers (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base+1); + + /* Read address control register */ + outb(0x60, cfg_base); + hi = inb(cfg_base+1); + outb(0x61, cfg_base); + low = inb(cfg_base+1); + info->fir_base = (hi<<8) + low; + + info->sir_base = info->fir_base; + + pr_debug("%s(), probing fir_base=0x%03x\n", __func__, info->fir_base); + + /* Read IRQ control register */ + outb(0x70, cfg_base); + reg = inb(cfg_base+1); + info->irq = reg & 0x0f; + pr_debug("%s(), probing irq=%d\n", __func__, info->irq); + + /* Read DMA channel */ + outb(0x74, cfg_base); + reg = inb(cfg_base+1); + info->dma = reg & 0x07; + + if(info->dma == 0x04) + net_warn_ratelimited("%s(), No DMA channel assigned !\n", + __func__); + else + pr_debug("%s(), probing dma=%d\n", __func__, info->dma); + + /* Read Enabled Status */ + outb(0x30, cfg_base); + reg = inb(cfg_base+1); + info->enabled = (reg & 0x80) && (reg & 0x01); + pr_debug("%s(), probing enabled=%d\n", __func__, info->enabled); + + /* Read Power Status */ + outb(0x22, cfg_base); + reg = inb(cfg_base+1); + info->suspended = (reg & 0x20); + pr_debug("%s(), probing suspended=%d\n", __func__, info->suspended); + + /* Exit configuration */ + outb(0xbb, cfg_base); + + + return 0; +} + +/* + * Function ali_ircc_setup (info) + * + * Set FIR FIFO and DMA Threshold + * Returns non-negative on success. + * + */ +static int ali_ircc_setup(chipio_t *info) +{ + unsigned char tmp; + int version; + int iobase = info->fir_base; + + + /* Locking comments : + * Most operations here need to be protected. We are called before + * the device instance is created in ali_ircc_open(), therefore + * nobody can bother us - Jean II */ + + /* Switch to FIR space */ + SIR2FIR(iobase); + + /* Master Reset */ + outb(0x40, iobase+FIR_MCR); // benjamin 2000/11/30 11:45AM + + /* Read FIR ID Version Register */ + switch_bank(iobase, BANK3); + version = inb(iobase+FIR_ID_VR); + + /* Should be 0x00 in the M1535/M1535D */ + if(version != 0x00) + { + net_err_ratelimited("%s, Wrong chip version %02x\n", + ALI_IRCC_DRIVER_NAME, version); + return -1; + } + + /* Set FIR FIFO Threshold Register */ + switch_bank(iobase, BANK1); + outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); + + /* Set FIR DMA Threshold Register */ + outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); + + /* CRC enable */ + switch_bank(iobase, BANK2); + outb(inb(iobase+FIR_IRDA_CR) | IRDA_CR_CRC, iobase+FIR_IRDA_CR); + + /* NDIS driver set TX Length here BANK2 Alias 3, Alias4*/ + + /* Switch to Bank 0 */ + switch_bank(iobase, BANK0); + + tmp = inb(iobase+FIR_LCR_B); + tmp &=~0x20; // disable SIP + tmp |= 0x80; // these two steps make RX mode + tmp &= 0xbf; + outb(tmp, iobase+FIR_LCR_B); + + /* Disable Interrupt */ + outb(0x00, iobase+FIR_IER); + + + /* Switch to SIR space */ + FIR2SIR(iobase); + + net_info_ratelimited("%s, driver loaded (Benjamin Kong)\n", + ALI_IRCC_DRIVER_NAME); + + /* Enable receive interrupts */ + // outb(UART_IER_RDI, iobase+UART_IER); //benjamin 2000/11/23 01:25PM + // Turn on the interrupts in ali_ircc_net_open + + + return 0; +} + +/* + * Function ali_ircc_read_dongle_id (int index, info) + * + * Try to read dongle identification. This procedure needs to be executed + * once after power-on/reset. It also needs to be used whenever you suspect + * that the user may have plugged/unplugged the IrDA Dongle. + */ +static int ali_ircc_read_dongle_id (int i, chipio_t *info) +{ + int dongle_id, reg; + int cfg_base = info->cfg_base; + + + /* Enter Configuration */ + outb(chips[i].entr1, cfg_base); + outb(chips[i].entr2, cfg_base); + + /* Select Logical Device 5 Registers (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base+1); + + /* Read Dongle ID */ + outb(0xf0, cfg_base); + reg = inb(cfg_base+1); + dongle_id = ((reg>>6)&0x02) | ((reg>>5)&0x01); + pr_debug("%s(), probing dongle_id=%d, dongle_types=%s\n", + __func__, dongle_id, dongle_types[dongle_id]); + + /* Exit configuration */ + outb(0xbb, cfg_base); + + + return dongle_id; +} + +/* + * Function ali_ircc_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t ali_ircc_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct ali_ircc_cb *self; + int ret; + + + self = netdev_priv(dev); + + spin_lock(&self->lock); + + /* Dispatch interrupt handler for the current speed */ + if (self->io.speed > 115200) + ret = ali_ircc_fir_interrupt(self); + else + ret = ali_ircc_sir_interrupt(self); + + spin_unlock(&self->lock); + + return ret; +} +/* + * Function ali_ircc_fir_interrupt(irq, struct ali_ircc_cb *self) + * + * Handle MIR/FIR interrupt + * + */ +static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self) +{ + __u8 eir, OldMessageCount; + int iobase, tmp; + + + iobase = self->io.fir_base; + + switch_bank(iobase, BANK0); + self->InterruptID = inb(iobase+FIR_IIR); + self->BusStatus = inb(iobase+FIR_BSR); + + OldMessageCount = (self->LineStatus + 1) & 0x07; + self->LineStatus = inb(iobase+FIR_LSR); + //self->ier = inb(iobase+FIR_IER); 2000/12/1 04:32PM + eir = self->InterruptID & self->ier; /* Mask out the interesting ones */ + + pr_debug("%s(), self->InterruptID = %x\n", __func__, self->InterruptID); + pr_debug("%s(), self->LineStatus = %x\n", __func__, self->LineStatus); + pr_debug("%s(), self->ier = %x\n", __func__, self->ier); + pr_debug("%s(), eir = %x\n", __func__, eir); + + /* Disable interrupts */ + SetCOMInterrupts(self, FALSE); + + /* Tx or Rx Interrupt */ + + if (eir & IIR_EOM) + { + if (self->io.direction == IO_XMIT) /* TX */ + { + pr_debug("%s(), ******* IIR_EOM (Tx) *******\n", + __func__); + + if(ali_ircc_dma_xmit_complete(self)) + { + if (irda_device_txqueue_empty(self->netdev)) + { + /* Prepare for receive */ + ali_ircc_dma_receive(self); + self->ier = IER_EOM; + } + } + else + { + self->ier = IER_EOM; + } + + } + else /* RX */ + { + pr_debug("%s(), ******* IIR_EOM (Rx) *******\n", + __func__); + + if(OldMessageCount > ((self->LineStatus+1) & 0x07)) + { + self->rcvFramesOverflow = TRUE; + pr_debug("%s(), ******* self->rcvFramesOverflow = TRUE ********\n", + __func__); + } + + if (ali_ircc_dma_receive_complete(self)) + { + pr_debug("%s(), ******* receive complete ********\n", + __func__); + + self->ier = IER_EOM; + } + else + { + pr_debug("%s(), ******* Not receive complete ********\n", + __func__); + + self->ier = IER_EOM | IER_TIMER; + } + + } + } + /* Timer Interrupt */ + else if (eir & IIR_TIMER) + { + if(OldMessageCount > ((self->LineStatus+1) & 0x07)) + { + self->rcvFramesOverflow = TRUE; + pr_debug("%s(), ******* self->rcvFramesOverflow = TRUE *******\n", + __func__); + } + /* Disable Timer */ + switch_bank(iobase, BANK1); + tmp = inb(iobase+FIR_CR); + outb( tmp& ~CR_TIMER_EN, iobase+FIR_CR); + + /* Check if this is a Tx timer interrupt */ + if (self->io.direction == IO_XMIT) + { + ali_ircc_dma_xmit(self); + + /* Interrupt on EOM */ + self->ier = IER_EOM; + + } + else /* Rx */ + { + if(ali_ircc_dma_receive_complete(self)) + { + self->ier = IER_EOM; + } + else + { + self->ier = IER_EOM | IER_TIMER; + } + } + } + + /* Restore Interrupt */ + SetCOMInterrupts(self, TRUE); + + return IRQ_RETVAL(eir); +} + +/* + * Function ali_ircc_sir_interrupt (irq, self, eir) + * + * Handle SIR interrupt + * + */ +static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self) +{ + int iobase; + int iir, lsr; + + + iobase = self->io.sir_base; + + iir = inb(iobase+UART_IIR) & UART_IIR_ID; + if (iir) { + /* Clear interrupt */ + lsr = inb(iobase+UART_LSR); + + pr_debug("%s(), iir=%02x, lsr=%02x, iobase=%#x\n", + __func__, iir, lsr, iobase); + + switch (iir) + { + case UART_IIR_RLSI: + pr_debug("%s(), RLSI\n", __func__); + break; + case UART_IIR_RDI: + /* Receive interrupt */ + ali_ircc_sir_receive(self); + break; + case UART_IIR_THRI: + if (lsr & UART_LSR_THRE) + { + /* Transmitter ready for data */ + ali_ircc_sir_write_wakeup(self); + } + break; + default: + pr_debug("%s(), unhandled IIR=%#x\n", + __func__, iir); + break; + } + + } + + + return IRQ_RETVAL(iir); +} + + +/* + * Function ali_ircc_sir_receive (self) + * + * Receive one frame from the infrared port + * + */ +static void ali_ircc_sir_receive(struct ali_ircc_cb *self) +{ + int boguscount = 0; + int iobase; + + IRDA_ASSERT(self != NULL, return;); + + iobase = self->io.sir_base; + + /* + * Receive all characters in Rx FIFO, unwrap and unstuff them. + * async_unwrap_char will deliver all found frames + */ + do { + async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, + inb(iobase+UART_RX)); + + /* Make sure we don't stay here too long */ + if (boguscount++ > 32) { + pr_debug("%s(), breaking!\n", __func__); + break; + } + } while (inb(iobase+UART_LSR) & UART_LSR_DR); + +} + +/* + * Function ali_ircc_sir_write_wakeup (tty) + * + * Called by the driver when there's room for more data. If we have + * more packets to send, we send them here. + * + */ +static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self) +{ + int actual = 0; + int iobase; + + IRDA_ASSERT(self != NULL, return;); + + + iobase = self->io.sir_base; + + /* Finished with frame? */ + if (self->tx_buff.len > 0) + { + /* Write data left in transmit buffer */ + actual = ali_ircc_sir_write(iobase, self->io.fifo_size, + self->tx_buff.data, self->tx_buff.len); + self->tx_buff.data += actual; + self->tx_buff.len -= actual; + } + else + { + if (self->new_speed) + { + /* We must wait until all data are gone */ + while(!(inb(iobase+UART_LSR) & UART_LSR_TEMT)) + pr_debug("%s(), UART_LSR_THRE\n", __func__); + + pr_debug("%s(), Changing speed! self->new_speed = %d\n", + __func__, self->new_speed); + ali_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + + // benjamin 2000/11/10 06:32PM + if (self->io.speed > 115200) + { + pr_debug("%s(), ali_ircc_change_speed from UART_LSR_TEMT\n", + __func__); + + self->ier = IER_EOM; + // SetCOMInterrupts(self, TRUE); + return; + } + } + else + { + netif_wake_queue(self->netdev); + } + + self->netdev->stats.tx_packets++; + + /* Turn on receive interrupts */ + outb(UART_IER_RDI, iobase+UART_IER); + } + +} + +static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud) +{ + struct net_device *dev = self->netdev; + int iobase; + + + pr_debug("%s(), setting speed = %d\n", __func__, baud); + + /* This function *must* be called with irq off and spin-lock. + * - Jean II */ + + iobase = self->io.fir_base; + + SetCOMInterrupts(self, FALSE); // 2000/11/24 11:43AM + + /* Go to MIR, FIR Speed */ + if (baud > 115200) + { + + + ali_ircc_fir_change_speed(self, baud); + + /* Install FIR xmit handler*/ + dev->netdev_ops = &ali_ircc_fir_ops; + + /* Enable Interuupt */ + self->ier = IER_EOM; // benjamin 2000/11/20 07:24PM + + /* Be ready for incoming frames */ + ali_ircc_dma_receive(self); // benajmin 2000/11/8 07:46PM not complete + } + /* Go to SIR Speed */ + else + { + ali_ircc_sir_change_speed(self, baud); + + /* Install SIR xmit handler*/ + dev->netdev_ops = &ali_ircc_sir_ops; + } + + + SetCOMInterrupts(self, TRUE); // 2000/11/24 11:43AM + + netif_wake_queue(self->netdev); + +} + +static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud) +{ + + int iobase; + struct ali_ircc_cb *self = priv; + struct net_device *dev; + + + IRDA_ASSERT(self != NULL, return;); + + dev = self->netdev; + iobase = self->io.fir_base; + + pr_debug("%s(), self->io.speed = %d, change to speed = %d\n", + __func__, self->io.speed, baud); + + /* Come from SIR speed */ + if(self->io.speed <=115200) + { + SIR2FIR(iobase); + } + + /* Update accounting for new speed */ + self->io.speed = baud; + + // Set Dongle Speed mode + ali_ircc_change_dongle_speed(self, baud); + +} + +/* + * Function ali_sir_change_speed (self, speed) + * + * Set speed of IrDA port to specified baudrate + * + */ +static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) +{ + struct ali_ircc_cb *self = priv; + int iobase; + int fcr; /* FIFO control reg */ + int lcr; /* Line control reg */ + int divisor; + + + pr_debug("%s(), Setting speed to: %d\n", __func__, speed); + + IRDA_ASSERT(self != NULL, return;); + + iobase = self->io.sir_base; + + /* Come from MIR or FIR speed */ + if(self->io.speed >115200) + { + // Set Dongle Speed mode first + ali_ircc_change_dongle_speed(self, speed); + + FIR2SIR(iobase); + } + + // Clear Line and Auxiluary status registers 2000/11/24 11:47AM + + inb(iobase+UART_LSR); + inb(iobase+UART_SCR); + + /* Update accounting for new speed */ + self->io.speed = speed; + + divisor = 115200/speed; + + fcr = UART_FCR_ENABLE_FIFO; + + /* + * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and + * almost 1,7 ms at 19200 bps. At speeds above that we can just forget + * about this timeout since it will always be fast enough. + */ + if (self->io.speed < 38400) + fcr |= UART_FCR_TRIGGER_1; + else + fcr |= UART_FCR_TRIGGER_14; + + /* IrDA ports use 8N1 */ + lcr = UART_LCR_WLEN8; + + outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */ + outb(divisor & 0xff, iobase+UART_DLL); /* Set speed */ + outb(divisor >> 8, iobase+UART_DLM); + outb(lcr, iobase+UART_LCR); /* Set 8N1 */ + outb(fcr, iobase+UART_FCR); /* Enable FIFO's */ + + /* without this, the connection will be broken after come back from FIR speed, + but with this, the SIR connection is harder to established */ + outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR); +} + +static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed) +{ + + struct ali_ircc_cb *self = priv; + int iobase,dongle_id; + int tmp = 0; + + + iobase = self->io.fir_base; /* or iobase = self->io.sir_base; */ + dongle_id = self->io.dongle_id; + + /* We are already locked, no need to do it again */ + + pr_debug("%s(), Set Speed for %s , Speed = %d\n", + __func__, dongle_types[dongle_id], speed); + + switch_bank(iobase, BANK2); + tmp = inb(iobase+FIR_IRDA_CR); + + /* IBM type dongle */ + if(dongle_id == 0) + { + if(speed == 4000000) + { + // __ __ + // SD/MODE __| |__ __ + // __ __ + // IRTX __ __| |__ + // T1 T2 T3 T4 T5 + + tmp &= ~IRDA_CR_HDLC; // HDLC=0 + tmp |= IRDA_CR_CRC; // CRC=1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + + // T1 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T2 -> SD/MODE:1 IRTX:0 + tmp &= ~0x01; + tmp |= 0x0a; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T3 -> SD/MODE:1 IRTX:1 + tmp |= 0x0b; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T4 -> SD/MODE:0 IRTX:1 + tmp &= ~0x08; + tmp |= 0x03; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T5 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // reset -> Normal TX output Signal + outb(tmp & ~0x02, iobase+FIR_IRDA_CR); + } + else /* speed <=1152000 */ + { + // __ + // SD/MODE __| |__ + // + // IRTX ________ + // T1 T2 T3 + + /* MIR 115200, 57600 */ + if (speed==1152000) + { + tmp |= 0xA0; //HDLC=1, 1.152Mbps=1 + } + else + { + tmp &=~0x80; //HDLC 0.576Mbps + tmp |= 0x20; //HDLC=1, + } + + tmp |= IRDA_CR_CRC; // CRC=1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + + /* MIR 115200, 57600 */ + + //switch_bank(iobase, BANK2); + // T1 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T2 -> SD/MODE:1 IRTX:0 + tmp &= ~0x01; + tmp |= 0x0a; + outb(tmp, iobase+FIR_IRDA_CR); + + // T3 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // reset -> Normal TX output Signal + outb(tmp & ~0x02, iobase+FIR_IRDA_CR); + } + } + else if (dongle_id == 1) /* HP HDSL-3600 */ + { + switch(speed) + { + case 4000000: + tmp &= ~IRDA_CR_HDLC; // HDLC=0 + break; + + case 1152000: + tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 + break; + + case 576000: + tmp &=~0x80; // HDLC 0.576Mbps + tmp |= 0x20; // HDLC=1, + break; + } + + tmp |= IRDA_CR_CRC; // CRC=1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + } + else /* HP HDSL-1100 */ + { + if(speed <= 115200) /* SIR */ + { + + tmp &= ~IRDA_CR_FIR_SIN; // HP sin select = 0 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + } + else /* MIR FIR */ + { + + switch(speed) + { + case 4000000: + tmp &= ~IRDA_CR_HDLC; // HDLC=0 + break; + + case 1152000: + tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 + break; + + case 576000: + tmp &=~0x80; // HDLC 0.576Mbps + tmp |= 0x20; // HDLC=1, + break; + } + + tmp |= IRDA_CR_CRC; // CRC=1 + tmp |= IRDA_CR_FIR_SIN; // HP sin select = 1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + } + } + + switch_bank(iobase, BANK0); + +} + +/* + * Function ali_ircc_sir_write (driver) + * + * Fill Tx FIFO with transmit data + * + */ +static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len) +{ + int actual = 0; + + + /* Tx FIFO should be empty! */ + if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { + pr_debug("%s(), failed, fifo not empty!\n", __func__); + return 0; + } + + /* Fill FIFO with current frame */ + while ((fifo_size-- > 0) && (actual < len)) { + /* Transmit next byte */ + outb(buf[actual], iobase+UART_TX); + + actual++; + } + + return actual; +} + +/* + * Function ali_ircc_net_open (dev) + * + * Start the device + * + */ +static int ali_ircc_net_open(struct net_device *dev) +{ + struct ali_ircc_cb *self; + int iobase; + char hwname[32]; + + + IRDA_ASSERT(dev != NULL, return -1;); + + self = netdev_priv(dev); + + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + /* Request IRQ and install Interrupt Handler */ + if (request_irq(self->io.irq, ali_ircc_interrupt, 0, dev->name, dev)) + { + net_warn_ratelimited("%s, unable to allocate irq=%d\n", + ALI_IRCC_DRIVER_NAME, self->io.irq); + return -EAGAIN; + } + + /* + * Always allocate the DMA channel after the IRQ, and clean up on + * failure. + */ + if (request_dma(self->io.dma, dev->name)) { + net_warn_ratelimited("%s, unable to allocate dma=%d\n", + ALI_IRCC_DRIVER_NAME, self->io.dma); + free_irq(self->io.irq, dev); + return -EAGAIN; + } + + /* Turn on interrups */ + outb(UART_IER_RDI , iobase+UART_IER); + + /* Ready to play! */ + netif_start_queue(dev); //benjamin by irport + + /* Give self a hardware name */ + sprintf(hwname, "ALI-FIR @ 0x%03x", self->io.fir_base); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + self->irlap = irlap_open(dev, &self->qos, hwname); + + + return 0; +} + +/* + * Function ali_ircc_net_close (dev) + * + * Stop the device + * + */ +static int ali_ircc_net_close(struct net_device *dev) +{ + + struct ali_ircc_cb *self; + //int iobase; + + + IRDA_ASSERT(dev != NULL, return -1;); + + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return 0;); + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + disable_dma(self->io.dma); + + /* Disable interrupts */ + SetCOMInterrupts(self, FALSE); + + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + + + return 0; +} + +/* + * Function ali_ircc_fir_hard_xmit (skb, dev) + * + * Transmit the frame + * + */ +static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct ali_ircc_cb *self; + unsigned long flags; + int iobase; + __u32 speed; + int mtt, diff; + + + self = netdev_priv(dev); + iobase = self->io.fir_base; + + netif_stop_queue(dev); + + /* Make sure tests *& speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* Note : you should make sure that speed changes are not going + * to corrupt any outgoing frame. Look at nsc-ircc for the gory + * details - Jean II */ + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame */ + if (!skb->len) { + ali_ircc_change_speed(self, speed); + netif_trans_update(dev); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } else + self->new_speed = speed; + } + + /* Register and copy this frame to DMA memory */ + self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; + self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; + self->tx_fifo.tail += skb->len; + + dev->stats.tx_bytes += skb->len; + + skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, + skb->len); + self->tx_fifo.len++; + self->tx_fifo.free++; + + /* Start transmit only if there is currently no transmit going on */ + if (self->tx_fifo.len == 1) + { + /* Check if we must wait the min turn time or not */ + mtt = irda_get_mtt(skb); + + if (mtt) + { + /* Check how much time we have used already */ + diff = ktime_us_delta(ktime_get(), self->stamp); + /* self->stamp is set from ali_ircc_dma_receive_complete() */ + + pr_debug("%s(), ******* diff = %d *******\n", + __func__, diff); + + /* Check if the mtt is larger than the time we have + * already used by all the protocol processing + */ + if (mtt > diff) + { + mtt -= diff; + + /* + * Use timer if delay larger than 1000 us, and + * use udelay for smaller values which should + * be acceptable + */ + if (mtt > 500) + { + /* Adjust for timer resolution */ + mtt = (mtt+250) / 500; /* 4 discard, 5 get advanced, Let's round off */ + + pr_debug("%s(), ************** mtt = %d ***********\n", + __func__, mtt); + + /* Setup timer */ + if (mtt == 1) /* 500 us */ + { + switch_bank(iobase, BANK1); + outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); + } + else if (mtt == 2) /* 1 ms */ + { + switch_bank(iobase, BANK1); + outb(TIMER_IIR_1ms, iobase+FIR_TIMER_IIR); + } + else /* > 2ms -> 4ms */ + { + switch_bank(iobase, BANK1); + outb(TIMER_IIR_2ms, iobase+FIR_TIMER_IIR); + } + + + /* Start timer */ + outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); + self->io.direction = IO_XMIT; + + /* Enable timer interrupt */ + self->ier = IER_TIMER; + SetCOMInterrupts(self, TRUE); + + /* Timer will take care of the rest */ + goto out; + } + else + udelay(mtt); + } // if (if (mtt > diff) + }// if (mtt) + + /* Enable EOM interrupt */ + self->ier = IER_EOM; + SetCOMInterrupts(self, TRUE); + + /* Transmit frame */ + ali_ircc_dma_xmit(self); + } // if (self->tx_fifo.len == 1) + + out: + + /* Not busy transmitting anymore if window is not full */ + if (self->tx_fifo.free < MAX_TX_WINDOW) + netif_wake_queue(self->netdev); + + /* Restore bank register */ + switch_bank(iobase, BANK0); + + netif_trans_update(dev); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + + +static void ali_ircc_dma_xmit(struct ali_ircc_cb *self) +{ + int iobase, tmp; + unsigned char FIFO_OPTI, Hi, Lo; + + + + iobase = self->io.fir_base; + + /* FIFO threshold , this method comes from NDIS5 code */ + + if(self->tx_fifo.queue[self->tx_fifo.ptr].len < TX_FIFO_Threshold) + FIFO_OPTI = self->tx_fifo.queue[self->tx_fifo.ptr].len-1; + else + FIFO_OPTI = TX_FIFO_Threshold; + + /* Disable DMA */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); + + self->io.direction = IO_XMIT; + + irda_setup_dma(self->io.dma, + ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - + self->tx_buff.head) + self->tx_buff_dma, + self->tx_fifo.queue[self->tx_fifo.ptr].len, + DMA_TX_MODE); + + /* Reset Tx FIFO */ + switch_bank(iobase, BANK0); + outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); + + /* Set Tx FIFO threshold */ + if (self->fifo_opti_buf!=FIFO_OPTI) + { + switch_bank(iobase, BANK1); + outb(FIFO_OPTI, iobase+FIR_FIFO_TR) ; + self->fifo_opti_buf=FIFO_OPTI; + } + + /* Set Tx DMA threshold */ + switch_bank(iobase, BANK1); + outb(TX_DMA_Threshold, iobase+FIR_DMA_TR); + + /* Set max Tx frame size */ + Hi = (self->tx_fifo.queue[self->tx_fifo.ptr].len >> 8) & 0x0f; + Lo = self->tx_fifo.queue[self->tx_fifo.ptr].len & 0xff; + switch_bank(iobase, BANK2); + outb(Hi, iobase+FIR_TX_DSR_HI); + outb(Lo, iobase+FIR_TX_DSR_LO); + + /* Disable SIP , Disable Brick Wall (we don't support in TX mode), Change to TX mode */ + switch_bank(iobase, BANK0); + tmp = inb(iobase+FIR_LCR_B); + tmp &= ~0x20; // Disable SIP + outb(((unsigned char)(tmp & 0x3f) | LCR_B_TX_MODE) & ~LCR_B_BW, iobase+FIR_LCR_B); + pr_debug("%s(), *** Change to TX mode: FIR_LCR_B = 0x%x ***\n", + __func__, inb(iobase + FIR_LCR_B)); + + outb(0, iobase+FIR_LSR); + + /* Enable DMA and Burst Mode */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) | CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); + + switch_bank(iobase, BANK0); + +} + +static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self) +{ + int iobase; + int ret = TRUE; + + + iobase = self->io.fir_base; + + /* Disable DMA */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); + + /* Check for underrun! */ + switch_bank(iobase, BANK0); + if((inb(iobase+FIR_LSR) & LSR_FRAME_ABORT) == LSR_FRAME_ABORT) + + { + net_err_ratelimited("%s(), ********* LSR_FRAME_ABORT *********\n", + __func__); + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; + } + else + { + self->netdev->stats.tx_packets++; + } + + /* Check if we need to change the speed */ + if (self->new_speed) + { + ali_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Finished with this frame, so prepare for next */ + self->tx_fifo.ptr++; + self->tx_fifo.len--; + + /* Any frames to be sent back-to-back? */ + if (self->tx_fifo.len) + { + ali_ircc_dma_xmit(self); + + /* Not finished yet! */ + ret = FALSE; + } + else + { /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + } + + /* Make sure we have room for more frames */ + if (self->tx_fifo.free < MAX_TX_WINDOW) { + /* Not busy transmitting anymore */ + /* Tell the network layer, that we can accept more frames */ + netif_wake_queue(self->netdev); + } + + switch_bank(iobase, BANK0); + + return ret; +} + +/* + * Function ali_ircc_dma_receive (self) + * + * Get ready for receiving a frame. The device will initiate a DMA + * if it starts to receive a frame. + * + */ +static int ali_ircc_dma_receive(struct ali_ircc_cb *self) +{ + int iobase, tmp; + + + iobase = self->io.fir_base; + + /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Disable DMA */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); + + /* Reset Message Count */ + switch_bank(iobase, BANK0); + outb(0x07, iobase+FIR_LSR); + + self->rcvFramesOverflow = FALSE; + + self->LineStatus = inb(iobase+FIR_LSR) ; + + /* Reset Rx FIFO info */ + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + + /* Reset Rx FIFO */ + // switch_bank(iobase, BANK0); + outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); + + self->st_fifo.len = self->st_fifo.pending_bytes = 0; + self->st_fifo.tail = self->st_fifo.head = 0; + + irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, + DMA_RX_MODE); + + /* Set Receive Mode,Brick Wall */ + //switch_bank(iobase, BANK0); + tmp = inb(iobase+FIR_LCR_B); + outb((unsigned char)(tmp &0x3f) | LCR_B_RX_MODE | LCR_B_BW , iobase + FIR_LCR_B); // 2000/12/1 05:16PM + pr_debug("%s(), *** Change To RX mode: FIR_LCR_B = 0x%x ***\n", + __func__, inb(iobase + FIR_LCR_B)); + + /* Set Rx Threshold */ + switch_bank(iobase, BANK1); + outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); + outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); + + /* Enable DMA and Burst Mode */ + // switch_bank(iobase, BANK1); + outb(CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); + + switch_bank(iobase, BANK0); + return 0; +} + +static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) +{ + struct st_fifo *st_fifo; + struct sk_buff *skb; + __u8 status, MessageCount; + int len, i, iobase, val; + + st_fifo = &self->st_fifo; + iobase = self->io.fir_base; + + switch_bank(iobase, BANK0); + MessageCount = inb(iobase+ FIR_LSR)&0x07; + + if (MessageCount > 0) + pr_debug("%s(), Message count = %d\n", __func__, MessageCount); + + for (i=0; i<=MessageCount; i++) + { + /* Bank 0 */ + switch_bank(iobase, BANK0); + status = inb(iobase+FIR_LSR); + + switch_bank(iobase, BANK2); + len = inb(iobase+FIR_RX_DSR_HI) & 0x0f; + len = len << 8; + len |= inb(iobase+FIR_RX_DSR_LO); + + pr_debug("%s(), RX Length = 0x%.2x,\n", __func__ , len); + pr_debug("%s(), RX Status = 0x%.2x,\n", __func__ , status); + + if (st_fifo->tail >= MAX_RX_WINDOW) { + pr_debug("%s(), window is full!\n", __func__); + continue; + } + + st_fifo->entries[st_fifo->tail].status = status; + st_fifo->entries[st_fifo->tail].len = len; + st_fifo->pending_bytes += len; + st_fifo->tail++; + st_fifo->len++; + } + + for (i=0; i<=MessageCount; i++) + { + /* Get first entry */ + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->pending_bytes -= len; + st_fifo->head++; + st_fifo->len--; + + /* Check for errors */ + if ((status & 0xd8) || self->rcvFramesOverflow || (len==0)) + { + pr_debug("%s(), ************* RX Errors ************\n", + __func__); + + /* Skip frame */ + self->netdev->stats.rx_errors++; + + self->rx_buff.data += len; + + if (status & LSR_FIFO_UR) + { + self->netdev->stats.rx_frame_errors++; + pr_debug("%s(), ************* FIFO Errors ************\n", + __func__); + } + if (status & LSR_FRAME_ERROR) + { + self->netdev->stats.rx_frame_errors++; + pr_debug("%s(), ************* FRAME Errors ************\n", + __func__); + } + + if (status & LSR_CRC_ERROR) + { + self->netdev->stats.rx_crc_errors++; + pr_debug("%s(), ************* CRC Errors ************\n", + __func__); + } + + if(self->rcvFramesOverflow) + { + self->netdev->stats.rx_frame_errors++; + pr_debug("%s(), ************* Overran DMA buffer ************\n", + __func__); + } + if(len == 0) + { + self->netdev->stats.rx_frame_errors++; + pr_debug("%s(), ********** Receive Frame Size = 0 *********\n", + __func__); + } + } + else + { + + if (st_fifo->pending_bytes < 32) + { + switch_bank(iobase, BANK0); + val = inb(iobase+FIR_BSR); + if ((val& BSR_FIFO_NOT_EMPTY)== 0x80) + { + pr_debug("%s(), ************* BSR_FIFO_NOT_EMPTY ************\n", + __func__); + + /* Put this entry back in fifo */ + st_fifo->head--; + st_fifo->len++; + st_fifo->pending_bytes += len; + st_fifo->entries[st_fifo->head].status = status; + st_fifo->entries[st_fifo->head].len = len; + + /* + * DMA not finished yet, so try again + * later, set timer value, resolution + * 500 us + */ + + switch_bank(iobase, BANK1); + outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); // 2001/1/2 05:07PM + + /* Enable Timer */ + outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); + + return FALSE; /* I'll be back! */ + } + } + + /* + * Remember the time we received this frame, so we can + * reduce the min turn time a bit since we will know + * how much time we have used for protocol processing + */ + self->stamp = ktime_get(); + + skb = dev_alloc_skb(len+1); + if (skb == NULL) + { + self->netdev->stats.rx_dropped++; + + return FALSE; + } + + /* Make sure IP header gets aligned */ + skb_reserve(skb, 1); + + /* Copy frame without CRC, CRC is removed by hardware*/ + skb_put(skb, len); + skb_copy_to_linear_data(skb, self->rx_buff.data, len); + + /* Move to next frame */ + self->rx_buff.data += len; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; + + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + } + } + + switch_bank(iobase, BANK0); + + return TRUE; +} + + + +/* + * Function ali_ircc_sir_hard_xmit (skb, dev) + * + * Transmit the frame! + * + */ +static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct ali_ircc_cb *self; + unsigned long flags; + int iobase; + __u32 speed; + + + IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); + + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); + + iobase = self->io.sir_base; + + netif_stop_queue(dev); + + /* Make sure tests *& speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* Note : you should make sure that speed changes are not going + * to corrupt any outgoing frame. Look at nsc-ircc for the gory + * details - Jean II */ + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame */ + if (!skb->len) { + ali_ircc_change_speed(self, speed); + netif_trans_update(dev); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } else + self->new_speed = speed; + } + + /* Init tx buffer */ + self->tx_buff.data = self->tx_buff.head; + + /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + self->netdev->stats.tx_bytes += self->tx_buff.len; + + /* Turn on transmit finished interrupt. Will fire immediately! */ + outb(UART_IER_THRI, iobase+UART_IER); + + netif_trans_update(dev); + spin_unlock_irqrestore(&self->lock, flags); + + dev_kfree_skb(skb); + + + return NETDEV_TX_OK; +} + + +/* + * Function ali_ircc_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct ali_ircc_cb *self; + unsigned long flags; + int ret = 0; + + + IRDA_ASSERT(dev != NULL, return -1;); + + self = netdev_priv(dev); + + IRDA_ASSERT(self != NULL, return -1;); + + pr_debug("%s(), %s, (cmd=0x%X)\n", __func__ , dev->name, cmd); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + pr_debug("%s(), SIOCSBANDWIDTH\n", __func__); + /* + * This function will also be used by IrLAP to change the + * speed, so we still must allow for speed change within + * interrupt context. + */ + if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&self->lock, flags); + ali_ircc_change_speed(self, irq->ifr_baudrate); + spin_unlock_irqrestore(&self->lock, flags); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + pr_debug("%s(), SIOCSMEDIABUSY\n", __func__); + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + pr_debug("%s(), SIOCGRECEIVING\n", __func__); + /* This is protected */ + irq->ifr_receiving = ali_ircc_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + + + return ret; +} + +/* + * Function ali_ircc_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int ali_ircc_is_receiving(struct ali_ircc_cb *self) +{ + unsigned long flags; + int status = FALSE; + int iobase; + + + IRDA_ASSERT(self != NULL, return FALSE;); + + spin_lock_irqsave(&self->lock, flags); + + if (self->io.speed > 115200) + { + iobase = self->io.fir_base; + + switch_bank(iobase, BANK1); + if((inb(iobase+FIR_FIFO_FR) & 0x3f) != 0) + { + /* We are receiving something */ + pr_debug("%s(), We are receiving something\n", + __func__); + status = TRUE; + } + switch_bank(iobase, BANK0); + } + else + { + status = (self->rx_buff.state != OUTSIDE_FRAME); + } + + spin_unlock_irqrestore(&self->lock, flags); + + + return status; +} + +static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state) +{ + struct ali_ircc_cb *self = platform_get_drvdata(dev); + + net_info_ratelimited("%s, Suspending\n", ALI_IRCC_DRIVER_NAME); + + if (self->io.suspended) + return 0; + + ali_ircc_net_close(self->netdev); + + self->io.suspended = 1; + + return 0; +} + +static int ali_ircc_resume(struct platform_device *dev) +{ + struct ali_ircc_cb *self = platform_get_drvdata(dev); + + if (!self->io.suspended) + return 0; + + ali_ircc_net_open(self->netdev); + + net_info_ratelimited("%s, Waking up\n", ALI_IRCC_DRIVER_NAME); + + self->io.suspended = 0; + + return 0; +} + +/* ALi Chip Function */ + +static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable) +{ + + unsigned char newMask; + + int iobase = self->io.fir_base; /* or sir_base */ + + pr_debug("%s(), -------- Start -------- ( Enable = %d )\n", + __func__, enable); + + /* Enable the interrupt which we wish to */ + if (enable){ + if (self->io.direction == IO_XMIT) + { + if (self->io.speed > 115200) /* FIR, MIR */ + { + newMask = self->ier; + } + else /* SIR */ + { + newMask = UART_IER_THRI | UART_IER_RDI; + } + } + else { + if (self->io.speed > 115200) /* FIR, MIR */ + { + newMask = self->ier; + } + else /* SIR */ + { + newMask = UART_IER_RDI; + } + } + } + else /* Disable all the interrupts */ + { + newMask = 0x00; + + } + + //SIR and FIR has different registers + if (self->io.speed > 115200) + { + switch_bank(iobase, BANK0); + outb(newMask, iobase+FIR_IER); + } + else + outb(newMask, iobase+UART_IER); + +} + +static void SIR2FIR(int iobase) +{ + //unsigned char tmp; + + + /* Already protected (change_speed() or setup()), no need to lock. + * Jean II */ + + outb(0x28, iobase+UART_MCR); + outb(0x68, iobase+UART_MCR); + outb(0x88, iobase+UART_MCR); + + outb(0x60, iobase+FIR_MCR); /* Master Reset */ + outb(0x20, iobase+FIR_MCR); /* Master Interrupt Enable */ + + //tmp = inb(iobase+FIR_LCR_B); /* SIP enable */ + //tmp |= 0x20; + //outb(tmp, iobase+FIR_LCR_B); + +} + +static void FIR2SIR(int iobase) +{ + unsigned char val; + + + /* Already protected (change_speed() or setup()), no need to lock. + * Jean II */ + + outb(0x20, iobase+FIR_MCR); /* IRQ to low */ + outb(0x00, iobase+UART_IER); + + outb(0xA0, iobase+FIR_MCR); /* Don't set master reset */ + outb(0x00, iobase+UART_FCR); + outb(0x07, iobase+UART_FCR); + + val = inb(iobase+UART_RX); + val = inb(iobase+UART_LSR); + val = inb(iobase+UART_MSR); + +} + +MODULE_AUTHOR("Benjamin Kong "); +MODULE_DESCRIPTION("ALi FIR Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" ALI_IRCC_DRIVER_NAME); + + +module_param_hw_array(io, int, ioport, NULL, 0); +MODULE_PARM_DESC(io, "Base I/O addresses"); +module_param_hw_array(irq, int, irq, NULL, 0); +MODULE_PARM_DESC(irq, "IRQ lines"); +module_param_hw_array(dma, int, dma, NULL, 0); +MODULE_PARM_DESC(dma, "DMA channels"); + +module_init(ali_ircc_init); +module_exit(ali_ircc_cleanup); diff --git a/drivers/staging/irda/drivers/ali-ircc.h b/drivers/staging/irda/drivers/ali-ircc.h new file mode 100644 index 000000000000..c2d9747a5108 --- /dev/null +++ b/drivers/staging/irda/drivers/ali-ircc.h @@ -0,0 +1,227 @@ +/********************************************************************* + * + * Filename: ali-ircc.h + * Version: 0.5 + * Description: Driver for the ALI M1535D and M1543C FIR Controller + * Status: Experimental. + * Author: Benjamin Kong + * Created at: 2000/10/16 03:46PM + * Modified at: 2001/1/3 02:56PM + * Modified by: Benjamin Kong + * + * Copyright (c) 2000 Benjamin Kong + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + ********************************************************************/ + +#ifndef ALI_IRCC_H +#define ALI_IRCC_H + +#include + +#include +#include +#include +#include + +/* SIR Register */ +/* Usr definition of linux/serial_reg.h */ + +/* FIR Register */ +#define BANK0 0x20 +#define BANK1 0x21 +#define BANK2 0x22 +#define BANK3 0x23 + +#define FIR_MCR 0x07 /* Master Control Register */ + +/* Bank 0 */ +#define FIR_DR 0x00 /* Alias 0, FIR Data Register (R/W) */ +#define FIR_IER 0x01 /* Alias 1, FIR Interrupt Enable Register (R/W) */ +#define FIR_IIR 0x02 /* Alias 2, FIR Interrupt Identification Register (Read only) */ +#define FIR_LCR_A 0x03 /* Alias 3, FIR Line Control Register A (R/W) */ +#define FIR_LCR_B 0x04 /* Alias 4, FIR Line Control Register B (R/W) */ +#define FIR_LSR 0x05 /* Alias 5, FIR Line Status Register (R/W) */ +#define FIR_BSR 0x06 /* Alias 6, FIR Bus Status Register (Read only) */ + + + /* Alias 1 */ + #define IER_FIFO 0x10 /* FIR FIFO Interrupt Enable */ + #define IER_TIMER 0x20 /* Timer Interrupt Enable */ + #define IER_EOM 0x40 /* End of Message Interrupt Enable */ + #define IER_ACT 0x80 /* Active Frame Interrupt Enable */ + + /* Alias 2 */ + #define IIR_FIFO 0x10 /* FIR FIFO Interrupt */ + #define IIR_TIMER 0x20 /* Timer Interrupt */ + #define IIR_EOM 0x40 /* End of Message Interrupt */ + #define IIR_ACT 0x80 /* Active Frame Interrupt */ + + /* Alias 3 */ + #define LCR_A_FIFO_RESET 0x80 /* FIFO Reset */ + + /* Alias 4 */ + #define LCR_B_BW 0x10 /* Brick Wall */ + #define LCR_B_SIP 0x20 /* SIP Enable */ + #define LCR_B_TX_MODE 0x40 /* Transmit Mode */ + #define LCR_B_RX_MODE 0x80 /* Receive Mode */ + + /* Alias 5 */ + #define LSR_FIR_LSA 0x00 /* FIR Line Status Address */ + #define LSR_FRAME_ABORT 0x08 /* Frame Abort */ + #define LSR_CRC_ERROR 0x10 /* CRC Error */ + #define LSR_SIZE_ERROR 0x20 /* Size Error */ + #define LSR_FRAME_ERROR 0x40 /* Frame Error */ + #define LSR_FIFO_UR 0x80 /* FIFO Underrun */ + #define LSR_FIFO_OR 0x80 /* FIFO Overrun */ + + /* Alias 6 */ + #define BSR_FIFO_NOT_EMPTY 0x80 /* FIFO Not Empty */ + +/* Bank 1 */ +#define FIR_CR 0x00 /* Alias 0, FIR Configuration Register (R/W) */ +#define FIR_FIFO_TR 0x01 /* Alias 1, FIR FIFO Threshold Register (R/W) */ +#define FIR_DMA_TR 0x02 /* Alias 2, FIR DMA Threshold Register (R/W) */ +#define FIR_TIMER_IIR 0x03 /* Alias 3, FIR Timer interrupt interval register (W/O) */ +#define FIR_FIFO_FR 0x03 /* Alias 3, FIR FIFO Flag register (R/O) */ +#define FIR_FIFO_RAR 0x04 /* Alias 4, FIR FIFO Read Address register (R/O) */ +#define FIR_FIFO_WAR 0x05 /* Alias 5, FIR FIFO Write Address register (R/O) */ +#define FIR_TR 0x06 /* Alias 6, Test REgister (W/O) */ + + /* Alias 0 */ + #define CR_DMA_EN 0x01 /* DMA Enable */ + #define CR_DMA_BURST 0x02 /* DMA Burst Mode */ + #define CR_TIMER_EN 0x08 /* Timer Enable */ + + /* Alias 3 */ + #define TIMER_IIR_500 0x00 /* 500 us */ + #define TIMER_IIR_1ms 0x01 /* 1 ms */ + #define TIMER_IIR_2ms 0x02 /* 2 ms */ + #define TIMER_IIR_4ms 0x03 /* 4 ms */ + +/* Bank 2 */ +#define FIR_IRDA_CR 0x00 /* Alias 0, IrDA Control Register (R/W) */ +#define FIR_BOF_CR 0x01 /* Alias 1, BOF Count Register (R/W) */ +#define FIR_BW_CR 0x02 /* Alias 2, Brick Wall Count Register (R/W) */ +#define FIR_TX_DSR_HI 0x03 /* Alias 3, TX Data Size Register (high) (R/W) */ +#define FIR_TX_DSR_LO 0x04 /* Alias 4, TX Data Size Register (low) (R/W) */ +#define FIR_RX_DSR_HI 0x05 /* Alias 5, RX Data Size Register (high) (R/W) */ +#define FIR_RX_DSR_LO 0x06 /* Alias 6, RX Data Size Register (low) (R/W) */ + + /* Alias 0 */ + #define IRDA_CR_HDLC1152 0x80 /* 1.152Mbps HDLC Select */ + #define IRDA_CR_CRC 0X40 /* CRC Select. */ + #define IRDA_CR_HDLC 0x20 /* HDLC select. */ + #define IRDA_CR_HP_MODE 0x10 /* HP mode (read only) */ + #define IRDA_CR_SD_ST 0x08 /* SD/MODE State. */ + #define IRDA_CR_FIR_SIN 0x04 /* FIR SIN Select. */ + #define IRDA_CR_ITTX_0 0x02 /* SOUT State. IRTX force to 0 */ + #define IRDA_CR_ITTX_1 0x03 /* SOUT State. IRTX force to 1 */ + +/* Bank 3 */ +#define FIR_ID_VR 0x00 /* Alias 0, FIR ID Version Register (R/O) */ +#define FIR_MODULE_CR 0x01 /* Alias 1, FIR Module Control Register (R/W) */ +#define FIR_IO_BASE_HI 0x02 /* Alias 2, FIR Higher I/O Base Address Register (R/O) */ +#define FIR_IO_BASE_LO 0x03 /* Alias 3, FIR Lower I/O Base Address Register (R/O) */ +#define FIR_IRQ_CR 0x04 /* Alias 4, FIR IRQ Channel Register (R/O) */ +#define FIR_DMA_CR 0x05 /* Alias 5, FIR DMA Channel Register (R/O) */ + +struct ali_chip { + char *name; + int cfg[2]; + unsigned char entr1; + unsigned char entr2; + unsigned char cid_index; + unsigned char cid_value; + int (*probe)(struct ali_chip *chip, chipio_t *info); + int (*init)(struct ali_chip *chip, chipio_t *info); +}; +typedef struct ali_chip ali_chip_t; + + +/* DMA modes needed */ +#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ +#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ + +#define MAX_TX_WINDOW 7 +#define MAX_RX_WINDOW 7 + +#define TX_FIFO_Threshold 8 +#define RX_FIFO_Threshold 1 +#define TX_DMA_Threshold 1 +#define RX_DMA_Threshold 1 + +/* For storing entries in the status FIFO */ + +struct st_fifo_entry { + int status; + int len; +}; + +struct st_fifo { + struct st_fifo_entry entries[MAX_RX_WINDOW]; + int pending_bytes; + int head; + int tail; + int len; +}; + +struct frame_cb { + void *start; /* Start of frame in DMA mem */ + int len; /* Length of frame in DMA mem */ +}; + +struct tx_fifo { + struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */ + int ptr; /* Currently being sent */ + int len; /* Length of queue */ + int free; /* Next free slot */ + void *tail; /* Next free start in DMA mem */ +}; + +/* Private data for each instance */ +struct ali_ircc_cb { + + struct st_fifo st_fifo; /* Info about received frames */ + struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ + + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct qos_info qos; /* QoS capabilities for this device */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + __u8 ier; /* Interrupt enable register */ + + __u8 InterruptID; /* Interrupt ID */ + __u8 BusStatus; /* Bus Status */ + __u8 LineStatus; /* Line Status */ + + unsigned char rcvFramesOverflow; + + ktime_t stamp; + + spinlock_t lock; /* For serializing operations */ + + __u32 new_speed; + int index; /* Instance index */ + + unsigned char fifo_opti_buf; +}; + +static inline void switch_bank(int iobase, int bank) +{ + outb(bank, iobase+FIR_MCR); +} + +#endif /* ALI_IRCC_H */ diff --git a/drivers/staging/irda/drivers/au1k_ir.c b/drivers/staging/irda/drivers/au1k_ir.c new file mode 100644 index 000000000000..be4ea6aa57a9 --- /dev/null +++ b/drivers/staging/irda/drivers/au1k_ir.c @@ -0,0 +1,989 @@ +/* + * Alchemy Semi Au1000 IrDA driver + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* registers */ +#define IR_RING_PTR_STATUS 0x00 +#define IR_RING_BASE_ADDR_H 0x04 +#define IR_RING_BASE_ADDR_L 0x08 +#define IR_RING_SIZE 0x0C +#define IR_RING_PROMPT 0x10 +#define IR_RING_ADDR_CMPR 0x14 +#define IR_INT_CLEAR 0x18 +#define IR_CONFIG_1 0x20 +#define IR_SIR_FLAGS 0x24 +#define IR_STATUS 0x28 +#define IR_READ_PHY_CONFIG 0x2C +#define IR_WRITE_PHY_CONFIG 0x30 +#define IR_MAX_PKT_LEN 0x34 +#define IR_RX_BYTE_CNT 0x38 +#define IR_CONFIG_2 0x3C +#define IR_ENABLE 0x40 + +/* Config1 */ +#define IR_RX_INVERT_LED (1 << 0) +#define IR_TX_INVERT_LED (1 << 1) +#define IR_ST (1 << 2) +#define IR_SF (1 << 3) +#define IR_SIR (1 << 4) +#define IR_MIR (1 << 5) +#define IR_FIR (1 << 6) +#define IR_16CRC (1 << 7) +#define IR_TD (1 << 8) +#define IR_RX_ALL (1 << 9) +#define IR_DMA_ENABLE (1 << 10) +#define IR_RX_ENABLE (1 << 11) +#define IR_TX_ENABLE (1 << 12) +#define IR_LOOPBACK (1 << 14) +#define IR_SIR_MODE (IR_SIR | IR_DMA_ENABLE | \ + IR_RX_ALL | IR_RX_ENABLE | IR_SF | \ + IR_16CRC) + +/* ir_status */ +#define IR_RX_STATUS (1 << 9) +#define IR_TX_STATUS (1 << 10) +#define IR_PHYEN (1 << 15) + +/* ir_write_phy_config */ +#define IR_BR(x) (((x) & 0x3f) << 10) /* baud rate */ +#define IR_PW(x) (((x) & 0x1f) << 5) /* pulse width */ +#define IR_P(x) ((x) & 0x1f) /* preamble bits */ + +/* Config2 */ +#define IR_MODE_INV (1 << 0) +#define IR_ONE_PIN (1 << 1) +#define IR_PHYCLK_40MHZ (0 << 2) +#define IR_PHYCLK_48MHZ (1 << 2) +#define IR_PHYCLK_56MHZ (2 << 2) +#define IR_PHYCLK_64MHZ (3 << 2) +#define IR_DP (1 << 4) +#define IR_DA (1 << 5) +#define IR_FLT_HIGH (0 << 6) +#define IR_FLT_MEDHI (1 << 6) +#define IR_FLT_MEDLO (2 << 6) +#define IR_FLT_LO (3 << 6) +#define IR_IEN (1 << 8) + +/* ir_enable */ +#define IR_HC (1 << 3) /* divide SBUS clock by 2 */ +#define IR_CE (1 << 2) /* clock enable */ +#define IR_C (1 << 1) /* coherency bit */ +#define IR_BE (1 << 0) /* set in big endian mode */ + +#define NUM_IR_DESC 64 +#define RING_SIZE_4 0x0 +#define RING_SIZE_16 0x3 +#define RING_SIZE_64 0xF +#define MAX_NUM_IR_DESC 64 +#define MAX_BUF_SIZE 2048 + +/* Ring descriptor flags */ +#define AU_OWN (1 << 7) /* tx,rx */ +#define IR_DIS_CRC (1 << 6) /* tx */ +#define IR_BAD_CRC (1 << 5) /* tx */ +#define IR_NEED_PULSE (1 << 4) /* tx */ +#define IR_FORCE_UNDER (1 << 3) /* tx */ +#define IR_DISABLE_TX (1 << 2) /* tx */ +#define IR_HW_UNDER (1 << 0) /* tx */ +#define IR_TX_ERROR (IR_DIS_CRC | IR_BAD_CRC | IR_HW_UNDER) + +#define IR_PHY_ERROR (1 << 6) /* rx */ +#define IR_CRC_ERROR (1 << 5) /* rx */ +#define IR_MAX_LEN (1 << 4) /* rx */ +#define IR_FIFO_OVER (1 << 3) /* rx */ +#define IR_SIR_ERROR (1 << 2) /* rx */ +#define IR_RX_ERROR (IR_PHY_ERROR | IR_CRC_ERROR | \ + IR_MAX_LEN | IR_FIFO_OVER | IR_SIR_ERROR) + +struct db_dest { + struct db_dest *pnext; + volatile u32 *vaddr; + dma_addr_t dma_addr; +}; + +struct ring_dest { + u8 count_0; /* 7:0 */ + u8 count_1; /* 12:8 */ + u8 reserved; + u8 flags; + u8 addr_0; /* 7:0 */ + u8 addr_1; /* 15:8 */ + u8 addr_2; /* 23:16 */ + u8 addr_3; /* 31:24 */ +}; + +/* Private data for each instance */ +struct au1k_private { + void __iomem *iobase; + int irq_rx, irq_tx; + + struct db_dest *pDBfree; + struct db_dest db[2 * NUM_IR_DESC]; + volatile struct ring_dest *rx_ring[NUM_IR_DESC]; + volatile struct ring_dest *tx_ring[NUM_IR_DESC]; + struct db_dest *rx_db_inuse[NUM_IR_DESC]; + struct db_dest *tx_db_inuse[NUM_IR_DESC]; + u32 rx_head; + u32 tx_head; + u32 tx_tail; + u32 tx_full; + + iobuff_t rx_buff; + + struct net_device *netdev; + struct qos_info qos; + struct irlap_cb *irlap; + + u8 open; + u32 speed; + u32 newspeed; + + struct resource *ioarea; + struct au1k_irda_platform_data *platdata; + struct clk *irda_clk; +}; + +static int qos_mtt_bits = 0x07; /* 1 ms or more */ + +static void au1k_irda_plat_set_phy_mode(struct au1k_private *p, int mode) +{ + if (p->platdata && p->platdata->set_phy_mode) + p->platdata->set_phy_mode(mode); +} + +static inline unsigned long irda_read(struct au1k_private *p, + unsigned long ofs) +{ + /* + * IrDA peripheral bug. You have to read the register + * twice to get the right value. + */ + (void)__raw_readl(p->iobase + ofs); + return __raw_readl(p->iobase + ofs); +} + +static inline void irda_write(struct au1k_private *p, unsigned long ofs, + unsigned long val) +{ + __raw_writel(val, p->iobase + ofs); + wmb(); +} + +/* + * Buffer allocation/deallocation routines. The buffer descriptor returned + * has the virtual and dma address of a buffer suitable for + * both, receive and transmit operations. + */ +static struct db_dest *GetFreeDB(struct au1k_private *aup) +{ + struct db_dest *db; + db = aup->pDBfree; + + if (db) + aup->pDBfree = db->pnext; + return db; +} + +/* + DMA memory allocation, derived from pci_alloc_consistent. + However, the Au1000 data cache is coherent (when programmed + so), therefore we return KSEG0 address, not KSEG1. +*/ +static void *dma_alloc(size_t size, dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC | GFP_DMA; + + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + ret = (void *)KSEG0ADDR(ret); + } + return ret; +} + +static void dma_free(void *vaddr, size_t size) +{ + vaddr = (void *)KSEG0ADDR(vaddr); + free_pages((unsigned long) vaddr, get_order(size)); +} + + +static void setup_hw_rings(struct au1k_private *aup, u32 rx_base, u32 tx_base) +{ + int i; + for (i = 0; i < NUM_IR_DESC; i++) { + aup->rx_ring[i] = (volatile struct ring_dest *) + (rx_base + sizeof(struct ring_dest) * i); + } + for (i = 0; i < NUM_IR_DESC; i++) { + aup->tx_ring[i] = (volatile struct ring_dest *) + (tx_base + sizeof(struct ring_dest) * i); + } +} + +static int au1k_irda_init_iobuf(iobuff_t *io, int size) +{ + io->head = kmalloc(size, GFP_KERNEL); + if (io->head != NULL) { + io->truesize = size; + io->in_frame = FALSE; + io->state = OUTSIDE_FRAME; + io->data = io->head; + } + return io->head ? 0 : -ENOMEM; +} + +/* + * Set the IrDA communications speed. + */ +static int au1k_irda_set_speed(struct net_device *dev, int speed) +{ + struct au1k_private *aup = netdev_priv(dev); + volatile struct ring_dest *ptxd; + unsigned long control; + int ret = 0, timeout = 10, i; + + if (speed == aup->speed) + return ret; + + /* disable PHY first */ + au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF); + irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN); + + /* disable RX/TX */ + irda_write(aup, IR_CONFIG_1, + irda_read(aup, IR_CONFIG_1) & ~(IR_RX_ENABLE | IR_TX_ENABLE)); + msleep(20); + while (irda_read(aup, IR_STATUS) & (IR_RX_STATUS | IR_TX_STATUS)) { + msleep(20); + if (!timeout--) { + printk(KERN_ERR "%s: rx/tx disable timeout\n", + dev->name); + break; + } + } + + /* disable DMA */ + irda_write(aup, IR_CONFIG_1, + irda_read(aup, IR_CONFIG_1) & ~IR_DMA_ENABLE); + msleep(20); + + /* After we disable tx/rx. the index pointers go back to zero. */ + aup->tx_head = aup->tx_tail = aup->rx_head = 0; + for (i = 0; i < NUM_IR_DESC; i++) { + ptxd = aup->tx_ring[i]; + ptxd->flags = 0; + ptxd->count_0 = 0; + ptxd->count_1 = 0; + } + + for (i = 0; i < NUM_IR_DESC; i++) { + ptxd = aup->rx_ring[i]; + ptxd->count_0 = 0; + ptxd->count_1 = 0; + ptxd->flags = AU_OWN; + } + + if (speed == 4000000) + au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_FIR); + else + au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR); + + switch (speed) { + case 9600: + irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(11) | IR_PW(12)); + irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); + break; + case 19200: + irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(5) | IR_PW(12)); + irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); + break; + case 38400: + irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(2) | IR_PW(12)); + irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); + break; + case 57600: + irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(1) | IR_PW(12)); + irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); + break; + case 115200: + irda_write(aup, IR_WRITE_PHY_CONFIG, IR_PW(12)); + irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); + break; + case 4000000: + irda_write(aup, IR_WRITE_PHY_CONFIG, IR_P(15)); + irda_write(aup, IR_CONFIG_1, IR_FIR | IR_DMA_ENABLE | + IR_RX_ENABLE); + break; + default: + printk(KERN_ERR "%s unsupported speed %x\n", dev->name, speed); + ret = -EINVAL; + break; + } + + aup->speed = speed; + irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) | IR_PHYEN); + + control = irda_read(aup, IR_STATUS); + irda_write(aup, IR_RING_PROMPT, 0); + + if (control & (1 << 14)) { + printk(KERN_ERR "%s: configuration error\n", dev->name); + } else { + if (control & (1 << 11)) + printk(KERN_DEBUG "%s Valid SIR config\n", dev->name); + if (control & (1 << 12)) + printk(KERN_DEBUG "%s Valid MIR config\n", dev->name); + if (control & (1 << 13)) + printk(KERN_DEBUG "%s Valid FIR config\n", dev->name); + if (control & (1 << 10)) + printk(KERN_DEBUG "%s TX enabled\n", dev->name); + if (control & (1 << 9)) + printk(KERN_DEBUG "%s RX enabled\n", dev->name); + } + + return ret; +} + +static void update_rx_stats(struct net_device *dev, u32 status, u32 count) +{ + struct net_device_stats *ps = &dev->stats; + + ps->rx_packets++; + + if (status & IR_RX_ERROR) { + ps->rx_errors++; + if (status & (IR_PHY_ERROR | IR_FIFO_OVER)) + ps->rx_missed_errors++; + if (status & IR_MAX_LEN) + ps->rx_length_errors++; + if (status & IR_CRC_ERROR) + ps->rx_crc_errors++; + } else + ps->rx_bytes += count; +} + +static void update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len) +{ + struct net_device_stats *ps = &dev->stats; + + ps->tx_packets++; + ps->tx_bytes += pkt_len; + + if (status & IR_TX_ERROR) { + ps->tx_errors++; + ps->tx_aborted_errors++; + } +} + +static void au1k_tx_ack(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + volatile struct ring_dest *ptxd; + + ptxd = aup->tx_ring[aup->tx_tail]; + while (!(ptxd->flags & AU_OWN) && (aup->tx_tail != aup->tx_head)) { + update_tx_stats(dev, ptxd->flags, + (ptxd->count_1 << 8) | ptxd->count_0); + ptxd->count_0 = 0; + ptxd->count_1 = 0; + wmb(); + aup->tx_tail = (aup->tx_tail + 1) & (NUM_IR_DESC - 1); + ptxd = aup->tx_ring[aup->tx_tail]; + + if (aup->tx_full) { + aup->tx_full = 0; + netif_wake_queue(dev); + } + } + + if (aup->tx_tail == aup->tx_head) { + if (aup->newspeed) { + au1k_irda_set_speed(dev, aup->newspeed); + aup->newspeed = 0; + } else { + irda_write(aup, IR_CONFIG_1, + irda_read(aup, IR_CONFIG_1) & ~IR_TX_ENABLE); + irda_write(aup, IR_CONFIG_1, + irda_read(aup, IR_CONFIG_1) | IR_RX_ENABLE); + irda_write(aup, IR_RING_PROMPT, 0); + } + } +} + +static int au1k_irda_rx(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + volatile struct ring_dest *prxd; + struct sk_buff *skb; + struct db_dest *pDB; + u32 flags, count; + + prxd = aup->rx_ring[aup->rx_head]; + flags = prxd->flags; + + while (!(flags & AU_OWN)) { + pDB = aup->rx_db_inuse[aup->rx_head]; + count = (prxd->count_1 << 8) | prxd->count_0; + if (!(flags & IR_RX_ERROR)) { + /* good frame */ + update_rx_stats(dev, flags, count); + skb = alloc_skb(count + 1, GFP_ATOMIC); + if (skb == NULL) { + dev->stats.rx_dropped++; + continue; + } + skb_reserve(skb, 1); + if (aup->speed == 4000000) + skb_put(skb, count); + else + skb_put(skb, count - 2); + skb_copy_to_linear_data(skb, (void *)pDB->vaddr, + count - 2); + skb->dev = dev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + prxd->count_0 = 0; + prxd->count_1 = 0; + } + prxd->flags |= AU_OWN; + aup->rx_head = (aup->rx_head + 1) & (NUM_IR_DESC - 1); + irda_write(aup, IR_RING_PROMPT, 0); + + /* next descriptor */ + prxd = aup->rx_ring[aup->rx_head]; + flags = prxd->flags; + + } + return 0; +} + +static irqreturn_t au1k_irda_interrupt(int dummy, void *dev_id) +{ + struct net_device *dev = dev_id; + struct au1k_private *aup = netdev_priv(dev); + + irda_write(aup, IR_INT_CLEAR, 0); /* ack irda interrupts */ + + au1k_irda_rx(dev); + au1k_tx_ack(dev); + + return IRQ_HANDLED; +} + +static int au1k_init(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + u32 enable, ring_address, phyck; + struct clk *c; + int i; + + c = clk_get(NULL, "irda_clk"); + if (IS_ERR(c)) + return PTR_ERR(c); + i = clk_prepare_enable(c); + if (i) { + clk_put(c); + return i; + } + + switch (clk_get_rate(c)) { + case 40000000: + phyck = IR_PHYCLK_40MHZ; + break; + case 48000000: + phyck = IR_PHYCLK_48MHZ; + break; + case 56000000: + phyck = IR_PHYCLK_56MHZ; + break; + case 64000000: + phyck = IR_PHYCLK_64MHZ; + break; + default: + clk_disable_unprepare(c); + clk_put(c); + return -EINVAL; + } + aup->irda_clk = c; + + enable = IR_HC | IR_CE | IR_C; +#ifndef CONFIG_CPU_LITTLE_ENDIAN + enable |= IR_BE; +#endif + aup->tx_head = 0; + aup->tx_tail = 0; + aup->rx_head = 0; + + for (i = 0; i < NUM_IR_DESC; i++) + aup->rx_ring[i]->flags = AU_OWN; + + irda_write(aup, IR_ENABLE, enable); + msleep(20); + + /* disable PHY */ + au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF); + irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN); + msleep(20); + + irda_write(aup, IR_MAX_PKT_LEN, MAX_BUF_SIZE); + + ring_address = (u32)virt_to_phys((void *)aup->rx_ring[0]); + irda_write(aup, IR_RING_BASE_ADDR_H, ring_address >> 26); + irda_write(aup, IR_RING_BASE_ADDR_L, (ring_address >> 10) & 0xffff); + + irda_write(aup, IR_RING_SIZE, + (RING_SIZE_64 << 8) | (RING_SIZE_64 << 12)); + + irda_write(aup, IR_CONFIG_2, phyck | IR_ONE_PIN); + irda_write(aup, IR_RING_ADDR_CMPR, 0); + + au1k_irda_set_speed(dev, 9600); + return 0; +} + +static int au1k_irda_start(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + char hwname[32]; + int retval; + + retval = au1k_init(dev); + if (retval) { + printk(KERN_ERR "%s: error in au1k_init\n", dev->name); + return retval; + } + + retval = request_irq(aup->irq_tx, &au1k_irda_interrupt, 0, + dev->name, dev); + if (retval) { + printk(KERN_ERR "%s: unable to get IRQ %d\n", + dev->name, dev->irq); + return retval; + } + retval = request_irq(aup->irq_rx, &au1k_irda_interrupt, 0, + dev->name, dev); + if (retval) { + free_irq(aup->irq_tx, dev); + printk(KERN_ERR "%s: unable to get IRQ %d\n", + dev->name, dev->irq); + return retval; + } + + /* Give self a hardware name */ + sprintf(hwname, "Au1000 SIR/FIR"); + aup->irlap = irlap_open(dev, &aup->qos, hwname); + netif_start_queue(dev); + + /* int enable */ + irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) | IR_IEN); + + /* power up */ + au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR); + + return 0; +} + +static int au1k_irda_stop(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + + au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF); + + /* disable interrupts */ + irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) & ~IR_IEN); + irda_write(aup, IR_CONFIG_1, 0); + irda_write(aup, IR_ENABLE, 0); /* disable clock */ + + if (aup->irlap) { + irlap_close(aup->irlap); + aup->irlap = NULL; + } + + netif_stop_queue(dev); + + /* disable the interrupt */ + free_irq(aup->irq_tx, dev); + free_irq(aup->irq_rx, dev); + + clk_disable_unprepare(aup->irda_clk); + clk_put(aup->irda_clk); + + return 0; +} + +/* + * Au1000 transmit routine. + */ +static int au1k_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + int speed = irda_get_next_speed(skb); + volatile struct ring_dest *ptxd; + struct db_dest *pDB; + u32 len, flags; + + if (speed != aup->speed && speed != -1) + aup->newspeed = speed; + + if ((skb->len == 0) && (aup->newspeed)) { + if (aup->tx_tail == aup->tx_head) { + au1k_irda_set_speed(dev, speed); + aup->newspeed = 0; + } + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + ptxd = aup->tx_ring[aup->tx_head]; + flags = ptxd->flags; + + if (flags & AU_OWN) { + printk(KERN_DEBUG "%s: tx_full\n", dev->name); + netif_stop_queue(dev); + aup->tx_full = 1; + return 1; + } else if (((aup->tx_head + 1) & (NUM_IR_DESC - 1)) == aup->tx_tail) { + printk(KERN_DEBUG "%s: tx_full\n", dev->name); + netif_stop_queue(dev); + aup->tx_full = 1; + return 1; + } + + pDB = aup->tx_db_inuse[aup->tx_head]; + +#if 0 + if (irda_read(aup, IR_RX_BYTE_CNT) != 0) { + printk(KERN_DEBUG "tx warning: rx byte cnt %x\n", + irda_read(aup, IR_RX_BYTE_CNT)); + } +#endif + + if (aup->speed == 4000000) { + /* FIR */ + skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len); + ptxd->count_0 = skb->len & 0xff; + ptxd->count_1 = (skb->len >> 8) & 0xff; + } else { + /* SIR */ + len = async_wrap_skb(skb, (u8 *)pDB->vaddr, MAX_BUF_SIZE); + ptxd->count_0 = len & 0xff; + ptxd->count_1 = (len >> 8) & 0xff; + ptxd->flags |= IR_DIS_CRC; + } + ptxd->flags |= AU_OWN; + wmb(); + + irda_write(aup, IR_CONFIG_1, + irda_read(aup, IR_CONFIG_1) | IR_TX_ENABLE); + irda_write(aup, IR_RING_PROMPT, 0); + + dev_kfree_skb(skb); + aup->tx_head = (aup->tx_head + 1) & (NUM_IR_DESC - 1); + return NETDEV_TX_OK; +} + +/* + * The Tx ring has been full longer than the watchdog timeout + * value. The transmitter must be hung? + */ +static void au1k_tx_timeout(struct net_device *dev) +{ + u32 speed; + struct au1k_private *aup = netdev_priv(dev); + + printk(KERN_ERR "%s: tx timeout\n", dev->name); + speed = aup->speed; + aup->speed = 0; + au1k_irda_set_speed(dev, speed); + aup->tx_full = 0; + netif_wake_queue(dev); +} + +static int au1k_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) +{ + struct if_irda_req *rq = (struct if_irda_req *)ifreq; + struct au1k_private *aup = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (cmd) { + case SIOCSBANDWIDTH: + if (capable(CAP_NET_ADMIN)) { + /* + * We are unable to set the speed if the + * device is not running. + */ + if (aup->open) + ret = au1k_irda_set_speed(dev, + rq->ifr_baudrate); + else { + printk(KERN_ERR "%s ioctl: !netif_running\n", + dev->name); + ret = 0; + } + } + break; + + case SIOCSMEDIABUSY: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + irda_device_set_media_busy(dev, TRUE); + ret = 0; + } + break; + + case SIOCGRECEIVING: + rq->ifr_receiving = 0; + break; + default: + break; + } + return ret; +} + +static const struct net_device_ops au1k_irda_netdev_ops = { + .ndo_open = au1k_irda_start, + .ndo_stop = au1k_irda_stop, + .ndo_start_xmit = au1k_irda_hard_xmit, + .ndo_tx_timeout = au1k_tx_timeout, + .ndo_do_ioctl = au1k_irda_ioctl, +}; + +static int au1k_irda_net_init(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + struct db_dest *pDB, *pDBfree; + int i, err, retval = 0; + dma_addr_t temp; + + err = au1k_irda_init_iobuf(&aup->rx_buff, 14384); + if (err) + goto out1; + + dev->netdev_ops = &au1k_irda_netdev_ops; + + irda_init_max_qos_capabilies(&aup->qos); + + /* The only value we must override it the baudrate */ + aup->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 | + IR_57600 | IR_115200 | IR_576000 | (IR_4000000 << 8); + + aup->qos.min_turn_time.bits = qos_mtt_bits; + irda_qos_bits_to_value(&aup->qos); + + retval = -ENOMEM; + + /* Tx ring follows rx ring + 512 bytes */ + /* we need a 1k aligned buffer */ + aup->rx_ring[0] = (struct ring_dest *) + dma_alloc(2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)), + &temp); + if (!aup->rx_ring[0]) + goto out2; + + /* allocate the data buffers */ + aup->db[0].vaddr = + dma_alloc(MAX_BUF_SIZE * 2 * NUM_IR_DESC, &temp); + if (!aup->db[0].vaddr) + goto out3; + + setup_hw_rings(aup, (u32)aup->rx_ring[0], (u32)aup->rx_ring[0] + 512); + + pDBfree = NULL; + pDB = aup->db; + for (i = 0; i < (2 * NUM_IR_DESC); i++) { + pDB->pnext = pDBfree; + pDBfree = pDB; + pDB->vaddr = + (u32 *)((unsigned)aup->db[0].vaddr + (MAX_BUF_SIZE * i)); + pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr); + pDB++; + } + aup->pDBfree = pDBfree; + + /* attach a data buffer to each descriptor */ + for (i = 0; i < NUM_IR_DESC; i++) { + pDB = GetFreeDB(aup); + if (!pDB) + goto out3; + aup->rx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); + aup->rx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff); + aup->rx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff); + aup->rx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff); + aup->rx_db_inuse[i] = pDB; + } + for (i = 0; i < NUM_IR_DESC; i++) { + pDB = GetFreeDB(aup); + if (!pDB) + goto out3; + aup->tx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); + aup->tx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff); + aup->tx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff); + aup->tx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff); + aup->tx_ring[i]->count_0 = 0; + aup->tx_ring[i]->count_1 = 0; + aup->tx_ring[i]->flags = 0; + aup->tx_db_inuse[i] = pDB; + } + + return 0; + +out3: + dma_free((void *)aup->rx_ring[0], + 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest))); +out2: + kfree(aup->rx_buff.head); +out1: + printk(KERN_ERR "au1k_irda_net_init() failed. Returns %d\n", retval); + return retval; +} + +static int au1k_irda_probe(struct platform_device *pdev) +{ + struct au1k_private *aup; + struct net_device *dev; + struct resource *r; + struct clk *c; + int err; + + dev = alloc_irdadev(sizeof(struct au1k_private)); + if (!dev) + return -ENOMEM; + + aup = netdev_priv(dev); + + aup->platdata = pdev->dev.platform_data; + + err = -EINVAL; + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!r) + goto out; + + aup->irq_tx = r->start; + + r = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!r) + goto out; + + aup->irq_rx = r->start; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + goto out; + + err = -EBUSY; + aup->ioarea = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!aup->ioarea) + goto out; + + /* bail out early if clock doesn't exist */ + c = clk_get(NULL, "irda_clk"); + if (IS_ERR(c)) { + err = PTR_ERR(c); + goto out; + } + clk_put(c); + + aup->iobase = ioremap_nocache(r->start, resource_size(r)); + if (!aup->iobase) + goto out2; + + dev->irq = aup->irq_rx; + + err = au1k_irda_net_init(dev); + if (err) + goto out3; + err = register_netdev(dev); + if (err) + goto out4; + + platform_set_drvdata(pdev, dev); + + printk(KERN_INFO "IrDA: Registered device %s\n", dev->name); + return 0; + +out4: + dma_free((void *)aup->db[0].vaddr, + MAX_BUF_SIZE * 2 * NUM_IR_DESC); + dma_free((void *)aup->rx_ring[0], + 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest))); + kfree(aup->rx_buff.head); +out3: + iounmap(aup->iobase); +out2: + release_resource(aup->ioarea); + kfree(aup->ioarea); +out: + free_netdev(dev); + return err; +} + +static int au1k_irda_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct au1k_private *aup = netdev_priv(dev); + + unregister_netdev(dev); + + dma_free((void *)aup->db[0].vaddr, + MAX_BUF_SIZE * 2 * NUM_IR_DESC); + dma_free((void *)aup->rx_ring[0], + 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest))); + kfree(aup->rx_buff.head); + + iounmap(aup->iobase); + release_resource(aup->ioarea); + kfree(aup->ioarea); + + free_netdev(dev); + + return 0; +} + +static struct platform_driver au1k_irda_driver = { + .driver = { + .name = "au1000-irda", + }, + .probe = au1k_irda_probe, + .remove = au1k_irda_remove, +}; + +module_platform_driver(au1k_irda_driver); + +MODULE_AUTHOR("Pete Popov "); +MODULE_DESCRIPTION("Au1000 IrDA Device Driver"); diff --git a/drivers/staging/irda/drivers/bfin_sir.c b/drivers/staging/irda/drivers/bfin_sir.c new file mode 100644 index 000000000000..3151b580dbd6 --- /dev/null +++ b/drivers/staging/irda/drivers/bfin_sir.c @@ -0,0 +1,817 @@ +/* + * Blackfin Infra-red Driver + * + * Copyright 2006-2009 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + * + */ +#include "bfin_sir.h" + +#ifdef CONFIG_SIR_BFIN_DMA +#define DMA_SIR_RX_XCNT 10 +#define DMA_SIR_RX_YCNT (PAGE_SIZE / DMA_SIR_RX_XCNT) +#define DMA_SIR_RX_FLUSH_JIFS (HZ * 4 / 250) +#endif + +#if ANOMALY_05000447 +static int max_rate = 57600; +#else +static int max_rate = 115200; +#endif + +static void turnaround_delay(int mtt) +{ + long ticks; + + mtt = mtt < 10000 ? 10000 : mtt; + ticks = 1 + mtt / (USEC_PER_SEC / HZ); + schedule_timeout_uninterruptible(ticks); +} + +static void bfin_sir_init_ports(struct bfin_sir_port *sp, struct platform_device *pdev) +{ + int i; + struct resource *res; + + for (i = 0; i < pdev->num_resources; i++) { + res = &pdev->resource[i]; + switch (res->flags) { + case IORESOURCE_MEM: + sp->membase = (void __iomem *)res->start; + break; + case IORESOURCE_IRQ: + sp->irq = res->start; + break; + case IORESOURCE_DMA: + sp->rx_dma_channel = res->start; + sp->tx_dma_channel = res->end; + break; + default: + break; + } + } + + sp->clk = get_sclk(); +#ifdef CONFIG_SIR_BFIN_DMA + sp->tx_done = 1; + init_timer(&(sp->rx_dma_timer)); +#endif +} + +static void bfin_sir_stop_tx(struct bfin_sir_port *port) +{ +#ifdef CONFIG_SIR_BFIN_DMA + disable_dma(port->tx_dma_channel); +#endif + + while (!(UART_GET_LSR(port) & THRE)) { + cpu_relax(); + continue; + } + + UART_CLEAR_IER(port, ETBEI); +} + +static void bfin_sir_enable_tx(struct bfin_sir_port *port) +{ + UART_SET_IER(port, ETBEI); +} + +static void bfin_sir_stop_rx(struct bfin_sir_port *port) +{ + UART_CLEAR_IER(port, ERBFI); +} + +static void bfin_sir_enable_rx(struct bfin_sir_port *port) +{ + UART_SET_IER(port, ERBFI); +} + +static int bfin_sir_set_speed(struct bfin_sir_port *port, int speed) +{ + int ret = -EINVAL; + unsigned int quot; + unsigned short val, lsr, lcr; + static int utime; + int count = 10; + + lcr = WLS(8); + + switch (speed) { + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + + /* + * IRDA is not affected by anomaly 05000230, so there is no + * need to tweak the divisor like he UART driver (which will + * slightly speed up the baud rate on us). + */ + quot = (port->clk + (8 * speed)) / (16 * speed); + + do { + udelay(utime); + lsr = UART_GET_LSR(port); + } while (!(lsr & TEMT) && count--); + + /* The useconds for 1 bits to transmit */ + utime = 1000000 / speed + 1; + + /* Clear UCEN bit to reset the UART state machine + * and control registers + */ + val = UART_GET_GCTL(port); + val &= ~UCEN; + UART_PUT_GCTL(port, val); + + /* Set DLAB in LCR to Access THR RBR IER */ + UART_SET_DLAB(port); + SSYNC(); + + UART_PUT_DLL(port, quot & 0xFF); + UART_PUT_DLH(port, (quot >> 8) & 0xFF); + SSYNC(); + + /* Clear DLAB in LCR */ + UART_CLEAR_DLAB(port); + SSYNC(); + + UART_PUT_LCR(port, lcr); + + val = UART_GET_GCTL(port); + val |= UCEN; + UART_PUT_GCTL(port, val); + + ret = 0; + break; + default: + printk(KERN_WARNING "bfin_sir: Invalid speed %d\n", speed); + break; + } + + val = UART_GET_GCTL(port); + /* If not add the 'RPOLC', we can't catch the receive interrupt. + * It's related with the HW layout and the IR transiver. + */ + val |= UMOD_IRDA | RPOLC; + UART_PUT_GCTL(port, val); + return ret; +} + +static int bfin_sir_is_receiving(struct net_device *dev) +{ + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + + if (!(UART_GET_IER(port) & ERBFI)) + return 0; + return self->rx_buff.state != OUTSIDE_FRAME; +} + +#ifdef CONFIG_SIR_BFIN_PIO +static void bfin_sir_tx_chars(struct net_device *dev) +{ + unsigned int chr; + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + + if (self->tx_buff.len != 0) { + chr = *(self->tx_buff.data); + UART_PUT_CHAR(port, chr); + self->tx_buff.data++; + self->tx_buff.len--; + } else { + self->stats.tx_packets++; + self->stats.tx_bytes += self->tx_buff.data - self->tx_buff.head; + if (self->newspeed) { + bfin_sir_set_speed(port, self->newspeed); + self->speed = self->newspeed; + self->newspeed = 0; + } + bfin_sir_stop_tx(port); + bfin_sir_enable_rx(port); + /* I'm hungry! */ + netif_wake_queue(dev); + } +} + +static void bfin_sir_rx_chars(struct net_device *dev) +{ + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + unsigned char ch; + + UART_CLEAR_LSR(port); + ch = UART_GET_CHAR(port); + async_unwrap_char(dev, &self->stats, &self->rx_buff, ch); +} + +static irqreturn_t bfin_sir_rx_int(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + + spin_lock(&self->lock); + while ((UART_GET_LSR(port) & DR)) + bfin_sir_rx_chars(dev); + spin_unlock(&self->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t bfin_sir_tx_int(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + + spin_lock(&self->lock); + if (UART_GET_LSR(port) & THRE) + bfin_sir_tx_chars(dev); + spin_unlock(&self->lock); + + return IRQ_HANDLED; +} +#endif /* CONFIG_SIR_BFIN_PIO */ + +#ifdef CONFIG_SIR_BFIN_DMA +static void bfin_sir_dma_tx_chars(struct net_device *dev) +{ + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + + if (!port->tx_done) + return; + port->tx_done = 0; + + if (self->tx_buff.len == 0) { + self->stats.tx_packets++; + if (self->newspeed) { + bfin_sir_set_speed(port, self->newspeed); + self->speed = self->newspeed; + self->newspeed = 0; + } + bfin_sir_enable_rx(port); + port->tx_done = 1; + netif_wake_queue(dev); + return; + } + + blackfin_dcache_flush_range((unsigned long)(self->tx_buff.data), + (unsigned long)(self->tx_buff.data+self->tx_buff.len)); + set_dma_config(port->tx_dma_channel, + set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, + INTR_ON_BUF, DIMENSION_LINEAR, DATA_SIZE_8, + DMA_SYNC_RESTART)); + set_dma_start_addr(port->tx_dma_channel, + (unsigned long)(self->tx_buff.data)); + set_dma_x_count(port->tx_dma_channel, self->tx_buff.len); + set_dma_x_modify(port->tx_dma_channel, 1); + enable_dma(port->tx_dma_channel); +} + +static irqreturn_t bfin_sir_dma_tx_int(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + + spin_lock(&self->lock); + if (!(get_dma_curr_irqstat(port->tx_dma_channel) & DMA_RUN)) { + clear_dma_irqstat(port->tx_dma_channel); + bfin_sir_stop_tx(port); + + self->stats.tx_packets++; + self->stats.tx_bytes += self->tx_buff.len; + self->tx_buff.len = 0; + if (self->newspeed) { + bfin_sir_set_speed(port, self->newspeed); + self->speed = self->newspeed; + self->newspeed = 0; + } + bfin_sir_enable_rx(port); + /* I'm hungry! */ + netif_wake_queue(dev); + port->tx_done = 1; + } + spin_unlock(&self->lock); + + return IRQ_HANDLED; +} + +static void bfin_sir_dma_rx_chars(struct net_device *dev) +{ + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + int i; + + UART_CLEAR_LSR(port); + + for (i = port->rx_dma_buf.head; i < port->rx_dma_buf.tail; i++) + async_unwrap_char(dev, &self->stats, &self->rx_buff, port->rx_dma_buf.buf[i]); +} + +void bfin_sir_rx_dma_timeout(struct net_device *dev) +{ + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + int x_pos, pos; + unsigned long flags; + + spin_lock_irqsave(&self->lock, flags); + x_pos = DMA_SIR_RX_XCNT - get_dma_curr_xcount(port->rx_dma_channel); + if (x_pos == DMA_SIR_RX_XCNT) + x_pos = 0; + + pos = port->rx_dma_nrows * DMA_SIR_RX_XCNT + x_pos; + + if (pos > port->rx_dma_buf.tail) { + port->rx_dma_buf.tail = pos; + bfin_sir_dma_rx_chars(dev); + port->rx_dma_buf.head = port->rx_dma_buf.tail; + } + spin_unlock_irqrestore(&self->lock, flags); +} + +static irqreturn_t bfin_sir_dma_rx_int(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + unsigned short irqstat; + + spin_lock(&self->lock); + + port->rx_dma_nrows++; + port->rx_dma_buf.tail = DMA_SIR_RX_XCNT * port->rx_dma_nrows; + bfin_sir_dma_rx_chars(dev); + if (port->rx_dma_nrows >= DMA_SIR_RX_YCNT) { + port->rx_dma_nrows = 0; + port->rx_dma_buf.tail = 0; + } + port->rx_dma_buf.head = port->rx_dma_buf.tail; + + irqstat = get_dma_curr_irqstat(port->rx_dma_channel); + clear_dma_irqstat(port->rx_dma_channel); + spin_unlock(&self->lock); + + mod_timer(&port->rx_dma_timer, jiffies + DMA_SIR_RX_FLUSH_JIFS); + return IRQ_HANDLED; +} +#endif /* CONFIG_SIR_BFIN_DMA */ + +static int bfin_sir_startup(struct bfin_sir_port *port, struct net_device *dev) +{ +#ifdef CONFIG_SIR_BFIN_DMA + dma_addr_t dma_handle; +#endif /* CONFIG_SIR_BFIN_DMA */ + + if (request_dma(port->rx_dma_channel, "BFIN_UART_RX") < 0) { + dev_warn(&dev->dev, "Unable to attach SIR RX DMA channel\n"); + return -EBUSY; + } + + if (request_dma(port->tx_dma_channel, "BFIN_UART_TX") < 0) { + dev_warn(&dev->dev, "Unable to attach SIR TX DMA channel\n"); + free_dma(port->rx_dma_channel); + return -EBUSY; + } + +#ifdef CONFIG_SIR_BFIN_DMA + + set_dma_callback(port->rx_dma_channel, bfin_sir_dma_rx_int, dev); + set_dma_callback(port->tx_dma_channel, bfin_sir_dma_tx_int, dev); + + port->rx_dma_buf.buf = dma_alloc_coherent(NULL, PAGE_SIZE, + &dma_handle, GFP_DMA); + port->rx_dma_buf.head = 0; + port->rx_dma_buf.tail = 0; + port->rx_dma_nrows = 0; + + set_dma_config(port->rx_dma_channel, + set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO, + INTR_ON_ROW, DIMENSION_2D, + DATA_SIZE_8, DMA_SYNC_RESTART)); + set_dma_x_count(port->rx_dma_channel, DMA_SIR_RX_XCNT); + set_dma_x_modify(port->rx_dma_channel, 1); + set_dma_y_count(port->rx_dma_channel, DMA_SIR_RX_YCNT); + set_dma_y_modify(port->rx_dma_channel, 1); + set_dma_start_addr(port->rx_dma_channel, (unsigned long)port->rx_dma_buf.buf); + enable_dma(port->rx_dma_channel); + + port->rx_dma_timer.data = (unsigned long)(dev); + port->rx_dma_timer.function = (void *)bfin_sir_rx_dma_timeout; + +#else + + if (request_irq(port->irq, bfin_sir_rx_int, 0, "BFIN_SIR_RX", dev)) { + dev_warn(&dev->dev, "Unable to attach SIR RX interrupt\n"); + return -EBUSY; + } + + if (request_irq(port->irq+1, bfin_sir_tx_int, 0, "BFIN_SIR_TX", dev)) { + dev_warn(&dev->dev, "Unable to attach SIR TX interrupt\n"); + free_irq(port->irq, dev); + return -EBUSY; + } +#endif + + return 0; +} + +static void bfin_sir_shutdown(struct bfin_sir_port *port, struct net_device *dev) +{ + unsigned short val; + + bfin_sir_stop_rx(port); + + val = UART_GET_GCTL(port); + val &= ~(UCEN | UMOD_MASK | RPOLC); + UART_PUT_GCTL(port, val); + +#ifdef CONFIG_SIR_BFIN_DMA + disable_dma(port->tx_dma_channel); + disable_dma(port->rx_dma_channel); + del_timer(&(port->rx_dma_timer)); + dma_free_coherent(NULL, PAGE_SIZE, port->rx_dma_buf.buf, 0); +#else + free_irq(port->irq+1, dev); + free_irq(port->irq, dev); +#endif + free_dma(port->tx_dma_channel); + free_dma(port->rx_dma_channel); +} + +#ifdef CONFIG_PM +static int bfin_sir_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct bfin_sir_port *sir_port; + struct net_device *dev; + struct bfin_sir_self *self; + + sir_port = platform_get_drvdata(pdev); + if (!sir_port) + return 0; + + dev = sir_port->dev; + self = netdev_priv(dev); + if (self->open) { + flush_work(&self->work); + bfin_sir_shutdown(self->sir_port, dev); + netif_device_detach(dev); + } + + return 0; +} +static int bfin_sir_resume(struct platform_device *pdev) +{ + struct bfin_sir_port *sir_port; + struct net_device *dev; + struct bfin_sir_self *self; + struct bfin_sir_port *port; + + sir_port = platform_get_drvdata(pdev); + if (!sir_port) + return 0; + + dev = sir_port->dev; + self = netdev_priv(dev); + port = self->sir_port; + if (self->open) { + if (self->newspeed) { + self->speed = self->newspeed; + self->newspeed = 0; + } + bfin_sir_startup(port, dev); + bfin_sir_set_speed(port, 9600); + bfin_sir_enable_rx(port); + netif_device_attach(dev); + } + return 0; +} +#else +#define bfin_sir_suspend NULL +#define bfin_sir_resume NULL +#endif + +static void bfin_sir_send_work(struct work_struct *work) +{ + struct bfin_sir_self *self = container_of(work, struct bfin_sir_self, work); + struct net_device *dev = self->sir_port->dev; + struct bfin_sir_port *port = self->sir_port; + unsigned short val; + int tx_cnt = 10; + + while (bfin_sir_is_receiving(dev) && --tx_cnt) + turnaround_delay(self->mtt); + + bfin_sir_stop_rx(port); + + /* To avoid losting RX interrupt, we reset IR function before + * sending data. We also can set the speed, which will + * reset all the UART. + */ + val = UART_GET_GCTL(port); + val &= ~(UMOD_MASK | RPOLC); + UART_PUT_GCTL(port, val); + SSYNC(); + val |= UMOD_IRDA | RPOLC; + UART_PUT_GCTL(port, val); + SSYNC(); + /* bfin_sir_set_speed(port, self->speed); */ + +#ifdef CONFIG_SIR_BFIN_DMA + bfin_sir_dma_tx_chars(dev); +#endif + bfin_sir_enable_tx(port); + netif_trans_update(dev); +} + +static int bfin_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct bfin_sir_self *self = netdev_priv(dev); + int speed = irda_get_next_speed(skb); + + netif_stop_queue(dev); + + self->mtt = irda_get_mtt(skb); + + if (speed != self->speed && speed != -1) + self->newspeed = speed; + + self->tx_buff.data = self->tx_buff.head; + if (skb->len == 0) + self->tx_buff.len = 0; + else + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); + + schedule_work(&self->work); + dev_kfree_skb(skb); + + return 0; +} + +static int bfin_sir_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) +{ + struct if_irda_req *rq = (struct if_irda_req *)ifreq; + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: + if (capable(CAP_NET_ADMIN)) { + if (self->open) { + ret = bfin_sir_set_speed(port, rq->ifr_baudrate); + bfin_sir_enable_rx(port); + } else { + dev_warn(&dev->dev, "SIOCSBANDWIDTH: !netif_running\n"); + ret = 0; + } + } + break; + + case SIOCSMEDIABUSY: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + irda_device_set_media_busy(dev, TRUE); + ret = 0; + } + break; + + case SIOCGRECEIVING: + rq->ifr_receiving = bfin_sir_is_receiving(dev); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static struct net_device_stats *bfin_sir_stats(struct net_device *dev) +{ + struct bfin_sir_self *self = netdev_priv(dev); + + return &self->stats; +} + +static int bfin_sir_open(struct net_device *dev) +{ + struct bfin_sir_self *self = netdev_priv(dev); + struct bfin_sir_port *port = self->sir_port; + int err; + + self->newspeed = 0; + self->speed = 9600; + + spin_lock_init(&self->lock); + + err = bfin_sir_startup(port, dev); + if (err) + goto err_startup; + + bfin_sir_set_speed(port, 9600); + + self->irlap = irlap_open(dev, &self->qos, DRIVER_NAME); + if (!self->irlap) { + err = -ENOMEM; + goto err_irlap; + } + + INIT_WORK(&self->work, bfin_sir_send_work); + + /* + * Now enable the interrupt then start the queue + */ + self->open = 1; + bfin_sir_enable_rx(port); + + netif_start_queue(dev); + + return 0; + +err_irlap: + self->open = 0; + bfin_sir_shutdown(port, dev); +err_startup: + return err; +} + +static int bfin_sir_stop(struct net_device *dev) +{ + struct bfin_sir_self *self = netdev_priv(dev); + + flush_work(&self->work); + bfin_sir_shutdown(self->sir_port, dev); + + if (self->rxskb) { + dev_kfree_skb(self->rxskb); + self->rxskb = NULL; + } + + /* Stop IrLAP */ + if (self->irlap) { + irlap_close(self->irlap); + self->irlap = NULL; + } + + netif_stop_queue(dev); + self->open = 0; + + return 0; +} + +static int bfin_sir_init_iobuf(iobuff_t *io, int size) +{ + io->head = kmalloc(size, GFP_KERNEL); + if (!io->head) + return -ENOMEM; + io->truesize = size; + io->in_frame = FALSE; + io->state = OUTSIDE_FRAME; + io->data = io->head; + return 0; +} + +static const struct net_device_ops bfin_sir_ndo = { + .ndo_open = bfin_sir_open, + .ndo_stop = bfin_sir_stop, + .ndo_start_xmit = bfin_sir_hard_xmit, + .ndo_do_ioctl = bfin_sir_ioctl, + .ndo_get_stats = bfin_sir_stats, +}; + +static int bfin_sir_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct bfin_sir_self *self; + unsigned int baudrate_mask; + struct bfin_sir_port *sir_port; + int err; + + if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(per) && \ + per[pdev->id][3] == pdev->id) { + err = peripheral_request_list(per[pdev->id], DRIVER_NAME); + if (err) + return err; + } else { + dev_err(&pdev->dev, "Invalid pdev id, please check board file\n"); + return -ENODEV; + } + + err = -ENOMEM; + sir_port = kmalloc(sizeof(*sir_port), GFP_KERNEL); + if (!sir_port) + goto err_mem_0; + + bfin_sir_init_ports(sir_port, pdev); + + dev = alloc_irdadev(sizeof(*self)); + if (!dev) + goto err_mem_1; + + self = netdev_priv(dev); + self->dev = &pdev->dev; + self->sir_port = sir_port; + sir_port->dev = dev; + + err = bfin_sir_init_iobuf(&self->rx_buff, IRDA_SKB_MAX_MTU); + if (err) + goto err_mem_2; + err = bfin_sir_init_iobuf(&self->tx_buff, IRDA_SIR_MAX_FRAME); + if (err) + goto err_mem_3; + + dev->netdev_ops = &bfin_sir_ndo; + dev->irq = sir_port->irq; + + irda_init_max_qos_capabilies(&self->qos); + + baudrate_mask = IR_9600; + + switch (max_rate) { + case 115200: + baudrate_mask |= IR_115200; + case 57600: + baudrate_mask |= IR_57600; + case 38400: + baudrate_mask |= IR_38400; + case 19200: + baudrate_mask |= IR_19200; + case 9600: + break; + default: + dev_warn(&pdev->dev, "Invalid maximum baud rate, using 9600\n"); + } + + self->qos.baud_rate.bits &= baudrate_mask; + + self->qos.min_turn_time.bits = 1; /* 10 ms or more */ + + irda_qos_bits_to_value(&self->qos); + + err = register_netdev(dev); + + if (err) { + kfree(self->tx_buff.head); +err_mem_3: + kfree(self->rx_buff.head); +err_mem_2: + free_netdev(dev); +err_mem_1: + kfree(sir_port); +err_mem_0: + peripheral_free_list(per[pdev->id]); + } else + platform_set_drvdata(pdev, sir_port); + + return err; +} + +static int bfin_sir_remove(struct platform_device *pdev) +{ + struct bfin_sir_port *sir_port; + struct net_device *dev = NULL; + struct bfin_sir_self *self; + + sir_port = platform_get_drvdata(pdev); + if (!sir_port) + return 0; + dev = sir_port->dev; + self = netdev_priv(dev); + unregister_netdev(dev); + kfree(self->tx_buff.head); + kfree(self->rx_buff.head); + free_netdev(dev); + kfree(sir_port); + + return 0; +} + +static struct platform_driver bfin_ir_driver = { + .probe = bfin_sir_probe, + .remove = bfin_sir_remove, + .suspend = bfin_sir_suspend, + .resume = bfin_sir_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +module_platform_driver(bfin_ir_driver); + +module_param(max_rate, int, 0); +MODULE_PARM_DESC(max_rate, "Maximum baud rate (115200, 57600, 38400, 19200, 9600)"); + +MODULE_AUTHOR("Graf Yang "); +MODULE_DESCRIPTION("Blackfin IrDA driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/bfin_sir.h b/drivers/staging/irda/drivers/bfin_sir.h new file mode 100644 index 000000000000..d47cf14bb4a5 --- /dev/null +++ b/drivers/staging/irda/drivers/bfin_sir.h @@ -0,0 +1,93 @@ +/* + * Blackfin Infra-red Driver + * + * Copyright 2006-2009 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#undef DRIVER_NAME + +#ifdef CONFIG_SIR_BFIN_DMA +struct dma_rx_buf { + char *buf; + int head; + int tail; +}; +#endif + +struct bfin_sir_port { + unsigned char __iomem *membase; + unsigned int irq; + unsigned int lsr; + unsigned long clk; + struct net_device *dev; +#ifdef CONFIG_SIR_BFIN_DMA + int tx_done; + struct dma_rx_buf rx_dma_buf; + struct timer_list rx_dma_timer; + int rx_dma_nrows; +#endif + unsigned int tx_dma_channel; + unsigned int rx_dma_channel; +}; + +struct bfin_sir_port_res { + unsigned long base_addr; + int irq; + unsigned int rx_dma_channel; + unsigned int tx_dma_channel; +}; + +struct bfin_sir_self { + struct bfin_sir_port *sir_port; + spinlock_t lock; + unsigned int open; + int speed; + int newspeed; + + struct sk_buff *txskb; + struct sk_buff *rxskb; + struct net_device_stats stats; + struct device *dev; + struct irlap_cb *irlap; + struct qos_info qos; + + iobuff_t tx_buff; + iobuff_t rx_buff; + + struct work_struct work; + int mtt; +}; + +#define DRIVER_NAME "bfin_sir" + +#include + +static const unsigned short per[][4] = { + /* rx pin tx pin NULL uart_number */ + {P_UART0_RX, P_UART0_TX, 0, 0}, + {P_UART1_RX, P_UART1_TX, 0, 1}, + {P_UART2_RX, P_UART2_TX, 0, 2}, + {P_UART3_RX, P_UART3_TX, 0, 3}, +}; diff --git a/drivers/staging/irda/drivers/donauboe.c b/drivers/staging/irda/drivers/donauboe.c new file mode 100644 index 000000000000..b337e6d23a88 --- /dev/null +++ b/drivers/staging/irda/drivers/donauboe.c @@ -0,0 +1,1732 @@ +/***************************************************************** + * + * Filename: donauboe.c + * Version: 2.17 + * Description: Driver for the Toshiba OBOE (or type-O or 701) + * FIR Chipset, also supports the DONAUOBOE (type-DO + * or d01) FIR chipset which as far as I know is + * register compatible. + * Documentation: http://libxg.free.fr/irda/lib-irda.html + * Status: Experimental. + * Author: James McKenzie + * Created at: Sat May 8 12:35:27 1999 + * Modified: Paul Bristow + * Modified: Mon Nov 11 19:10:05 1999 + * Modified: James McKenzie + * Modified: Thu Mar 16 12:49:00 2000 (Substantial rewrite) + * Modified: Sat Apr 29 00:23:03 2000 (Added DONAUOBOE support) + * Modified: Wed May 24 23:45:02 2000 (Fixed chipio_t structure) + * Modified: 2.13 Christian Gennerat + * Modified: 2.13 dim jan 07 21:57:39 2001 (tested with kernel 2.4 & irnet/ppp) + * Modified: 2.14 Christian Gennerat + * Modified: 2.14 lun fev 05 17:55:59 2001 (adapted to patch-2.4.1-pre8-irda1) + * Modified: 2.15 Martin Lucina + * Modified: 2.15 Fri Jun 21 20:40:59 2002 (sync with 2.4.18, substantial fixes) + * Modified: 2.16 Martin Lucina + * Modified: 2.16 Sat Jun 22 18:54:29 2002 (fix freeregion, default to verbose) + * Modified: 2.17 Christian Gennerat + * Modified: 2.17 jeu sep 12 08:50:20 2002 (save_flags();cli(); replaced by spinlocks) + * Modified: 2.18 Christian Gennerat + * Modified: 2.18 ven jan 10 03:14:16 2003 Change probe default options + * + * Copyright (c) 1999 James McKenzie, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither James McKenzie nor Cambridge University admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + * Applicable Models : Libretto 100/110CT and many more. + * Toshiba refers to this chip as the type-O IR port, + * or the type-DO IR port. + * + ********************************************************************/ + +/* Look at toshoboe.h (currently in include/net/irda) for details of */ +/* Where to get documentation on the chip */ + +/* See below for a description of the logic in this driver */ + +/* User servicable parts */ +/* USE_PROBE Create the code which probes the chip and does a few tests */ +/* do_probe module parameter Enable this code */ +/* Probe code is very useful for understanding how the hardware works */ +/* Use it with various combinations of TT_LEN, RX_LEN */ +/* Strongly recommended, disable if the probe fails on your machine */ +/* and send me the output of dmesg */ +#define USE_PROBE 1 +#undef USE_PROBE + +/* Trace Transmit ring, interrupts, Receive ring or not ? */ +#define PROBE_VERBOSE 1 + +/* Debug option, examine sent and received raw data */ +/* Irdadump is better, but does not see all packets. enable it if you want. */ +#undef DUMP_PACKETS + +/* MIR mode has not been tested. Some behaviour is different */ +/* Seems to work against an Ericsson R520 for me. -Martin */ +#define USE_MIR + +/* Schedule back to back hardware transmits wherever possible, otherwise */ +/* we need an interrupt for every frame, unset if oboe works for a bit and */ +/* then hangs */ +#define OPTIMIZE_TX + +/* Set the number of slots in the rings */ +/* If you get rx/tx fifo overflows at high bitrates, you can try increasing */ +/* these */ + +#define RING_SIZE (OBOE_RING_SIZE_RX8 | OBOE_RING_SIZE_TX8) +#define TX_SLOTS 8 +#define RX_SLOTS 8 + + +/* Less user servicable parts below here */ + +/* Test, Transmit and receive buffer sizes, adjust at your peril */ +/* remarks: nfs usually needs 1k blocks */ +/* remarks: in SIR mode, CRC is received, -> RX_LEN=TX_LEN+2 */ +/* remarks: test accepts large blocks. Standard is 0x80 */ +/* When TT_LEN > RX_LEN (SIR mode) data is stored in successive slots. */ +/* When 3 or more slots are needed for each test packet, */ +/* data received in the first slots is overwritten, even */ +/* if OBOE_CTL_RX_HW_OWNS is not set, without any error! */ +#define TT_LEN 0x80 +#define TX_LEN 0xc00 +#define RX_LEN 0xc04 +/* Real transmitted length (SIR mode) is about 14+(2%*TX_LEN) more */ +/* long than user-defined length (see async_wrap_skb) and is less then 4K */ +/* Real received length is (max RX_LEN) differs from user-defined */ +/* length only b the CRC (2 or 4 bytes) */ +#define BUF_SAFETY 0x7a +#define RX_BUF_SZ (RX_LEN) +#define TX_BUF_SZ (TX_LEN+BUF_SAFETY) + + +/* Logic of the netdev part of this driver */ + +/* The RX ring is filled with buffers, when a packet arrives */ +/* it is DMA'd into the buffer which is marked used and RxDone called */ +/* RxDone forms an skb (and checks the CRC if in SIR mode) and ships */ +/* the packet off upstairs */ + +/* The transmitter on the oboe chip can work in one of two modes */ +/* for each ring->tx[] the transmitter can either */ +/* a) transmit the packet, leave the trasmitter enabled and proceed to */ +/* the next ring */ +/* OR */ +/* b) transmit the packet, switch off the transmitter and issue TxDone */ + +/* All packets are entered into the ring in mode b), if the ring was */ +/* empty the transmitter is started. */ + +/* If OPTIMIZE_TX is defined then in TxDone if the ring contains */ +/* more than one packet, all but the last are set to mode a) [HOWEVER */ +/* the hardware may not notice this, this is why we start in mode b) ] */ +/* then restart the transmitter */ + +/* If OPTIMIZE_TX is not defined then we just restart the transmitter */ +/* if the ring isn't empty */ + +/* Speed changes are delayed until the TxRing is empty */ +/* mtt is handled by generating packets with bad CRCs, before the data */ + +/* TODO: */ +/* check the mtt works ok */ +/* finish the watchdog */ + +/* No user servicable parts below here */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +//#include +//#include +#include +#include + +#include "donauboe.h" + +#define INB(port) inb_p(port) +#define OUTB(val,port) outb_p(val,port) +#define OUTBP(val,port) outb_p(val,port) + +#define PROMPT OUTB(OBOE_PROMPT_BIT,OBOE_PROMPT); + +#if PROBE_VERBOSE +#define PROBE_DEBUG(args...) (printk (args)) +#else +#define PROBE_DEBUG(args...) ; +#endif + +/* Set the DMA to be byte at a time */ +#define CONFIG0H_DMA_OFF OBOE_CONFIG0H_RCVANY +#define CONFIG0H_DMA_ON_NORX CONFIG0H_DMA_OFF| OBOE_CONFIG0H_ENDMAC +#define CONFIG0H_DMA_ON CONFIG0H_DMA_ON_NORX | OBOE_CONFIG0H_ENRX + +static const struct pci_device_id toshoboe_pci_tbl[] = { + { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIR701, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIRD01, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, toshoboe_pci_tbl); + +#define DRIVER_NAME "toshoboe" +static char *driver_name = DRIVER_NAME; + +static int max_baud = 4000000; +#ifdef USE_PROBE +static bool do_probe = false; +#endif + + +/**********************************************************************/ +static int +toshoboe_checkfcs (unsigned char *buf, int len) +{ + int i; + union + { + __u16 value; + __u8 bytes[2]; + } + fcs; + + fcs.value = INIT_FCS; + + for (i = 0; i < len; ++i) + fcs.value = irda_fcs (fcs.value, *(buf++)); + + return fcs.value == GOOD_FCS; +} + +/***********************************************************************/ +/* Generic chip handling code */ +#ifdef DUMP_PACKETS +static unsigned char dump[50]; +static void +_dumpbufs (unsigned char *data, int len, char tete) +{ +int i,j; +char head=tete; +for (i=0;iint_tx, self->int_rx, self->int_txunder, self->int_rxover, + self->int_sip); + printk (KERN_ERR "RX %02x TX %02x RingBase %08x\n", + INB (OBOE_RXSLOT), INB (OBOE_TXSLOT), ringbase); + printk (KERN_ERR "RING_SIZE %02x IER %02x ISR %02x\n", + INB (OBOE_RING_SIZE), INB (OBOE_IER), INB (OBOE_ISR)); + printk (KERN_ERR "CONFIG1 %02x STATUS %02x\n", + INB (OBOE_CONFIG1), INB (OBOE_STATUS)); + printk (KERN_ERR "CONFIG0 %02x%02x ENABLE %02x%02x\n", + INB (OBOE_CONFIG0H), INB (OBOE_CONFIG0L), + INB (OBOE_ENABLEH), INB (OBOE_ENABLEL)); + printk (KERN_ERR "NEW_PCONFIG %02x%02x CURR_PCONFIG %02x%02x\n", + INB (OBOE_NEW_PCONFIGH), INB (OBOE_NEW_PCONFIGL), + INB (OBOE_CURR_PCONFIGH), INB (OBOE_CURR_PCONFIGL)); + printk (KERN_ERR "MAXLEN %02x%02x RXCOUNT %02x%02x\n", + INB (OBOE_MAXLENH), INB (OBOE_MAXLENL), + INB (OBOE_RXCOUNTL), INB (OBOE_RXCOUNTH)); + + if (self->ring) + { + int i; + ringbase = virt_to_bus (self->ring); + printk (KERN_ERR "Ring at %08x:\n", ringbase); + printk (KERN_ERR "RX:"); + for (i = 0; i < RX_SLOTS; ++i) + printk (" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); + printk ("\n"); + printk (KERN_ERR "TX:"); + for (i = 0; i < RX_SLOTS; ++i) + printk (" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); + printk ("\n"); + } +} +#endif + +/*Don't let the chip look at memory */ +static void +toshoboe_disablebm (struct toshoboe_cb *self) +{ + __u8 command; + pci_read_config_byte (self->pdev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte (self->pdev, PCI_COMMAND, command); + +} + +/* Shutdown the chip and point the taskfile reg somewhere else */ +static void +toshoboe_stopchip (struct toshoboe_cb *self) +{ + /*Disable interrupts */ + OUTB (0x0, OBOE_IER); + /*Disable DMA, Disable Rx, Disable Tx */ + OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); + /*Disable SIR MIR FIR, Tx and Rx */ + OUTB (0x00, OBOE_ENABLEH); + /*Point the ring somewhere safe */ + OUTB (0x3f, OBOE_RING_BASE2); + OUTB (0xff, OBOE_RING_BASE1); + OUTB (0xff, OBOE_RING_BASE0); + + OUTB (RX_LEN >> 8, OBOE_MAXLENH); + OUTB (RX_LEN & 0xff, OBOE_MAXLENL); + + /*Acknoledge any pending interrupts */ + OUTB (0xff, OBOE_ISR); + + /*Why */ + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + + /*switch it off */ + OUTB (OBOE_CONFIG1_OFF, OBOE_CONFIG1); + + toshoboe_disablebm (self); +} + +/* Transmitter initialization */ +static void +toshoboe_start_DMA (struct toshoboe_cb *self, int opts) +{ + OUTB (0x0, OBOE_ENABLEH); + OUTB (CONFIG0H_DMA_ON | opts, OBOE_CONFIG0H); + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + PROMPT; +} + +/*Set the baud rate */ +static void +toshoboe_setbaud (struct toshoboe_cb *self) +{ + __u16 pconfig = 0; + __u8 config0l = 0; + + pr_debug("%s(%d/%d)\n", __func__, self->speed, self->io.speed); + + switch (self->speed) + { + case 2400: + case 4800: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: +#ifdef USE_MIR + case 1152000: +#endif + case 4000000: + break; + default: + + printk (KERN_ERR DRIVER_NAME ": switch to unsupported baudrate %d\n", + self->speed); + return; + } + + switch (self->speed) + { + /* For SIR the preamble is done by adding XBOFs */ + /* to the packet */ + /* set to filtered SIR mode, filter looks for BOF and EOF */ + case 2400: + pconfig |= 47 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 4800: + pconfig |= 23 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 9600: + pconfig |= 11 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 19200: + pconfig |= 5 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 38400: + pconfig |= 2 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 57600: + pconfig |= 1 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 115200: + pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + default: + /*Set to packet based reception */ + OUTB (RX_LEN >> 8, OBOE_MAXLENH); + OUTB (RX_LEN & 0xff, OBOE_MAXLENL); + break; + } + + switch (self->speed) + { + case 2400: + case 4800: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + config0l = OBOE_CONFIG0L_ENSIR; + if (self->async) + { + /*Set to character based reception */ + /*System will lock if MAXLEN=0 */ + /*so have to be careful */ + OUTB (0x01, OBOE_MAXLENH); + OUTB (0x01, OBOE_MAXLENL); + OUTB (0x00, OBOE_MAXLENH); + } + else + { + /*Set to packet based reception */ + config0l |= OBOE_CONFIG0L_ENSIRF; + OUTB (RX_LEN >> 8, OBOE_MAXLENH); + OUTB (RX_LEN & 0xff, OBOE_MAXLENL); + } + break; + +#ifdef USE_MIR + /* MIR mode */ + /* Set for 16 bit CRC and enable MIR */ + /* Preamble now handled by the chip */ + case 1152000: + pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 8 << OBOE_PCONFIG_WIDTHSHIFT; + pconfig |= 1 << OBOE_PCONFIG_PREAMBLESHIFT; + config0l = OBOE_CONFIG0L_CRC16 | OBOE_CONFIG0L_ENMIR; + break; +#endif + /* FIR mode */ + /* Set for 32 bit CRC and enable FIR */ + /* Preamble handled by the chip */ + case 4000000: + pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; + /* Documentation says 14, but toshiba use 15 in their drivers */ + pconfig |= 15 << OBOE_PCONFIG_PREAMBLESHIFT; + config0l = OBOE_CONFIG0L_ENFIR; + break; + } + + /* Copy into new PHY config buffer */ + OUTBP (pconfig >> 8, OBOE_NEW_PCONFIGH); + OUTB (pconfig & 0xff, OBOE_NEW_PCONFIGL); + OUTB (config0l, OBOE_CONFIG0L); + + /* Now make OBOE copy from new PHY to current PHY */ + OUTB (0x0, OBOE_ENABLEH); + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + PROMPT; + + /* speed change executed */ + self->new_speed = 0; + self->io.speed = self->speed; +} + +/*Let the chip look at memory */ +static void +toshoboe_enablebm (struct toshoboe_cb *self) +{ + pci_set_master (self->pdev); +} + +/*setup the ring */ +static void +toshoboe_initring (struct toshoboe_cb *self) +{ + int i; + + for (i = 0; i < TX_SLOTS; ++i) + { + self->ring->tx[i].len = 0; + self->ring->tx[i].control = 0x00; + self->ring->tx[i].address = virt_to_bus (self->tx_bufs[i]); + } + + for (i = 0; i < RX_SLOTS; ++i) + { + self->ring->rx[i].len = RX_LEN; + self->ring->rx[i].len = 0; + self->ring->rx[i].address = virt_to_bus (self->rx_bufs[i]); + self->ring->rx[i].control = OBOE_CTL_RX_HW_OWNS; + } +} + +static void +toshoboe_resetptrs (struct toshoboe_cb *self) +{ + /* Can reset pointers by twidling DMA */ + OUTB (0x0, OBOE_ENABLEH); + OUTBP (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + + self->rxs = inb_p (OBOE_RXSLOT) & OBOE_SLOT_MASK; + self->txs = inb_p (OBOE_TXSLOT) & OBOE_SLOT_MASK; +} + +/* Called in locked state */ +static void +toshoboe_initptrs (struct toshoboe_cb *self) +{ + + /* spin_lock_irqsave(self->spinlock, flags); */ + /* save_flags (flags); */ + + /* Can reset pointers by twidling DMA */ + toshoboe_resetptrs (self); + + OUTB (0x0, OBOE_ENABLEH); + OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + + self->txpending = 0; + + /* spin_unlock_irqrestore(self->spinlock, flags); */ + /* restore_flags (flags); */ +} + +/* Wake the chip up and get it looking at the rings */ +/* Called in locked state */ +static void +toshoboe_startchip (struct toshoboe_cb *self) +{ + __u32 physaddr; + + toshoboe_initring (self); + toshoboe_enablebm (self); + OUTBP (OBOE_CONFIG1_RESET, OBOE_CONFIG1); + OUTBP (OBOE_CONFIG1_ON, OBOE_CONFIG1); + + /* Stop the clocks */ + OUTB (0, OBOE_ENABLEH); + + /*Set size of rings */ + OUTB (RING_SIZE, OBOE_RING_SIZE); + + /*Acknoledge any pending interrupts */ + OUTB (0xff, OBOE_ISR); + + /*Enable ints */ + OUTB (OBOE_INT_TXDONE | OBOE_INT_RXDONE | + OBOE_INT_TXUNDER | OBOE_INT_RXOVER | OBOE_INT_SIP , OBOE_IER); + + /*Acknoledge any pending interrupts */ + OUTB (0xff, OBOE_ISR); + + /*Set the maximum packet length to 0xfff (4095) */ + OUTB (RX_LEN >> 8, OBOE_MAXLENH); + OUTB (RX_LEN & 0xff, OBOE_MAXLENL); + + /*Shutdown DMA */ + OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); + + /*Find out where the rings live */ + physaddr = virt_to_bus (self->ring); + + IRDA_ASSERT ((physaddr & 0x3ff) == 0, + printk (KERN_ERR DRIVER_NAME "ring not correctly aligned\n"); + return;); + + OUTB ((physaddr >> 10) & 0xff, OBOE_RING_BASE0); + OUTB ((physaddr >> 18) & 0xff, OBOE_RING_BASE1); + OUTB ((physaddr >> 26) & 0x3f, OBOE_RING_BASE2); + + /*Enable DMA controller in byte mode and RX */ + OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); + + /* Start up the clocks */ + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + + /*set to sensible speed */ + self->speed = 9600; + toshoboe_setbaud (self); + toshoboe_initptrs (self); +} + +static void +toshoboe_isntstuck (struct toshoboe_cb *self) +{ +} + +static void +toshoboe_checkstuck (struct toshoboe_cb *self) +{ + unsigned long flags; + + if (0) + { + spin_lock_irqsave(&self->spinlock, flags); + + /* This will reset the chip completely */ + printk (KERN_ERR DRIVER_NAME ": Resetting chip\n"); + + toshoboe_stopchip (self); + toshoboe_startchip (self); + spin_unlock_irqrestore(&self->spinlock, flags); + } +} + +/*Generate packet of about mtt us long */ +static int +toshoboe_makemttpacket (struct toshoboe_cb *self, void *buf, int mtt) +{ + int xbofs; + + xbofs = ((int) (mtt/100)) * (int) (self->speed); + xbofs=xbofs/80000; /*Eight bits per byte, and mtt is in us*/ + xbofs++; + + pr_debug(DRIVER_NAME ": generated mtt of %d bytes for %d us at %d baud\n", + xbofs, mtt, self->speed); + + if (xbofs > TX_LEN) + { + printk (KERN_ERR DRIVER_NAME ": wanted %d bytes MTT but TX_LEN is %d\n", + xbofs, TX_LEN); + xbofs = TX_LEN; + } + + /*xbofs will do for SIR, MIR and FIR,SIR mode doesn't generate a checksum anyway */ + memset (buf, XBOF, xbofs); + + return xbofs; +} + +#ifdef USE_PROBE +/***********************************************************************/ +/* Probe code */ + +static void +toshoboe_dumptx (struct toshoboe_cb *self) +{ + int i; + PROBE_DEBUG(KERN_WARNING "TX:"); + for (i = 0; i < RX_SLOTS; ++i) + PROBE_DEBUG(" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); + PROBE_DEBUG(" [%d]\n",self->speed); +} + +static void +toshoboe_dumprx (struct toshoboe_cb *self, int score) +{ + int i; + PROBE_DEBUG(" %d\nRX:",score); + for (i = 0; i < RX_SLOTS; ++i) + PROBE_DEBUG(" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); + PROBE_DEBUG("\n"); +} + +static inline int +stuff_byte (__u8 byte, __u8 * buf) +{ + switch (byte) + { + case BOF: /* FALLTHROUGH */ + case EOF: /* FALLTHROUGH */ + case CE: + /* Insert transparently coded */ + buf[0] = CE; /* Send link escape */ + buf[1] = byte ^ IRDA_TRANS; /* Complement bit 5 */ + return 2; + /* break; */ + default: + /* Non-special value, no transparency required */ + buf[0] = byte; + return 1; + /* break; */ + } +} + +static irqreturn_t +toshoboe_probeinterrupt (int irq, void *dev_id) +{ + struct toshoboe_cb *self = dev_id; + __u8 irqstat; + + irqstat = INB (OBOE_ISR); + +/* was it us */ + if (!(irqstat & OBOE_INT_MASK)) + return IRQ_NONE; + +/* Ack all the interrupts */ + OUTB (irqstat, OBOE_ISR); + + if (irqstat & OBOE_INT_TXDONE) + { + int txp; + + self->int_tx++; + PROBE_DEBUG("T"); + + txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; + if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) + { + self->int_tx+=100; + PROBE_DEBUG("S"); + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); + } + } + + if (irqstat & OBOE_INT_RXDONE) { + self->int_rx++; + PROBE_DEBUG("R"); } + if (irqstat & OBOE_INT_TXUNDER) { + self->int_txunder++; + PROBE_DEBUG("U"); } + if (irqstat & OBOE_INT_RXOVER) { + self->int_rxover++; + PROBE_DEBUG("O"); } + if (irqstat & OBOE_INT_SIP) { + self->int_sip++; + PROBE_DEBUG("I"); } + return IRQ_HANDLED; +} + +static int +toshoboe_maketestpacket (unsigned char *buf, int badcrc, int fir) +{ + int i; + int len = 0; + union + { + __u16 value; + __u8 bytes[2]; + } + fcs; + + if (fir) + { + memset (buf, 0, TT_LEN); + return TT_LEN; + } + + fcs.value = INIT_FCS; + + memset (buf, XBOF, 10); + len += 10; + buf[len++] = BOF; + + for (i = 0; i < TT_LEN; ++i) + { + len += stuff_byte (i, buf + len); + fcs.value = irda_fcs (fcs.value, i); + } + + len += stuff_byte (fcs.bytes[0] ^ badcrc, buf + len); + len += stuff_byte (fcs.bytes[1] ^ badcrc, buf + len); + buf[len++] = EOF; + len++; + return len; +} + +static int +toshoboe_probefail (struct toshoboe_cb *self, char *msg) +{ + printk (KERN_ERR DRIVER_NAME "probe(%d) failed %s\n",self-> speed, msg); + toshoboe_dumpregs (self); + toshoboe_stopchip (self); + free_irq (self->io.irq, (void *) self); + return 0; +} + +static int +toshoboe_numvalidrcvs (struct toshoboe_cb *self) +{ + int i, ret = 0; + for (i = 0; i < RX_SLOTS; ++i) + if ((self->ring->rx[i].control & 0xe0) == 0) + ret++; + + return ret; +} + +static int +toshoboe_numrcvs (struct toshoboe_cb *self) +{ + int i, ret = 0; + for (i = 0; i < RX_SLOTS; ++i) + if (!(self->ring->rx[i].control & OBOE_CTL_RX_HW_OWNS)) + ret++; + + return ret; +} + +static int +toshoboe_probe (struct toshoboe_cb *self) +{ + int i, j, n; +#ifdef USE_MIR + static const int bauds[] = { 9600, 115200, 4000000, 1152000 }; +#else + static const int bauds[] = { 9600, 115200, 4000000 }; +#endif + unsigned long flags; + + if (request_irq (self->io.irq, toshoboe_probeinterrupt, + self->io.irqflags, "toshoboe", (void *) self)) + { + printk (KERN_ERR DRIVER_NAME ": probe failed to allocate irq %d\n", + self->io.irq); + return 0; + } + + /* test 1: SIR filter and back to back */ + + for (j = 0; j < ARRAY_SIZE(bauds); ++j) + { + int fir = (j > 1); + toshoboe_stopchip (self); + + + spin_lock_irqsave(&self->spinlock, flags); + /*Address is already setup */ + toshoboe_startchip (self); + self->int_rx = self->int_tx = 0; + self->speed = bauds[j]; + toshoboe_setbaud (self); + toshoboe_initptrs (self); + spin_unlock_irqrestore(&self->spinlock, flags); + + self->ring->tx[self->txs].control = +/* (FIR only) OBOE_CTL_TX_SIP needed for switching to next slot */ +/* MIR: all received data is stored in one slot */ + (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX + : OBOE_CTL_TX_HW_OWNS ; + self->ring->tx[self->txs].len = + toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); + self->txs++; + self->txs %= TX_SLOTS; + + self->ring->tx[self->txs].control = + (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_SIP + : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; + self->ring->tx[self->txs].len = + toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); + self->txs++; + self->txs %= TX_SLOTS; + + self->ring->tx[self->txs].control = + (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX + : OBOE_CTL_TX_HW_OWNS ; + self->ring->tx[self->txs].len = + toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); + self->txs++; + self->txs %= TX_SLOTS; + + self->ring->tx[self->txs].control = + (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX + | OBOE_CTL_TX_SIP | OBOE_CTL_TX_BAD_CRC + : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; + self->ring->tx[self->txs].len = + toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); + self->txs++; + self->txs %= TX_SLOTS; + + toshoboe_dumptx (self); + /* Turn on TX and RX and loopback */ + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); + + i = 0; + n = fir ? 1 : 4; + while (toshoboe_numvalidrcvs (self) != n) + { + if (i > 4800) + return toshoboe_probefail (self, "filter test"); + udelay ((9600*(TT_LEN+16))/self->speed); + i++; + } + + n = fir ? 203 : 102; + while ((toshoboe_numrcvs(self) != self->int_rx) || (self->int_tx != n)) + { + if (i > 4800) + return toshoboe_probefail (self, "interrupt test"); + udelay ((9600*(TT_LEN+16))/self->speed); + i++; + } + toshoboe_dumprx (self,i); + + } + + /* test 2: SIR in char at a time */ + + toshoboe_stopchip (self); + self->int_rx = self->int_tx = 0; + + spin_lock_irqsave(&self->spinlock, flags); + toshoboe_startchip (self); + spin_unlock_irqrestore(&self->spinlock, flags); + + self->async = 1; + self->speed = 115200; + toshoboe_setbaud (self); + self->ring->tx[self->txs].control = + OBOE_CTL_TX_RTCENTX | OBOE_CTL_TX_HW_OWNS; + self->ring->tx[self->txs].len = 4; + + ((unsigned char *) self->tx_bufs[self->txs])[0] = 'f'; + ((unsigned char *) self->tx_bufs[self->txs])[1] = 'i'; + ((unsigned char *) self->tx_bufs[self->txs])[2] = 's'; + ((unsigned char *) self->tx_bufs[self->txs])[3] = 'h'; + toshoboe_dumptx (self); + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); + + i = 0; + while (toshoboe_numvalidrcvs (self) != 4) + { + if (i > 100) + return toshoboe_probefail (self, "Async test"); + udelay (100); + i++; + } + + while ((toshoboe_numrcvs (self) != self->int_rx) || (self->int_tx != 1)) + { + if (i > 100) + return toshoboe_probefail (self, "Async interrupt test"); + udelay (100); + i++; + } + toshoboe_dumprx (self,i); + + self->async = 0; + self->speed = 9600; + toshoboe_setbaud (self); + toshoboe_stopchip (self); + + free_irq (self->io.irq, (void *) self); + + printk (KERN_WARNING DRIVER_NAME ": Self test passed ok\n"); + + return 1; +} +#endif + +/******************************************************************/ +/* Netdev style code */ + +/* Transmit something */ +static netdev_tx_t +toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev) +{ + struct toshoboe_cb *self; + __s32 speed; + int mtt, len, ctl; + unsigned long flags; + struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; + + self = netdev_priv(dev); + + IRDA_ASSERT (self != NULL, return NETDEV_TX_OK; ); + + pr_debug("%s.tx:%x(%x)%x\n", + __func__, skb->len, self->txpending, INB(OBOE_ENABLEH)); + if (!cb->magic) { + pr_debug("%s.Not IrLAP:%x\n", __func__, cb->magic); +#ifdef DUMP_PACKETS + _dumpbufs(skb->data,skb->len,'>'); +#endif + } + + /* change speed pending, wait for its execution */ + if (self->new_speed) + return NETDEV_TX_BUSY; + + /* device stopped (apm) wait for restart */ + if (self->stopped) + return NETDEV_TX_BUSY; + + toshoboe_checkstuck (self); + + /* Check if we need to change the speed */ + /* But not now. Wait after transmission if mtt not required */ + speed=irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) + { + spin_lock_irqsave(&self->spinlock, flags); + + if (self->txpending || skb->len) + { + self->new_speed = speed; + pr_debug("%s: Queued TxDone scheduled speed change %d\n" , + __func__, speed); + /* if no data, that's all! */ + if (!skb->len) + { + spin_unlock_irqrestore(&self->spinlock, flags); + dev_kfree_skb (skb); + return NETDEV_TX_OK; + } + /* True packet, go on, but */ + /* do not accept anything before change speed execution */ + netif_stop_queue(dev); + /* ready to process TxDone interrupt */ + spin_unlock_irqrestore(&self->spinlock, flags); + } + else + { + /* idle and no data, change speed now */ + self->speed = speed; + toshoboe_setbaud (self); + spin_unlock_irqrestore(&self->spinlock, flags); + dev_kfree_skb (skb); + return NETDEV_TX_OK; + } + + } + + if ((mtt = irda_get_mtt(skb))) + { + /* This is fair since the queue should be empty anyway */ + spin_lock_irqsave(&self->spinlock, flags); + + if (self->txpending) + { + spin_unlock_irqrestore(&self->spinlock, flags); + return NETDEV_TX_BUSY; + } + + /* If in SIR mode we need to generate a string of XBOFs */ + /* In MIR and FIR we need to generate a string of data */ + /* which we will add a wrong checksum to */ + + mtt = toshoboe_makemttpacket (self, self->tx_bufs[self->txs], mtt); + pr_debug("%s.mtt:%x(%x)%d\n", __func__, skb->len, mtt, self->txpending); + if (mtt) + { + self->ring->tx[self->txs].len = mtt & 0xfff; + + ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; + if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) + { + ctl |= OBOE_CTL_TX_BAD_CRC | OBOE_CTL_TX_SIP ; + } +#ifdef USE_MIR + else if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_MIRON) + { + ctl |= OBOE_CTL_TX_BAD_CRC; + } +#endif + self->ring->tx[self->txs].control = ctl; + + OUTB (0x0, OBOE_ENABLEH); + /* It is only a timer. Do not send mtt packet outside! */ + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); + + self->txpending++; + + self->txs++; + self->txs %= TX_SLOTS; + + } + else + { + printk(KERN_ERR DRIVER_NAME ": problem with mtt packet - ignored\n"); + } + spin_unlock_irqrestore(&self->spinlock, flags); + } + +#ifdef DUMP_PACKETS +dumpbufs(skb->data,skb->len,'>'); +#endif + + spin_lock_irqsave(&self->spinlock, flags); + + if (self->ring->tx[self->txs].control & OBOE_CTL_TX_HW_OWNS) + { + pr_debug("%s.ful:%x(%x)%x\n", + __func__, skb->len, self->ring->tx[self->txs].control, + self->txpending); + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); + spin_unlock_irqrestore(&self->spinlock, flags); + return NETDEV_TX_BUSY; + } + + if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_SIRON) + { + len = async_wrap_skb (skb, self->tx_bufs[self->txs], TX_BUF_SZ); + } + else + { + len = skb->len; + skb_copy_from_linear_data(skb, self->tx_bufs[self->txs], len); + } + self->ring->tx[self->txs].len = len & 0x0fff; + + /*Sometimes the HW doesn't see us assert RTCENTX in the interrupt code */ + /*later this plays safe, we garuntee the last packet to be transmitted */ + /*has RTCENTX set */ + + ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; + if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) + { + ctl |= OBOE_CTL_TX_SIP ; + } + self->ring->tx[self->txs].control = ctl; + + /* If transmitter is idle start in one-shot mode */ + + if (!self->txpending) + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); + + self->txpending++; + + self->txs++; + self->txs %= TX_SLOTS; + + spin_unlock_irqrestore(&self->spinlock, flags); + dev_kfree_skb (skb); + + return NETDEV_TX_OK; +} + +/*interrupt handler */ +static irqreturn_t +toshoboe_interrupt (int irq, void *dev_id) +{ + struct toshoboe_cb *self = dev_id; + __u8 irqstat; + struct sk_buff *skb = NULL; + + irqstat = INB (OBOE_ISR); + +/* was it us */ + if (!(irqstat & OBOE_INT_MASK)) + return IRQ_NONE; + +/* Ack all the interrupts */ + OUTB (irqstat, OBOE_ISR); + + toshoboe_isntstuck (self); + +/* Txdone */ + if (irqstat & OBOE_INT_TXDONE) + { + int txp, txpc; + int i; + + txp = self->txpending; + self->txpending = 0; + + for (i = 0; i < TX_SLOTS; ++i) + { + if (self->ring->tx[i].control & OBOE_CTL_TX_HW_OWNS) + self->txpending++; + } + pr_debug("%s.txd(%x)%x/%x\n", __func__, irqstat, txp, self->txpending); + + txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; + + /* Got anything queued ? start it together */ + if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) + { + txpc = txp; +#ifdef OPTIMIZE_TX + while (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) + { + txp = txpc; + txpc++; + txpc %= TX_SLOTS; + self->netdev->stats.tx_packets++; + if (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) + self->ring->tx[txp].control &= ~OBOE_CTL_TX_RTCENTX; + } + self->netdev->stats.tx_packets--; +#else + self->netdev->stats.tx_packets++; +#endif + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); + } + + if ((!self->txpending) && (self->new_speed)) + { + self->speed = self->new_speed; + pr_debug("%s: Executed TxDone scheduled speed change %d\n", + __func__, self->speed); + toshoboe_setbaud (self); + } + + /* Tell network layer that we want more frames */ + if (!self->new_speed) + netif_wake_queue(self->netdev); + } + + if (irqstat & OBOE_INT_RXDONE) + { + while (!(self->ring->rx[self->rxs].control & OBOE_CTL_RX_HW_OWNS)) + { + int len = self->ring->rx[self->rxs].len; + skb = NULL; + pr_debug("%s.rcv:%x(%x)\n", __func__ + , len, self->ring->rx[self->rxs].control); + +#ifdef DUMP_PACKETS +dumpbufs(self->rx_bufs[self->rxs],len,'<'); +#endif + + if (self->ring->rx[self->rxs].control == 0) + { + __u8 enable = INB (OBOE_ENABLEH); + + /* In SIR mode we need to check the CRC as this */ + /* hasn't been done by the hardware */ + if (enable & OBOE_ENABLEH_SIRON) + { + if (!toshoboe_checkfcs (self->rx_bufs[self->rxs], len)) + len = 0; + /*Trim off the CRC */ + if (len > 1) + len -= 2; + else + len = 0; + pr_debug("%s.SIR:%x(%x)\n", __func__, len, enable); + } + +#ifdef USE_MIR + else if (enable & OBOE_ENABLEH_MIRON) + { + if (len > 1) + len -= 2; + else + len = 0; + pr_debug("%s.MIR:%x(%x)\n", __func__, len, enable); + } +#endif + else if (enable & OBOE_ENABLEH_FIRON) + { + if (len > 3) + len -= 4; /*FIXME: check this */ + else + len = 0; + pr_debug("%s.FIR:%x(%x)\n", __func__, len, enable); + } + else + pr_debug("%s.?IR:%x(%x)\n", __func__, len, enable); + + if (len) + { + skb = dev_alloc_skb (len + 1); + if (skb) + { + skb_reserve (skb, 1); + + skb_put (skb, len); + skb_copy_to_linear_data(skb, self->rx_bufs[self->rxs], + len); + self->netdev->stats.rx_packets++; + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb->protocol = htons (ETH_P_IRDA); + } + else + { + printk (KERN_INFO + "%s(), memory squeeze, dropping frame.\n", + __func__); + } + } + } + else + { + /* TODO: =========================================== */ + /* if OBOE_CTL_RX_LENGTH, our buffers are too small */ + /* (MIR or FIR) data is lost. */ + /* (SIR) data is splitted in several slots. */ + /* we have to join all the received buffers received */ + /*in a large buffer before checking CRC. */ + pr_debug("%s.err:%x(%x)\n", __func__ + , len, self->ring->rx[self->rxs].control); + } + + self->ring->rx[self->rxs].len = 0x0; + self->ring->rx[self->rxs].control = OBOE_CTL_RX_HW_OWNS; + + self->rxs++; + self->rxs %= RX_SLOTS; + + if (skb) + netif_rx (skb); + + } + } + + if (irqstat & OBOE_INT_TXUNDER) + { + printk (KERN_WARNING DRIVER_NAME ": tx fifo underflow\n"); + } + if (irqstat & OBOE_INT_RXOVER) + { + printk (KERN_WARNING DRIVER_NAME ": rx fifo overflow\n"); + } +/* This must be useful for something... */ + if (irqstat & OBOE_INT_SIP) + { + self->int_sip++; + pr_debug("%s.sip:%x(%x)%x\n", + __func__, self->int_sip, irqstat, self->txpending); + } + return IRQ_HANDLED; +} + + +static int +toshoboe_net_open (struct net_device *dev) +{ + struct toshoboe_cb *self; + unsigned long flags; + int rc; + + self = netdev_priv(dev); + + if (self->async) + return -EBUSY; + + if (self->stopped) + return 0; + + rc = request_irq (self->io.irq, toshoboe_interrupt, + IRQF_SHARED, dev->name, self); + if (rc) + return rc; + + spin_lock_irqsave(&self->spinlock, flags); + toshoboe_startchip (self); + spin_unlock_irqrestore(&self->spinlock, flags); + + /* Ready to play! */ + netif_start_queue(dev); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + self->irlap = irlap_open (dev, &self->qos, driver_name); + + self->irdad = 1; + + return 0; +} + +static int +toshoboe_net_close (struct net_device *dev) +{ + struct toshoboe_cb *self; + + IRDA_ASSERT (dev != NULL, return -1; ); + self = netdev_priv(dev); + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close (self->irlap); + self->irlap = NULL; + + self->irdad = 0; + + free_irq (self->io.irq, (void *) self); + + if (!self->stopped) + { + toshoboe_stopchip (self); + } + + return 0; +} + +/* + * Function toshoboe_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int +toshoboe_net_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct toshoboe_cb *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT (dev != NULL, return -1; ); + + self = netdev_priv(dev); + + IRDA_ASSERT (self != NULL, return -1; ); + + pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); + + /* Disable interrupts & save flags */ + spin_lock_irqsave(&self->spinlock, flags); + + switch (cmd) + { + case SIOCSBANDWIDTH: /* Set bandwidth */ + /* This function will also be used by IrLAP to change the + * speed, so we still must allow for speed change within + * interrupt context. + */ + pr_debug("%s(BANDWIDTH), %s, (%X/%ld\n", + __func__, dev->name, INB(OBOE_STATUS), irq->ifr_baudrate); + if (!in_interrupt () && !capable (CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + + /* self->speed=irq->ifr_baudrate; */ + /* toshoboe_setbaud(self); */ + /* Just change speed once - inserted by Paul Bristow */ + self->new_speed = irq->ifr_baudrate; + break; + case SIOCSMEDIABUSY: /* Set media busy */ + pr_debug("%s(MEDIABUSY), %s, (%X/%x)\n", + __func__, dev->name, + INB(OBOE_STATUS), capable(CAP_NET_ADMIN)); + if (!capable (CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + irda_device_set_media_busy (self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = (INB (OBOE_STATUS) & OBOE_STATUS_RXBUSY) ? 1 : 0; + pr_debug("%s(RECEIVING), %s, (%X/%x)\n", + __func__, dev->name, INB(OBOE_STATUS), irq->ifr_receiving); + break; + default: + pr_debug("%s(?), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); + ret = -EOPNOTSUPP; + } +out: + spin_unlock_irqrestore(&self->spinlock, flags); + return ret; + +} + +MODULE_DESCRIPTION("Toshiba OBOE IrDA Device Driver"); +MODULE_AUTHOR("James McKenzie "); +MODULE_LICENSE("GPL"); + +module_param (max_baud, int, 0); +MODULE_PARM_DESC(max_baud, "Maximum baud rate"); + +#ifdef USE_PROBE +module_param (do_probe, bool, 0); +MODULE_PARM_DESC(do_probe, "Enable/disable chip probing and self-test"); +#endif + +static void +toshoboe_close (struct pci_dev *pci_dev) +{ + int i; + struct toshoboe_cb *self = pci_get_drvdata(pci_dev); + + IRDA_ASSERT (self != NULL, return; ); + + if (!self->stopped) + { + toshoboe_stopchip (self); + } + + release_region (self->io.fir_base, self->io.fir_ext); + + for (i = 0; i < TX_SLOTS; ++i) + { + kfree (self->tx_bufs[i]); + self->tx_bufs[i] = NULL; + } + + for (i = 0; i < RX_SLOTS; ++i) + { + kfree (self->rx_bufs[i]); + self->rx_bufs[i] = NULL; + } + + unregister_netdev(self->netdev); + + kfree (self->ringbuf); + self->ringbuf = NULL; + self->ring = NULL; + + free_netdev(self->netdev); +} + +static const struct net_device_ops toshoboe_netdev_ops = { + .ndo_open = toshoboe_net_open, + .ndo_stop = toshoboe_net_close, + .ndo_start_xmit = toshoboe_hard_xmit, + .ndo_do_ioctl = toshoboe_net_ioctl, +}; + +static int +toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid) +{ + struct toshoboe_cb *self; + struct net_device *dev; + int i = 0; + int ok = 0; + int err; + + if ((err=pci_enable_device(pci_dev))) + return err; + + dev = alloc_irdadev(sizeof (struct toshoboe_cb)); + if (dev == NULL) + { + printk (KERN_ERR DRIVER_NAME ": can't allocate memory for " + "IrDA control block\n"); + return -ENOMEM; + } + + self = netdev_priv(dev); + self->netdev = dev; + self->pdev = pci_dev; + self->base = pci_resource_start(pci_dev,0); + + self->io.fir_base = self->base; + self->io.fir_ext = OBOE_IO_EXTENT; + self->io.irq = pci_dev->irq; + self->io.irqflags = IRQF_SHARED; + + self->speed = self->io.speed = 9600; + self->async = 0; + + /* Lock the port that we need */ + if (NULL==request_region (self->io.fir_base, self->io.fir_ext, driver_name)) + { + printk (KERN_ERR DRIVER_NAME ": can't get iobase of 0x%03x\n" + ,self->io.fir_base); + err = -EBUSY; + goto freeself; + } + + spin_lock_init(&self->spinlock); + + irda_init_max_qos_capabilies (&self->qos); + self->qos.baud_rate.bits = 0; + + if (max_baud >= 2400) + self->qos.baud_rate.bits |= IR_2400; + /*if (max_baud>=4800) idev->qos.baud_rate.bits|=IR_4800; */ + if (max_baud >= 9600) + self->qos.baud_rate.bits |= IR_9600; + if (max_baud >= 19200) + self->qos.baud_rate.bits |= IR_19200; + if (max_baud >= 115200) + self->qos.baud_rate.bits |= IR_115200; +#ifdef USE_MIR + if (max_baud >= 1152000) + { + self->qos.baud_rate.bits |= IR_1152000; + } +#endif + if (max_baud >= 4000000) + { + self->qos.baud_rate.bits |= (IR_4000000 << 8); + } + + /*FIXME: work this out... */ + self->qos.min_turn_time.bits = 0xff; + + irda_qos_bits_to_value (&self->qos); + + /* Allocate twice the size to guarantee alignment */ + self->ringbuf = kmalloc(OBOE_RING_LEN << 1, GFP_KERNEL); + if (!self->ringbuf) + { + err = -ENOMEM; + goto freeregion; + } + +#if (BITS_PER_LONG == 64) +#error broken on 64-bit: casts pointer to 32-bit, and then back to pointer. +#endif + + /*We need to align the taskfile on a taskfile size boundary */ + { + unsigned long addr; + + addr = (__u32) self->ringbuf; + addr &= ~(OBOE_RING_LEN - 1); + addr += OBOE_RING_LEN; + self->ring = (struct OboeRing *) addr; + } + + memset (self->ring, 0, OBOE_RING_LEN); + self->io.mem_base = (__u32) self->ring; + + ok = 1; + for (i = 0; i < TX_SLOTS; ++i) + { + self->tx_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL); + if (!self->tx_bufs[i]) + ok = 0; + } + + for (i = 0; i < RX_SLOTS; ++i) + { + self->rx_bufs[i] = kmalloc (RX_BUF_SZ, GFP_KERNEL); + if (!self->rx_bufs[i]) + ok = 0; + } + + if (!ok) + { + err = -ENOMEM; + goto freebufs; + } + + +#ifdef USE_PROBE + if (do_probe) + if (!toshoboe_probe (self)) + { + err = -ENODEV; + goto freebufs; + } +#endif + + SET_NETDEV_DEV(dev, &pci_dev->dev); + dev->netdev_ops = &toshoboe_netdev_ops; + + err = register_netdev(dev); + if (err) + { + printk (KERN_ERR DRIVER_NAME ": register_netdev() failed\n"); + err = -ENOMEM; + goto freebufs; + } + printk (KERN_INFO "IrDA: Registered device %s\n", dev->name); + + pci_set_drvdata(pci_dev,self); + + printk (KERN_INFO DRIVER_NAME ": Using multiple tasks\n"); + + return 0; + +freebufs: + for (i = 0; i < TX_SLOTS; ++i) + kfree (self->tx_bufs[i]); + for (i = 0; i < RX_SLOTS; ++i) + kfree (self->rx_bufs[i]); + kfree(self->ringbuf); + +freeregion: + release_region (self->io.fir_base, self->io.fir_ext); + +freeself: + free_netdev(dev); + + return err; +} + +static int +toshoboe_gotosleep (struct pci_dev *pci_dev, pm_message_t crap) +{ + struct toshoboe_cb *self = pci_get_drvdata(pci_dev); + unsigned long flags; + int i = 10; + + if (!self || self->stopped) + return 0; + + if ((!self->irdad) && (!self->async)) + return 0; + +/* Flush all packets */ + while ((i--) && (self->txpending)) + msleep(10); + + spin_lock_irqsave(&self->spinlock, flags); + + toshoboe_stopchip (self); + self->stopped = 1; + self->txpending = 0; + + spin_unlock_irqrestore(&self->spinlock, flags); + return 0; +} + +static int +toshoboe_wakeup (struct pci_dev *pci_dev) +{ + struct toshoboe_cb *self = pci_get_drvdata(pci_dev); + unsigned long flags; + + if (!self || !self->stopped) + return 0; + + if ((!self->irdad) && (!self->async)) + return 0; + + spin_lock_irqsave(&self->spinlock, flags); + + toshoboe_startchip (self); + self->stopped = 0; + + netif_wake_queue(self->netdev); + spin_unlock_irqrestore(&self->spinlock, flags); + return 0; +} + +static struct pci_driver donauboe_pci_driver = { + .name = "donauboe", + .id_table = toshoboe_pci_tbl, + .probe = toshoboe_open, + .remove = toshoboe_close, + .suspend = toshoboe_gotosleep, + .resume = toshoboe_wakeup +}; + +module_pci_driver(donauboe_pci_driver); diff --git a/drivers/staging/irda/drivers/donauboe.h b/drivers/staging/irda/drivers/donauboe.h new file mode 100644 index 000000000000..d92d54e839b9 --- /dev/null +++ b/drivers/staging/irda/drivers/donauboe.h @@ -0,0 +1,362 @@ +/********************************************************************* + * + * Filename: toshoboe.h + * Version: 2.16 + * Description: Driver for the Toshiba OBOE (or type-O or 701) + * FIR Chipset, also supports the DONAUOBOE (type-DO + * or d01) FIR chipset which as far as I know is + * register compatible. + * Status: Experimental. + * Author: James McKenzie + * Created at: Sat May 8 12:35:27 1999 + * Modified: 2.16 Martin Lucina + * Modified: 2.16 Sat Jun 22 18:54:29 2002 (sync headers) + * Modified: 2.17 Christian Gennerat + * Modified: 2.17 jeu sep 12 08:50:20 2002 (add lock to be used by spinlocks) + * + * Copyright (c) 1999 James McKenzie, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither James McKenzie nor Cambridge University admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + * Applicable Models : Libretto 100/110CT and many more. + * Toshiba refers to this chip as the type-O IR port, + * or the type-DO IR port. + * + * IrDA chip set list from Toshiba Computer Engineering Corp. + * model method maker controller Version + * Portege 320CT FIR,SIR Toshiba Oboe(Triangle) + * Portege 3010CT FIR,SIR Toshiba Oboe(Sydney) + * Portege 3015CT FIR,SIR Toshiba Oboe(Sydney) + * Portege 3020CT FIR,SIR Toshiba Oboe(Sydney) + * Portege 7020CT FIR,SIR ? ? + * + * Satell. 4090XCDT FIR,SIR ? ? + * + * Libretto 100CT FIR,SIR Toshiba Oboe + * Libretto 1000CT FIR,SIR Toshiba Oboe + * + * TECRA750DVD FIR,SIR Toshiba Oboe(Triangle) REV ID=14h + * TECRA780 FIR,SIR Toshiba Oboe(Sandlot) REV ID=32h,33h + * TECRA750CDT FIR,SIR Toshiba Oboe(Triangle) REV ID=13h,14h + * TECRA8000 FIR,SIR Toshiba Oboe(ISKUR) REV ID=23h + * + ********************************************************************/ + +/* The documentation for this chip is allegedly released */ +/* However I have not seen it, not have I managed to contact */ +/* anyone who has. HOWEVER the chip bears a striking resemblance */ +/* to the IrDA controller in the Toshiba RISC TMPR3922 chip */ +/* the documentation for this is freely available at */ +/* http://www.madingley.org/james/resources/toshoboe/TMPR3922.pdf */ +/* The mapping between the registers in that document and the */ +/* Registers in the 701 oboe chip are as follows */ + + +/* 3922 reg 701 regs, by bit numbers */ +/* 7- 0 15- 8 24-16 31-25 */ +/* $28 0x0 0x1 */ +/* $2c SEE NOTE 1 */ +/* $30 0x6 0x7 */ +/* $34 0x8 0x9 SEE NOTE 2 */ +/* $38 0x10 0x11 */ +/* $3C 0xe SEE NOTE 3 */ +/* $40 0x12 0x13 */ +/* $44 0x14 0x15 */ +/* $48 0x16 0x17 */ +/* $4c 0x18 0x19 */ +/* $50 0x1a 0x1b */ + +/* FIXME: could be 0x1b 0x1a here */ + +/* $54 0x1d 0x1c */ +/* $5C 0xf SEE NOTE 4 */ +/* $130 SEE NOTE 5 */ +/* $134 SEE NOTE 6 */ +/* */ +/* NOTES: */ +/* 1. The pointer to ring is packed in most unceremoniusly */ +/* 701 Register Address bits (A9-A0 must be zero) */ +/* 0x4: A17 A16 A15 A14 A13 A12 A11 A10 */ +/* 0x5: A25 A24 A23 A22 A21 A20 A19 A18 */ +/* 0x2: 0 0 A31 A30 A29 A28 A27 A26 */ +/* */ +/* 2. The M$ drivers do a write 0x1 to 0x9, however the 3922 */ +/* documentation would suggest that a write of 0x1 to 0x8 */ +/* would be more appropriate. */ +/* */ +/* 3. This assignment is tenuous at best, register 0xe seems to */ +/* have bits arranged 0 0 0 R/W R/W R/W R/W R/W */ +/* if either of the lower two bits are set the chip seems to */ +/* switch off */ +/* */ +/* 4. Bits 7-4 seem to be different 4 seems just to be generic */ +/* receiver busy flag */ +/* */ +/* 5. and 6. The IER and ISR have a different bit assignment */ +/* The lower three bits of both read back as ones */ +/* ISR is register 0xc, IER is register 0xd */ +/* 7 6 5 4 3 2 1 0 */ +/* 0xc: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */ +/* 0xd: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */ +/* TxDone xmitt done (generated only if generate interrupt bit */ +/* is set in the ring) */ +/* RxDone recv completed (or other recv condition if you set it */ +/* up */ +/* TxUnder underflow in Transmit FIFO */ +/* RxOver overflow in Recv FIFO */ +/* SipRcv received serial gap (or other condition you set) */ +/* Interrupts are enabled by writing a one to the IER register */ +/* Interrupts are cleared by writing a one to the ISR register */ +/* */ +/* 6. The remaining registers: 0x6 and 0x3 appear to be */ +/* reserved parts of 16 or 32 bit registersthe remainder */ +/* 0xa 0xb 0x1e 0x1f could possibly be (by their behaviour) */ +/* the Unicast Filter register at $58. */ +/* */ +/* 7. While the core obviously expects 32 bit accesses all the */ +/* M$ drivers do 8 bit accesses, infact the Miniport ones */ +/* write and read back the byte serveral times (why?) */ + + +#ifndef TOSHOBOE_H +#define TOSHOBOE_H + +/* Registers */ + +#define OBOE_IO_EXTENT 0x1f + +/*Receive and transmit slot pointers */ +#define OBOE_REG(i) (i+(self->base)) +#define OBOE_RXSLOT OBOE_REG(0x0) +#define OBOE_TXSLOT OBOE_REG(0x1) +#define OBOE_SLOT_MASK 0x3f + +#define OBOE_TXRING_OFFSET 0x200 +#define OBOE_TXRING_OFFSET_IN_SLOTS 0x40 + +/*pointer to the ring */ +#define OBOE_RING_BASE0 OBOE_REG(0x4) +#define OBOE_RING_BASE1 OBOE_REG(0x5) +#define OBOE_RING_BASE2 OBOE_REG(0x2) +#define OBOE_RING_BASE3 OBOE_REG(0x3) + +/*Number of slots in the ring */ +#define OBOE_RING_SIZE OBOE_REG(0x7) +#define OBOE_RING_SIZE_RX4 0x00 +#define OBOE_RING_SIZE_RX8 0x01 +#define OBOE_RING_SIZE_RX16 0x03 +#define OBOE_RING_SIZE_RX32 0x07 +#define OBOE_RING_SIZE_RX64 0x0f +#define OBOE_RING_SIZE_TX4 0x00 +#define OBOE_RING_SIZE_TX8 0x10 +#define OBOE_RING_SIZE_TX16 0x30 +#define OBOE_RING_SIZE_TX32 0x70 +#define OBOE_RING_SIZE_TX64 0xf0 + +#define OBOE_RING_MAX_SIZE 64 + +/*Causes the gubbins to re-examine the ring */ +#define OBOE_PROMPT OBOE_REG(0x9) +#define OBOE_PROMPT_BIT 0x1 + +/* Interrupt Status Register */ +#define OBOE_ISR OBOE_REG(0xc) +/* Interrupt Enable Register */ +#define OBOE_IER OBOE_REG(0xd) +/* Interrupt bits for IER and ISR */ +#define OBOE_INT_TXDONE 0x80 +#define OBOE_INT_RXDONE 0x40 +#define OBOE_INT_TXUNDER 0x20 +#define OBOE_INT_RXOVER 0x10 +#define OBOE_INT_SIP 0x08 +#define OBOE_INT_MASK 0xf8 + +/*Reset Register */ +#define OBOE_CONFIG1 OBOE_REG(0xe) +#define OBOE_CONFIG1_RST 0x01 +#define OBOE_CONFIG1_DISABLE 0x02 +#define OBOE_CONFIG1_4 0x08 +#define OBOE_CONFIG1_8 0x08 + +#define OBOE_CONFIG1_ON 0x8 +#define OBOE_CONFIG1_RESET 0xf +#define OBOE_CONFIG1_OFF 0xe + +#define OBOE_STATUS OBOE_REG(0xf) +#define OBOE_STATUS_RXBUSY 0x10 +#define OBOE_STATUS_FIRRX 0x04 +#define OBOE_STATUS_MIRRX 0x02 +#define OBOE_STATUS_SIRRX 0x01 + + +/*Speed control registers */ +#define OBOE_CONFIG0L OBOE_REG(0x10) +#define OBOE_CONFIG0H OBOE_REG(0x11) + +#define OBOE_CONFIG0H_TXONLOOP 0x80 /*Transmit when looping (dangerous) */ +#define OBOE_CONFIG0H_LOOP 0x40 /*Loopback Tx->Rx */ +#define OBOE_CONFIG0H_ENTX 0x10 /*Enable Tx */ +#define OBOE_CONFIG0H_ENRX 0x08 /*Enable Rx */ +#define OBOE_CONFIG0H_ENDMAC 0x04 /*Enable/reset* the DMA controller */ +#define OBOE_CONFIG0H_RCVANY 0x02 /*DMA mode 1=bytes, 0=dwords */ + +#define OBOE_CONFIG0L_CRC16 0x80 /*CRC 1=16 bit 0=32 bit */ +#define OBOE_CONFIG0L_ENFIR 0x40 /*Enable FIR */ +#define OBOE_CONFIG0L_ENMIR 0x20 /*Enable MIR */ +#define OBOE_CONFIG0L_ENSIR 0x10 /*Enable SIR */ +#define OBOE_CONFIG0L_ENSIRF 0x08 /*Enable SIR framer */ +#define OBOE_CONFIG0L_SIRTEST 0x04 /*Enable SIR framer in MIR and FIR */ +#define OBOE_CONFIG0L_INVERTTX 0x02 /*Invert Tx Line */ +#define OBOE_CONFIG0L_INVERTRX 0x01 /*Invert Rx Line */ + +#define OBOE_BOF OBOE_REG(0x12) +#define OBOE_EOF OBOE_REG(0x13) + +#define OBOE_ENABLEL OBOE_REG(0x14) +#define OBOE_ENABLEH OBOE_REG(0x15) + +#define OBOE_ENABLEH_PHYANDCLOCK 0x80 /*Toggle low to copy config in */ +#define OBOE_ENABLEH_CONFIGERR 0x40 +#define OBOE_ENABLEH_FIRON 0x20 +#define OBOE_ENABLEH_MIRON 0x10 +#define OBOE_ENABLEH_SIRON 0x08 +#define OBOE_ENABLEH_ENTX 0x04 +#define OBOE_ENABLEH_ENRX 0x02 +#define OBOE_ENABLEH_CRC16 0x01 + +#define OBOE_ENABLEL_BROADCAST 0x01 + +#define OBOE_CURR_PCONFIGL OBOE_REG(0x16) /*Current config */ +#define OBOE_CURR_PCONFIGH OBOE_REG(0x17) + +#define OBOE_NEW_PCONFIGL OBOE_REG(0x18) +#define OBOE_NEW_PCONFIGH OBOE_REG(0x19) + +#define OBOE_PCONFIGH_BAUDMASK 0xfc +#define OBOE_PCONFIGH_WIDTHMASK 0x04 +#define OBOE_PCONFIGL_WIDTHMASK 0xe0 +#define OBOE_PCONFIGL_PREAMBLEMASK 0x1f + +#define OBOE_PCONFIG_BAUDMASK 0xfc00 +#define OBOE_PCONFIG_BAUDSHIFT 10 +#define OBOE_PCONFIG_WIDTHMASK 0x04e0 +#define OBOE_PCONFIG_WIDTHSHIFT 5 +#define OBOE_PCONFIG_PREAMBLEMASK 0x001f +#define OBOE_PCONFIG_PREAMBLESHIFT 0 + +#define OBOE_MAXLENL OBOE_REG(0x1a) +#define OBOE_MAXLENH OBOE_REG(0x1b) + +#define OBOE_RXCOUNTH OBOE_REG(0x1c) /*Reset on recipt */ +#define OBOE_RXCOUNTL OBOE_REG(0x1d) /*of whole packet */ + +/* The PCI ID of the OBOE chip */ +#ifndef PCI_DEVICE_ID_FIR701 +#define PCI_DEVICE_ID_FIR701 0x0701 +#endif + +#ifndef PCI_DEVICE_ID_FIRD01 +#define PCI_DEVICE_ID_FIRD01 0x0d01 +#endif + +struct OboeSlot +{ + __u16 len; /*Tweleve bits of packet length */ + __u8 unused; + __u8 control; /*Slot control/status see below */ + __u32 address; /*Slot buffer address */ +} +__packed; + +#define OBOE_NTASKS OBOE_TXRING_OFFSET_IN_SLOTS + +struct OboeRing +{ + struct OboeSlot rx[OBOE_NTASKS]; + struct OboeSlot tx[OBOE_NTASKS]; +}; + +#define OBOE_RING_LEN (sizeof(struct OboeRing)) + + +#define OBOE_CTL_TX_HW_OWNS 0x80 /*W/R This slot owned by the hardware */ +#define OBOE_CTL_TX_DISTX_CRC 0x40 /*W Disable CRC generation for [FM]IR */ +#define OBOE_CTL_TX_BAD_CRC 0x20 /*W Generate bad CRC */ +#define OBOE_CTL_TX_SIP 0x10 /*W Generate an SIP after xmittion */ +#define OBOE_CTL_TX_MKUNDER 0x08 /*W Generate an underrun error */ +#define OBOE_CTL_TX_RTCENTX 0x04 /*W Enable receiver and generate TXdone */ + /* After this slot is processed */ +#define OBOE_CTL_TX_UNDER 0x01 /*R Set by hardware to indicate underrun */ + + +#define OBOE_CTL_RX_HW_OWNS 0x80 /*W/R This slot owned by hardware */ +#define OBOE_CTL_RX_PHYERR 0x40 /*R Decoder error on receiption */ +#define OBOE_CTL_RX_CRCERR 0x20 /*R CRC error only set for [FM]IR */ +#define OBOE_CTL_RX_LENGTH 0x10 /*R Packet > max Rx length */ +#define OBOE_CTL_RX_OVER 0x08 /*R set to indicate an overflow */ +#define OBOE_CTL_RX_SIRBAD 0x04 /*R SIR had BOF in packet or ABORT sequence */ +#define OBOE_CTL_RX_RXEOF 0x02 /*R Finished receiving on this slot */ + + +struct toshoboe_cb +{ + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + struct tty_driver ttydev; + + struct irlap_cb *irlap; /* The link layer we are binded to */ + + chipio_t io; /* IrDA controller information */ + struct qos_info qos; /* QoS capabilities for this device */ + + __u32 flags; /* Interface flags */ + + struct pci_dev *pdev; /*PCI device */ + int base; /*IO base */ + + + int txpending; /*how many tx's are pending */ + int txs, rxs; /*Which slots are we at */ + + int irdad; /*Driver under control of netdev end */ + int async; /*Driver under control of async end */ + + + int stopped; /*Stopped by some or other APM stuff */ + + int filter; /*In SIR mode do we want to receive + frames or byte ranges */ + + void *ringbuf; /*The ring buffer */ + struct OboeRing *ring; /*The ring */ + + void *tx_bufs[OBOE_RING_MAX_SIZE]; /*The buffers */ + void *rx_bufs[OBOE_RING_MAX_SIZE]; + + + int speed; /*Current setting of the speed */ + int new_speed; /*Set to request a speed change */ + +/* The spinlock protect critical parts of the driver. + * Locking is done like this : + * spin_lock_irqsave(&self->spinlock, flags); + * Releasing the lock : + * spin_unlock_irqrestore(&self->spinlock, flags); + */ + spinlock_t spinlock; + /* Used for the probe and diagnostics code */ + int int_rx; + int int_tx; + int int_txunder; + int int_rxover; + int int_sip; +}; + + +#endif diff --git a/drivers/staging/irda/drivers/esi-sir.c b/drivers/staging/irda/drivers/esi-sir.c new file mode 100644 index 000000000000..019a3e848bcb --- /dev/null +++ b/drivers/staging/irda/drivers/esi-sir.c @@ -0,0 +1,157 @@ +/********************************************************************* + * + * Filename: esi.c + * Version: 1.6 + * Description: Driver for the Extended Systems JetEye PC dongle + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sat Feb 21 18:54:38 1998 + * Modified at: Sun Oct 27 22:01:04 2002 + * Modified by: Martin Diehl + * + * Copyright (c) 1999 Dag Brattli, , + * Copyright (c) 1998 Thomas Davis, , + * Copyright (c) 2002 Martin Diehl, , + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + ********************************************************************/ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +static int esi_open(struct sir_dev *); +static int esi_close(struct sir_dev *); +static int esi_change_speed(struct sir_dev *, unsigned); +static int esi_reset(struct sir_dev *); + +static struct dongle_driver esi = { + .owner = THIS_MODULE, + .driver_name = "JetEye PC ESI-9680 PC", + .type = IRDA_ESI_DONGLE, + .open = esi_open, + .close = esi_close, + .reset = esi_reset, + .set_speed = esi_change_speed, +}; + +static int __init esi_sir_init(void) +{ + return irda_register_dongle(&esi); +} + +static void __exit esi_sir_cleanup(void) +{ + irda_unregister_dongle(&esi); +} + +static int esi_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + /* Power up and set dongle to 9600 baud */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200; + qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int esi_close(struct sir_dev *dev) +{ + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function esi_change_speed (task) + * + * Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle + * Apparently (see old esi-driver) no delays are needed here... + * + */ +static int esi_change_speed(struct sir_dev *dev, unsigned speed) +{ + int ret = 0; + int dtr, rts; + + switch (speed) { + case 19200: + dtr = TRUE; + rts = FALSE; + break; + case 115200: + dtr = rts = TRUE; + break; + default: + ret = -EINVAL; + speed = 9600; + /* fall through */ + case 9600: + dtr = FALSE; + rts = TRUE; + break; + } + + /* Change speed of dongle */ + sirdev_set_dtr_rts(dev, dtr, rts); + dev->speed = speed; + + return ret; +} + +/* + * Function esi_reset (task) + * + * Reset dongle; + * + */ +static int esi_reset(struct sir_dev *dev) +{ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + /* Hm, the old esi-driver left the dongle unpowered relying on + * the following speed change to repower. This might work for + * the esi because we only need the modem lines. However, now the + * general rule is reset must bring the dongle to some working + * well-known state because speed change might write to registers. + * The old esi-driver didn't any delay here - let's hope it' fine. + */ + + sirdev_set_dtr_rts(dev, FALSE, TRUE); + dev->speed = 9600; + + return 0; +} + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-1"); /* IRDA_ESI_DONGLE */ + +module_init(esi_sir_init); +module_exit(esi_sir_cleanup); + diff --git a/drivers/staging/irda/drivers/girbil-sir.c b/drivers/staging/irda/drivers/girbil-sir.c new file mode 100644 index 000000000000..7e0a5b8c6d53 --- /dev/null +++ b/drivers/staging/irda/drivers/girbil-sir.c @@ -0,0 +1,252 @@ +/********************************************************************* + * + * Filename: girbil.c + * Version: 1.2 + * Description: Implementation for the Greenwich GIrBIL dongle + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sat Feb 6 21:02:33 1999 + * Modified at: Fri Dec 17 09:13:20 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +static int girbil_reset(struct sir_dev *dev); +static int girbil_open(struct sir_dev *dev); +static int girbil_close(struct sir_dev *dev); +static int girbil_change_speed(struct sir_dev *dev, unsigned speed); + +/* Control register 1 */ +#define GIRBIL_TXEN 0x01 /* Enable transmitter */ +#define GIRBIL_RXEN 0x02 /* Enable receiver */ +#define GIRBIL_ECAN 0x04 /* Cancel self emitted data */ +#define GIRBIL_ECHO 0x08 /* Echo control characters */ + +/* LED Current Register (0x2) */ +#define GIRBIL_HIGH 0x20 +#define GIRBIL_MEDIUM 0x21 +#define GIRBIL_LOW 0x22 + +/* Baud register (0x3) */ +#define GIRBIL_2400 0x30 +#define GIRBIL_4800 0x31 +#define GIRBIL_9600 0x32 +#define GIRBIL_19200 0x33 +#define GIRBIL_38400 0x34 +#define GIRBIL_57600 0x35 +#define GIRBIL_115200 0x36 + +/* Mode register (0x4) */ +#define GIRBIL_IRDA 0x40 +#define GIRBIL_ASK 0x41 + +/* Control register 2 (0x5) */ +#define GIRBIL_LOAD 0x51 /* Load the new baud rate value */ + +static struct dongle_driver girbil = { + .owner = THIS_MODULE, + .driver_name = "Greenwich GIrBIL", + .type = IRDA_GIRBIL_DONGLE, + .open = girbil_open, + .close = girbil_close, + .reset = girbil_reset, + .set_speed = girbil_change_speed, +}; + +static int __init girbil_sir_init(void) +{ + return irda_register_dongle(&girbil); +} + +static void __exit girbil_sir_cleanup(void) +{ + irda_unregister_dongle(&girbil); +} + +static int girbil_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + /* Power on dongle */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x03; + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int girbil_close(struct sir_dev *dev) +{ + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function girbil_change_speed (dev, speed) + * + * Set the speed for the Girbil type dongle. + * + */ + +#define GIRBIL_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) + +static int girbil_change_speed(struct sir_dev *dev, unsigned speed) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + u8 control[2]; + static int ret = 0; + + /* dongle alread reset - port and dongle at default speed */ + + switch(state) { + + case SIRDEV_STATE_DONGLE_SPEED: + + /* Set DTR and Clear RTS to enter command mode */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + udelay(25); /* better wait a little while */ + + ret = 0; + switch (speed) { + default: + ret = -EINVAL; + /* fall through */ + case 9600: + control[0] = GIRBIL_9600; + break; + case 19200: + control[0] = GIRBIL_19200; + break; + case 34800: + control[0] = GIRBIL_38400; + break; + case 57600: + control[0] = GIRBIL_57600; + break; + case 115200: + control[0] = GIRBIL_115200; + break; + } + control[1] = GIRBIL_LOAD; + + /* Write control bytes */ + sirdev_raw_write(dev, control, 2); + + dev->speed = speed; + + state = GIRBIL_STATE_WAIT_SPEED; + delay = 100; + break; + + case GIRBIL_STATE_WAIT_SPEED: + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + udelay(25); /* better wait a little while */ + break; + + default: + net_err_ratelimited("%s - undefined state %d\n", + __func__, state); + ret = -EINVAL; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +/* + * Function girbil_reset (driver) + * + * This function resets the girbil dongle. + * + * Algorithm: + * 0. set RTS, and wait at least 5 ms + * 1. clear RTS + */ + + +#define GIRBIL_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET + 1) +#define GIRBIL_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET + 2) +#define GIRBIL_STATE_WAIT3_RESET (SIRDEV_STATE_DONGLE_RESET + 3) + +static int girbil_reset(struct sir_dev *dev) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + u8 control = GIRBIL_TXEN | GIRBIL_RXEN; + int ret = 0; + + switch (state) { + case SIRDEV_STATE_DONGLE_RESET: + /* Reset dongle */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + /* Sleep at least 5 ms */ + delay = 20; + state = GIRBIL_STATE_WAIT1_RESET; + break; + + case GIRBIL_STATE_WAIT1_RESET: + /* Set DTR and clear RTS to enter command mode */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + delay = 20; + state = GIRBIL_STATE_WAIT2_RESET; + break; + + case GIRBIL_STATE_WAIT2_RESET: + /* Write control byte */ + sirdev_raw_write(dev, &control, 1); + delay = 20; + state = GIRBIL_STATE_WAIT3_RESET; + break; + + case GIRBIL_STATE_WAIT3_RESET: + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + dev->speed = 9600; + break; + + default: + net_err_ratelimited("%s(), undefined state %d\n", + __func__, state); + ret = -1; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("Greenwich GIrBIL dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-4"); /* IRDA_GIRBIL_DONGLE */ + +module_init(girbil_sir_init); +module_exit(girbil_sir_cleanup); diff --git a/drivers/staging/irda/drivers/irda-usb.c b/drivers/staging/irda/drivers/irda-usb.c new file mode 100644 index 000000000000..723e49bc4baa --- /dev/null +++ b/drivers/staging/irda/drivers/irda-usb.c @@ -0,0 +1,1914 @@ +/***************************************************************************** + * + * Filename: irda-usb.c + * Version: 0.10 + * Description: IrDA-USB Driver + * Status: Experimental + * Author: Dag Brattli + * + * Copyright (C) 2000, Roman Weissgaerber + * Copyright (C) 2001, Dag Brattli + * Copyright (C) 2001, Jean Tourrilhes + * Copyright (C) 2004, SigmaTel, Inc. + * Copyright (C) 2005, Milan Beno + * Copyright (C) 2006, Nick Fedchik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +/* + * IMPORTANT NOTE + * -------------- + * + * As of kernel 2.5.20, this is the state of compliance and testing of + * this driver (irda-usb) with regards to the USB low level drivers... + * + * This driver has been tested SUCCESSFULLY with the following drivers : + * o usb-uhci-hcd (For Intel/Via USB controllers) + * o uhci-hcd (Alternate/JE driver for Intel/Via USB controllers) + * o ohci-hcd (For other USB controllers) + * + * This driver has NOT been tested with the following drivers : + * o ehci-hcd (USB 2.0 controllers) + * + * Note that all HCD drivers do URB_ZERO_PACKET and timeout properly, + * so we don't have to worry about that anymore. + * One common problem is the failure to set the address on the dongle, + * but this happens before the driver gets loaded... + * + * Jean II + */ + +/*------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "irda-usb.h" + +/*------------------------------------------------------------------*/ + +static int qos_mtt_bits = 0; + +/* These are the currently known IrDA USB dongles. Add new dongles here */ +static const struct usb_device_id dongles[] = { + /* ACTiSYS Corp., ACT-IR2000U FIR-USB Adapter */ + { USB_DEVICE(0x9c4, 0x011), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */ + { USB_DEVICE(0x4428, 0x012), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* KC Technology Inc., KC-180 USB IrDA Device */ + { USB_DEVICE(0x50f, 0x180), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ + { USB_DEVICE(0x8e9, 0x100), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* SigmaTel STIR4210/4220/4116 USB IrDA (VFIR) Bridge */ + { USB_DEVICE(0x66f, 0x4210), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, + { USB_DEVICE(0x66f, 0x4220), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, + { USB_DEVICE(0x66f, 0x4116), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, + { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_APP_SPEC, + .bInterfaceSubClass = USB_CLASS_IRDA, + .driver_info = IUC_DEFAULT, }, + { }, /* The end */ +}; + +/* + * Important note : + * Devices based on the SigmaTel chipset (0x66f, 0x4200) are not designed + * using the "USB-IrDA specification" (yes, there exist such a thing), and + * therefore not supported by this driver (don't add them above). + * There is a Linux driver, stir4200, that support those USB devices. + * Jean II + */ + +MODULE_DEVICE_TABLE(usb, dongles); + +/*------------------------------------------------------------------*/ + +static void irda_usb_init_qos(struct irda_usb_cb *self) ; +static struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf); +static void irda_usb_disconnect(struct usb_interface *intf); +static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self); +static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, + struct net_device *dev); +static int irda_usb_open(struct irda_usb_cb *self); +static void irda_usb_close(struct irda_usb_cb *self); +static void speed_bulk_callback(struct urb *urb); +static void write_bulk_callback(struct urb *urb); +static void irda_usb_receive(struct urb *urb); +static void irda_usb_rx_defer_expired(unsigned long data); +static int irda_usb_net_open(struct net_device *dev); +static int irda_usb_net_close(struct net_device *dev); +static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void irda_usb_net_timeout(struct net_device *dev); + +/************************ TRANSMIT ROUTINES ************************/ +/* + * Receive packets from the IrDA stack and send them on the USB pipe. + * Handle speed change, timeout and lot's of ugliness... + */ + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_build_header(self, skb, header) + * + * Builds USB-IrDA outbound header + * + * When we send an IrDA frame over an USB pipe, we add to it a 1 byte + * header. This function create this header with the proper values. + * + * Important note : the USB-IrDA spec 1.0 say very clearly in chapter 5.4.2.2 + * that the setting of the link speed and xbof number in this outbound header + * should be applied *AFTER* the frame has been sent. + * Unfortunately, some devices are not compliant with that... It seems that + * reading the spec is far too difficult... + * Jean II + */ +static void irda_usb_build_header(struct irda_usb_cb *self, + __u8 *header, + int force) +{ + /* Here we check if we have an STIR421x chip, + * and if either speed or xbofs (or both) needs + * to be changed. + */ + if (self->capability & IUC_STIR421X && + ((self->new_speed != -1) || (self->new_xbofs != -1))) { + + /* With STIR421x, speed and xBOFs must be set at the same + * time, even if only one of them changes. + */ + if (self->new_speed == -1) + self->new_speed = self->speed ; + + if (self->new_xbofs == -1) + self->new_xbofs = self->xbofs ; + } + + /* Set the link speed */ + if (self->new_speed != -1) { + /* Hum... Ugly hack :-( + * Some device are not compliant with the spec and change + * parameters *before* sending the frame. - Jean II + */ + if ((self->capability & IUC_SPEED_BUG) && + (!force) && (self->speed != -1)) { + /* No speed and xbofs change here + * (we'll do it later in the write callback) */ + pr_debug("%s(), not changing speed yet\n", __func__); + *header = 0; + return; + } + + pr_debug("%s(), changing speed to %d\n", + __func__, self->new_speed); + self->speed = self->new_speed; + /* We will do ` self->new_speed = -1; ' in the completion + * handler just in case the current URB fail - Jean II */ + + switch (self->speed) { + case 2400: + *header = SPEED_2400; + break; + default: + case 9600: + *header = SPEED_9600; + break; + case 19200: + *header = SPEED_19200; + break; + case 38400: + *header = SPEED_38400; + break; + case 57600: + *header = SPEED_57600; + break; + case 115200: + *header = SPEED_115200; + break; + case 576000: + *header = SPEED_576000; + break; + case 1152000: + *header = SPEED_1152000; + break; + case 4000000: + *header = SPEED_4000000; + self->new_xbofs = 0; + break; + case 16000000: + *header = SPEED_16000000; + self->new_xbofs = 0; + break; + } + } else + /* No change */ + *header = 0; + + /* Set the negotiated additional XBOFS */ + if (self->new_xbofs != -1) { + pr_debug("%s(), changing xbofs to %d\n", + __func__, self->new_xbofs); + self->xbofs = self->new_xbofs; + /* We will do ` self->new_xbofs = -1; ' in the completion + * handler just in case the current URB fail - Jean II */ + + switch (self->xbofs) { + case 48: + *header |= 0x10; + break; + case 28: + case 24: /* USB spec 1.0 says 24 */ + *header |= 0x20; + break; + default: + case 12: + *header |= 0x30; + break; + case 5: /* Bug in IrLAP spec? (should be 6) */ + case 6: + *header |= 0x40; + break; + case 3: + *header |= 0x50; + break; + case 2: + *header |= 0x60; + break; + case 1: + *header |= 0x70; + break; + case 0: + *header |= 0x80; + break; + } + } +} + +/* +* calculate turnaround time for SigmaTel header +*/ +static __u8 get_turnaround_time(struct sk_buff *skb) +{ + int turnaround_time = irda_get_mtt(skb); + + if ( turnaround_time == 0 ) + return 0; + else if ( turnaround_time <= 10 ) + return 1; + else if ( turnaround_time <= 50 ) + return 2; + else if ( turnaround_time <= 100 ) + return 3; + else if ( turnaround_time <= 500 ) + return 4; + else if ( turnaround_time <= 1000 ) + return 5; + else if ( turnaround_time <= 5000 ) + return 6; + else + return 7; +} + + +/*------------------------------------------------------------------*/ +/* + * Send a command to change the speed of the dongle + * Need to be called with spinlock on. + */ +static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self) +{ + __u8 *frame; + struct urb *urb; + int ret; + + pr_debug("%s(), speed=%d, xbofs=%d\n", __func__, + self->new_speed, self->new_xbofs); + + /* Grab the speed URB */ + urb = self->speed_urb; + if (urb->status != 0) { + net_warn_ratelimited("%s(), URB still in use!\n", __func__); + return; + } + + /* Allocate the fake frame */ + frame = self->speed_buff; + + /* Set the new speed and xbofs in this fake frame */ + irda_usb_build_header(self, frame, 1); + + if (self->capability & IUC_STIR421X) { + if (frame[0] == 0) return ; // do nothing if no change + frame[1] = 0; // other parameters don't change here + frame[2] = 0; + } + + /* Submit the 0 length IrDA frame to trigger new speed settings */ + usb_fill_bulk_urb(urb, self->usbdev, + usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), + frame, IRDA_USB_SPEED_MTU, + speed_bulk_callback, self); + urb->transfer_buffer_length = self->header_length; + urb->transfer_flags = 0; + + /* Irq disabled -> GFP_ATOMIC */ + if ((ret = usb_submit_urb(urb, GFP_ATOMIC))) { + net_warn_ratelimited("%s(), failed Speed URB\n", __func__); + } +} + +/*------------------------------------------------------------------*/ +/* + * Speed URB callback + * Now, we can only get called for the speed URB. + */ +static void speed_bulk_callback(struct urb *urb) +{ + struct irda_usb_cb *self = urb->context; + + /* We should always have a context */ + IRDA_ASSERT(self != NULL, return;); + /* We should always be called for the speed URB */ + IRDA_ASSERT(urb == self->speed_urb, return;); + + /* Check for timeout and other USB nasties */ + if (urb->status != 0) { + /* I get a lot of -ECONNABORTED = -103 here - Jean II */ + pr_debug("%s(), URB complete status %d, transfer_flags 0x%04X\n", + __func__, urb->status, urb->transfer_flags); + + /* Don't do anything here, that might confuse the USB layer. + * Instead, we will wait for irda_usb_net_timeout(), the + * network layer watchdog, to fix the situation. + * Jean II */ + /* A reset of the dongle might be welcomed here - Jean II */ + return; + } + + /* urb is now available */ + //urb->status = 0; -> tested above + + /* New speed and xbof is now committed in hardware */ + self->new_speed = -1; + self->new_xbofs = -1; + + /* Allow the stack to send more packets */ + netif_wake_queue(self->netdev); +} + +/*------------------------------------------------------------------*/ +/* + * Send an IrDA frame to the USB dongle (for transmission) + */ +static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct irda_usb_cb *self = netdev_priv(netdev); + struct urb *urb = self->tx_urb; + unsigned long flags; + s32 speed; + s16 xbofs; + int res, mtt; + + pr_debug("%s() on %s\n", __func__, netdev->name); + + netif_stop_queue(netdev); + + /* Protect us from USB callbacks, net watchdog and else. */ + spin_lock_irqsave(&self->lock, flags); + + /* Check if the device is still there. + * We need to check self->present under the spinlock because + * of irda_usb_disconnect() is synchronous - Jean II */ + if (!self->present) { + pr_debug("%s(), Device is gone...\n", __func__); + goto drop; + } + + /* Check if we need to change the number of xbofs */ + xbofs = irda_get_next_xbofs(skb); + if ((xbofs != self->xbofs) && (xbofs != -1)) { + self->new_xbofs = xbofs; + } + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->speed) && (speed != -1)) { + /* Set the desired speed */ + self->new_speed = speed; + + /* Check for empty frame */ + if (!skb->len) { + /* IrLAP send us an empty frame to make us change the + * speed. Changing speed with the USB adapter is in + * fact sending an empty frame to the adapter, so we + * could just let the present function do its job. + * However, we would wait for min turn time, + * do an extra memcpy and increment packet counters... + * Jean II */ + irda_usb_change_speed_xbofs(self); + netif_trans_update(netdev); + /* Will netif_wake_queue() in callback */ + goto drop; + } + } + + if (urb->status != 0) { + net_warn_ratelimited("%s(), URB still in use!\n", __func__); + goto drop; + } + + skb_copy_from_linear_data(skb, self->tx_buff + self->header_length, skb->len); + + /* Change setting for next frame */ + if (self->capability & IUC_STIR421X) { + __u8 turnaround_time; + __u8* frame = self->tx_buff; + turnaround_time = get_turnaround_time( skb ); + irda_usb_build_header(self, frame, 0); + frame[2] = turnaround_time; + if ((skb->len != 0) && + ((skb->len % 128) == 0) && + ((skb->len % 512) != 0)) { + /* add extra byte for special SigmaTel feature */ + frame[1] = 1; + skb_put(skb, 1); + } else { + frame[1] = 0; + } + } else { + irda_usb_build_header(self, self->tx_buff, 0); + } + + /* FIXME: Make macro out of this one */ + ((struct irda_skb_cb *)skb->cb)->context = self; + + usb_fill_bulk_urb(urb, self->usbdev, + usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), + self->tx_buff, skb->len + self->header_length, + write_bulk_callback, skb); + + /* This flag (URB_ZERO_PACKET) indicates that what we send is not + * a continuous stream of data but separate packets. + * In this case, the USB layer will insert an empty USB frame (TD) + * after each of our packets that is exact multiple of the frame size. + * This is how the dongle will detect the end of packet - Jean II */ + urb->transfer_flags = URB_ZERO_PACKET; + + /* Generate min turn time. FIXME: can we do better than this? */ + /* Trying to a turnaround time at this level is trying to measure + * processor clock cycle with a wrist-watch, approximate at best... + * + * What we know is the last time we received a frame over USB. + * Due to latency over USB that depend on the USB load, we don't + * know when this frame was received over IrDA (a few ms before ?) + * Then, same story for our outgoing frame... + * + * In theory, the USB dongle is supposed to handle the turnaround + * by itself (spec 1.0, chater 4, page 6). Who knows ??? That's + * why this code is enabled only for dongles that doesn't meet + * the spec. + * Jean II */ + if (self->capability & IUC_NO_TURN) { + mtt = irda_get_mtt(skb); + if (mtt) { + int diff; + diff = ktime_us_delta(ktime_get(), self->stamp); +#ifdef IU_USB_MIN_RTT + /* Factor in USB delays -> Get rid of udelay() that + * would be lost in the noise - Jean II */ + diff += IU_USB_MIN_RTT; +#endif /* IU_USB_MIN_RTT */ + + /* Check if the mtt is larger than the time we have + * already used by all the protocol processing + */ + if (mtt > diff) { + mtt -= diff; + if (mtt > 1000) + mdelay(mtt/1000); + else + udelay(mtt); + } + } + } + + /* Ask USB to send the packet - Irq disabled -> GFP_ATOMIC */ + if ((res = usb_submit_urb(urb, GFP_ATOMIC))) { + net_warn_ratelimited("%s(), failed Tx URB\n", __func__); + netdev->stats.tx_errors++; + /* Let USB recover : We will catch that in the watchdog */ + /*netif_start_queue(netdev);*/ + } else { + /* Increment packet stats */ + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += skb->len; + + netif_trans_update(netdev); + } + spin_unlock_irqrestore(&self->lock, flags); + + return NETDEV_TX_OK; + +drop: + /* Drop silently the skb and exit */ + dev_kfree_skb(skb); + spin_unlock_irqrestore(&self->lock, flags); + return NETDEV_TX_OK; +} + +/*------------------------------------------------------------------*/ +/* + * Note : this function will be called only for tx_urb... + */ +static void write_bulk_callback(struct urb *urb) +{ + unsigned long flags; + struct sk_buff *skb = urb->context; + struct irda_usb_cb *self = ((struct irda_skb_cb *) skb->cb)->context; + + /* We should always have a context */ + IRDA_ASSERT(self != NULL, return;); + /* We should always be called for the speed URB */ + IRDA_ASSERT(urb == self->tx_urb, return;); + + /* Free up the skb */ + dev_kfree_skb_any(skb); + urb->context = NULL; + + /* Check for timeout and other USB nasties */ + if (urb->status != 0) { + /* I get a lot of -ECONNABORTED = -103 here - Jean II */ + pr_debug("%s(), URB complete status %d, transfer_flags 0x%04X\n", + __func__, urb->status, urb->transfer_flags); + + /* Don't do anything here, that might confuse the USB layer, + * and we could go in recursion and blow the kernel stack... + * Instead, we will wait for irda_usb_net_timeout(), the + * network layer watchdog, to fix the situation. + * Jean II */ + /* A reset of the dongle might be welcomed here - Jean II */ + return; + } + + /* urb is now available */ + //urb->status = 0; -> tested above + + /* Make sure we read self->present properly */ + spin_lock_irqsave(&self->lock, flags); + + /* If the network is closed, stop everything */ + if ((!self->netopen) || (!self->present)) { + pr_debug("%s(), Network is gone...\n", __func__); + spin_unlock_irqrestore(&self->lock, flags); + return; + } + + /* If changes to speed or xbofs is pending... */ + if ((self->new_speed != -1) || (self->new_xbofs != -1)) { + if ((self->new_speed != self->speed) || + (self->new_xbofs != self->xbofs)) { + /* We haven't changed speed yet (because of + * IUC_SPEED_BUG), so do it now - Jean II */ + pr_debug("%s(), Changing speed now...\n", __func__); + irda_usb_change_speed_xbofs(self); + } else { + /* New speed and xbof is now committed in hardware */ + self->new_speed = -1; + self->new_xbofs = -1; + /* Done, waiting for next packet */ + netif_wake_queue(self->netdev); + } + } else { + /* Otherwise, allow the stack to send more packets */ + netif_wake_queue(self->netdev); + } + spin_unlock_irqrestore(&self->lock, flags); +} + +/*------------------------------------------------------------------*/ +/* + * Watchdog timer from the network layer. + * After a predetermined timeout, if we don't give confirmation that + * the packet has been sent (i.e. no call to netif_wake_queue()), + * the network layer will call this function. + * Note that URB that we submit have also a timeout. When the URB timeout + * expire, the normal URB callback is called (write_bulk_callback()). + */ +static void irda_usb_net_timeout(struct net_device *netdev) +{ + unsigned long flags; + struct irda_usb_cb *self = netdev_priv(netdev); + struct urb *urb; + int done = 0; /* If we have made any progress */ + + pr_debug("%s(), Network layer thinks we timed out!\n", __func__); + IRDA_ASSERT(self != NULL, return;); + + /* Protect us from USB callbacks, net Tx and else. */ + spin_lock_irqsave(&self->lock, flags); + + /* self->present *MUST* be read under spinlock */ + if (!self->present) { + net_warn_ratelimited("%s(), device not present!\n", __func__); + netif_stop_queue(netdev); + spin_unlock_irqrestore(&self->lock, flags); + return; + } + + /* Check speed URB */ + urb = self->speed_urb; + if (urb->status != 0) { + pr_debug("%s: Speed change timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", + netdev->name, urb->status, urb->transfer_flags); + + switch (urb->status) { + case -EINPROGRESS: + usb_unlink_urb(urb); + /* Note : above will *NOT* call netif_wake_queue() + * in completion handler, we will come back here. + * Jean II */ + done = 1; + break; + case -ECONNRESET: + case -ENOENT: /* urb unlinked by us */ + default: /* ??? - Play safe */ + urb->status = 0; + netif_wake_queue(self->netdev); + done = 1; + break; + } + } + + /* Check Tx URB */ + urb = self->tx_urb; + if (urb->status != 0) { + struct sk_buff *skb = urb->context; + + pr_debug("%s: Tx timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", + netdev->name, urb->status, urb->transfer_flags); + + /* Increase error count */ + netdev->stats.tx_errors++; + +#ifdef IU_BUG_KICK_TIMEOUT + /* Can't be a bad idea to reset the speed ;-) - Jean II */ + if(self->new_speed == -1) + self->new_speed = self->speed; + if(self->new_xbofs == -1) + self->new_xbofs = self->xbofs; + irda_usb_change_speed_xbofs(self); +#endif /* IU_BUG_KICK_TIMEOUT */ + + switch (urb->status) { + case -EINPROGRESS: + usb_unlink_urb(urb); + /* Note : above will *NOT* call netif_wake_queue() + * in completion handler, because urb->status will + * be -ENOENT. We will fix that at the next watchdog, + * leaving more time to USB to recover... + * Jean II */ + done = 1; + break; + case -ECONNRESET: + case -ENOENT: /* urb unlinked by us */ + default: /* ??? - Play safe */ + if(skb != NULL) { + dev_kfree_skb_any(skb); + urb->context = NULL; + } + urb->status = 0; + netif_wake_queue(self->netdev); + done = 1; + break; + } + } + spin_unlock_irqrestore(&self->lock, flags); + + /* Maybe we need a reset */ + /* Note : Some drivers seem to use a usb_set_interface() when they + * need to reset the hardware. Hum... + */ + + /* if(done == 0) */ +} + +/************************* RECEIVE ROUTINES *************************/ +/* + * Receive packets from the USB layer stack and pass them to the IrDA stack. + * Try to work around USB failures... + */ + +/* + * Note : + * Some of you may have noticed that most dongle have an interrupt in pipe + * that we don't use. Here is the little secret... + * When we hang a Rx URB on the bulk in pipe, it generates some USB traffic + * in every USB frame. This is unnecessary overhead. + * The interrupt in pipe will generate an event every time a packet is + * received. Reading an interrupt pipe adds minimal overhead, but has some + * latency (~1ms). + * If we are connected (speed != 9600), we want to minimise latency, so + * we just always hang the Rx URB and ignore the interrupt. + * If we are not connected (speed == 9600), there is usually no Rx traffic, + * and we want to minimise the USB overhead. In this case we should wait + * on the interrupt pipe and hang the Rx URB only when an interrupt is + * received. + * Jean II + * + * Note : don't read the above as what we are currently doing, but as + * something we could do with KC dongle. Also don't forget that the + * interrupt pipe is not part of the original standard, so this would + * need to be optional... + * Jean II + */ + +/*------------------------------------------------------------------*/ +/* + * Submit a Rx URB to the USB layer to handle reception of a frame + * Mostly called by the completion callback of the previous URB. + * + * Jean II + */ +static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, struct urb *urb) +{ + struct irda_skb_cb *cb; + int ret; + + /* This should never happen */ + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(urb != NULL, return;); + + /* Save ourselves in the skb */ + cb = (struct irda_skb_cb *) skb->cb; + cb->context = self; + + /* Reinitialize URB */ + usb_fill_bulk_urb(urb, self->usbdev, + usb_rcvbulkpipe(self->usbdev, self->bulk_in_ep), + skb->data, IRDA_SKB_MAX_MTU, + irda_usb_receive, skb); + urb->status = 0; + + /* Can be called from irda_usb_receive (irq handler) -> GFP_ATOMIC */ + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + /* If this ever happen, we are in deep s***. + * Basically, the Rx path will stop... */ + net_warn_ratelimited("%s(), Failed to submit Rx URB %d\n", + __func__, ret); + } +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_receive(urb) + * + * Called by the USB subsystem when a frame has been received + * + */ +static void irda_usb_receive(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct irda_usb_cb *self; + struct irda_skb_cb *cb; + struct sk_buff *newskb; + struct sk_buff *dataskb; + struct urb *next_urb; + unsigned int len, docopy; + + pr_debug("%s(), len=%d\n", __func__, urb->actual_length); + + /* Find ourselves */ + cb = (struct irda_skb_cb *) skb->cb; + IRDA_ASSERT(cb != NULL, return;); + self = (struct irda_usb_cb *) cb->context; + IRDA_ASSERT(self != NULL, return;); + + /* If the network is closed or the device gone, stop everything */ + if ((!self->netopen) || (!self->present)) { + pr_debug("%s(), Network is gone!\n", __func__); + /* Don't re-submit the URB : will stall the Rx path */ + return; + } + + /* Check the status */ + if (urb->status != 0) { + switch (urb->status) { + case -EILSEQ: + self->netdev->stats.rx_crc_errors++; + /* Also precursor to a hot-unplug on UHCI. */ + /* Fallthrough... */ + case -ECONNRESET: + /* Random error, if I remember correctly */ + /* uhci_cleanup_unlink() is going to kill the Rx + * URB just after we return. No problem, at this + * point the URB will be idle ;-) - Jean II */ + case -ESHUTDOWN: + /* That's usually a hot-unplug. Submit will fail... */ + case -ETIME: + /* Usually precursor to a hot-unplug on OHCI. */ + default: + self->netdev->stats.rx_errors++; + pr_debug("%s(), RX status %d, transfer_flags 0x%04X\n", + __func__, urb->status, urb->transfer_flags); + break; + } + /* If we received an error, we don't want to resubmit the + * Rx URB straight away but to give the USB layer a little + * bit of breathing room. + * We are in the USB thread context, therefore there is a + * danger of recursion (new URB we submit fails, we come + * back here). + * With recent USB stack (2.6.15+), I'm seeing that on + * hot unplug of the dongle... + * Lowest effective timer is 10ms... + * Jean II */ + self->rx_defer_timer.function = irda_usb_rx_defer_expired; + self->rx_defer_timer.data = (unsigned long) urb; + mod_timer(&self->rx_defer_timer, + jiffies + msecs_to_jiffies(10)); + + return; + } + + /* Check for empty frames */ + if (urb->actual_length <= self->header_length) { + net_warn_ratelimited("%s(), empty frame!\n", __func__); + goto done; + } + + /* + * Remember the time we received this frame, so we can + * reduce the min turn time a bit since we will know + * how much time we have used for protocol processing + */ + self->stamp = ktime_get(); + + /* Check if we need to copy the data to a new skb or not. + * For most frames, we use ZeroCopy and pass the already + * allocated skb up the stack. + * If the frame is small, it is more efficient to copy it + * to save memory (copy will be fast anyway - that's + * called Rx-copy-break). Jean II */ + docopy = (urb->actual_length < IRDA_RX_COPY_THRESHOLD); + + /* Allocate a new skb */ + if (self->capability & IUC_STIR421X) + newskb = dev_alloc_skb(docopy ? urb->actual_length : + IRDA_SKB_MAX_MTU + + USB_IRDA_STIR421X_HEADER); + else + newskb = dev_alloc_skb(docopy ? urb->actual_length : + IRDA_SKB_MAX_MTU); + + if (!newskb) { + self->netdev->stats.rx_dropped++; + /* We could deliver the current skb, but this would stall + * the Rx path. Better drop the packet... Jean II */ + goto done; + } + + /* Make sure IP header get aligned (IrDA header is 5 bytes) */ + /* But IrDA-USB header is 1 byte. Jean II */ + //skb_reserve(newskb, USB_IRDA_HEADER - 1); + + if(docopy) { + /* Copy packet, so we can recycle the original */ + skb_copy_from_linear_data(skb, newskb->data, urb->actual_length); + /* Deliver this new skb */ + dataskb = newskb; + /* And hook the old skb to the URB + * Note : we don't need to "clean up" the old skb, + * as we never touched it. Jean II */ + } else { + /* We are using ZeroCopy. Deliver old skb */ + dataskb = skb; + /* And hook the new skb to the URB */ + skb = newskb; + } + + /* Set proper length on skb & remove USB-IrDA header */ + skb_put(dataskb, urb->actual_length); + skb_pull(dataskb, self->header_length); + + /* Ask the networking layer to queue the packet for the IrDA stack */ + dataskb->dev = self->netdev; + skb_reset_mac_header(dataskb); + dataskb->protocol = htons(ETH_P_IRDA); + len = dataskb->len; + netif_rx(dataskb); + + /* Keep stats up to date */ + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; + +done: + /* Note : at this point, the URB we've just received (urb) + * is still referenced by the USB layer. For example, if we + * have received a -ECONNRESET, uhci_cleanup_unlink() will + * continue to process it (in fact, cleaning it up). + * If we were to submit this URB, disaster would ensue. + * Therefore, we submit our idle URB, and put this URB in our + * idle slot.... + * Jean II */ + /* Note : with this scheme, we could submit the idle URB before + * processing the Rx URB. I don't think it would buy us anything as + * we are running in the USB thread context. Jean II */ + next_urb = self->idle_rx_urb; + + /* Recycle Rx URB : Now, the idle URB is the present one */ + urb->context = NULL; + self->idle_rx_urb = urb; + + /* Submit the idle URB to replace the URB we've just received. + * Do it last to avoid race conditions... Jean II */ + irda_usb_submit(self, skb, next_urb); +} + +/*------------------------------------------------------------------*/ +/* + * In case of errors, we want the USB layer to have time to recover. + * Now, it is time to resubmit ouur Rx URB... + */ +static void irda_usb_rx_defer_expired(unsigned long data) +{ + struct urb *urb = (struct urb *) data; + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct irda_usb_cb *self; + struct irda_skb_cb *cb; + struct urb *next_urb; + + /* Find ourselves */ + cb = (struct irda_skb_cb *) skb->cb; + IRDA_ASSERT(cb != NULL, return;); + self = (struct irda_usb_cb *) cb->context; + IRDA_ASSERT(self != NULL, return;); + + /* Same stuff as when Rx is done, see above... */ + next_urb = self->idle_rx_urb; + urb->context = NULL; + self->idle_rx_urb = urb; + irda_usb_submit(self, skb, next_urb); +} + +/*------------------------------------------------------------------*/ +/* + * Callbak from IrDA layer. IrDA wants to know if we have + * started receiving anything. + */ +static int irda_usb_is_receiving(struct irda_usb_cb *self) +{ + /* Note : because of the way UHCI works, it's almost impossible + * to get this info. The Controller DMA directly to memory and + * signal only when the whole frame is finished. To know if the + * first TD of the URB has been filled or not seems hard work... + * + * The other solution would be to use the "receiving" command + * on the default decriptor with a usb_control_msg(), but that + * would add USB traffic and would return result only in the + * next USB frame (~1ms). + * + * I've been told that current dongles send status info on their + * interrupt endpoint, and that's what the Windows driver uses + * to know this info. Unfortunately, this is not yet in the spec... + * + * Jean II + */ + + return 0; /* For now */ +} + +#define STIR421X_PATCH_PRODUCT_VER "Product Version: " +#define STIR421X_PATCH_STMP_TAG "STMP" +#define STIR421X_PATCH_CODE_OFFSET 512 /* patch image starts before here */ +/* marks end of patch file header (PC DOS text file EOF character) */ +#define STIR421X_PATCH_END_OF_HDR_TAG 0x1A +#define STIR421X_PATCH_BLOCK_SIZE 1023 + +/* + * Function stir421x_fwupload (struct irda_usb_cb *self, + * unsigned char *patch, + * const unsigned int patch_len) + * + * Upload firmware code to SigmaTel 421X IRDA-USB dongle + */ +static int stir421x_fw_upload(struct irda_usb_cb *self, + const unsigned char *patch, + const unsigned int patch_len) +{ + int ret = -ENOMEM; + int actual_len = 0; + unsigned int i; + unsigned int block_size = 0; + unsigned char *patch_block; + + patch_block = kzalloc(STIR421X_PATCH_BLOCK_SIZE, GFP_KERNEL); + if (patch_block == NULL) + return -ENOMEM; + + /* break up patch into 1023-byte sections */ + for (i = 0; i < patch_len; i += block_size) { + block_size = patch_len - i; + + if (block_size > STIR421X_PATCH_BLOCK_SIZE) + block_size = STIR421X_PATCH_BLOCK_SIZE; + + /* upload the patch section */ + memcpy(patch_block, patch + i, block_size); + + ret = usb_bulk_msg(self->usbdev, + usb_sndbulkpipe(self->usbdev, + self->bulk_out_ep), + patch_block, block_size, + &actual_len, msecs_to_jiffies(500)); + pr_debug("%s(): Bulk send %u bytes, ret=%d\n", + __func__, actual_len, ret); + + if (ret < 0) + break; + + mdelay(10); + } + + kfree(patch_block); + + return ret; + } + +/* + * Function stir421x_patch_device(struct irda_usb_cb *self) + * + * Get a firmware code from userspase using hotplug request_firmware() call + */ +static int stir421x_patch_device(struct irda_usb_cb *self) +{ + unsigned int i; + int ret; + char stir421x_fw_name[12]; + const struct firmware *fw; + const unsigned char *fw_version_ptr; /* pointer to version string */ + unsigned long fw_version = 0; + + /* + * Known firmware patch file names for STIR421x dongles + * are "42101001.sb" or "42101002.sb" + */ + sprintf(stir421x_fw_name, "4210%4X.sb", + le16_to_cpu(self->usbdev->descriptor.bcdDevice)); + ret = request_firmware(&fw, stir421x_fw_name, &self->usbdev->dev); + if (ret < 0) + return ret; + + /* We get a patch from userspace */ + net_info_ratelimited("%s(): Received firmware %s (%zu bytes)\n", + __func__, stir421x_fw_name, fw->size); + + ret = -EINVAL; + + /* Get the bcd product version */ + if (!memcmp(fw->data, STIR421X_PATCH_PRODUCT_VER, + sizeof(STIR421X_PATCH_PRODUCT_VER) - 1)) { + fw_version_ptr = fw->data + + sizeof(STIR421X_PATCH_PRODUCT_VER) - 1; + + /* Let's check if the product version is dotted */ + if (fw_version_ptr[3] == '.' && + fw_version_ptr[7] == '.') { + unsigned long major, minor, build; + major = simple_strtoul(fw_version_ptr, NULL, 10); + minor = simple_strtoul(fw_version_ptr + 4, NULL, 10); + build = simple_strtoul(fw_version_ptr + 8, NULL, 10); + + fw_version = (major << 12) + + (minor << 8) + + ((build / 10) << 4) + + (build % 10); + + pr_debug("%s(): Firmware Product version %ld\n", + __func__, fw_version); + } + } + + if (self->usbdev->descriptor.bcdDevice == cpu_to_le16(fw_version)) { + /* + * If we're here, we've found a correct patch + * The actual image starts after the "STMP" keyword + * so forward to the firmware header tag + */ + for (i = 0; i < fw->size && fw->data[i] != + STIR421X_PATCH_END_OF_HDR_TAG; i++) ; + /* here we check for the out of buffer case */ + if (i < STIR421X_PATCH_CODE_OFFSET && i < fw->size && + STIR421X_PATCH_END_OF_HDR_TAG == fw->data[i]) { + if (!memcmp(fw->data + i + 1, STIR421X_PATCH_STMP_TAG, + sizeof(STIR421X_PATCH_STMP_TAG) - 1)) { + + /* We can upload the patch to the target */ + i += sizeof(STIR421X_PATCH_STMP_TAG); + ret = stir421x_fw_upload(self, &fw->data[i], + fw->size - i); + } + } + } + + release_firmware(fw); + + return ret; +} + + +/********************** IRDA DEVICE CALLBACKS **********************/ +/* + * Main calls from the IrDA/Network subsystem. + * Mostly registering a new irda-usb device and removing it.... + * We only deal with the IrDA side of the business, the USB side will + * be dealt with below... + */ + + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + * + * Note : don't mess with self->netopen - Jean II + */ +static int irda_usb_net_open(struct net_device *netdev) +{ + struct irda_usb_cb *self; + unsigned long flags; + char hwname[16]; + int i; + + IRDA_ASSERT(netdev != NULL, return -1;); + self = netdev_priv(netdev); + IRDA_ASSERT(self != NULL, return -1;); + + spin_lock_irqsave(&self->lock, flags); + /* Can only open the device if it's there */ + if(!self->present) { + spin_unlock_irqrestore(&self->lock, flags); + net_warn_ratelimited("%s(), device not present!\n", __func__); + return -1; + } + + if(self->needspatch) { + spin_unlock_irqrestore(&self->lock, flags); + net_warn_ratelimited("%s(), device needs patch\n", __func__); + return -EIO ; + } + + /* Initialise default speed and xbofs value + * (IrLAP will change that soon) */ + self->speed = -1; + self->xbofs = -1; + self->new_speed = -1; + self->new_xbofs = -1; + + /* To do *before* submitting Rx urbs and starting net Tx queue + * Jean II */ + self->netopen = 1; + spin_unlock_irqrestore(&self->lock, flags); + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + * Note : will send immediately a speed change... + */ + sprintf(hwname, "usb#%d", self->usbdev->devnum); + self->irlap = irlap_open(netdev, &self->qos, hwname); + IRDA_ASSERT(self->irlap != NULL, return -1;); + + /* Allow IrLAP to send data to us */ + netif_start_queue(netdev); + + /* We submit all the Rx URB except for one that we keep idle. + * Need to be initialised before submitting other USBs, because + * in some cases as soon as we submit the URBs the USB layer + * will trigger a dummy receive - Jean II */ + self->idle_rx_urb = self->rx_urb[IU_MAX_ACTIVE_RX_URBS]; + self->idle_rx_urb->context = NULL; + + /* Now that we can pass data to IrLAP, allow the USB layer + * to send us some data... */ + for (i = 0; i < IU_MAX_ACTIVE_RX_URBS; i++) { + struct sk_buff *skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!skb) { + /* If this ever happen, we are in deep s***. + * Basically, we can't start the Rx path... */ + return -1; + } + //skb_reserve(newskb, USB_IRDA_HEADER - 1); + irda_usb_submit(self, skb, self->rx_urb[i]); + } + + /* Ready to play !!! */ + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_net_close (self) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int irda_usb_net_close(struct net_device *netdev) +{ + struct irda_usb_cb *self; + int i; + + IRDA_ASSERT(netdev != NULL, return -1;); + self = netdev_priv(netdev); + IRDA_ASSERT(self != NULL, return -1;); + + /* Clear this flag *before* unlinking the urbs and *before* + * stopping the network Tx queue - Jean II */ + self->netopen = 0; + + /* Stop network Tx queue */ + netif_stop_queue(netdev); + + /* Kill defered Rx URB */ + del_timer(&self->rx_defer_timer); + + /* Deallocate all the Rx path buffers (URBs and skb) */ + for (i = 0; i < self->max_rx_urb; i++) { + struct urb *urb = self->rx_urb[i]; + struct sk_buff *skb = (struct sk_buff *) urb->context; + /* Cancel the receive command */ + usb_kill_urb(urb); + /* The skb is ours, free it */ + if(skb) { + dev_kfree_skb(skb); + urb->context = NULL; + } + } + /* Cancel Tx and speed URB - need to be synchronous to avoid races */ + usb_kill_urb(self->tx_urb); + usb_kill_urb(self->speed_urb); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + unsigned long flags; + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct irda_usb_cb *self; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return -1;); + + pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + /* Protect us from USB callbacks, net watchdog and else. */ + spin_lock_irqsave(&self->lock, flags); + /* Check if the device is still there */ + if(self->present) { + /* Set the desired speed */ + self->new_speed = irq->ifr_baudrate; + irda_usb_change_speed_xbofs(self); + } + spin_unlock_irqrestore(&self->lock, flags); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + /* Check if the IrDA stack is still there */ + if(self->netopen) + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = irda_usb_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/*------------------------------------------------------------------*/ + +/********************* IRDA CONFIG SUBROUTINES *********************/ +/* + * Various subroutines dealing with IrDA and network stuff we use to + * configure and initialise each irda-usb instance. + * These functions are used below in the main calls of the driver... + */ + +/*------------------------------------------------------------------*/ +/* + * Set proper values in the IrDA QOS structure + */ +static inline void irda_usb_init_qos(struct irda_usb_cb *self) +{ + struct irda_class_desc *desc; + + + desc = self->irda_desc; + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* See spec section 7.2 for meaning. + * Values are little endian (as most USB stuff), the IrDA stack + * use it in native order (see parameters.c). - Jean II */ + self->qos.baud_rate.bits = le16_to_cpu(desc->wBaudRate); + self->qos.min_turn_time.bits = desc->bmMinTurnaroundTime; + self->qos.additional_bofs.bits = desc->bmAdditionalBOFs; + self->qos.window_size.bits = desc->bmWindowSize; + self->qos.data_size.bits = desc->bmDataSize; + + pr_debug("%s(), dongle says speed=0x%X, size=0x%X, window=0x%X, bofs=0x%X, turn=0x%X\n", + __func__, self->qos.baud_rate.bits, self->qos.data_size.bits, + self->qos.window_size.bits, self->qos.additional_bofs.bits, + self->qos.min_turn_time.bits); + + /* Don't always trust what the dongle tell us */ + if(self->capability & IUC_SIR_ONLY) + self->qos.baud_rate.bits &= 0x00ff; + if(self->capability & IUC_SMALL_PKT) + self->qos.data_size.bits = 0x07; + if(self->capability & IUC_NO_WINDOW) + self->qos.window_size.bits = 0x01; + if(self->capability & IUC_MAX_WINDOW) + self->qos.window_size.bits = 0x7f; + if(self->capability & IUC_MAX_XBOFS) + self->qos.additional_bofs.bits = 0x01; + +#if 1 + /* Module parameter can override the rx window size */ + if (qos_mtt_bits) + self->qos.min_turn_time.bits = qos_mtt_bits; +#endif + /* + * Note : most of those values apply only for the receive path, + * the transmit path will be set differently - Jean II + */ + irda_qos_bits_to_value(&self->qos); +} + +/*------------------------------------------------------------------*/ +static const struct net_device_ops irda_usb_netdev_ops = { + .ndo_open = irda_usb_net_open, + .ndo_stop = irda_usb_net_close, + .ndo_do_ioctl = irda_usb_net_ioctl, + .ndo_start_xmit = irda_usb_hard_xmit, + .ndo_tx_timeout = irda_usb_net_timeout, +}; + +/* + * Initialise the network side of the irda-usb instance + * Called when a new USB instance is registered in irda_usb_probe() + */ +static inline int irda_usb_open(struct irda_usb_cb *self) +{ + struct net_device *netdev = self->netdev; + + netdev->netdev_ops = &irda_usb_netdev_ops; + + irda_usb_init_qos(self); + + return register_netdev(netdev); +} + +/*------------------------------------------------------------------*/ +/* + * Cleanup the network side of the irda-usb instance + * Called when a USB instance is removed in irda_usb_disconnect() + */ +static inline void irda_usb_close(struct irda_usb_cb *self) +{ + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Remove the speed buffer */ + kfree(self->speed_buff); + self->speed_buff = NULL; + + kfree(self->tx_buff); + self->tx_buff = NULL; +} + +/********************** USB CONFIG SUBROUTINES **********************/ +/* + * Various subroutines dealing with USB stuff we use to configure and + * initialise each irda-usb instance. + * These functions are used below in the main calls of the driver... + */ + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_parse_endpoints(dev, ifnum) + * + * Parse the various endpoints and find the one we need. + * + * The endpoint are the pipes used to communicate with the USB device. + * The spec defines 2 endpoints of type bulk transfer, one in, and one out. + * These are used to pass frames back and forth with the dongle. + * Most dongle have also an interrupt endpoint, that will be probably + * documented in the next spec... + */ +static inline int irda_usb_parse_endpoints(struct irda_usb_cb *self, struct usb_host_endpoint *endpoint, int ennum) +{ + int i; /* Endpoint index in table */ + + /* Init : no endpoints */ + self->bulk_in_ep = 0; + self->bulk_out_ep = 0; + self->bulk_int_ep = 0; + + /* Let's look at all those endpoints */ + for(i = 0; i < ennum; i++) { + /* All those variables will get optimised by the compiler, + * so let's aim for clarity... - Jean II */ + __u8 ep; /* Endpoint address */ + __u8 dir; /* Endpoint direction */ + __u8 attr; /* Endpoint attribute */ + __u16 psize; /* Endpoint max packet size in bytes */ + + /* Get endpoint address, direction and attribute */ + ep = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + dir = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK; + attr = endpoint[i].desc.bmAttributes; + psize = le16_to_cpu(endpoint[i].desc.wMaxPacketSize); + + /* Is it a bulk endpoint ??? */ + if(attr == USB_ENDPOINT_XFER_BULK) { + /* We need to find an IN and an OUT */ + if(dir == USB_DIR_IN) { + /* This is our Rx endpoint */ + self->bulk_in_ep = ep; + } else { + /* This is our Tx endpoint */ + self->bulk_out_ep = ep; + self->bulk_out_mtu = psize; + } + } else { + if((attr == USB_ENDPOINT_XFER_INT) && + (dir == USB_DIR_IN)) { + /* This is our interrupt endpoint */ + self->bulk_int_ep = ep; + } else { + net_err_ratelimited("%s(), Unrecognised endpoint %02X\n", + __func__, ep); + } + } + } + + pr_debug("%s(), And our endpoints are : in=%02X, out=%02X (%d), int=%02X\n", + __func__, self->bulk_in_ep, self->bulk_out_ep, + self->bulk_out_mtu, self->bulk_int_ep); + + return (self->bulk_in_ep != 0) && (self->bulk_out_ep != 0); +} + +#ifdef IU_DUMP_CLASS_DESC +/*------------------------------------------------------------------*/ +/* + * Function usb_irda_dump_class_desc(desc) + * + * Prints out the contents of the IrDA class descriptor + * + */ +static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) +{ + /* Values are little endian */ + printk("bLength=%x\n", desc->bLength); + printk("bDescriptorType=%x\n", desc->bDescriptorType); + printk("bcdSpecRevision=%x\n", le16_to_cpu(desc->bcdSpecRevision)); + printk("bmDataSize=%x\n", desc->bmDataSize); + printk("bmWindowSize=%x\n", desc->bmWindowSize); + printk("bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime); + printk("wBaudRate=%x\n", le16_to_cpu(desc->wBaudRate)); + printk("bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs); + printk("bIrdaRateSniff=%x\n", desc->bIrdaRateSniff); + printk("bMaxUnicastList=%x\n", desc->bMaxUnicastList); +} +#endif /* IU_DUMP_CLASS_DESC */ + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_find_class_desc(intf) + * + * Returns instance of IrDA class descriptor, or NULL if not found + * + * The class descriptor is some extra info that IrDA USB devices will + * offer to us, describing their IrDA characteristics. We will use that in + * irda_usb_init_qos() + */ +static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf) +{ + struct usb_device *dev = interface_to_usbdev (intf); + struct irda_class_desc *desc; + int ret; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return NULL; + + /* USB-IrDA class spec 1.0: + * 6.1.3: Standard "Get Descriptor" Device Request is not + * appropriate to retrieve class-specific descriptor + * 6.2.5: Class Specific "Get Class Descriptor" Interface Request + * is mandatory and returns the USB-IrDA class descriptor + */ + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), + IU_REQ_GET_CLASS_DESC, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, intf->altsetting->desc.bInterfaceNumber, desc, + sizeof(*desc), 500); + + pr_debug("%s(), ret=%d\n", __func__, ret); + if (ret < sizeof(*desc)) { + net_warn_ratelimited("usb-irda: class_descriptor read %s (%d)\n", + ret < 0 ? "failed" : "too short", ret); + } + else if (desc->bDescriptorType != USB_DT_IRDA) { + net_warn_ratelimited("usb-irda: bad class_descriptor type\n"); + } + else { +#ifdef IU_DUMP_CLASS_DESC + irda_usb_dump_class_desc(desc); +#endif /* IU_DUMP_CLASS_DESC */ + + return desc; + } + kfree(desc); + return NULL; +} + +/*********************** USB DEVICE CALLBACKS ***********************/ +/* + * Main calls from the USB subsystem. + * Mostly registering a new irda-usb device and removing it.... + */ + +/*------------------------------------------------------------------*/ +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + * The USB layer protect us from reentrancy (via BKL), so we don't need + * to spinlock in there... Jean II + */ +static int irda_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *net; + struct usb_device *dev = interface_to_usbdev(intf); + struct irda_usb_cb *self; + struct usb_host_interface *interface; + struct irda_class_desc *irda_desc; + int ret = -ENOMEM; + int i; /* Driver instance index / Rx URB index */ + + /* Note : the probe make sure to call us only for devices that + * matches the list of dongle (top of the file). So, we + * don't need to check if the dongle is really ours. + * Jean II */ + + net_info_ratelimited("IRDA-USB found at address %d, Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + net = alloc_irdadev(sizeof(*self)); + if (!net) + goto err_out; + + SET_NETDEV_DEV(net, &intf->dev); + self = netdev_priv(net); + self->netdev = net; + spin_lock_init(&self->lock); + init_timer(&self->rx_defer_timer); + + self->capability = id->driver_info; + self->needspatch = ((self->capability & IUC_STIR421X) != 0); + + /* Create all of the needed urbs */ + if (self->capability & IUC_STIR421X) { + self->max_rx_urb = IU_SIGMATEL_MAX_RX_URBS; + self->header_length = USB_IRDA_STIR421X_HEADER; + } else { + self->max_rx_urb = IU_MAX_RX_URBS; + self->header_length = USB_IRDA_HEADER; + } + + self->rx_urb = kcalloc(self->max_rx_urb, sizeof(struct urb *), + GFP_KERNEL); + if (!self->rx_urb) + goto err_free_net; + + for (i = 0; i < self->max_rx_urb; i++) { + self->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!self->rx_urb[i]) { + goto err_out_1; + } + } + self->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!self->tx_urb) { + goto err_out_1; + } + self->speed_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!self->speed_urb) { + goto err_out_2; + } + + /* Is this really necessary? (no, except maybe for broken devices) */ + if (usb_reset_configuration (dev) < 0) { + dev_err(&intf->dev, "reset_configuration failed\n"); + ret = -EIO; + goto err_out_3; + } + + /* Is this really necessary? */ + /* Note : some driver do hardcode the interface number, some others + * specify an alternate, but very few driver do like this. + * Jean II */ + ret = usb_set_interface(dev, intf->altsetting->desc.bInterfaceNumber, 0); + pr_debug("usb-irda: set interface %d result %d\n", + intf->altsetting->desc.bInterfaceNumber, ret); + switch (ret) { + case 0: + break; + case -EPIPE: /* -EPIPE = -32 */ + /* Martin Diehl says if we get a -EPIPE we should + * be fine and we don't need to do a usb_clear_halt(). + * - Jean II */ + pr_debug("%s(), Received -EPIPE, ignoring...\n", + __func__); + break; + default: + pr_debug("%s(), Unknown error %d\n", __func__, ret); + ret = -EIO; + goto err_out_3; + } + + /* Find our endpoints */ + interface = intf->cur_altsetting; + if(!irda_usb_parse_endpoints(self, interface->endpoint, + interface->desc.bNumEndpoints)) { + net_err_ratelimited("%s(), Bogus endpoints...\n", __func__); + ret = -EIO; + goto err_out_3; + } + + self->usbdev = dev; + + /* Find IrDA class descriptor */ + irda_desc = irda_usb_find_class_desc(intf); + ret = -ENODEV; + if (!irda_desc) + goto err_out_3; + + if (self->needspatch) { + ret = usb_control_msg (self->usbdev, usb_sndctrlpipe (self->usbdev, 0), + 0x02, 0x40, 0, 0, NULL, 0, 500); + if (ret < 0) { + pr_debug("usb_control_msg failed %d\n", ret); + goto err_out_3; + } else { + mdelay(10); + } + } + + self->irda_desc = irda_desc; + self->present = 1; + self->netopen = 0; + self->usbintf = intf; + + /* Allocate the buffer for speed changes */ + /* Don't change this buffer size and allocation without doing + * some heavy and complete testing. Don't ask why :-( + * Jean II */ + ret = -ENOMEM; + self->speed_buff = kzalloc(IRDA_USB_SPEED_MTU, GFP_KERNEL); + if (!self->speed_buff) + goto err_out_3; + + self->tx_buff = kzalloc(IRDA_SKB_MAX_MTU + self->header_length, + GFP_KERNEL); + if (!self->tx_buff) + goto err_out_4; + + ret = irda_usb_open(self); + if (ret) + goto err_out_5; + + net_info_ratelimited("IrDA: Registered device %s\n", net->name); + usb_set_intfdata(intf, self); + + if (self->needspatch) { + /* Now we fetch and upload the firmware patch */ + ret = stir421x_patch_device(self); + self->needspatch = (ret < 0); + if (self->needspatch) { + net_err_ratelimited("STIR421X: Couldn't upload patch\n"); + goto err_out_6; + } + + /* replace IrDA class descriptor with what patched device is now reporting */ + irda_desc = irda_usb_find_class_desc (self->usbintf); + if (!irda_desc) { + ret = -ENODEV; + goto err_out_6; + } + kfree(self->irda_desc); + self->irda_desc = irda_desc; + irda_usb_init_qos(self); + } + + return 0; +err_out_6: + unregister_netdev(self->netdev); +err_out_5: + kfree(self->tx_buff); +err_out_4: + kfree(self->speed_buff); +err_out_3: + /* Free all urbs that we may have created */ + usb_free_urb(self->speed_urb); +err_out_2: + usb_free_urb(self->tx_urb); +err_out_1: + for (i = 0; i < self->max_rx_urb; i++) + usb_free_urb(self->rx_urb[i]); + kfree(self->rx_urb); +err_free_net: + free_netdev(net); +err_out: + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * The current irda-usb device is removed, the USB layer tell us + * to shut it down... + * One of the constraints is that when we exit this function, + * we cannot use the usb_device no more. Gone. Destroyed. kfree(). + * Most other subsystem allow you to destroy the instance at a time + * when it's convenient to you, to postpone it to a later date, but + * not the USB subsystem. + * So, we must make bloody sure that everything gets deactivated. + * Jean II + */ +static void irda_usb_disconnect(struct usb_interface *intf) +{ + unsigned long flags; + struct irda_usb_cb *self = usb_get_intfdata(intf); + int i; + + usb_set_intfdata(intf, NULL); + if (!self) + return; + + /* Make sure that the Tx path is not executing. - Jean II */ + spin_lock_irqsave(&self->lock, flags); + + /* Oups ! We are not there any more. + * This will stop/desactivate the Tx path. - Jean II */ + self->present = 0; + + /* Kill defered Rx URB */ + del_timer(&self->rx_defer_timer); + + /* We need to have irq enabled to unlink the URBs. That's OK, + * at this point the Tx path is gone - Jean II */ + spin_unlock_irqrestore(&self->lock, flags); + + /* Hum... Check if networking is still active (avoid races) */ + if((self->netopen) || (self->irlap)) { + /* Accept no more transmissions */ + /*netif_device_detach(self->netdev);*/ + netif_stop_queue(self->netdev); + /* Stop all the receive URBs. Must be synchronous. */ + for (i = 0; i < self->max_rx_urb; i++) + usb_kill_urb(self->rx_urb[i]); + /* Cancel Tx and speed URB. + * Make sure it's synchronous to avoid races. */ + usb_kill_urb(self->tx_urb); + usb_kill_urb(self->speed_urb); + } + + /* Cleanup the device stuff */ + irda_usb_close(self); + /* No longer attached to USB bus */ + self->usbdev = NULL; + self->usbintf = NULL; + + /* Clean up our urbs */ + for (i = 0; i < self->max_rx_urb; i++) + usb_free_urb(self->rx_urb[i]); + kfree(self->rx_urb); + /* Clean up Tx and speed URB */ + usb_free_urb(self->tx_urb); + usb_free_urb(self->speed_urb); + + /* Free self and network device */ + free_netdev(self->netdev); + pr_debug("%s(), USB IrDA Disconnected\n", __func__); +} + +#ifdef CONFIG_PM +/* USB suspend, so power off the transmitter/receiver */ +static int irda_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct irda_usb_cb *self = usb_get_intfdata(intf); + int i; + + netif_device_detach(self->netdev); + + if (self->tx_urb != NULL) + usb_kill_urb(self->tx_urb); + if (self->speed_urb != NULL) + usb_kill_urb(self->speed_urb); + for (i = 0; i < self->max_rx_urb; i++) { + if (self->rx_urb[i] != NULL) + usb_kill_urb(self->rx_urb[i]); + } + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int irda_usb_resume(struct usb_interface *intf) +{ + struct irda_usb_cb *self = usb_get_intfdata(intf); + int i; + + for (i = 0; i < self->max_rx_urb; i++) { + if (self->rx_urb[i] != NULL) + usb_submit_urb(self->rx_urb[i], GFP_KERNEL); + } + + netif_device_attach(self->netdev); + return 0; +} +#endif + +/*------------------------------------------------------------------*/ +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .name = "irda-usb", + .probe = irda_usb_probe, + .disconnect = irda_usb_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = irda_usb_suspend, + .resume = irda_usb_resume, +#endif +}; + +module_usb_driver(irda_driver); + +/* + * Module parameters + */ +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); +MODULE_AUTHOR("Roman Weissgaerber , Dag Brattli , Jean Tourrilhes and Nick Fedchik "); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/irda-usb.h b/drivers/staging/irda/drivers/irda-usb.h new file mode 100644 index 000000000000..8ac389fa9348 --- /dev/null +++ b/drivers/staging/irda/drivers/irda-usb.h @@ -0,0 +1,174 @@ +/***************************************************************************** + * + * Filename: irda-usb.h + * Version: 0.10 + * Description: IrDA-USB Driver + * Status: Experimental + * Author: Dag Brattli + * + * Copyright (C) 2001, Roman Weissgaerber + * Copyright (C) 2000, Dag Brattli + * Copyright (C) 2001, Jean Tourrilhes + * Copyright (C) 2004, SigmaTel, Inc. + * Copyright (C) 2005, Milan Beno + * Copyright (C) 2006, Nick FEdchik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include + +#include +#include /* struct irlap_cb */ + +#define RX_COPY_THRESHOLD 200 +#define IRDA_USB_MAX_MTU 2051 +#define IRDA_USB_SPEED_MTU 64 /* Weird, but work like this */ + +/* Maximum number of active URB on the Rx path + * This is the amount of buffers the we keep between the USB harware and the + * IrDA stack. + * + * Note : the network layer does also queue the packets between us and the + * IrDA stack, and is actually pretty fast and efficient in doing that. + * Therefore, we don't need to have a large number of URBs, and we can + * perfectly live happy with only one. We certainly don't need to keep the + * full IrTTP window around here... + * I repeat for those who have trouble to understand : 1 URB is plenty + * good enough to handle back-to-back (brickwalled) frames. I tried it, + * it works (it's the hardware that has trouble doing it). + * + * Having 2 URBs would allow the USB stack to process one URB while we take + * care of the other and then swap the URBs... + * On the other hand, increasing the number of URB will have penalities + * in term of latency and will interact with the link management in IrLAP... + * Jean II */ +#define IU_MAX_ACTIVE_RX_URBS 1 /* Don't touch !!! */ + +/* When a Rx URB is passed back to us, we can't reuse it immediately, + * because it may still be referenced by the USB layer. Therefore we + * need to keep one extra URB in the Rx path. + * Jean II */ +#define IU_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + 1) + +/* Various ugly stuff to try to workaround generic problems */ +/* Send speed command in case of timeout, just for trying to get things sane */ +#define IU_BUG_KICK_TIMEOUT +/* Show the USB class descriptor */ +#undef IU_DUMP_CLASS_DESC +/* Assume a minimum round trip latency for USB transfer (in us)... + * USB transfer are done in the next USB slot if there is no traffic + * (1/19 msec) and is done at 12 Mb/s : + * Waiting for slot + tx = (53us + 16us) * 2 = 137us minimum. + * Rx notification will only be done at the end of the USB frame period : + * OHCI : frame period = 1ms + * UHCI : frame period = 1ms, but notification can take 2 or 3 ms :-( + * EHCI : frame period = 125us */ +#define IU_USB_MIN_RTT 500 /* This should be safe in most cases */ + +/* Inbound header */ +#define MEDIA_BUSY 0x80 + +#define SPEED_2400 0x01 +#define SPEED_9600 0x02 +#define SPEED_19200 0x03 +#define SPEED_38400 0x04 +#define SPEED_57600 0x05 +#define SPEED_115200 0x06 +#define SPEED_576000 0x07 +#define SPEED_1152000 0x08 +#define SPEED_4000000 0x09 +#define SPEED_16000000 0x0a + +/* Basic capabilities */ +#define IUC_DEFAULT 0x00 /* Basic device compliant with 1.0 spec */ +/* Main bugs */ +#define IUC_SPEED_BUG 0x01 /* Device doesn't set speed after the frame */ +#define IUC_NO_WINDOW 0x02 /* Device doesn't behave with big Rx window */ +#define IUC_NO_TURN 0x04 /* Device doesn't do turnaround by itself */ +/* Not currently used */ +#define IUC_SIR_ONLY 0x08 /* Device doesn't behave at FIR speeds */ +#define IUC_SMALL_PKT 0x10 /* Device doesn't behave with big Rx packets */ +#define IUC_MAX_WINDOW 0x20 /* Device underestimate the Rx window */ +#define IUC_MAX_XBOFS 0x40 /* Device need more xbofs than advertised */ +#define IUC_STIR421X 0x80 /* SigmaTel 4210/4220/4116 VFIR */ + +/* USB class definitions */ +#define USB_IRDA_HEADER 0x01 +#define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */ +#define USB_DT_IRDA 0x21 +#define USB_IRDA_STIR421X_HEADER 0x03 +#define IU_SIGMATEL_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + \ + USB_IRDA_STIR421X_HEADER) + +struct irda_class_desc { + __u8 bLength; + __u8 bDescriptorType; + __le16 bcdSpecRevision; + __u8 bmDataSize; + __u8 bmWindowSize; + __u8 bmMinTurnaroundTime; + __le16 wBaudRate; + __u8 bmAdditionalBOFs; + __u8 bIrdaRateSniff; + __u8 bMaxUnicastList; +} __packed; + +/* class specific interface request to get the IrDA-USB class descriptor + * (6.2.5, USB-IrDA class spec 1.0) */ + +#define IU_REQ_GET_CLASS_DESC 0x06 +#define STIR421X_MAX_PATCH_DOWNLOAD_SIZE 1023 + +struct irda_usb_cb { + struct irda_class_desc *irda_desc; + struct usb_device *usbdev; /* init: probe_irda */ + struct usb_interface *usbintf; /* init: probe_irda */ + int netopen; /* Device is active for network */ + int present; /* Device is present on the bus */ + __u32 capability; /* Capability of the hardware */ + __u8 bulk_in_ep; /* Rx Endpoint assignments */ + __u8 bulk_out_ep; /* Tx Endpoint assignments */ + __u16 bulk_out_mtu; /* Max Tx packet size in bytes */ + __u8 bulk_int_ep; /* Interrupt Endpoint assignments */ + + __u8 max_rx_urb; + struct urb **rx_urb; /* URBs used to receive data frames */ + struct urb *idle_rx_urb; /* Pointer to idle URB in Rx path */ + struct urb *tx_urb; /* URB used to send data frames */ + struct urb *speed_urb; /* URB used to send speed commands */ + + struct net_device *netdev; /* Yes! we are some kind of netdev. */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct qos_info qos; + char *speed_buff; /* Buffer for speed changes */ + char *tx_buff; + + ktime_t stamp; + + spinlock_t lock; /* For serializing Tx operations */ + + __u16 xbofs; /* Current xbofs setting */ + __s16 new_xbofs; /* xbofs we need to set */ + __u32 speed; /* Current speed */ + __s32 new_speed; /* speed we need to set */ + + __u8 header_length; /* USB-IrDA frame header size */ + int needspatch; /* device needs firmware patch */ + + struct timer_list rx_defer_timer; /* Wait for Rx error to clear */ +}; + diff --git a/drivers/staging/irda/drivers/irtty-sir.c b/drivers/staging/irda/drivers/irtty-sir.c new file mode 100644 index 000000000000..7a20a9a4663a --- /dev/null +++ b/drivers/staging/irda/drivers/irtty-sir.c @@ -0,0 +1,570 @@ +/********************************************************************* + * + * Filename: irtty-sir.c + * Version: 2.0 + * Description: IrDA line discipline implementation + * Status: Experimental. + * Author: Dag Brattli + * Created at: Tue Dec 9 21:18:38 1997 + * Modified at: Sun Oct 27 22:13:30 2002 + * Modified by: Martin Diehl + * Sources: slip.c by Laurence Culhane, + * Fred N. van Kempen, + * + * Copyright (c) 1998-2000 Dag Brattli, + * Copyright (c) 2002 Martin Diehl, + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sir-dev.h" +#include "irtty-sir.h" + +static int qos_mtt_bits = 0x03; /* 5 ms or more */ + +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); + +/* ------------------------------------------------------- */ + +/* device configuration callbacks always invoked with irda-thread context */ + +/* find out, how many chars we have in buffers below us + * this is allowed to lie, i.e. return less chars than we + * actually have. The returned value is used to determine + * how long the irdathread should wait before doing the + * real blocking wait_until_sent() + */ + +static int irtty_chars_in_buffer(struct sir_dev *dev) +{ + struct sirtty_cb *priv = dev->priv; + + IRDA_ASSERT(priv != NULL, return -1;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); + + return tty_chars_in_buffer(priv->tty); +} + +/* Wait (sleep) until underlaying hardware finished transmission + * i.e. hardware buffers are drained + * this must block and not return before all characters are really sent + * + * If the tty sits on top of a 16550A-like uart, there are typically + * up to 16 bytes in the fifo - f.e. 9600 bps 8N1 needs 16.7 msec + * + * With usbserial the uart-fifo is basically replaced by the converter's + * outgoing endpoint buffer, which can usually hold 64 bytes (at least). + * With pl2303 it appears we are safe with 60msec here. + * + * I really wish all serial drivers would provide + * correct implementation of wait_until_sent() + */ + +#define USBSERIAL_TX_DONE_DELAY 60 + +static void irtty_wait_until_sent(struct sir_dev *dev) +{ + struct sirtty_cb *priv = dev->priv; + struct tty_struct *tty; + + IRDA_ASSERT(priv != NULL, return;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); + + tty = priv->tty; + if (tty->ops->wait_until_sent) { + tty->ops->wait_until_sent(tty, msecs_to_jiffies(100)); + } + else { + msleep(USBSERIAL_TX_DONE_DELAY); + } +} + +/* + * Function irtty_change_speed (dev, speed) + * + * Change the speed of the serial port. + * + * This may sleep in set_termios (usbserial driver f.e.) and must + * not be called from interrupt/timer/tasklet therefore. + * All such invocations are deferred to kIrDAd now so we can sleep there. + */ + +static int irtty_change_speed(struct sir_dev *dev, unsigned speed) +{ + struct sirtty_cb *priv = dev->priv; + struct tty_struct *tty; + struct ktermios old_termios; + int cflag; + + IRDA_ASSERT(priv != NULL, return -1;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); + + tty = priv->tty; + + down_write(&tty->termios_rwsem); + old_termios = tty->termios; + cflag = tty->termios.c_cflag; + tty_encode_baud_rate(tty, speed, speed); + if (tty->ops->set_termios) + tty->ops->set_termios(tty, &old_termios); + priv->io.speed = speed; + up_write(&tty->termios_rwsem); + + return 0; +} + +/* + * Function irtty_set_dtr_rts (dev, dtr, rts) + * + * This function can be used by dongles etc. to set or reset the status + * of the dtr and rts lines + */ + +static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) +{ + struct sirtty_cb *priv = dev->priv; + int set = 0; + int clear = 0; + + IRDA_ASSERT(priv != NULL, return -1;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); + + if (rts) + set |= TIOCM_RTS; + else + clear |= TIOCM_RTS; + if (dtr) + set |= TIOCM_DTR; + else + clear |= TIOCM_DTR; + + /* + * We can't use ioctl() because it expects a non-null file structure, + * and we don't have that here. + * This function is not yet defined for all tty driver, so + * let's be careful... Jean II + */ + IRDA_ASSERT(priv->tty->ops->tiocmset != NULL, return -1;); + priv->tty->ops->tiocmset(priv->tty, set, clear); + + return 0; +} + +/* ------------------------------------------------------- */ + +/* called from sir_dev when there is more data to send + * context is either netdev->hard_xmit or some transmit-completion bh + * i.e. we are under spinlock here and must not sleep. + */ + +static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t len) +{ + struct sirtty_cb *priv = dev->priv; + struct tty_struct *tty; + int writelen; + + IRDA_ASSERT(priv != NULL, return -1;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); + + tty = priv->tty; + if (!tty->ops->write) + return 0; + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + writelen = tty_write_room(tty); + if (writelen > len) + writelen = len; + return tty->ops->write(tty, ptr, writelen); +} + +/* ------------------------------------------------------- */ + +/* irda line discipline callbacks */ + +/* + * Function irtty_receive_buf( tty, cp, count) + * + * Handle the 'receiver data ready' interrupt. This function is called + * by the 'tty_io' module in the kernel when a block of IrDA data has + * been received, which can now be decapsulated and delivered for + * further processing + * + * calling context depends on underlying driver and tty->port->low_latency! + * for example (low_latency: 1 / 0): + * serial.c: uart-interrupt / softint + * usbserial: urb-complete-interrupt / softint + */ + +static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct sir_dev *dev; + struct sirtty_cb *priv = tty->disc_data; + int i; + + IRDA_ASSERT(priv != NULL, return;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); + + if (unlikely(count==0)) /* yes, this happens */ + return; + + dev = priv->dev; + if (!dev) { + net_warn_ratelimited("%s(), not ready yet!\n", __func__); + return; + } + + for (i = 0; i < count; i++) { + /* + * Characters received with a parity error, etc? + */ + if (fp && *fp++) { + pr_debug("Framing or parity error!\n"); + sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */ + return; + } + } + + sirdev_receive(dev, cp, count); +} + +/* + * Function irtty_write_wakeup (tty) + * + * Called by the driver when there's room for more data. If we have + * more packets to send, we send them here. + * + */ +static void irtty_write_wakeup(struct tty_struct *tty) +{ + struct sirtty_cb *priv = tty->disc_data; + + IRDA_ASSERT(priv != NULL, return;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); + + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + if (priv->dev) + sirdev_write_complete(priv->dev); +} + +/* ------------------------------------------------------- */ + +/* + * Function irtty_stop_receiver (tty, stop) + * + */ + +static inline void irtty_stop_receiver(struct tty_struct *tty, int stop) +{ + struct ktermios old_termios; + int cflag; + + down_write(&tty->termios_rwsem); + old_termios = tty->termios; + cflag = tty->termios.c_cflag; + + if (stop) + cflag &= ~CREAD; + else + cflag |= CREAD; + + tty->termios.c_cflag = cflag; + if (tty->ops->set_termios) + tty->ops->set_termios(tty, &old_termios); + up_write(&tty->termios_rwsem); +} + +/*****************************************************************/ + +/* serialize ldisc open/close with sir_dev */ +static DEFINE_MUTEX(irtty_mutex); + +/* notifier from sir_dev when irda% device gets opened (ifup) */ + +static int irtty_start_dev(struct sir_dev *dev) +{ + struct sirtty_cb *priv; + struct tty_struct *tty; + + /* serialize with ldisc open/close */ + mutex_lock(&irtty_mutex); + + priv = dev->priv; + if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) { + mutex_unlock(&irtty_mutex); + return -ESTALE; + } + + tty = priv->tty; + + if (tty->ops->start) + tty->ops->start(tty); + /* Make sure we can receive more data */ + irtty_stop_receiver(tty, FALSE); + + mutex_unlock(&irtty_mutex); + return 0; +} + +/* notifier from sir_dev when irda% device gets closed (ifdown) */ + +static int irtty_stop_dev(struct sir_dev *dev) +{ + struct sirtty_cb *priv; + struct tty_struct *tty; + + /* serialize with ldisc open/close */ + mutex_lock(&irtty_mutex); + + priv = dev->priv; + if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) { + mutex_unlock(&irtty_mutex); + return -ESTALE; + } + + tty = priv->tty; + + /* Make sure we don't receive more data */ + irtty_stop_receiver(tty, TRUE); + if (tty->ops->stop) + tty->ops->stop(tty); + + mutex_unlock(&irtty_mutex); + + return 0; +} + +/* ------------------------------------------------------- */ + +static struct sir_driver sir_tty_drv = { + .owner = THIS_MODULE, + .driver_name = "sir_tty", + .start_dev = irtty_start_dev, + .stop_dev = irtty_stop_dev, + .do_write = irtty_do_write, + .chars_in_buffer = irtty_chars_in_buffer, + .wait_until_sent = irtty_wait_until_sent, + .set_speed = irtty_change_speed, + .set_dtr_rts = irtty_set_dtr_rts, +}; + +/* ------------------------------------------------------- */ + +/* + * Function irtty_ioctl (tty, file, cmd, arg) + * + * The Swiss army knife of system calls :-) + * + */ +static int irtty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct irtty_info { char name[6]; } info; + struct sir_dev *dev; + struct sirtty_cb *priv = tty->disc_data; + int err = 0; + + IRDA_ASSERT(priv != NULL, return -ENODEV;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EBADR;); + + pr_debug("%s(cmd=0x%X)\n", __func__, cmd); + + dev = priv->dev; + IRDA_ASSERT(dev != NULL, return -1;); + + switch (cmd) { + case IRTTY_IOCTDONGLE: + /* this call blocks for completion */ + err = sirdev_set_dongle(dev, (IRDA_DONGLE) arg); + break; + + case IRTTY_IOCGET: + IRDA_ASSERT(dev->netdev != NULL, return -1;); + + memset(&info, 0, sizeof(info)); + strncpy(info.name, dev->netdev->name, sizeof(info.name)-1); + + if (copy_to_user((void __user *)arg, &info, sizeof(info))) + err = -EFAULT; + break; + default: + err = tty_mode_ioctl(tty, file, cmd, arg); + break; + } + return err; +} + + +/* + * Function irtty_open(tty) + * + * This function is called by the TTY module when the IrDA line + * discipline is called for. Because we are sure the tty line exists, + * we only have to link it to a free IrDA channel. + */ +static int irtty_open(struct tty_struct *tty) +{ + struct sir_dev *dev; + struct sirtty_cb *priv; + int ret = 0; + + /* Module stuff handled via irda_ldisc.owner - Jean II */ + + /* stop the underlying driver */ + irtty_stop_receiver(tty, TRUE); + if (tty->ops->stop) + tty->ops->stop(tty); + + tty_driver_flush_buffer(tty); + + /* apply mtt override */ + sir_tty_drv.qos_mtt_bits = qos_mtt_bits; + + /* get a sir device instance for this driver */ + dev = sirdev_get_instance(&sir_tty_drv, tty->name); + if (!dev) { + ret = -ENODEV; + goto out; + } + + /* allocate private device info block */ + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto out_put; + } + + priv->magic = IRTTY_MAGIC; + priv->tty = tty; + priv->dev = dev; + + /* serialize with start_dev - in case we were racing with ifup */ + mutex_lock(&irtty_mutex); + + dev->priv = priv; + tty->disc_data = priv; + tty->receive_room = 65536; + + mutex_unlock(&irtty_mutex); + + pr_debug("%s - %s: irda line discipline opened\n", __func__, tty->name); + + return 0; + +out_put: + sirdev_put_instance(dev); +out: + return ret; +} + +/* + * Function irtty_close (tty) + * + * Close down a IrDA channel. This means flushing out any pending queues, + * and then restoring the TTY line discipline to what it was before it got + * hooked to IrDA (which usually is TTY again). + */ +static void irtty_close(struct tty_struct *tty) +{ + struct sirtty_cb *priv = tty->disc_data; + + IRDA_ASSERT(priv != NULL, return;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); + + /* Hm, with a dongle attached the dongle driver wants + * to close the dongle - which requires the use of + * some tty write and/or termios or ioctl operations. + * Are we allowed to call those when already requested + * to shutdown the ldisc? + * If not, we should somehow mark the dev being staled. + * Question remains, how to close the dongle in this case... + * For now let's assume we are granted to issue tty driver calls + * until we return here from the ldisc close. I'm just wondering + * how this behaves with hotpluggable serial hardware like + * rs232-pcmcia card or usb-serial... + * + * priv->tty = NULL?; + */ + + /* we are dead now */ + tty->disc_data = NULL; + + sirdev_put_instance(priv->dev); + + /* Stop tty */ + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + if (tty->ops->stop) + tty->ops->stop(tty); + + kfree(priv); + + pr_debug("%s - %s: irda line discipline closed\n", __func__, tty->name); +} + +/* ------------------------------------------------------- */ + +static struct tty_ldisc_ops irda_ldisc = { + .magic = TTY_LDISC_MAGIC, + .name = "irda", + .flags = 0, + .open = irtty_open, + .close = irtty_close, + .read = NULL, + .write = NULL, + .ioctl = irtty_ioctl, + .poll = NULL, + .receive_buf = irtty_receive_buf, + .write_wakeup = irtty_write_wakeup, + .owner = THIS_MODULE, +}; + +/* ------------------------------------------------------- */ + +static int __init irtty_sir_init(void) +{ + int err; + + if ((err = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0) + net_err_ratelimited("IrDA: can't register line discipline (err = %d)\n", + err); + return err; +} + +static void __exit irtty_sir_cleanup(void) +{ + int err; + + if ((err = tty_unregister_ldisc(N_IRDA))) { + net_err_ratelimited("%s(), can't unregister line discipline (err = %d)\n", + __func__, err); + } +} + +module_init(irtty_sir_init); +module_exit(irtty_sir_cleanup); + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("IrDA TTY device driver"); +MODULE_ALIAS_LDISC(N_IRDA); +MODULE_LICENSE("GPL"); + diff --git a/drivers/staging/irda/drivers/irtty-sir.h b/drivers/staging/irda/drivers/irtty-sir.h new file mode 100644 index 000000000000..b132d8f6eb13 --- /dev/null +++ b/drivers/staging/irda/drivers/irtty-sir.h @@ -0,0 +1,34 @@ +/********************************************************************* + * + * sir_tty.h: definitions for the irtty_sir client driver (former irtty) + * + * Copyright (c) 2002 Martin Diehl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + ********************************************************************/ + +#ifndef IRTTYSIR_H +#define IRTTYSIR_H + +#include +#include // chipio_t + +#define IRTTY_IOC_MAGIC 'e' +#define IRTTY_IOCTDONGLE _IO(IRTTY_IOC_MAGIC, 1) +#define IRTTY_IOCGET _IOR(IRTTY_IOC_MAGIC, 2, struct irtty_info) +#define IRTTY_IOC_MAXNR 2 + +struct sirtty_cb { + magic_t magic; + + struct sir_dev *dev; + struct tty_struct *tty; + + chipio_t io; /* IrDA controller information */ +}; + +#endif diff --git a/drivers/staging/irda/drivers/kingsun-sir.c b/drivers/staging/irda/drivers/kingsun-sir.c new file mode 100644 index 000000000000..4fd4ac2fe09f --- /dev/null +++ b/drivers/staging/irda/drivers/kingsun-sir.c @@ -0,0 +1,634 @@ +/***************************************************************************** +* +* Filename: kingsun-sir.c +* Version: 0.1.1 +* Description: Irda KingSun/DonShine USB Dongle +* Status: Experimental +* Author: Alex Villacís Lasso +* +* Based on stir4200 and mcs7780 drivers, with (strange?) differences +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * This is my current (2007-04-25) understanding of how this dongle is supposed + * to work. This is based on reverse-engineering and examination of the packet + * data sent and received by the WinXP driver using USBSnoopy. Feel free to + * update here as more of this dongle is known: + * + * General: Unlike the other USB IrDA dongles, this particular dongle exposes, + * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle, + * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in + * order to receive data. + * Transmission: Just like stir4200, this dongle uses a raw stream of data, + * which needs to be wrapped and escaped in a similar way as in stir4200.c. + * Reception: Poll-based, as in stir4200. Each read returns the contents of a + * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes + * (1-7) of valid data contained within the remaining 7 bytes. For example, if + * the buffer had the following contents: + * 06 ff ff ff c0 01 04 aa + * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the + * end is garbage (left over from a previous reception) and is discarded. + * If a read returns an "impossible" value as the length of valid data (such as + * 0x36) in the first byte, then the buffer is uninitialized (as is the case of + * first plug-in) and its contents should be discarded. There is currently no + * evidence that the top 5 bits of the 1st byte of the buffer can have values + * other than 0 once reception begins. + * Once valid bytes are collected, the assembled stream is a sequence of + * wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c. + * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after + * a successful read from the host, which means that in absence of further + * reception, repeated reads from the dongle will return the exact same + * contents repeatedly. Attempts to be smart and cache a previous read seem + * to result in corrupted packets, so this driver depends on the unwrap logic + * to sort out any repeated reads. + * Speed change: no commands observed so far to change speed, assumed fixed + * 9600bps (SIR). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* + * According to lsusb, 0x07c0 is assigned to + * "Code Mercenaries Hard- und Software GmbH" + */ +#define KING_VENDOR_ID 0x07c0 +#define KING_PRODUCT_ID 0x4200 + +/* These are the currently known USB ids */ +static const struct usb_device_id dongles[] = { + /* KingSun Co,Ltd IrDA/USB Bridge */ + { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dongles); + +#define KINGSUN_MTT 0x07 + +#define KINGSUN_FIFO_SIZE 4096 +#define KINGSUN_EP_IN 0 +#define KINGSUN_EP_OUT 1 + +struct kingsun_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + + struct qos_info qos; + + __u8 *in_buf; /* receive buffer */ + __u8 *out_buf; /* transmit buffer */ + __u8 max_rx; /* max. atomic read from dongle + (usually 8), also size of in_buf */ + __u8 max_tx; /* max. atomic write to dongle + (usually 8) */ + + iobuff_t rx_buff; /* receive unwrap state machine */ + spinlock_t lock; + int receiving; + + __u8 ep_in; + __u8 ep_out; + + struct urb *tx_urb; + struct urb *rx_urb; +}; + +/* Callback transmission routine */ +static void kingsun_send_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + struct net_device *netdev = kingsun->netdev; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + dev_err(&kingsun->usbdev->dev, + "kingsun_send_irq: Network not running!\n"); + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + dev_err(&kingsun->usbdev->dev, + "kingsun_send_irq: urb asynchronously failed - %d\n", + urb->status); + } + netif_wake_queue(netdev); +} + +/* + * Called from net/core when new frame is available. + */ +static netdev_tx_t kingsun_hard_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct kingsun_cb *kingsun; + int wraplen; + int ret = 0; + + netif_stop_queue(netdev); + + /* the IRDA wrapping routines don't deal with non linear skb */ + SKB_LINEAR_ASSERT(skb); + + kingsun = netdev_priv(netdev); + + spin_lock(&kingsun->lock); + + /* Append data to the end of whatever data remains to be transmitted */ + wraplen = async_wrap_skb(skb, + kingsun->out_buf, + KINGSUN_FIFO_SIZE); + + /* Calculate how much data can be transmitted in this urb */ + usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, + usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), + kingsun->out_buf, wraplen, kingsun_send_irq, + kingsun, 1); + + if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) { + dev_err(&kingsun->usbdev->dev, + "kingsun_hard_xmit: failed tx_urb submit: %d\n", ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + netdev->stats.tx_errors++; + netif_start_queue(netdev); + } + } else { + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += skb->len; + } + + dev_kfree_skb(skb); + spin_unlock(&kingsun->lock); + + return NETDEV_TX_OK; +} + +/* Receive callback function */ +static void kingsun_rcv_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + int ret; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + kingsun->receiving = 0; + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + dev_err(&kingsun->usbdev->dev, + "kingsun_rcv_irq: urb asynchronously failed - %d\n", + urb->status); + kingsun->receiving = 0; + return; + } + + if (urb->actual_length == kingsun->max_rx) { + __u8 *bytes = urb->transfer_buffer; + int i; + + /* The very first byte in the buffer indicates the length of + valid data in the read. This byte must be in the range + 1..kingsun->max_rx -1 . Values outside this range indicate + an uninitialized Rx buffer when the dongle has just been + plugged in. */ + if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) { + for (i = 1; i <= bytes[0]; i++) { + async_unwrap_char(kingsun->netdev, + &kingsun->netdev->stats, + &kingsun->rx_buff, bytes[i]); + } + kingsun->receiving = + (kingsun->rx_buff.state != OUTSIDE_FRAME) + ? 1 : 0; + } + } else if (urb->actual_length > 0) { + dev_err(&kingsun->usbdev->dev, + "%s(): Unexpected response length, expected %d got %d\n", + __func__, kingsun->max_rx, urb->actual_length); + } + /* This urb has already been filled in kingsun_net_open */ + ret = usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * Function kingsun_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + */ +static int kingsun_net_open(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + int err = -ENOMEM; + char hwname[16]; + + /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ + kingsun->receiving = 0; + + /* Initialize for SIR to copy data directly into skb. */ + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU; + kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!kingsun->rx_buff.skb) + goto free_mem; + + skb_reserve(kingsun->rx_buff.skb, 1); + kingsun->rx_buff.head = kingsun->rx_buff.skb->data; + + kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->rx_urb) + goto free_mem; + + kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->tx_urb) + goto free_mem; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + */ + sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); + kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); + if (!kingsun->irlap) { + dev_err(&kingsun->usbdev->dev, "irlap_open failed\n"); + goto free_mem; + } + + /* Start first reception */ + usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, + usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), + kingsun->in_buf, kingsun->max_rx, + kingsun_rcv_irq, kingsun, 1); + kingsun->rx_urb->status = 0; + err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + if (err) { + dev_err(&kingsun->usbdev->dev, + "first urb-submit failed: %d\n", err); + goto close_irlap; + } + + netif_start_queue(netdev); + + /* Situation at this point: + - all work buffers allocated + - urbs allocated and ready to fill + - max rx packet known (in max_rx) + - unwrap state machine initialized, in state outside of any frame + - receive request in progress + - IrLAP layer started, about to hand over packets to send + */ + + return 0; + + close_irlap: + irlap_close(kingsun->irlap); + free_mem: + if (kingsun->tx_urb) { + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb) { + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + if (kingsun->rx_buff.skb) { + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + } + return err; +} + +/* + * Function kingsun_net_close (kingsun) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int kingsun_net_close(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* Mop up receive && transmit urb's */ + usb_kill_urb(kingsun->tx_urb); + usb_kill_urb(kingsun->rx_urb); + + usb_free_urb(kingsun->tx_urb); + usb_free_urb(kingsun->rx_urb); + + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->receiving = 0; + + /* Stop and remove instance of IrLAP */ + if (kingsun->irlap) + irlap_close(kingsun->irlap); + + kingsun->irlap = NULL; + + return 0; +} + +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, + int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct kingsun_cb *kingsun = netdev_priv(netdev); + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the device is still there */ + if (netif_device_present(kingsun->netdev)) + /* No observed commands for speed change */ + ret = -EOPNOTSUPP; + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the IrDA stack is still there */ + if (netif_running(kingsun->netdev)) + irda_device_set_media_busy(kingsun->netdev, TRUE); + break; + + case SIOCGRECEIVING: + /* Only approximately true */ + irq->ifr_receiving = kingsun->receiving; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static const struct net_device_ops kingsun_ops = { + .ndo_start_xmit = kingsun_hard_xmit, + .ndo_open = kingsun_net_open, + .ndo_stop = kingsun_net_close, + .ndo_do_ioctl = kingsun_net_ioctl, +}; + +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + */ +static int kingsun_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + + struct usb_device *dev = interface_to_usbdev(intf); + struct kingsun_cb *kingsun = NULL; + struct net_device *net = NULL; + int ret = -ENOMEM; + int pipe, maxp_in, maxp_out; + __u8 ep_in; + __u8 ep_out; + + /* Check that there really are two interrupt endpoints. + Check based on the one in drivers/usb/input/usbmouse.c + */ + interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints != 2) { + dev_err(&intf->dev, + "kingsun-sir: expected 2 endpoints, found %d\n", + interface->desc.bNumEndpoints); + return -ENODEV; + } + endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; + if (!usb_endpoint_is_int_in(endpoint)) { + dev_err(&intf->dev, + "kingsun-sir: endpoint 0 is not interrupt IN\n"); + return -ENODEV; + } + + ep_in = endpoint->bEndpointAddress; + pipe = usb_rcvintpipe(dev, ep_in); + maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + if (maxp_in > 255 || maxp_in <= 1) { + dev_err(&intf->dev, + "endpoint 0 has max packet size %d not in range\n", + maxp_in); + return -ENODEV; + } + + endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; + if (!usb_endpoint_is_int_out(endpoint)) { + dev_err(&intf->dev, + "kingsun-sir: endpoint 1 is not interrupt OUT\n"); + return -ENODEV; + } + + ep_out = endpoint->bEndpointAddress; + pipe = usb_sndintpipe(dev, ep_out); + maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + /* Allocate network device container. */ + net = alloc_irdadev(sizeof(*kingsun)); + if(!net) + goto err_out1; + + SET_NETDEV_DEV(net, &intf->dev); + kingsun = netdev_priv(net); + kingsun->irlap = NULL; + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + kingsun->ep_in = ep_in; + kingsun->ep_out = ep_out; + kingsun->in_buf = NULL; + kingsun->out_buf = NULL; + kingsun->max_rx = (__u8)maxp_in; + kingsun->max_tx = (__u8)maxp_out; + kingsun->netdev = net; + kingsun->usbdev = dev; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.skb = NULL; + kingsun->receiving = 0; + spin_lock_init(&kingsun->lock); + + /* Allocate input buffer */ + kingsun->in_buf = kmalloc(kingsun->max_rx, GFP_KERNEL); + if (!kingsun->in_buf) + goto free_mem; + + /* Allocate output buffer */ + kingsun->out_buf = kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL); + if (!kingsun->out_buf) + goto free_mem; + + printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, " + "Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&kingsun->qos); + + /* That's the Rx capability. */ + kingsun->qos.baud_rate.bits &= IR_9600; + kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; + irda_qos_bits_to_value(&kingsun->qos); + + /* Override the network functions we need to use */ + net->netdev_ops = &kingsun_ops; + + ret = register_netdev(net); + if (ret != 0) + goto free_mem; + + dev_info(&net->dev, "IrDA: Registered KingSun/DonShine device %s\n", + net->name); + + usb_set_intfdata(intf, kingsun); + + /* Situation at this point: + - all work buffers allocated + - urbs not allocated, set to NULL + - max rx packet known (in max_rx) + - unwrap state machine (partially) initialized, but skb == NULL + */ + + return 0; + +free_mem: + kfree(kingsun->out_buf); + kfree(kingsun->in_buf); + free_netdev(net); +err_out1: + return ret; +} + +/* + * The current device is removed, the USB layer tell us to shut it down... + */ +static void kingsun_disconnect(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (!kingsun) + return; + + unregister_netdev(kingsun->netdev); + + /* Mop up receive && transmit urb's */ + if (kingsun->tx_urb != NULL) { + usb_kill_urb(kingsun->tx_urb); + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb != NULL) { + usb_kill_urb(kingsun->rx_urb); + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + + kfree(kingsun->out_buf); + kfree(kingsun->in_buf); + free_netdev(kingsun->netdev); + + usb_set_intfdata(intf, NULL); +} + +#ifdef CONFIG_PM +/* USB suspend, so power off the transmitter/receiver */ +static int kingsun_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + netif_device_detach(kingsun->netdev); + if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb); + if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb); + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int kingsun_resume(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (kingsun->rx_urb != NULL) + usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + netif_device_attach(kingsun->netdev); + + return 0; +} +#endif + +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .name = "kingsun-sir", + .probe = kingsun_probe, + .disconnect = kingsun_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = kingsun_suspend, + .resume = kingsun_resume, +#endif +}; + +module_usb_driver(irda_driver); + +MODULE_AUTHOR("Alex Villacís Lasso "); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/ks959-sir.c b/drivers/staging/irda/drivers/ks959-sir.c new file mode 100644 index 000000000000..8025741e7586 --- /dev/null +++ b/drivers/staging/irda/drivers/ks959-sir.c @@ -0,0 +1,912 @@ +/***************************************************************************** +* +* Filename: ks959-sir.c +* Version: 0.1.2 +* Description: Irda KingSun KS-959 USB Dongle +* Status: Experimental +* Author: Alex Villacís Lasso +* with help from Domen Puncer +* +* Based on stir4200, mcs7780, kingsun-sir drivers. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * Following is my most current (2007-07-17) understanding of how the Kingsun + * KS-959 dongle is supposed to work. This information was deduced by + * reverse-engineering and examining the USB traffic captured with USBSnoopy + * from the WinXP driver. Feel free to update here as more of the dongle is + * known. + * + * My most sincere thanks must go to Domen Puncer for + * invaluable help in cracking the obfuscation and padding required for this + * dongle. + * + * General: This dongle exposes one interface with one interrupt IN endpoint. + * However, the interrupt endpoint is NOT used at all for this dongle. Instead, + * this dongle uses control transfers for everything, including sending and + * receiving the IrDA frame data. Apparently the interrupt endpoint is just a + * dummy to ensure the dongle has a valid interface to present to the PC.And I + * thought the DonShine dongle was weird... In addition, this dongle uses + * obfuscation (?!?!), applied at the USB level, to hide the traffic, both sent + * and received, from the dongle. I call it obfuscation because the XOR keying + * and padding required to produce an USB traffic acceptable for the dongle can + * not be explained by any other technical requirement. + * + * Transmission: To transmit an IrDA frame, the driver must prepare a control + * URB with the following as a setup packet: + * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE + * bRequest 0x09 + * wValue + * wIndex 0x0000 + * wLength + * The payload packet must be manually wrapped and escaped (as in stir4200.c), + * then padded and obfuscated before being sent. Both padding and obfuscation + * are implemented in the procedure obfuscate_tx_buffer(). Suffice to say, the + * designer/programmer of the dongle used his name as a source for the + * obfuscation. WTF?! + * Apparently the dongle cannot handle payloads larger than 256 bytes. The + * driver has to perform fragmentation in order to send anything larger than + * this limit. + * + * Reception: To receive data, the driver must poll the dongle regularly (like + * kingsun-sir.c) with control URBs and the following as a setup packet: + * bRequestType USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE + * bRequest 0x01 + * wValue 0x0200 + * wIndex 0x0000 + * wLength 0x0800 (size of available buffer) + * If there is data to be read, it will be returned as the response payload. + * This data is (apparently) not padded, but it is obfuscated. To de-obfuscate + * it, the driver must XOR every byte, in sequence, with a value that starts at + * 1 and is incremented with each byte processed, and then with 0x55. The value + * incremented with each byte processed overflows as an unsigned char. The + * resulting bytes form a wrapped SIR frame that is unwrapped and unescaped + * as in stir4200.c The incremented value is NOT reset with each frame, but is + * kept across the entire session with the dongle. Also, the dongle inserts an + * extra garbage byte with value 0x95 (after decoding) every 0xff bytes, which + * must be skipped. + * + * Speed change: To change the speed of the dongle, the driver prepares a + * control URB with the following as a setup packet: + * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE + * bRequest 0x09 + * wValue 0x0200 + * wIndex 0x0001 + * wLength 0x0008 (length of the payload) + * The payload is a 8-byte record, apparently identical to the one used in + * drivers/usb/serial/cypress_m8.c to change speed: + * __u32 baudSpeed; + * unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits + * unsigned int : 1; + * unsigned int stopBits : 1; + * unsigned int parityEnable : 1; + * unsigned int parityType : 1; + * unsigned int : 1; + * unsigned int reset : 1; + * unsigned char reserved[3]; // set to 0 + * + * For now only SIR speeds have been observed with this dongle. Therefore, + * nothing is known on what changes (if any) must be done to frame wrapping / + * unwrapping for higher than SIR speeds. This driver assumes no change is + * necessary and announces support for all the way to 57600 bps. Although the + * package announces support for up to 4MBps, tests with a Sony Ericcson K300 + * phone show corruption when receiving large frames at 115200 bps, the highest + * speed announced by the phone. However, transmission at 115200 bps is OK. Go + * figure. Since I don't know whether the phone or the dongle is at fault, max + * announced speed is 57600 bps until someone produces a device that can run + * at higher speeds with this dongle. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define KS959_VENDOR_ID 0x07d0 +#define KS959_PRODUCT_ID 0x4959 + +/* These are the currently known USB ids */ +static const struct usb_device_id dongles[] = { + /* KingSun Co,Ltd IrDA/USB Bridge */ + {USB_DEVICE(KS959_VENDOR_ID, KS959_PRODUCT_ID)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, dongles); + +#define KINGSUN_MTT 0x07 +#define KINGSUN_REQ_RECV 0x01 +#define KINGSUN_REQ_SEND 0x09 + +#define KINGSUN_RCV_FIFO_SIZE 2048 /* Max length we can receive */ +#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */ +#define KINGSUN_SND_PACKET_SIZE 256 /* Max packet dongle can handle */ + +struct ks959_speedparams { + __le32 baudrate; /* baud rate, little endian */ + __u8 flags; + __u8 reserved[3]; +} __packed; + +#define KS_DATA_5_BITS 0x00 +#define KS_DATA_6_BITS 0x01 +#define KS_DATA_7_BITS 0x02 +#define KS_DATA_8_BITS 0x03 + +#define KS_STOP_BITS_1 0x00 +#define KS_STOP_BITS_2 0x08 + +#define KS_PAR_DISABLE 0x00 +#define KS_PAR_EVEN 0x10 +#define KS_PAR_ODD 0x30 +#define KS_RESET 0x80 + +struct ks959_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + + struct qos_info qos; + + struct usb_ctrlrequest *tx_setuprequest; + struct urb *tx_urb; + __u8 *tx_buf_clear; + unsigned int tx_buf_clear_used; + unsigned int tx_buf_clear_sent; + __u8 *tx_buf_xored; + + struct usb_ctrlrequest *rx_setuprequest; + struct urb *rx_urb; + __u8 *rx_buf; + __u8 rx_variable_xormask; + iobuff_t rx_unwrap_buff; + + struct usb_ctrlrequest *speed_setuprequest; + struct urb *speed_urb; + struct ks959_speedparams speedparams; + unsigned int new_speed; + + spinlock_t lock; + int receiving; +}; + +/* Procedure to perform the obfuscation/padding expected by the dongle + * + * buf_cleartext (IN) Cleartext version of the IrDA frame to transmit + * len_cleartext (IN) Length of the cleartext version of IrDA frame + * buf_xoredtext (OUT) Obfuscated version of frame built by proc + * len_maxbuf (OUT) Maximum space available at buf_xoredtext + * + * (return) length of obfuscated frame with padding + * + * If not enough space (as indicated by len_maxbuf vs. required padding), + * zero is returned + * + * The value of lookup_string is actually a required portion of the algorithm. + * Seems the designer of the dongle wanted to state who exactly is responsible + * for implementing obfuscation. Send your best (or other) wishes to him ]:-) + */ +static unsigned int obfuscate_tx_buffer(const __u8 * buf_cleartext, + unsigned int len_cleartext, + __u8 * buf_xoredtext, + unsigned int len_maxbuf) +{ + unsigned int len_xoredtext; + + /* Calculate required length with padding, check for necessary space */ + len_xoredtext = ((len_cleartext + 7) & ~0x7) + 0x10; + if (len_xoredtext <= len_maxbuf) { + static const __u8 lookup_string[] = "wangshuofei19710"; + __u8 xor_mask; + + /* Unlike the WinXP driver, we *do* clear out the padding */ + memset(buf_xoredtext, 0, len_xoredtext); + + xor_mask = lookup_string[(len_cleartext & 0x0f) ^ 0x06] ^ 0x55; + + while (len_cleartext-- > 0) { + *buf_xoredtext++ = *buf_cleartext++ ^ xor_mask; + } + } else { + len_xoredtext = 0; + } + return len_xoredtext; +} + +/* Callback transmission routine */ +static void ks959_speed_irq(struct urb *urb) +{ + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + dev_err(&urb->dev->dev, + "ks959_speed_irq: urb asynchronously failed - %d\n", + urb->status); + } +} + +/* Send a control request to change speed of the dongle */ +static int ks959_change_speed(struct ks959_cb *kingsun, unsigned speed) +{ + static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400, + 57600, 115200, 576000, 1152000, 4000000, 0 + }; + int err; + unsigned int i; + + if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL) + return -ENOMEM; + + /* Check that requested speed is among the supported ones */ + for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ; + if (supported_speeds[i] == 0) + return -EOPNOTSUPP; + + memset(&(kingsun->speedparams), 0, sizeof(struct ks959_speedparams)); + kingsun->speedparams.baudrate = cpu_to_le32(speed); + kingsun->speedparams.flags = KS_DATA_8_BITS; + + /* speed_setuprequest pre-filled in ks959_probe */ + usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev, + usb_sndctrlpipe(kingsun->usbdev, 0), + (unsigned char *)kingsun->speed_setuprequest, + &(kingsun->speedparams), + sizeof(struct ks959_speedparams), ks959_speed_irq, + kingsun); + kingsun->speed_urb->status = 0; + err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC); + + return err; +} + +/* Submit one fragment of an IrDA frame to the dongle */ +static void ks959_send_irq(struct urb *urb); +static int ks959_submit_tx_fragment(struct ks959_cb *kingsun) +{ + unsigned int padlen; + unsigned int wraplen; + int ret; + + /* Check whether current plaintext can produce a padded buffer that fits + within the range handled by the dongle */ + wraplen = (KINGSUN_SND_PACKET_SIZE & ~0x7) - 0x10; + if (wraplen > kingsun->tx_buf_clear_used) + wraplen = kingsun->tx_buf_clear_used; + + /* Perform dongle obfuscation. Also remove the portion of the frame that + was just obfuscated and will now be sent to the dongle. */ + padlen = obfuscate_tx_buffer(kingsun->tx_buf_clear, wraplen, + kingsun->tx_buf_xored, + KINGSUN_SND_PACKET_SIZE); + + /* Calculate how much data can be transmitted in this urb */ + kingsun->tx_setuprequest->wValue = cpu_to_le16(wraplen); + kingsun->tx_setuprequest->wLength = cpu_to_le16(padlen); + /* Rest of the fields were filled in ks959_probe */ + usb_fill_control_urb(kingsun->tx_urb, kingsun->usbdev, + usb_sndctrlpipe(kingsun->usbdev, 0), + (unsigned char *)kingsun->tx_setuprequest, + kingsun->tx_buf_xored, padlen, + ks959_send_irq, kingsun); + kingsun->tx_urb->status = 0; + ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC); + + /* Remember how much data was sent, in order to update at callback */ + kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0; + return ret; +} + +/* Callback transmission routine */ +static void ks959_send_irq(struct urb *urb) +{ + struct ks959_cb *kingsun = urb->context; + struct net_device *netdev = kingsun->netdev; + int ret = 0; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + dev_err(&kingsun->usbdev->dev, + "ks959_send_irq: Network not running!\n"); + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + dev_err(&kingsun->usbdev->dev, + "ks959_send_irq: urb asynchronously failed - %d\n", + urb->status); + return; + } + + if (kingsun->tx_buf_clear_used > 0) { + /* Update data remaining to be sent */ + if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) { + memmove(kingsun->tx_buf_clear, + kingsun->tx_buf_clear + + kingsun->tx_buf_clear_sent, + kingsun->tx_buf_clear_used - + kingsun->tx_buf_clear_sent); + } + kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent; + kingsun->tx_buf_clear_sent = 0; + + if (kingsun->tx_buf_clear_used > 0) { + /* There is more data to be sent */ + if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) { + dev_err(&kingsun->usbdev->dev, + "ks959_send_irq: failed tx_urb submit: %d\n", + ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + netdev->stats.tx_errors++; + netif_start_queue(netdev); + } + } + } else { + /* All data sent, send next speed && wake network queue */ + if (kingsun->new_speed != -1 && + cpu_to_le32(kingsun->new_speed) != + kingsun->speedparams.baudrate) + ks959_change_speed(kingsun, kingsun->new_speed); + + netif_wake_queue(netdev); + } + } +} + +/* + * Called from net/core when new frame is available. + */ +static netdev_tx_t ks959_hard_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct ks959_cb *kingsun; + unsigned int wraplen; + int ret = 0; + + netif_stop_queue(netdev); + + /* the IRDA wrapping routines don't deal with non linear skb */ + SKB_LINEAR_ASSERT(skb); + + kingsun = netdev_priv(netdev); + + spin_lock(&kingsun->lock); + kingsun->new_speed = irda_get_next_speed(skb); + + /* Append data to the end of whatever data remains to be transmitted */ + wraplen = + async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE); + kingsun->tx_buf_clear_used = wraplen; + + if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) { + dev_err(&kingsun->usbdev->dev, + "ks959_hard_xmit: failed tx_urb submit: %d\n", ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + netdev->stats.tx_errors++; + netif_start_queue(netdev); + } + } else { + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += skb->len; + + } + + dev_kfree_skb(skb); + spin_unlock(&kingsun->lock); + + return NETDEV_TX_OK; +} + +/* Receive callback function */ +static void ks959_rcv_irq(struct urb *urb) +{ + struct ks959_cb *kingsun = urb->context; + int ret; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + kingsun->receiving = 0; + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + dev_err(&kingsun->usbdev->dev, + "kingsun_rcv_irq: urb asynchronously failed - %d\n", + urb->status); + kingsun->receiving = 0; + return; + } + + if (urb->actual_length > 0) { + __u8 *bytes = urb->transfer_buffer; + unsigned int i; + + for (i = 0; i < urb->actual_length; i++) { + /* De-obfuscation implemented here: variable portion of + xormask is incremented, and then used with the encoded + byte for the XOR. The result of the operation is used + to unwrap the SIR frame. */ + kingsun->rx_variable_xormask++; + bytes[i] = + bytes[i] ^ kingsun->rx_variable_xormask ^ 0x55u; + + /* rx_variable_xormask doubles as an index counter so we + can skip the byte at 0xff (wrapped around to 0). + */ + if (kingsun->rx_variable_xormask != 0) { + async_unwrap_char(kingsun->netdev, + &kingsun->netdev->stats, + &kingsun->rx_unwrap_buff, + bytes[i]); + } + } + kingsun->receiving = + (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0; + } + + /* This urb has already been filled in kingsun_net_open. Setup + packet must be re-filled, but it is assumed that urb keeps the + pointer to the initial setup packet, as well as the payload buffer. + Setup packet is already pre-filled at ks959_probe. + */ + urb->status = 0; + ret = usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * Function kingsun_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + */ +static int ks959_net_open(struct net_device *netdev) +{ + struct ks959_cb *kingsun = netdev_priv(netdev); + int err = -ENOMEM; + char hwname[16]; + + /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ + kingsun->receiving = 0; + + /* Initialize for SIR to copy data directly into skb. */ + kingsun->rx_unwrap_buff.in_frame = FALSE; + kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; + kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU; + kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!kingsun->rx_unwrap_buff.skb) + goto free_mem; + + skb_reserve(kingsun->rx_unwrap_buff.skb, 1); + kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data; + + kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->rx_urb) + goto free_mem; + + kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->tx_urb) + goto free_mem; + + kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->speed_urb) + goto free_mem; + + /* Initialize speed for dongle */ + kingsun->new_speed = 9600; + err = ks959_change_speed(kingsun, 9600); + if (err < 0) + goto free_mem; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + */ + sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); + kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); + if (!kingsun->irlap) { + err = -ENOMEM; + dev_err(&kingsun->usbdev->dev, "irlap_open failed\n"); + goto free_mem; + } + + /* Start reception. Setup request already pre-filled in ks959_probe */ + usb_fill_control_urb(kingsun->rx_urb, kingsun->usbdev, + usb_rcvctrlpipe(kingsun->usbdev, 0), + (unsigned char *)kingsun->rx_setuprequest, + kingsun->rx_buf, KINGSUN_RCV_FIFO_SIZE, + ks959_rcv_irq, kingsun); + kingsun->rx_urb->status = 0; + err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + if (err) { + dev_err(&kingsun->usbdev->dev, + "first urb-submit failed: %d\n", err); + goto close_irlap; + } + + netif_start_queue(netdev); + + /* Situation at this point: + - all work buffers allocated + - urbs allocated and ready to fill + - max rx packet known (in max_rx) + - unwrap state machine initialized, in state outside of any frame + - receive request in progress + - IrLAP layer started, about to hand over packets to send + */ + + return 0; + + close_irlap: + irlap_close(kingsun->irlap); + free_mem: + usb_free_urb(kingsun->speed_urb); + kingsun->speed_urb = NULL; + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + if (kingsun->rx_unwrap_buff.skb) { + kfree_skb(kingsun->rx_unwrap_buff.skb); + kingsun->rx_unwrap_buff.skb = NULL; + kingsun->rx_unwrap_buff.head = NULL; + } + return err; +} + +/* + * Function kingsun_net_close (kingsun) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int ks959_net_close(struct net_device *netdev) +{ + struct ks959_cb *kingsun = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* Mop up receive && transmit urb's */ + usb_kill_urb(kingsun->tx_urb); + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + + usb_kill_urb(kingsun->speed_urb); + usb_free_urb(kingsun->speed_urb); + kingsun->speed_urb = NULL; + + usb_kill_urb(kingsun->rx_urb); + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + + kfree_skb(kingsun->rx_unwrap_buff.skb); + kingsun->rx_unwrap_buff.skb = NULL; + kingsun->rx_unwrap_buff.head = NULL; + kingsun->rx_unwrap_buff.in_frame = FALSE; + kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; + kingsun->receiving = 0; + + /* Stop and remove instance of IrLAP */ + if (kingsun->irlap) + irlap_close(kingsun->irlap); + + kingsun->irlap = NULL; + + return 0; +} + +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int ks959_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *)rq; + struct ks959_cb *kingsun = netdev_priv(netdev); + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the device is still there */ + if (netif_device_present(kingsun->netdev)) + return ks959_change_speed(kingsun, irq->ifr_baudrate); + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the IrDA stack is still there */ + if (netif_running(kingsun->netdev)) + irda_device_set_media_busy(kingsun->netdev, TRUE); + break; + + case SIOCGRECEIVING: + /* Only approximately true */ + irq->ifr_receiving = kingsun->receiving; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static const struct net_device_ops ks959_ops = { + .ndo_start_xmit = ks959_hard_xmit, + .ndo_open = ks959_net_open, + .ndo_stop = ks959_net_close, + .ndo_do_ioctl = ks959_net_ioctl, +}; +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + */ +static int ks959_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct ks959_cb *kingsun = NULL; + struct net_device *net = NULL; + int ret = -ENOMEM; + + /* Allocate network device container. */ + net = alloc_irdadev(sizeof(*kingsun)); + if (!net) + goto err_out1; + + SET_NETDEV_DEV(net, &intf->dev); + kingsun = netdev_priv(net); + kingsun->netdev = net; + kingsun->usbdev = dev; + kingsun->irlap = NULL; + kingsun->tx_setuprequest = NULL; + kingsun->tx_urb = NULL; + kingsun->tx_buf_clear = NULL; + kingsun->tx_buf_xored = NULL; + kingsun->tx_buf_clear_used = 0; + kingsun->tx_buf_clear_sent = 0; + + kingsun->rx_setuprequest = NULL; + kingsun->rx_urb = NULL; + kingsun->rx_buf = NULL; + kingsun->rx_variable_xormask = 0; + kingsun->rx_unwrap_buff.in_frame = FALSE; + kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; + kingsun->rx_unwrap_buff.skb = NULL; + kingsun->receiving = 0; + spin_lock_init(&kingsun->lock); + + kingsun->speed_setuprequest = NULL; + kingsun->speed_urb = NULL; + kingsun->speedparams.baudrate = 0; + + /* Allocate input buffer */ + kingsun->rx_buf = kmalloc(KINGSUN_RCV_FIFO_SIZE, GFP_KERNEL); + if (!kingsun->rx_buf) + goto free_mem; + + /* Allocate input setup packet */ + kingsun->rx_setuprequest = + kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!kingsun->rx_setuprequest) + goto free_mem; + kingsun->rx_setuprequest->bRequestType = + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + kingsun->rx_setuprequest->bRequest = KINGSUN_REQ_RECV; + kingsun->rx_setuprequest->wValue = cpu_to_le16(0x0200); + kingsun->rx_setuprequest->wIndex = 0; + kingsun->rx_setuprequest->wLength = cpu_to_le16(KINGSUN_RCV_FIFO_SIZE); + + /* Allocate output buffer */ + kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL); + if (!kingsun->tx_buf_clear) + goto free_mem; + kingsun->tx_buf_xored = kmalloc(KINGSUN_SND_PACKET_SIZE, GFP_KERNEL); + if (!kingsun->tx_buf_xored) + goto free_mem; + + /* Allocate and initialize output setup packet */ + kingsun->tx_setuprequest = + kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!kingsun->tx_setuprequest) + goto free_mem; + kingsun->tx_setuprequest->bRequestType = + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + kingsun->tx_setuprequest->bRequest = KINGSUN_REQ_SEND; + kingsun->tx_setuprequest->wValue = 0; + kingsun->tx_setuprequest->wIndex = 0; + kingsun->tx_setuprequest->wLength = 0; + + /* Allocate and initialize speed setup packet */ + kingsun->speed_setuprequest = + kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!kingsun->speed_setuprequest) + goto free_mem; + kingsun->speed_setuprequest->bRequestType = + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND; + kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200); + kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001); + kingsun->speed_setuprequest->wLength = + cpu_to_le16(sizeof(struct ks959_speedparams)); + + printk(KERN_INFO "KingSun KS-959 IRDA/USB found at address %d, " + "Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&kingsun->qos); + + /* Baud rates known to be supported. Please uncomment if devices (other + than a SonyEriccson K300 phone) can be shown to support higher speed + with this dongle. + */ + kingsun->qos.baud_rate.bits = + IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600; + kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; + irda_qos_bits_to_value(&kingsun->qos); + + /* Override the network functions we need to use */ + net->netdev_ops = &ks959_ops; + + ret = register_netdev(net); + if (ret != 0) + goto free_mem; + + dev_info(&net->dev, "IrDA: Registered KingSun KS-959 device %s\n", + net->name); + + usb_set_intfdata(intf, kingsun); + + /* Situation at this point: + - all work buffers allocated + - setup requests pre-filled + - urbs not allocated, set to NULL + - max rx packet known (is KINGSUN_FIFO_SIZE) + - unwrap state machine (partially) initialized, but skb == NULL + */ + + return 0; + + free_mem: + kfree(kingsun->speed_setuprequest); + kfree(kingsun->tx_setuprequest); + kfree(kingsun->tx_buf_xored); + kfree(kingsun->tx_buf_clear); + kfree(kingsun->rx_setuprequest); + kfree(kingsun->rx_buf); + free_netdev(net); + err_out1: + return ret; +} + +/* + * The current device is removed, the USB layer tell us to shut it down... + */ +static void ks959_disconnect(struct usb_interface *intf) +{ + struct ks959_cb *kingsun = usb_get_intfdata(intf); + + if (!kingsun) + return; + + unregister_netdev(kingsun->netdev); + + /* Mop up receive && transmit urb's */ + if (kingsun->speed_urb != NULL) { + usb_kill_urb(kingsun->speed_urb); + usb_free_urb(kingsun->speed_urb); + kingsun->speed_urb = NULL; + } + if (kingsun->tx_urb != NULL) { + usb_kill_urb(kingsun->tx_urb); + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb != NULL) { + usb_kill_urb(kingsun->rx_urb); + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + + kfree(kingsun->speed_setuprequest); + kfree(kingsun->tx_setuprequest); + kfree(kingsun->tx_buf_xored); + kfree(kingsun->tx_buf_clear); + kfree(kingsun->rx_setuprequest); + kfree(kingsun->rx_buf); + free_netdev(kingsun->netdev); + + usb_set_intfdata(intf, NULL); +} + +#ifdef CONFIG_PM +/* USB suspend, so power off the transmitter/receiver */ +static int ks959_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct ks959_cb *kingsun = usb_get_intfdata(intf); + + netif_device_detach(kingsun->netdev); + if (kingsun->speed_urb != NULL) + usb_kill_urb(kingsun->speed_urb); + if (kingsun->tx_urb != NULL) + usb_kill_urb(kingsun->tx_urb); + if (kingsun->rx_urb != NULL) + usb_kill_urb(kingsun->rx_urb); + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int ks959_resume(struct usb_interface *intf) +{ + struct ks959_cb *kingsun = usb_get_intfdata(intf); + + if (kingsun->rx_urb != NULL) { + /* Setup request already filled in ks959_probe */ + usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + } + netif_device_attach(kingsun->netdev); + + return 0; +} +#endif + +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .name = "ks959-sir", + .probe = ks959_probe, + .disconnect = ks959_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = ks959_suspend, + .resume = ks959_resume, +#endif +}; + +module_usb_driver(irda_driver); + +MODULE_AUTHOR("Alex Villacís Lasso "); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun KS-959"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/ksdazzle-sir.c b/drivers/staging/irda/drivers/ksdazzle-sir.c new file mode 100644 index 000000000000..d2a0755df596 --- /dev/null +++ b/drivers/staging/irda/drivers/ksdazzle-sir.c @@ -0,0 +1,813 @@ +/***************************************************************************** +* +* Filename: ksdazzle.c +* Version: 0.1.2 +* Description: Irda KingSun Dazzle USB Dongle +* Status: Experimental +* Author: Alex Villacís Lasso +* +* Based on stir4200, mcs7780, kingsun-sir drivers. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * Following is my most current (2007-07-26) understanding of how the Kingsun + * 07D0:4100 dongle (sometimes known as the MA-660) is supposed to work. This + * information was deduced by examining the USB traffic captured with USBSnoopy + * from the WinXP driver. Feel free to update here as more of the dongle is + * known. + * + * General: This dongle exposes one interface with two interrupt endpoints, one + * IN and one OUT. In this regard, it is similar to what the Kingsun/Donshine + * dongle (07c0:4200) exposes. Traffic is raw and needs to be wrapped and + * unwrapped manually as in stir4200, kingsun-sir, and ks959-sir. + * + * Transmission: To transmit an IrDA frame, it is necessary to wrap it, then + * split it into multiple segments of up to 7 bytes each, and transmit each in + * sequence. It seems that sending a single big block (like kingsun-sir does) + * won't work with this dongle. Each segment needs to be prefixed with a value + * equal to (unsigned char)0xF8 + , inside a payload + * of exactly 8 bytes. For example, a segment of 1 byte gets prefixed by 0xF9, + * and one of 7 bytes gets prefixed by 0xFF. The bytes at the end of the + * payload, not considered by the prefix, are ignored (set to 0 by this + * implementation). + * + * Reception: To receive data, the driver must poll the dongle regularly (like + * kingsun-sir.c) with interrupt URBs. If data is available, it will be returned + * in payloads from 0 to 8 bytes long. When concatenated, these payloads form + * a raw IrDA stream that needs to be unwrapped as in stir4200 and kingsun-sir + * + * Speed change: To change the speed of the dongle, the driver prepares a + * control URB with the following as a setup packet: + * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE + * bRequest 0x09 + * wValue 0x0200 + * wIndex 0x0001 + * wLength 0x0008 (length of the payload) + * The payload is a 8-byte record, apparently identical to the one used in + * drivers/usb/serial/cypress_m8.c to change speed: + * __u32 baudSpeed; + * unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits + * unsigned int : 1; + * unsigned int stopBits : 1; + * unsigned int parityEnable : 1; + * unsigned int parityType : 1; + * unsigned int : 1; + * unsigned int reset : 1; + * unsigned char reserved[3]; // set to 0 + * + * For now only SIR speeds have been observed with this dongle. Therefore, + * nothing is known on what changes (if any) must be done to frame wrapping / + * unwrapping for higher than SIR speeds. This driver assumes no change is + * necessary and announces support for all the way to 115200 bps. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define KSDAZZLE_VENDOR_ID 0x07d0 +#define KSDAZZLE_PRODUCT_ID 0x4100 + +/* These are the currently known USB ids */ +static const struct usb_device_id dongles[] = { + /* KingSun Co,Ltd IrDA/USB Bridge */ + {USB_DEVICE(KSDAZZLE_VENDOR_ID, KSDAZZLE_PRODUCT_ID)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, dongles); + +#define KINGSUN_MTT 0x07 +#define KINGSUN_REQ_RECV 0x01 +#define KINGSUN_REQ_SEND 0x09 + +#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */ +#define KINGSUN_RCV_MAX 2048 /* Max transfer we can receive */ + +struct ksdazzle_speedparams { + __le32 baudrate; /* baud rate, little endian */ + __u8 flags; + __u8 reserved[3]; +} __packed; + +#define KS_DATA_5_BITS 0x00 +#define KS_DATA_6_BITS 0x01 +#define KS_DATA_7_BITS 0x02 +#define KS_DATA_8_BITS 0x03 + +#define KS_STOP_BITS_1 0x00 +#define KS_STOP_BITS_2 0x08 + +#define KS_PAR_DISABLE 0x00 +#define KS_PAR_EVEN 0x10 +#define KS_PAR_ODD 0x30 +#define KS_RESET 0x80 + +#define KINGSUN_EP_IN 0 +#define KINGSUN_EP_OUT 1 + +struct ksdazzle_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + + struct qos_info qos; + + struct urb *tx_urb; + __u8 *tx_buf_clear; + unsigned int tx_buf_clear_used; + unsigned int tx_buf_clear_sent; + __u8 tx_payload[8]; + + struct urb *rx_urb; + __u8 *rx_buf; + iobuff_t rx_unwrap_buff; + + struct usb_ctrlrequest *speed_setuprequest; + struct urb *speed_urb; + struct ksdazzle_speedparams speedparams; + unsigned int new_speed; + + __u8 ep_in; + __u8 ep_out; + + spinlock_t lock; + int receiving; +}; + +/* Callback transmission routine */ +static void ksdazzle_speed_irq(struct urb *urb) +{ + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) + dev_err(&urb->dev->dev, + "ksdazzle_speed_irq: urb asynchronously failed - %d\n", + urb->status); +} + +/* Send a control request to change speed of the dongle */ +static int ksdazzle_change_speed(struct ksdazzle_cb *kingsun, unsigned speed) +{ + static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400, + 57600, 115200, 576000, 1152000, 4000000, 0 + }; + int err; + unsigned int i; + + if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL) + return -ENOMEM; + + /* Check that requested speed is among the supported ones */ + for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ; + if (supported_speeds[i] == 0) + return -EOPNOTSUPP; + + memset(&(kingsun->speedparams), 0, sizeof(struct ksdazzle_speedparams)); + kingsun->speedparams.baudrate = cpu_to_le32(speed); + kingsun->speedparams.flags = KS_DATA_8_BITS; + + /* speed_setuprequest pre-filled in ksdazzle_probe */ + usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev, + usb_sndctrlpipe(kingsun->usbdev, 0), + (unsigned char *)kingsun->speed_setuprequest, + &(kingsun->speedparams), + sizeof(struct ksdazzle_speedparams), + ksdazzle_speed_irq, kingsun); + kingsun->speed_urb->status = 0; + err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC); + + return err; +} + +/* Submit one fragment of an IrDA frame to the dongle */ +static void ksdazzle_send_irq(struct urb *urb); +static int ksdazzle_submit_tx_fragment(struct ksdazzle_cb *kingsun) +{ + unsigned int wraplen; + int ret; + + /* We can send at most 7 bytes of payload at a time */ + wraplen = 7; + if (wraplen > kingsun->tx_buf_clear_used) + wraplen = kingsun->tx_buf_clear_used; + + /* Prepare payload prefix with used length */ + memset(kingsun->tx_payload, 0, 8); + kingsun->tx_payload[0] = (unsigned char)0xf8 + wraplen; + memcpy(kingsun->tx_payload + 1, kingsun->tx_buf_clear, wraplen); + + usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, + usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), + kingsun->tx_payload, 8, ksdazzle_send_irq, kingsun, 1); + kingsun->tx_urb->status = 0; + ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC); + + /* Remember how much data was sent, in order to update at callback */ + kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0; + return ret; +} + +/* Callback transmission routine */ +static void ksdazzle_send_irq(struct urb *urb) +{ + struct ksdazzle_cb *kingsun = urb->context; + struct net_device *netdev = kingsun->netdev; + int ret = 0; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + dev_err(&kingsun->usbdev->dev, + "ksdazzle_send_irq: Network not running!\n"); + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + dev_err(&kingsun->usbdev->dev, + "ksdazzle_send_irq: urb asynchronously failed - %d\n", + urb->status); + return; + } + + if (kingsun->tx_buf_clear_used > 0) { + /* Update data remaining to be sent */ + if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) { + memmove(kingsun->tx_buf_clear, + kingsun->tx_buf_clear + + kingsun->tx_buf_clear_sent, + kingsun->tx_buf_clear_used - + kingsun->tx_buf_clear_sent); + } + kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent; + kingsun->tx_buf_clear_sent = 0; + + if (kingsun->tx_buf_clear_used > 0) { + /* There is more data to be sent */ + if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) { + dev_err(&kingsun->usbdev->dev, + "ksdazzle_send_irq: failed tx_urb submit: %d\n", + ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + netdev->stats.tx_errors++; + netif_start_queue(netdev); + } + } + } else { + /* All data sent, send next speed && wake network queue */ + if (kingsun->new_speed != -1 && + cpu_to_le32(kingsun->new_speed) != + kingsun->speedparams.baudrate) + ksdazzle_change_speed(kingsun, + kingsun->new_speed); + + netif_wake_queue(netdev); + } + } +} + +/* + * Called from net/core when new frame is available. + */ +static netdev_tx_t ksdazzle_hard_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct ksdazzle_cb *kingsun; + unsigned int wraplen; + int ret = 0; + + netif_stop_queue(netdev); + + /* the IRDA wrapping routines don't deal with non linear skb */ + SKB_LINEAR_ASSERT(skb); + + kingsun = netdev_priv(netdev); + + spin_lock(&kingsun->lock); + kingsun->new_speed = irda_get_next_speed(skb); + + /* Append data to the end of whatever data remains to be transmitted */ + wraplen = + async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE); + kingsun->tx_buf_clear_used = wraplen; + + if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) { + dev_err(&kingsun->usbdev->dev, + "ksdazzle_hard_xmit: failed tx_urb submit: %d\n", ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + netdev->stats.tx_errors++; + netif_start_queue(netdev); + } + } else { + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += skb->len; + + } + + dev_kfree_skb(skb); + spin_unlock(&kingsun->lock); + + return NETDEV_TX_OK; +} + +/* Receive callback function */ +static void ksdazzle_rcv_irq(struct urb *urb) +{ + struct ksdazzle_cb *kingsun = urb->context; + struct net_device *netdev = kingsun->netdev; + + /* in process of stopping, just drop data */ + if (!netif_running(netdev)) { + kingsun->receiving = 0; + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + dev_err(&kingsun->usbdev->dev, + "ksdazzle_rcv_irq: urb asynchronously failed - %d\n", + urb->status); + kingsun->receiving = 0; + return; + } + + if (urb->actual_length > 0) { + __u8 *bytes = urb->transfer_buffer; + unsigned int i; + + for (i = 0; i < urb->actual_length; i++) { + async_unwrap_char(netdev, &netdev->stats, + &kingsun->rx_unwrap_buff, bytes[i]); + } + kingsun->receiving = + (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0; + } + + /* This urb has already been filled in ksdazzle_net_open. It is assumed that + urb keeps the pointer to the payload buffer. + */ + urb->status = 0; + usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * Function ksdazzle_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + */ +static int ksdazzle_net_open(struct net_device *netdev) +{ + struct ksdazzle_cb *kingsun = netdev_priv(netdev); + int err = -ENOMEM; + char hwname[16]; + + /* At this point, urbs are NULL, and skb is NULL (see ksdazzle_probe) */ + kingsun->receiving = 0; + + /* Initialize for SIR to copy data directly into skb. */ + kingsun->rx_unwrap_buff.in_frame = FALSE; + kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; + kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU; + kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!kingsun->rx_unwrap_buff.skb) + goto free_mem; + + skb_reserve(kingsun->rx_unwrap_buff.skb, 1); + kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data; + + kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->rx_urb) + goto free_mem; + + kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->tx_urb) + goto free_mem; + + kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->speed_urb) + goto free_mem; + + /* Initialize speed for dongle */ + kingsun->new_speed = 9600; + err = ksdazzle_change_speed(kingsun, 9600); + if (err < 0) + goto free_mem; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + */ + sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); + kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); + if (!kingsun->irlap) { + err = -ENOMEM; + dev_err(&kingsun->usbdev->dev, "irlap_open failed\n"); + goto free_mem; + } + + /* Start reception. */ + usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, + usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), + kingsun->rx_buf, KINGSUN_RCV_MAX, ksdazzle_rcv_irq, + kingsun, 1); + kingsun->rx_urb->status = 0; + err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + if (err) { + dev_err(&kingsun->usbdev->dev, "first urb-submit failed: %d\n", err); + goto close_irlap; + } + + netif_start_queue(netdev); + + /* Situation at this point: + - all work buffers allocated + - urbs allocated and ready to fill + - max rx packet known (in max_rx) + - unwrap state machine initialized, in state outside of any frame + - receive request in progress + - IrLAP layer started, about to hand over packets to send + */ + + return 0; + + close_irlap: + irlap_close(kingsun->irlap); + free_mem: + usb_free_urb(kingsun->speed_urb); + kingsun->speed_urb = NULL; + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + if (kingsun->rx_unwrap_buff.skb) { + kfree_skb(kingsun->rx_unwrap_buff.skb); + kingsun->rx_unwrap_buff.skb = NULL; + kingsun->rx_unwrap_buff.head = NULL; + } + return err; +} + +/* + * Function ksdazzle_net_close (dev) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int ksdazzle_net_close(struct net_device *netdev) +{ + struct ksdazzle_cb *kingsun = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* Mop up receive && transmit urb's */ + usb_kill_urb(kingsun->tx_urb); + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + + usb_kill_urb(kingsun->speed_urb); + usb_free_urb(kingsun->speed_urb); + kingsun->speed_urb = NULL; + + usb_kill_urb(kingsun->rx_urb); + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + + kfree_skb(kingsun->rx_unwrap_buff.skb); + kingsun->rx_unwrap_buff.skb = NULL; + kingsun->rx_unwrap_buff.head = NULL; + kingsun->rx_unwrap_buff.in_frame = FALSE; + kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; + kingsun->receiving = 0; + + /* Stop and remove instance of IrLAP */ + irlap_close(kingsun->irlap); + + kingsun->irlap = NULL; + + return 0; +} + +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int ksdazzle_net_ioctl(struct net_device *netdev, struct ifreq *rq, + int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *)rq; + struct ksdazzle_cb *kingsun = netdev_priv(netdev); + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the device is still there */ + if (netif_device_present(kingsun->netdev)) + return ksdazzle_change_speed(kingsun, + irq->ifr_baudrate); + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the IrDA stack is still there */ + if (netif_running(kingsun->netdev)) + irda_device_set_media_busy(kingsun->netdev, TRUE); + break; + + case SIOCGRECEIVING: + /* Only approximately true */ + irq->ifr_receiving = kingsun->receiving; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static const struct net_device_ops ksdazzle_ops = { + .ndo_start_xmit = ksdazzle_hard_xmit, + .ndo_open = ksdazzle_net_open, + .ndo_stop = ksdazzle_net_close, + .ndo_do_ioctl = ksdazzle_net_ioctl, +}; + +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + */ +static int ksdazzle_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + + struct usb_device *dev = interface_to_usbdev(intf); + struct ksdazzle_cb *kingsun = NULL; + struct net_device *net = NULL; + int ret = -ENOMEM; + int pipe, maxp_in, maxp_out; + __u8 ep_in; + __u8 ep_out; + + /* Check that there really are two interrupt endpoints. Check based on the + one in drivers/usb/input/usbmouse.c + */ + interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints != 2) { + dev_err(&intf->dev, "ksdazzle: expected 2 endpoints, found %d\n", + interface->desc.bNumEndpoints); + return -ENODEV; + } + endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; + if (!usb_endpoint_is_int_in(endpoint)) { + dev_err(&intf->dev, + "ksdazzle: endpoint 0 is not interrupt IN\n"); + return -ENODEV; + } + + ep_in = endpoint->bEndpointAddress; + pipe = usb_rcvintpipe(dev, ep_in); + maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + if (maxp_in > 255 || maxp_in <= 1) { + dev_err(&intf->dev, + "ksdazzle: endpoint 0 has max packet size %d not in range [2..255]\n", + maxp_in); + return -ENODEV; + } + + endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; + if (!usb_endpoint_is_int_out(endpoint)) { + dev_err(&intf->dev, + "ksdazzle: endpoint 1 is not interrupt OUT\n"); + return -ENODEV; + } + + ep_out = endpoint->bEndpointAddress; + pipe = usb_sndintpipe(dev, ep_out); + maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + /* Allocate network device container. */ + net = alloc_irdadev(sizeof(*kingsun)); + if (!net) + goto err_out1; + + SET_NETDEV_DEV(net, &intf->dev); + kingsun = netdev_priv(net); + kingsun->netdev = net; + kingsun->usbdev = dev; + kingsun->ep_in = ep_in; + kingsun->ep_out = ep_out; + kingsun->irlap = NULL; + kingsun->tx_urb = NULL; + kingsun->tx_buf_clear = NULL; + kingsun->tx_buf_clear_used = 0; + kingsun->tx_buf_clear_sent = 0; + + kingsun->rx_urb = NULL; + kingsun->rx_buf = NULL; + kingsun->rx_unwrap_buff.in_frame = FALSE; + kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; + kingsun->rx_unwrap_buff.skb = NULL; + kingsun->receiving = 0; + spin_lock_init(&kingsun->lock); + + kingsun->speed_setuprequest = NULL; + kingsun->speed_urb = NULL; + kingsun->speedparams.baudrate = 0; + + /* Allocate input buffer */ + kingsun->rx_buf = kmalloc(KINGSUN_RCV_MAX, GFP_KERNEL); + if (!kingsun->rx_buf) + goto free_mem; + + /* Allocate output buffer */ + kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL); + if (!kingsun->tx_buf_clear) + goto free_mem; + + /* Allocate and initialize speed setup packet */ + kingsun->speed_setuprequest = + kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!kingsun->speed_setuprequest) + goto free_mem; + kingsun->speed_setuprequest->bRequestType = + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND; + kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200); + kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001); + kingsun->speed_setuprequest->wLength = + cpu_to_le16(sizeof(struct ksdazzle_speedparams)); + + printk(KERN_INFO "KingSun/Dazzle IRDA/USB found at address %d, " + "Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&kingsun->qos); + + /* Baud rates known to be supported. Please uncomment if devices (other + than a SonyEriccson K300 phone) can be shown to support higher speeds + with this dongle. + */ + kingsun->qos.baud_rate.bits = + IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200; + kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; + irda_qos_bits_to_value(&kingsun->qos); + + /* Override the network functions we need to use */ + net->netdev_ops = &ksdazzle_ops; + + ret = register_netdev(net); + if (ret != 0) + goto free_mem; + + dev_info(&net->dev, "IrDA: Registered KingSun/Dazzle device %s\n", + net->name); + + usb_set_intfdata(intf, kingsun); + + /* Situation at this point: + - all work buffers allocated + - setup requests pre-filled + - urbs not allocated, set to NULL + - max rx packet known (is KINGSUN_FIFO_SIZE) + - unwrap state machine (partially) initialized, but skb == NULL + */ + + return 0; + + free_mem: + kfree(kingsun->speed_setuprequest); + kfree(kingsun->tx_buf_clear); + kfree(kingsun->rx_buf); + free_netdev(net); + err_out1: + return ret; +} + +/* + * The current device is removed, the USB layer tell us to shut it down... + */ +static void ksdazzle_disconnect(struct usb_interface *intf) +{ + struct ksdazzle_cb *kingsun = usb_get_intfdata(intf); + + if (!kingsun) + return; + + unregister_netdev(kingsun->netdev); + + /* Mop up receive && transmit urb's */ + usb_kill_urb(kingsun->speed_urb); + usb_free_urb(kingsun->speed_urb); + kingsun->speed_urb = NULL; + + usb_kill_urb(kingsun->tx_urb); + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + + usb_kill_urb(kingsun->rx_urb); + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + + kfree(kingsun->speed_setuprequest); + kfree(kingsun->tx_buf_clear); + kfree(kingsun->rx_buf); + free_netdev(kingsun->netdev); + + usb_set_intfdata(intf, NULL); +} + +#ifdef CONFIG_PM +/* USB suspend, so power off the transmitter/receiver */ +static int ksdazzle_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct ksdazzle_cb *kingsun = usb_get_intfdata(intf); + + netif_device_detach(kingsun->netdev); + if (kingsun->speed_urb != NULL) + usb_kill_urb(kingsun->speed_urb); + if (kingsun->tx_urb != NULL) + usb_kill_urb(kingsun->tx_urb); + if (kingsun->rx_urb != NULL) + usb_kill_urb(kingsun->rx_urb); + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int ksdazzle_resume(struct usb_interface *intf) +{ + struct ksdazzle_cb *kingsun = usb_get_intfdata(intf); + + if (kingsun->rx_urb != NULL) { + /* Setup request already filled in ksdazzle_probe */ + usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + } + netif_device_attach(kingsun->netdev); + + return 0; +} +#endif + +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .name = "ksdazzle-sir", + .probe = ksdazzle_probe, + .disconnect = ksdazzle_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = ksdazzle_suspend, + .resume = ksdazzle_resume, +#endif +}; + +module_usb_driver(irda_driver); + +MODULE_AUTHOR("Alex Villacís Lasso "); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun Dazzle"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/litelink-sir.c b/drivers/staging/irda/drivers/litelink-sir.c new file mode 100644 index 000000000000..8eefcb44bac3 --- /dev/null +++ b/drivers/staging/irda/drivers/litelink-sir.c @@ -0,0 +1,199 @@ +/********************************************************************* + * + * Filename: litelink.c + * Version: 1.1 + * Description: Driver for the Parallax LiteLink dongle + * Status: Stable + * Author: Dag Brattli + * Created at: Fri May 7 12:50:33 1999 + * Modified at: Fri Dec 17 09:14:23 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + ********************************************************************/ + +/* + * Modified at: Thu Jan 15 2003 + * Modified by: Eugene Crosser + * + * Convert to "new" IRDA infrastructure for kernel 2.6 + */ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */ +#define MAX_DELAY 10000 /* 1 ms */ + +static int litelink_open(struct sir_dev *dev); +static int litelink_close(struct sir_dev *dev); +static int litelink_change_speed(struct sir_dev *dev, unsigned speed); +static int litelink_reset(struct sir_dev *dev); + +/* These are the baudrates supported - 9600 must be last one! */ +static unsigned baud_rates[] = { 115200, 57600, 38400, 19200, 9600 }; + +static struct dongle_driver litelink = { + .owner = THIS_MODULE, + .driver_name = "Parallax LiteLink", + .type = IRDA_LITELINK_DONGLE, + .open = litelink_open, + .close = litelink_close, + .reset = litelink_reset, + .set_speed = litelink_change_speed, +}; + +static int __init litelink_sir_init(void) +{ + return irda_register_dongle(&litelink); +} + +static void __exit litelink_sir_cleanup(void) +{ + irda_unregister_dongle(&litelink); +} + +static int litelink_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + /* Power up dongle */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Set the speeds we can accept */ + qos->baud_rate.bits &= IR_115200|IR_57600|IR_38400|IR_19200|IR_9600; + qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int litelink_close(struct sir_dev *dev) +{ + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function litelink_change_speed (task) + * + * Change speed of the Litelink dongle. To cycle through the available + * baud rates, pulse RTS low for a few ms. + */ +static int litelink_change_speed(struct sir_dev *dev, unsigned speed) +{ + int i; + + /* dongle already reset by irda-thread - current speed (dongle and + * port) is the default speed (115200 for litelink!) + */ + + /* Cycle through avaiable baudrates until we reach the correct one */ + for (i = 0; baud_rates[i] != speed; i++) { + + /* end-of-list reached due to invalid speed request */ + if (baud_rates[i] == 9600) + break; + + /* Set DTR, clear RTS */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Set DTR, Set RTS */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + } + + dev->speed = baud_rates[i]; + + /* invalid baudrate should not happen - but if, we return -EINVAL and + * the dongle configured for 9600 so the stack has a chance to recover + */ + + return (dev->speed == speed) ? 0 : -EINVAL; +} + +/* + * Function litelink_reset (task) + * + * Reset the Litelink type dongle. + * + */ +static int litelink_reset(struct sir_dev *dev) +{ + /* probably the power-up can be dropped here, but with only + * 15 usec delay it's not worth the risk unless somebody with + * the hardware confirms it doesn't break anything... + */ + + /* Power on dongle */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Clear RTS to reset dongle */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* This dongles speed defaults to 115200 bps */ + dev->speed = 115200; + + return 0; +} + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("Parallax Litelink dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-5"); /* IRDA_LITELINK_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize Litelink module + * + */ +module_init(litelink_sir_init); + +/* + * Function cleanup_module (void) + * + * Cleanup Litelink module + * + */ +module_exit(litelink_sir_cleanup); diff --git a/drivers/staging/irda/drivers/ma600-sir.c b/drivers/staging/irda/drivers/ma600-sir.c new file mode 100644 index 000000000000..a764817b47f1 --- /dev/null +++ b/drivers/staging/irda/drivers/ma600-sir.c @@ -0,0 +1,253 @@ +/********************************************************************* + * + * Filename: ma600.c + * Version: 0.1 + * Description: Implementation of the MA600 dongle + * Status: Experimental. + * Author: Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95 + * Created at: Sat Jun 10 20:02:35 2000 + * Modified at: Sat Aug 16 09:34:13 2003 + * Modified by: Martin Diehl (modified for new sir_dev) + * + * Note: very thanks to Mr. Maru Wang for providing + * information on the MA600 dongle + * + * Copyright (c) 2000 Leung, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + ********************************************************************/ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +static int ma600_open(struct sir_dev *); +static int ma600_close(struct sir_dev *); +static int ma600_change_speed(struct sir_dev *, unsigned); +static int ma600_reset(struct sir_dev *); + +/* control byte for MA600 */ +#define MA600_9600 0x00 +#define MA600_19200 0x01 +#define MA600_38400 0x02 +#define MA600_57600 0x03 +#define MA600_115200 0x04 +#define MA600_DEV_ID1 0x05 +#define MA600_DEV_ID2 0x06 +#define MA600_2400 0x08 + +static struct dongle_driver ma600 = { + .owner = THIS_MODULE, + .driver_name = "MA600", + .type = IRDA_MA600_DONGLE, + .open = ma600_open, + .close = ma600_close, + .reset = ma600_reset, + .set_speed = ma600_change_speed, +}; + + +static int __init ma600_sir_init(void) +{ + return irda_register_dongle(&ma600); +} + +static void __exit ma600_sir_cleanup(void) +{ + irda_unregister_dongle(&ma600); +} + +/* + Power on: + (0) Clear RTS and DTR for 1 second + (1) Set RTS and DTR for 1 second + (2) 9600 bps now + Note: assume RTS, DTR are clear before +*/ +static int ma600_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Explicitly set the speeds we can accept */ + qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400 + |IR_57600|IR_115200; + /* Hm, 0x01 means 10ms - for >= 1ms we would need 0x07 */ + qos->min_turn_time.bits = 0x01; /* Needs at least 1 ms */ + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int ma600_close(struct sir_dev *dev) +{ + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +static __u8 get_control_byte(__u32 speed) +{ + __u8 byte; + + switch (speed) { + default: + case 115200: + byte = MA600_115200; + break; + case 57600: + byte = MA600_57600; + break; + case 38400: + byte = MA600_38400; + break; + case 19200: + byte = MA600_19200; + break; + case 9600: + byte = MA600_9600; + break; + case 2400: + byte = MA600_2400; + break; + } + + return byte; +} + +/* + * Function ma600_change_speed (dev, speed) + * + * Set the speed for the MA600 type dongle. + * + * The dongle has already been reset to a known state (dongle default) + * We cycle through speeds by pulsing RTS low and then high. + */ + +/* + * Function ma600_change_speed (dev, speed) + * + * Set the speed for the MA600 type dongle. + * + * Algorithm + * 1. Reset (already done by irda thread state machine) + * 2. clear RTS, set DTR and wait for 1ms + * 3. send Control Byte to the MA600 through TXD to set new baud rate + * wait until the stop bit of Control Byte is sent (for 9600 baud rate, + * it takes about 10 msec) + * 4. set RTS, set DTR (return to NORMAL Operation) + * 5. wait at least 10 ms, new setting (baud rate, etc) takes effect here + * after + */ + +/* total delays are only about 20ms - let's just sleep for now to + * avoid the state machine complexity before we get things working + */ + +static int ma600_change_speed(struct sir_dev *dev, unsigned speed) +{ + u8 byte; + + pr_debug("%s(), speed=%d (was %d)\n", __func__, + speed, dev->speed); + + /* dongle already reset, dongle and port at default speed (9600) */ + + /* Set RTS low for 1 ms */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + mdelay(1); + + /* Write control byte */ + byte = get_control_byte(speed); + sirdev_raw_write(dev, &byte, sizeof(byte)); + + /* Wait at least 10ms: fake wait_until_sent - 10 bits at 9600 baud*/ + msleep(15); /* old ma600 uses 15ms */ + +#if 1 + /* read-back of the control byte. ma600 is the first dongle driver + * which uses this so there might be some unidentified issues. + * Disable this in case of problems with readback. + */ + + sirdev_raw_read(dev, &byte, sizeof(byte)); + if (byte != get_control_byte(speed)) { + net_warn_ratelimited("%s(): bad control byte read-back %02x != %02x\n", + __func__, (unsigned)byte, + (unsigned)get_control_byte(speed)); + return -1; + } + else + pr_debug("%s() control byte write read OK\n", __func__); +#endif + + /* Set DTR, Set RTS */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Wait at least 10ms */ + msleep(10); + + /* dongle is now switched to the new speed */ + dev->speed = speed; + + return 0; +} + +/* + * Function ma600_reset (dev) + * + * This function resets the ma600 dongle. + * + * Algorithm: + * 0. DTR=0, RTS=1 and wait 10 ms + * 1. DTR=1, RTS=1 and wait 10 ms + * 2. 9600 bps now + */ + +/* total delays are only about 20ms - let's just sleep for now to + * avoid the state machine complexity before we get things working + */ + +static int ma600_reset(struct sir_dev *dev) +{ + /* Reset the dongle : set DTR low for 10 ms */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + msleep(10); + + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + msleep(10); + + dev->speed = 9600; /* That's the dongle-default */ + + return 0; +} + +MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95"); +MODULE_DESCRIPTION("MA600 dongle driver version 0.1"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */ + +module_init(ma600_sir_init); +module_exit(ma600_sir_cleanup); + diff --git a/drivers/staging/irda/drivers/mcp2120-sir.c b/drivers/staging/irda/drivers/mcp2120-sir.c new file mode 100644 index 000000000000..2e33f91bfe8f --- /dev/null +++ b/drivers/staging/irda/drivers/mcp2120-sir.c @@ -0,0 +1,224 @@ +/********************************************************************* + * + * + * Filename: mcp2120.c + * Version: 1.0 + * Description: Implementation for the MCP2120 (Microchip) + * Status: Experimental. + * Author: Felix Tang (tangf@eyetap.org) + * Created at: Sun Mar 31 19:32:12 EST 2002 + * Based on code by: Dag Brattli + * + * Copyright (c) 2002 Felix Tang, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + ********************************************************************/ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +static int mcp2120_reset(struct sir_dev *dev); +static int mcp2120_open(struct sir_dev *dev); +static int mcp2120_close(struct sir_dev *dev); +static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed); + +#define MCP2120_9600 0x87 +#define MCP2120_19200 0x8B +#define MCP2120_38400 0x85 +#define MCP2120_57600 0x83 +#define MCP2120_115200 0x81 + +#define MCP2120_COMMIT 0x11 + +static struct dongle_driver mcp2120 = { + .owner = THIS_MODULE, + .driver_name = "Microchip MCP2120", + .type = IRDA_MCP2120_DONGLE, + .open = mcp2120_open, + .close = mcp2120_close, + .reset = mcp2120_reset, + .set_speed = mcp2120_change_speed, +}; + +static int __init mcp2120_sir_init(void) +{ + return irda_register_dongle(&mcp2120); +} + +static void __exit mcp2120_sir_cleanup(void) +{ + irda_unregister_dongle(&mcp2120); +} + +static int mcp2120_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + /* seems no explicit power-on required here and reset switching it on anyway */ + + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x01; + irda_qos_bits_to_value(qos); + + return 0; +} + +static int mcp2120_close(struct sir_dev *dev) +{ + /* Power off dongle */ + /* reset and inhibit mcp2120 */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + // sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function mcp2120_change_speed (dev, speed) + * + * Set the speed for the MCP2120. + * + */ + +#define MCP2120_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED+1) + +static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + u8 control[2]; + static int ret = 0; + + switch (state) { + case SIRDEV_STATE_DONGLE_SPEED: + /* Set DTR to enter command mode */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + udelay(500); + + ret = 0; + switch (speed) { + default: + speed = 9600; + ret = -EINVAL; + /* fall through */ + case 9600: + control[0] = MCP2120_9600; + //printk("mcp2120 9600\n"); + break; + case 19200: + control[0] = MCP2120_19200; + //printk("mcp2120 19200\n"); + break; + case 34800: + control[0] = MCP2120_38400; + //printk("mcp2120 38400\n"); + break; + case 57600: + control[0] = MCP2120_57600; + //printk("mcp2120 57600\n"); + break; + case 115200: + control[0] = MCP2120_115200; + //printk("mcp2120 115200\n"); + break; + } + control[1] = MCP2120_COMMIT; + + /* Write control bytes */ + sirdev_raw_write(dev, control, 2); + dev->speed = speed; + + state = MCP2120_STATE_WAIT_SPEED; + delay = 100; + //printk("mcp2120_change_speed: dongle_speed\n"); + break; + + case MCP2120_STATE_WAIT_SPEED: + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + //printk("mcp2120_change_speed: mcp_wait\n"); + break; + + default: + net_err_ratelimited("%s(), undefine state %d\n", + __func__, state); + ret = -EINVAL; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +/* + * Function mcp2120_reset (driver) + * + * This function resets the mcp2120 dongle. + * + * Info: -set RTS to reset mcp2120 + * -set DTR to set mcp2120 software command mode + * -mcp2120 defaults to 9600 baud after reset + * + * Algorithm: + * 0. Set RTS to reset mcp2120. + * 1. Clear RTS and wait for device reset timer of 30 ms (max). + * + */ + +#define MCP2120_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1) +#define MCP2120_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2) + +static int mcp2120_reset(struct sir_dev *dev) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + int ret = 0; + + switch (state) { + case SIRDEV_STATE_DONGLE_RESET: + //printk("mcp2120_reset: dongle_reset\n"); + /* Reset dongle by setting RTS*/ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + state = MCP2120_STATE_WAIT1_RESET; + delay = 50; + break; + + case MCP2120_STATE_WAIT1_RESET: + //printk("mcp2120_reset: mcp2120_wait1\n"); + /* clear RTS and wait for at least 30 ms. */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + state = MCP2120_STATE_WAIT2_RESET; + delay = 50; + break; + + case MCP2120_STATE_WAIT2_RESET: + //printk("mcp2120_reset mcp2120_wait2\n"); + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + break; + + default: + net_err_ratelimited("%s(), undefined state %d\n", + __func__, state); + ret = -EINVAL; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +MODULE_AUTHOR("Felix Tang "); +MODULE_DESCRIPTION("Microchip MCP2120"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */ + +module_init(mcp2120_sir_init); +module_exit(mcp2120_sir_cleanup); diff --git a/drivers/staging/irda/drivers/mcs7780.c b/drivers/staging/irda/drivers/mcs7780.c new file mode 100644 index 000000000000..c3f0b254b344 --- /dev/null +++ b/drivers/staging/irda/drivers/mcs7780.c @@ -0,0 +1,987 @@ +/***************************************************************************** +* +* Filename: mcs7780.c +* Version: 0.4-alpha +* Description: Irda MosChip USB Dongle Driver +* Authors: Lukasz Stelmach +* Brian Pugh +* Judy Fischbach +* +* Based on stir4200 driver, but some things done differently. +* Based on earlier driver by Paul Stewart +* +* Copyright (C) 2000, Roman Weissgaerber +* Copyright (C) 2001, Dag Brattli +* Copyright (C) 2001, Jean Tourrilhes +* Copyright (C) 2004, Stephen Hemminger +* Copyright (C) 2005, Lukasz Stelmach +* Copyright (C) 2005, Brian Pugh +* Copyright (C) 2005, Judy Fischbach +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * MCS7780 is a simple USB to IrDA bridge by MosChip. It is neither + * compatibile with irda-usb nor with stir4200. Although it is quite + * similar to the later as far as general idea of operation is concerned. + * That is it requires the software to do all the framing job at SIR speeds. + * The hardware does take care of the framing at MIR and FIR speeds. + * It supports all speeds from 2400 through 4Mbps + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "mcs7780.h" + +#define MCS_VENDOR_ID 0x9710 +#define MCS_PRODUCT_ID 0x7780 + +static const struct usb_device_id mcs_table[] = { + /* MosChip Corp., MCS7780 FIR-USB Adapter */ + {USB_DEVICE(MCS_VENDOR_ID, MCS_PRODUCT_ID)}, + {}, +}; + +MODULE_AUTHOR("Brian Pugh "); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for MosChip MCS7780"); +MODULE_VERSION("0.3alpha"); +MODULE_LICENSE("GPL"); + +MODULE_DEVICE_TABLE(usb, mcs_table); + +static int qos_mtt_bits = 0x07 /* > 1ms */ ; +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); + +static int receive_mode = 0x1; +module_param(receive_mode, int, 0); +MODULE_PARM_DESC(receive_mode, + "Receive mode of the device (1:fast, 0:slow, default:1)"); + +static int sir_tweak = 1; +module_param(sir_tweak, int, 0444); +MODULE_PARM_DESC(sir_tweak, + "Default pulse width (1:1.6us, 0:3/16 bit, default:1)."); + +static int transceiver_type = MCS_TSC_VISHAY; +module_param(transceiver_type, int, 0444); +MODULE_PARM_DESC(transceiver_type, "IR transceiver type, see mcs7780.h."); + +static struct usb_driver mcs_driver = { + .name = "mcs7780", + .probe = mcs_probe, + .disconnect = mcs_disconnect, + .id_table = mcs_table, +}; + +/* speed flag selection by direct addressing. +addr = (speed >> 8) & 0x0f + +0x1 57600 0x2 115200 0x4 1152000 0x5 9600 +0x6 38400 0x9 2400 0xa 576000 0xb 19200 + +4Mbps (or 2400) must be checked separately. Since it also has +to be programmed in a different manner that is not a big problem. +*/ +static __u16 mcs_speed_set[16] = { 0, + MCS_SPEED_57600, + MCS_SPEED_115200, + 0, + MCS_SPEED_1152000, + MCS_SPEED_9600, + MCS_SPEED_38400, + 0, 0, + MCS_SPEED_2400, + MCS_SPEED_576000, + MCS_SPEED_19200, + 0, 0, 0, +}; + +/* Set given 16 bit register with a 16 bit value. Send control message + * to set dongle register. */ +static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val) +{ + struct usb_device *dev = mcs->usbdev; + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, + MCS_WR_RTYPE, val, reg, NULL, 0, + msecs_to_jiffies(MCS_CTRL_TIMEOUT)); +} + +/* Get 16 bit register value. Send contol message to read dongle register. */ +static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val) +{ + struct usb_device *dev = mcs->usbdev; + void *dmabuf; + int ret; + + dmabuf = kmalloc(sizeof(__u16), GFP_KERNEL); + if (!dmabuf) + return -ENOMEM; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, + MCS_RD_RTYPE, 0, reg, dmabuf, 2, + msecs_to_jiffies(MCS_CTRL_TIMEOUT)); + + memcpy(val, dmabuf, sizeof(__u16)); + kfree(dmabuf); + + return ret; +} + +/* Setup a communication between mcs7780 and TFDU chips. It is described + * in more detail in the data sheet. The setup sequence puts the the + * vishay tranceiver into high speed mode. It will also receive SIR speed + * packets but at reduced sensitivity. + */ + +/* 0: OK 1:ERROR */ +static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs) +{ + int ret = 0; + __u16 rval; + + /* mcs_get_reg should read exactly two bytes from the dongle */ + ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); + if (unlikely(ret != 2)) { + ret = -EIO; + goto error; + } + + /* The MCS_XCVR_CONF bit puts the transceiver into configuration + * mode. The MCS_MODE0 bit must start out high (1) and then + * transition to low and the MCS_STFIR and MCS_MODE1 bits must + * be low. + */ + rval |= (MCS_MODE0 | MCS_XCVR_CONF); + rval &= ~MCS_STFIR; + rval &= ~MCS_MODE1; + ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); + if (unlikely(ret)) + goto error; + + rval &= ~MCS_MODE0; + ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); + if (unlikely(ret)) + goto error; + + rval &= ~MCS_XCVR_CONF; + ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); + if (unlikely(ret)) + goto error; + + ret = 0; +error: + return ret; +} + +/* Setup a communication between mcs7780 and agilent chip. */ +static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs) +{ + net_warn_ratelimited("This transceiver type is not supported yet\n"); + return 1; +} + +/* Setup a communication between mcs7780 and sharp chip. */ +static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs) +{ + net_warn_ratelimited("This transceiver type is not supported yet\n"); + return 1; +} + +/* Common setup for all transceivers */ +static inline int mcs_setup_transceiver(struct mcs_cb *mcs) +{ + int ret = 0; + __u16 rval; + const char *msg; + + msg = "Basic transceiver setup error"; + + /* read value of MODE Register, set the DRIVER and RESET bits + * and write value back out to MODE Register + */ + ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); + if(unlikely(ret != 2)) + goto error; + rval |= MCS_DRIVER; /* put the mcs7780 into configuration mode. */ + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + if(unlikely(ret)) + goto error; + + rval = 0; /* set min pulse width to 0 initially. */ + ret = mcs_set_reg(mcs, MCS_MINRXPW_REG, rval); + if(unlikely(ret)) + goto error; + + ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); + if(unlikely(ret != 2)) + goto error; + + rval &= ~MCS_FIR; /* turn off fir mode. */ + if(mcs->sir_tweak) + rval |= MCS_SIR16US; /* 1.6us pulse width */ + else + rval &= ~MCS_SIR16US; /* 3/16 bit time pulse width */ + + /* make sure ask mode and back to back packets are off. */ + rval &= ~(MCS_BBTG | MCS_ASK); + + rval &= ~MCS_SPEED_MASK; + rval |= MCS_SPEED_9600; /* make sure initial speed is 9600. */ + mcs->speed = 9600; + mcs->new_speed = 0; /* new_speed is set to 0 */ + rval &= ~MCS_PLLPWDN; /* disable power down. */ + + /* make sure device determines direction and that the auto send sip + * pulse are on. + */ + rval |= MCS_DTD | MCS_SIPEN; + + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + if(unlikely(ret)) + goto error; + + msg = "transceiver model specific setup error"; + switch (mcs->transceiver_type) { + case MCS_TSC_VISHAY: + ret = mcs_setup_transceiver_vishay(mcs); + break; + + case MCS_TSC_SHARP: + ret = mcs_setup_transceiver_sharp(mcs); + break; + + case MCS_TSC_AGILENT: + ret = mcs_setup_transceiver_agilent(mcs); + break; + + default: + net_warn_ratelimited("Unknown transceiver type: %d\n", + mcs->transceiver_type); + ret = 1; + } + if (unlikely(ret)) + goto error; + + /* If transceiver is not SHARP, then if receive mode set + * on the RXFAST bit in the XCVR Register otherwise unset it + */ + if (mcs->transceiver_type != MCS_TSC_SHARP) { + + ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); + if (unlikely(ret != 2)) + goto error; + if (mcs->receive_mode) + rval |= MCS_RXFAST; + else + rval &= ~MCS_RXFAST; + ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); + if (unlikely(ret)) + goto error; + } + + msg = "transceiver reset"; + + ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); + if (unlikely(ret != 2)) + goto error; + + /* reset the mcs7780 so all changes take effect. */ + rval &= ~MCS_RESET; + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + if (unlikely(ret)) + goto error; + else + return ret; + +error: + net_err_ratelimited("%s\n", msg); + return ret; +} + +/* Wraps the data in format for SIR */ +static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf) +{ + int wraplen; + + /* 2: full frame length, including "the length" */ + wraplen = async_wrap_skb(skb, buf + 2, 4094); + + wraplen += 2; + buf[0] = wraplen & 0xff; + buf[1] = (wraplen >> 8) & 0xff; + + return wraplen; +} + +/* Wraps the data in format for FIR */ +static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf) +{ + unsigned int len = 0; + __u32 fcs = ~(crc32_le(~0, skb->data, skb->len)); + + /* add 2 bytes for length value and 4 bytes for fcs. */ + len = skb->len + 6; + + /* The mcs7780 requires that the first two bytes are the packet + * length in little endian order. Note: the length value includes + * the two bytes for the length value itself. + */ + buf[0] = len & 0xff; + buf[1] = (len >> 8) & 0xff; + /* copy the data into the tx buffer. */ + skb_copy_from_linear_data(skb, buf + 2, skb->len); + /* put the fcs in the last four bytes in little endian order. */ + buf[len - 4] = fcs & 0xff; + buf[len - 3] = (fcs >> 8) & 0xff; + buf[len - 2] = (fcs >> 16) & 0xff; + buf[len - 1] = (fcs >> 24) & 0xff; + + return len; +} + +/* Wraps the data in format for MIR */ +static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf) +{ + __u16 fcs = 0; + int len = skb->len + 4; + + fcs = ~(irda_calc_crc16(~fcs, skb->data, skb->len)); + /* put the total packet length in first. Note: packet length + * value includes the two bytes that hold the packet length + * itself. + */ + buf[0] = len & 0xff; + buf[1] = (len >> 8) & 0xff; + /* copy the data */ + skb_copy_from_linear_data(skb, buf + 2, skb->len); + /* put the fcs in last two bytes in little endian order. */ + buf[len - 2] = fcs & 0xff; + buf[len - 1] = (fcs >> 8) & 0xff; + + return len; +} + +/* Unwrap received packets at MIR speed. A 16 bit crc_ccitt checksum is + * used for the fcs. When performed over the entire packet the result + * should be GOOD_FCS = 0xf0b8. Hands the unwrapped data off to the IrDA + * layer via a sk_buff. + */ +static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len) +{ + __u16 fcs; + int new_len; + struct sk_buff *skb; + + /* Assume that the frames are going to fill a single packet + * rather than span multiple packets. + */ + + new_len = len - 2; + if(unlikely(new_len <= 0)) { + net_err_ratelimited("%s short frame length %d\n", + mcs->netdev->name, new_len); + ++mcs->netdev->stats.rx_errors; + ++mcs->netdev->stats.rx_length_errors; + return; + } + fcs = 0; + fcs = irda_calc_crc16(~fcs, buf, len); + + if(fcs != GOOD_FCS) { + net_err_ratelimited("crc error calc 0x%x len %d\n", + fcs, new_len); + mcs->netdev->stats.rx_errors++; + mcs->netdev->stats.rx_crc_errors++; + return; + } + + skb = dev_alloc_skb(new_len + 1); + if(unlikely(!skb)) { + ++mcs->netdev->stats.rx_dropped; + return; + } + + skb_reserve(skb, 1); + skb_copy_to_linear_data(skb, buf, new_len); + skb_put(skb, new_len); + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + skb->dev = mcs->netdev; + + netif_rx(skb); + + mcs->netdev->stats.rx_packets++; + mcs->netdev->stats.rx_bytes += new_len; +} + +/* Unwrap received packets at FIR speed. A 32 bit crc_ccitt checksum is + * used for the fcs. Hands the unwrapped data off to the IrDA + * layer via a sk_buff. + */ +static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len) +{ + __u32 fcs; + int new_len; + struct sk_buff *skb; + + /* Assume that the frames are going to fill a single packet + * rather than span multiple packets. This is most likely a false + * assumption. + */ + + new_len = len - 4; + if(unlikely(new_len <= 0)) { + net_err_ratelimited("%s short frame length %d\n", + mcs->netdev->name, new_len); + ++mcs->netdev->stats.rx_errors; + ++mcs->netdev->stats.rx_length_errors; + return; + } + + fcs = ~(crc32_le(~0, buf, new_len)); + if(fcs != get_unaligned_le32(buf + new_len)) { + net_err_ratelimited("crc error calc 0x%x len %d\n", + fcs, new_len); + mcs->netdev->stats.rx_errors++; + mcs->netdev->stats.rx_crc_errors++; + return; + } + + skb = dev_alloc_skb(new_len + 1); + if(unlikely(!skb)) { + ++mcs->netdev->stats.rx_dropped; + return; + } + + skb_reserve(skb, 1); + skb_copy_to_linear_data(skb, buf, new_len); + skb_put(skb, new_len); + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + skb->dev = mcs->netdev; + + netif_rx(skb); + + mcs->netdev->stats.rx_packets++; + mcs->netdev->stats.rx_bytes += new_len; +} + + +/* Allocates urbs for both receive and transmit. + * If alloc fails return error code 0 (fail) otherwise + * return error code 1 (success). + */ +static inline int mcs_setup_urbs(struct mcs_cb *mcs) +{ + mcs->rx_urb = NULL; + + mcs->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mcs->tx_urb) + return 0; + + mcs->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mcs->rx_urb) { + usb_free_urb(mcs->tx_urb); + mcs->tx_urb = NULL; + return 0; + } + + return 1; +} + +/* Sets up state to be initially outside frame, gets receive urb, + * sets status to successful and then submits the urb to start + * receiving the data. + */ +static inline int mcs_receive_start(struct mcs_cb *mcs) +{ + mcs->rx_buff.in_frame = FALSE; + mcs->rx_buff.state = OUTSIDE_FRAME; + + usb_fill_bulk_urb(mcs->rx_urb, mcs->usbdev, + usb_rcvbulkpipe(mcs->usbdev, mcs->ep_in), + mcs->in_buf, 4096, mcs_receive_irq, mcs); + + mcs->rx_urb->status = 0; + return usb_submit_urb(mcs->rx_urb, GFP_KERNEL); +} + +/* Finds the in and out endpoints for the mcs control block */ +static inline int mcs_find_endpoints(struct mcs_cb *mcs, + struct usb_host_endpoint *ep, int epnum) +{ + int i; + int ret = 0; + + /* If no place to store the endpoints just return */ + if (!ep) + return ret; + + /* cycle through all endpoints, find the first two that are DIR_IN */ + for (i = 0; i < epnum; i++) { + if (ep[i].desc.bEndpointAddress & USB_DIR_IN) + mcs->ep_in = ep[i].desc.bEndpointAddress; + else + mcs->ep_out = ep[i].desc.bEndpointAddress; + + /* MosChip says that the chip has only two bulk + * endpoints. Find one for each direction and move on. + */ + if ((mcs->ep_in != 0) && (mcs->ep_out != 0)) { + ret = 1; + break; + } + } + + return ret; +} + +static void mcs_speed_work(struct work_struct *work) +{ + struct mcs_cb *mcs = container_of(work, struct mcs_cb, work); + struct net_device *netdev = mcs->netdev; + + mcs_speed_change(mcs); + netif_wake_queue(netdev); +} + +/* Function to change the speed of the mcs7780. Fully supports SIR, + * MIR, and FIR speeds. + */ +static int mcs_speed_change(struct mcs_cb *mcs) +{ + int ret = 0; + int rst = 0; + int cnt = 0; + __u16 nspeed; + __u16 rval; + + nspeed = mcs_speed_set[(mcs->new_speed >> 8) & 0x0f]; + + do { + mcs_get_reg(mcs, MCS_RESV_REG, &rval); + } while(cnt++ < 100 && (rval & MCS_IRINTX)); + + if (cnt > 100) { + net_err_ratelimited("unable to change speed\n"); + ret = -EIO; + goto error; + } + + mcs_get_reg(mcs, MCS_MODE_REG, &rval); + + /* MINRXPW values recommended by MosChip */ + if (mcs->new_speed <= 115200) { + rval &= ~MCS_FIR; + + if ((rst = (mcs->speed > 115200))) + mcs_set_reg(mcs, MCS_MINRXPW_REG, 0); + + } else if (mcs->new_speed <= 1152000) { + rval &= ~MCS_FIR; + + if ((rst = !(mcs->speed == 576000 || mcs->speed == 1152000))) + mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); + + } else { + rval |= MCS_FIR; + + if ((rst = (mcs->speed != 4000000))) + mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); + + } + + rval &= ~MCS_SPEED_MASK; + rval |= nspeed; + + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + if (unlikely(ret)) + goto error; + + if (rst) + switch (mcs->transceiver_type) { + case MCS_TSC_VISHAY: + ret = mcs_setup_transceiver_vishay(mcs); + break; + + case MCS_TSC_SHARP: + ret = mcs_setup_transceiver_sharp(mcs); + break; + + case MCS_TSC_AGILENT: + ret = mcs_setup_transceiver_agilent(mcs); + break; + + default: + ret = 1; + net_warn_ratelimited("Unknown transceiver type: %d\n", + mcs->transceiver_type); + } + if (unlikely(ret)) + goto error; + + mcs_get_reg(mcs, MCS_MODE_REG, &rval); + rval &= ~MCS_RESET; + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + + mcs->speed = mcs->new_speed; +error: + mcs->new_speed = 0; + return ret; +} + +/* Ioctl calls not supported at this time. Can be an area of future work. */ +static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) +{ + /* struct if_irda_req *irq = (struct if_irda_req *)rq; */ + /* struct mcs_cb *mcs = netdev_priv(netdev); */ + int ret = 0; + + switch (cmd) { + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* Network device is taken down, done by "ifconfig irda0 down" */ +static int mcs_net_close(struct net_device *netdev) +{ + int ret = 0; + struct mcs_cb *mcs = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + kfree_skb(mcs->rx_buff.skb); + + /* kill and free the receive and transmit URBs */ + usb_kill_urb(mcs->rx_urb); + usb_free_urb(mcs->rx_urb); + usb_kill_urb(mcs->tx_urb); + usb_free_urb(mcs->tx_urb); + + /* Stop and remove instance of IrLAP */ + if (mcs->irlap) + irlap_close(mcs->irlap); + + mcs->irlap = NULL; + return ret; +} + +/* Network device is taken up, done by "ifconfig irda0 up" */ +static int mcs_net_open(struct net_device *netdev) +{ + struct mcs_cb *mcs = netdev_priv(netdev); + char hwname[16]; + int ret = 0; + + ret = usb_clear_halt(mcs->usbdev, + usb_sndbulkpipe(mcs->usbdev, mcs->ep_in)); + if (ret) + goto error1; + ret = usb_clear_halt(mcs->usbdev, + usb_rcvbulkpipe(mcs->usbdev, mcs->ep_out)); + if (ret) + goto error1; + + ret = mcs_setup_transceiver(mcs); + if (ret) + goto error1; + + ret = -ENOMEM; + + /* Initialize for SIR/FIR to copy data directly into skb. */ + mcs->receiving = 0; + mcs->rx_buff.truesize = IRDA_SKB_MAX_MTU; + mcs->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!mcs->rx_buff.skb) + goto error1; + + skb_reserve(mcs->rx_buff.skb, 1); + mcs->rx_buff.head = mcs->rx_buff.skb->data; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + * Note : will send immediately a speed change... + */ + sprintf(hwname, "usb#%d", mcs->usbdev->devnum); + mcs->irlap = irlap_open(netdev, &mcs->qos, hwname); + if (!mcs->irlap) { + net_err_ratelimited("mcs7780: irlap_open failed\n"); + goto error2; + } + + if (!mcs_setup_urbs(mcs)) + goto error3; + + ret = mcs_receive_start(mcs); + if (ret) + goto error4; + + netif_start_queue(netdev); + return 0; + +error4: + usb_free_urb(mcs->rx_urb); + usb_free_urb(mcs->tx_urb); +error3: + irlap_close(mcs->irlap); +error2: + kfree_skb(mcs->rx_buff.skb); +error1: + return ret; +} + +/* Receive callback function. */ +static void mcs_receive_irq(struct urb *urb) +{ + __u8 *bytes; + struct mcs_cb *mcs = urb->context; + int i; + int ret; + + if (!netif_running(mcs->netdev)) + return; + + if (urb->status) + return; + + if (urb->actual_length > 0) { + bytes = urb->transfer_buffer; + + /* MCS returns frames without BOF and EOF + * I assume it returns whole frames. + */ + /* SIR speed */ + if(mcs->speed < 576000) { + async_unwrap_char(mcs->netdev, &mcs->netdev->stats, + &mcs->rx_buff, 0xc0); + + for (i = 0; i < urb->actual_length; i++) + async_unwrap_char(mcs->netdev, &mcs->netdev->stats, + &mcs->rx_buff, bytes[i]); + + async_unwrap_char(mcs->netdev, &mcs->netdev->stats, + &mcs->rx_buff, 0xc1); + } + /* MIR speed */ + else if(mcs->speed == 576000 || mcs->speed == 1152000) { + mcs_unwrap_mir(mcs, urb->transfer_buffer, + urb->actual_length); + } + /* FIR speed */ + else { + mcs_unwrap_fir(mcs, urb->transfer_buffer, + urb->actual_length); + } + } + + ret = usb_submit_urb(urb, GFP_ATOMIC); +} + +/* Transmit callback function. */ +static void mcs_send_irq(struct urb *urb) +{ + struct mcs_cb *mcs = urb->context; + struct net_device *ndev = mcs->netdev; + + if (unlikely(mcs->new_speed)) + schedule_work(&mcs->work); + else + netif_wake_queue(ndev); +} + +/* Transmit callback function. */ +static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + unsigned long flags; + struct mcs_cb *mcs; + int wraplen; + int ret = 0; + + netif_stop_queue(ndev); + mcs = netdev_priv(ndev); + + spin_lock_irqsave(&mcs->lock, flags); + + mcs->new_speed = irda_get_next_speed(skb); + if (likely(mcs->new_speed == mcs->speed)) + mcs->new_speed = 0; + + /* SIR speed */ + if(mcs->speed < 576000) { + wraplen = mcs_wrap_sir_skb(skb, mcs->out_buf); + } + /* MIR speed */ + else if(mcs->speed == 576000 || mcs->speed == 1152000) { + wraplen = mcs_wrap_mir_skb(skb, mcs->out_buf); + } + /* FIR speed */ + else { + wraplen = mcs_wrap_fir_skb(skb, mcs->out_buf); + } + usb_fill_bulk_urb(mcs->tx_urb, mcs->usbdev, + usb_sndbulkpipe(mcs->usbdev, mcs->ep_out), + mcs->out_buf, wraplen, mcs_send_irq, mcs); + + if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) { + net_err_ratelimited("failed tx_urb: %d\n", ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + mcs->netdev->stats.tx_errors++; + netif_start_queue(ndev); + } + } else { + mcs->netdev->stats.tx_packets++; + mcs->netdev->stats.tx_bytes += skb->len; + } + + dev_kfree_skb(skb); + spin_unlock_irqrestore(&mcs->lock, flags); + return NETDEV_TX_OK; +} + +static const struct net_device_ops mcs_netdev_ops = { + .ndo_open = mcs_net_open, + .ndo_stop = mcs_net_close, + .ndo_start_xmit = mcs_hard_xmit, + .ndo_do_ioctl = mcs_net_ioctl, +}; + +/* + * This function is called by the USB subsystem for each new device in the + * system. Need to verify the device and if it is, then start handling it. + */ +static int mcs_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct net_device *ndev = NULL; + struct mcs_cb *mcs; + int ret = -ENOMEM; + + ndev = alloc_irdadev(sizeof(*mcs)); + if (!ndev) + goto error1; + + pr_debug("MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum); + + SET_NETDEV_DEV(ndev, &intf->dev); + + ret = usb_reset_configuration(udev); + if (ret != 0) { + net_err_ratelimited("mcs7780: usb reset configuration failed\n"); + goto error2; + } + + mcs = netdev_priv(ndev); + mcs->usbdev = udev; + mcs->netdev = ndev; + spin_lock_init(&mcs->lock); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&mcs->qos); + + /* That's the Rx capability. */ + mcs->qos.baud_rate.bits &= + IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 + | IR_576000 | IR_1152000 | (IR_4000000 << 8); + + + mcs->qos.min_turn_time.bits &= qos_mtt_bits; + irda_qos_bits_to_value(&mcs->qos); + + /* Speed change work initialisation*/ + INIT_WORK(&mcs->work, mcs_speed_work); + + ndev->netdev_ops = &mcs_netdev_ops; + + if (!intf->cur_altsetting) { + ret = -ENOMEM; + goto error2; + } + + ret = mcs_find_endpoints(mcs, intf->cur_altsetting->endpoint, + intf->cur_altsetting->desc.bNumEndpoints); + if (!ret) { + ret = -ENODEV; + goto error2; + } + + ret = register_netdev(ndev); + if (ret != 0) + goto error2; + + pr_debug("IrDA: Registered MosChip MCS7780 device as %s\n", + ndev->name); + + mcs->transceiver_type = transceiver_type; + mcs->sir_tweak = sir_tweak; + mcs->receive_mode = receive_mode; + + usb_set_intfdata(intf, mcs); + return 0; + +error2: + free_netdev(ndev); + +error1: + return ret; +} + +/* The current device is removed, the USB layer tells us to shut down. */ +static void mcs_disconnect(struct usb_interface *intf) +{ + struct mcs_cb *mcs = usb_get_intfdata(intf); + + if (!mcs) + return; + + cancel_work_sync(&mcs->work); + + unregister_netdev(mcs->netdev); + free_netdev(mcs->netdev); + + usb_set_intfdata(intf, NULL); + pr_debug("MCS7780 now disconnected.\n"); +} + +module_usb_driver(mcs_driver); diff --git a/drivers/staging/irda/drivers/mcs7780.h b/drivers/staging/irda/drivers/mcs7780.h new file mode 100644 index 000000000000..a6e8f7dbafc9 --- /dev/null +++ b/drivers/staging/irda/drivers/mcs7780.h @@ -0,0 +1,165 @@ +/***************************************************************************** +* +* Filename: mcs7780.h +* Version: 0.2-alpha +* Description: Irda MosChip USB Dongle +* Status: Experimental +* Authors: Lukasz Stelmach +* Brian Pugh +* +* Copyright (C) 2005, Lukasz Stelmach +* Copyright (C) 2005, Brian Pugh +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ +#ifndef _MCS7780_H +#define _MCS7780_H + +#define MCS_MODE_SIR 0 +#define MCS_MODE_MIR 1 +#define MCS_MODE_FIR 2 + +#define MCS_CTRL_TIMEOUT 500 +#define MCS_XMIT_TIMEOUT 500 +/* Possible transceiver types */ +#define MCS_TSC_VISHAY 0 /* Vishay TFD, default choice */ +#define MCS_TSC_AGILENT 1 /* Agilent 3602/3600 */ +#define MCS_TSC_SHARP 2 /* Sharp GP2W1000YP */ + +/* Requests */ +#define MCS_RD_RTYPE 0xC0 +#define MCS_WR_RTYPE 0x40 +#define MCS_RDREQ 0x0F +#define MCS_WRREQ 0x0E + +/* Register 0x00 */ +#define MCS_MODE_REG 0 +#define MCS_FIR ((__u16)0x0001) +#define MCS_SIR16US ((__u16)0x0002) +#define MCS_BBTG ((__u16)0x0004) +#define MCS_ASK ((__u16)0x0008) +#define MCS_PARITY ((__u16)0x0010) + +/* SIR/MIR speed constants */ +#define MCS_SPEED_SHIFT 5 +#define MCS_SPEED_MASK ((__u16)0x00E0) +#define MCS_SPEED(x) ((x & MCS_SPEED_MASK) >> MCS_SPEED_SHIFT) +#define MCS_SPEED_2400 ((0 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_9600 ((1 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_19200 ((2 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_38400 ((3 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_57600 ((4 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_115200 ((5 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_576000 ((6 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_1152000 ((7 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) + +#define MCS_PLLPWDN ((__u16)0x0100) +#define MCS_DRIVER ((__u16)0x0200) +#define MCS_DTD ((__u16)0x0400) +#define MCS_DIR ((__u16)0x0800) +#define MCS_SIPEN ((__u16)0x1000) +#define MCS_SENDSIP ((__u16)0x2000) +#define MCS_CHGDIR ((__u16)0x4000) +#define MCS_RESET ((__u16)0x8000) + +/* Register 0x02 */ +#define MCS_XCVR_REG 2 +#define MCS_MODE0 ((__u16)0x0001) +#define MCS_STFIR ((__u16)0x0002) +#define MCS_XCVR_CONF ((__u16)0x0004) +#define MCS_RXFAST ((__u16)0x0008) +/* TXCUR [6:4] */ +#define MCS_TXCUR_SHIFT 4 +#define MCS_TXCUR_MASK ((__u16)0x0070) +#define MCS_TXCUR(x) ((x & MCS_TXCUR_MASK) >> MCS_TXCUR_SHIFT) +#define MCS_SETTXCUR(x,y) \ + ((x & ~MCS_TXCUR_MASK) | (y << MCS_TXCUR_SHIFT) & MCS_TXCUR_MASK) + +#define MCS_MODE1 ((__u16)0x0080) +#define MCS_SMODE0 ((__u16)0x0100) +#define MCS_SMODE1 ((__u16)0x0200) +#define MCS_INVTX ((__u16)0x0400) +#define MCS_INVRX ((__u16)0x0800) + +#define MCS_MINRXPW_REG 4 + +#define MCS_RESV_REG 7 +#define MCS_IRINTX ((__u16)0x0001) +#define MCS_IRINRX ((__u16)0x0002) + +struct mcs_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct qos_info qos; + unsigned int speed; /* Current speed */ + unsigned int new_speed; /* new speed */ + + struct work_struct work; /* Change speed work */ + + struct sk_buff *tx_pending; + char in_buf[4096]; /* transmit/receive buffer */ + char out_buf[4096]; /* transmit/receive buffer */ + __u8 *fifo_status; + + iobuff_t rx_buff; /* receive unwrap state machine */ + spinlock_t lock; + int receiving; + + __u8 ep_in; + __u8 ep_out; + + struct urb *rx_urb; + struct urb *tx_urb; + + int transceiver_type; + int sir_tweak; + int receive_mode; +}; + +static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val); +static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val); + +static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs); +static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs); +static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs); +static inline int mcs_setup_transceiver(struct mcs_cb *mcs); +static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf); +static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf); +static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf); +static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len); +static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len); +static inline int mcs_setup_urbs(struct mcs_cb *mcs); +static inline int mcs_receive_start(struct mcs_cb *mcs); +static inline int mcs_find_endpoints(struct mcs_cb *mcs, + struct usb_host_endpoint *ep, int epnum); + +static int mcs_speed_change(struct mcs_cb *mcs); + +static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd); +static int mcs_net_close(struct net_device *netdev); +static int mcs_net_open(struct net_device *netdev); + +static void mcs_receive_irq(struct urb *urb); +static void mcs_send_irq(struct urb *urb); +static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb, + struct net_device *netdev); + +static int mcs_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void mcs_disconnect(struct usb_interface *intf); + +#endif /* _MCS7780_H */ diff --git a/drivers/staging/irda/drivers/nsc-ircc.c b/drivers/staging/irda/drivers/nsc-ircc.c new file mode 100644 index 000000000000..7beae147be11 --- /dev/null +++ b/drivers/staging/irda/drivers/nsc-ircc.c @@ -0,0 +1,2410 @@ +/********************************************************************* + * + * Filename: nsc-ircc.c + * Version: 1.0 + * Description: Driver for the NSC PC'108 and PC'338 IrDA chipsets + * Status: Stable. + * Author: Dag Brattli + * Created at: Sat Nov 7 21:43:15 1998 + * Modified at: Wed Mar 1 11:29:34 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-2000 Dag Brattli + * Copyright (c) 1998 Lichen Wang, + * Copyright (c) 1998 Actisys Corp., www.actisys.com + * Copyright (c) 2000-2004 Jean Tourrilhes + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + * Notice that all functions that needs to access the chip in _any_ + * way, must save BSR register on entry, and restore it on exit. + * It is _very_ important to follow this policy! + * + * __u8 bank; + * + * bank = inb(iobase+BSR); + * + * do_your_stuff_here(); + * + * outb(bank, iobase+BSR); + * + * If you find bugs in this file, its very likely that the same bug + * will also be in w83977af_ir.c since the implementations are quite + * similar. + * + ********************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "nsc-ircc.h" + +#define CHIP_IO_EXTENT 8 +#define BROKEN_DONGLE_ID + +static char *driver_name = "nsc-ircc"; + +/* Power Management */ +#define NSC_IRCC_DRIVER_NAME "nsc-ircc" +static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state); +static int nsc_ircc_resume(struct platform_device *dev); + +static struct platform_driver nsc_ircc_driver = { + .suspend = nsc_ircc_suspend, + .resume = nsc_ircc_resume, + .driver = { + .name = NSC_IRCC_DRIVER_NAME, + }, +}; + +/* Module parameters */ +static int qos_mtt_bits = 0x07; /* 1 ms or more */ +static int dongle_id; + +/* Use BIOS settions by default, but user may supply module parameters */ +static unsigned int io[] = { ~0, ~0, ~0, ~0, ~0 }; +static unsigned int irq[] = { 0, 0, 0, 0, 0 }; +static unsigned int dma[] = { 0, 0, 0, 0, 0 }; + +static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info); +#ifdef CONFIG_PNP +static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id); +#endif + +/* These are the known NSC chips */ +static nsc_chip_t chips[] = { +/* Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */ + { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, + nsc_ircc_probe_108, nsc_ircc_init_108 }, + { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8, + nsc_ircc_probe_338, nsc_ircc_init_338 }, + /* Contributed by Steffen Pingel - IBM X40 */ + { "PC8738x", { 0x164e, 0x4e, 0x2e }, 0x20, 0xf4, 0xff, + nsc_ircc_probe_39x, nsc_ircc_init_39x }, + /* Contributed by Jan Frey - IBM A30/A31 */ + { "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff, + nsc_ircc_probe_39x, nsc_ircc_init_39x }, + /* IBM ThinkPads using PC8738x (T60/X60/Z60) */ + { "IBM-PC8738x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff, + nsc_ircc_probe_39x, nsc_ircc_init_39x }, + /* IBM ThinkPads using PC8394T (T43/R52/?) */ + { "IBM-PC8394T", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf9, 0xff, + nsc_ircc_probe_39x, nsc_ircc_init_39x }, + { NULL } +}; + +static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL, NULL }; + +static char *dongle_types[] = { + "Differential serial interface", + "Differential serial interface", + "Reserved", + "Reserved", + "Sharp RY5HD01", + "Reserved", + "Single-ended serial interface", + "Consumer-IR only", + "HP HSDL-2300, HP HSDL-3600/HSDL-3610", + "IBM31T1100 or Temic TFDS6000/TFDS6500", + "Reserved", + "Reserved", + "HP HSDL-1100/HSDL-2100", + "HP HSDL-1100/HSDL-2100", + "Supports SIR Mode only", + "No dongle connected", +}; + +/* PNP probing */ +static chipio_t pnp_info; +static const struct pnp_device_id nsc_ircc_pnp_table[] = { + { .id = "NSC6001", .driver_data = 0 }, + { .id = "HWPC224", .driver_data = 0 }, + { .id = "IBM0071", .driver_data = NSC_FORCE_DONGLE_TYPE9 }, + { } +}; + +MODULE_DEVICE_TABLE(pnp, nsc_ircc_pnp_table); + +static struct pnp_driver nsc_ircc_pnp_driver = { +#ifdef CONFIG_PNP + .name = "nsc-ircc", + .id_table = nsc_ircc_pnp_table, + .probe = nsc_ircc_pnp_probe, +#endif +}; + +/* Some prototypes */ +static int nsc_ircc_open(chipio_t *info); +static int nsc_ircc_close(struct nsc_ircc_cb *self); +static int nsc_ircc_setup(chipio_t *info); +static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self); +static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self); +static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase); +static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb, + struct net_device *dev); +static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, + struct net_device *dev); +static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size); +static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase); +static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud); +static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self); +static int nsc_ircc_read_dongle_id (int iobase); +static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id); + +static int nsc_ircc_net_open(struct net_device *dev); +static int nsc_ircc_net_close(struct net_device *dev); +static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); + +/* Globals */ +static int pnp_registered; +static int pnp_succeeded; + +/* + * Function nsc_ircc_init () + * + * Initialize chip. Just try to find out how many chips we are dealing with + * and where they are + */ +static int __init nsc_ircc_init(void) +{ + chipio_t info; + nsc_chip_t *chip; + int ret; + int cfg_base; + int cfg, id; + int reg; + int i = 0; + + ret = platform_driver_register(&nsc_ircc_driver); + if (ret) { + net_err_ratelimited("%s, Can't register driver!\n", + driver_name); + return ret; + } + + /* Register with PnP subsystem to detect disable ports */ + ret = pnp_register_driver(&nsc_ircc_pnp_driver); + + if (!ret) + pnp_registered = 1; + + ret = -ENODEV; + + /* Probe for all the NSC chipsets we know about */ + for (chip = chips; chip->name ; chip++) { + pr_debug("%s(), Probing for %s ...\n", __func__, + chip->name); + + /* Try all config registers for this chip */ + for (cfg = 0; cfg < ARRAY_SIZE(chip->cfg); cfg++) { + cfg_base = chip->cfg[cfg]; + if (!cfg_base) + continue; + + /* Read index register */ + reg = inb(cfg_base); + if (reg == 0xff) { + pr_debug("%s() no chip at 0x%03x\n", + __func__, cfg_base); + continue; + } + + /* Read chip identification register */ + outb(chip->cid_index, cfg_base); + id = inb(cfg_base+1); + if ((id & chip->cid_mask) == chip->cid_value) { + pr_debug("%s() Found %s chip, revision=%d\n", + __func__, chip->name, + id & ~chip->cid_mask); + + /* + * If we found a correct PnP setting, + * we first try it. + */ + if (pnp_succeeded) { + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = pnp_info.fir_base; + info.dma = pnp_info.dma; + info.irq = pnp_info.irq; + + if (info.fir_base < 0x2000) { + net_info_ratelimited("%s, chip->init\n", + driver_name); + chip->init(chip, &info); + } else + chip->probe(chip, &info); + + if (nsc_ircc_open(&info) >= 0) + ret = 0; + } + + /* + * Opening based on PnP values failed. + * Let's fallback to user values, or probe + * the chip. + */ + if (ret) { + pr_debug("%s, PnP init failed\n", + driver_name); + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = io[i]; + info.dma = dma[i]; + info.irq = irq[i]; + + /* + * If the user supplies the base address, then + * we init the chip, if not we probe the values + * set by the BIOS + */ + if (io[i] < 0x2000) { + chip->init(chip, &info); + } else + chip->probe(chip, &info); + + if (nsc_ircc_open(&info) >= 0) + ret = 0; + } + i++; + } else { + pr_debug("%s(), Wrong chip id=0x%02x\n", + __func__, id); + } + } + } + + if (ret) { + platform_driver_unregister(&nsc_ircc_driver); + pnp_unregister_driver(&nsc_ircc_pnp_driver); + pnp_registered = 0; + } + + return ret; +} + +/* + * Function nsc_ircc_cleanup () + * + * Close all configured chips + * + */ +static void __exit nsc_ircc_cleanup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dev_self); i++) { + if (dev_self[i]) + nsc_ircc_close(dev_self[i]); + } + + platform_driver_unregister(&nsc_ircc_driver); + + if (pnp_registered) + pnp_unregister_driver(&nsc_ircc_pnp_driver); + + pnp_registered = 0; +} + +static const struct net_device_ops nsc_ircc_sir_ops = { + .ndo_open = nsc_ircc_net_open, + .ndo_stop = nsc_ircc_net_close, + .ndo_start_xmit = nsc_ircc_hard_xmit_sir, + .ndo_do_ioctl = nsc_ircc_net_ioctl, +}; + +static const struct net_device_ops nsc_ircc_fir_ops = { + .ndo_open = nsc_ircc_net_open, + .ndo_stop = nsc_ircc_net_close, + .ndo_start_xmit = nsc_ircc_hard_xmit_fir, + .ndo_do_ioctl = nsc_ircc_net_ioctl, +}; + +/* + * Function nsc_ircc_open (iobase, irq) + * + * Open driver instance + * + */ +static int __init nsc_ircc_open(chipio_t *info) +{ + struct net_device *dev; + struct nsc_ircc_cb *self; + void *ret; + int err, chip_index; + + for (chip_index = 0; chip_index < ARRAY_SIZE(dev_self); chip_index++) { + if (!dev_self[chip_index]) + break; + } + + if (chip_index == ARRAY_SIZE(dev_self)) { + net_err_ratelimited("%s(), maximum number of supported chips reached!\n", + __func__); + return -ENOMEM; + } + + net_info_ratelimited("%s, Found chip at base=0x%03x\n", + driver_name, info->cfg_base); + + if ((nsc_ircc_setup(info)) == -1) + return -1; + + net_info_ratelimited("%s, driver loaded (Dag Brattli)\n", driver_name); + + dev = alloc_irdadev(sizeof(struct nsc_ircc_cb)); + if (dev == NULL) { + net_err_ratelimited("%s(), can't allocate memory for control block!\n", + __func__); + return -ENOMEM; + } + + self = netdev_priv(dev); + self->netdev = dev; + spin_lock_init(&self->lock); + + /* Need to store self somewhere */ + dev_self[chip_index] = self; + self->index = chip_index; + + /* Initialize IO */ + self->io.cfg_base = info->cfg_base; + self->io.fir_base = info->fir_base; + self->io.irq = info->irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = info->dma; + self->io.fifo_size = 32; + + /* Reserve the ioports that we need */ + ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name); + if (!ret) { + net_warn_ratelimited("%s(), can't get iobase of 0x%03x\n", + __func__, self->io.fir_base); + err = -ENODEV; + goto out1; + } + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* The only value we must override it the baudrate */ + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200|IR_576000|IR_1152000 |(IR_4000000 << 8); + + self->qos.min_turn_time.bits = qos_mtt_bits; + irda_qos_bits_to_value(&self->qos); + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384; + self->tx_buff.truesize = 14384; + + /* Allocate memory if needed */ + self->rx_buff.head = + dma_zalloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + err = -ENOMEM; + goto out2; + + } + + self->tx_buff.head = + dma_zalloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + err = -ENOMEM; + goto out3; + } + + self->rx_buff.in_frame = FALSE; + self->rx_buff.state = OUTSIDE_FRAME; + self->tx_buff.data = self->tx_buff.head; + self->rx_buff.data = self->rx_buff.head; + + /* Reset Tx queue info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Override the network functions we need to use */ + dev->netdev_ops = &nsc_ircc_sir_ops; + + err = register_netdev(dev); + if (err) { + net_err_ratelimited("%s(), register_netdev() failed!\n", + __func__); + goto out4; + } + net_info_ratelimited("IrDA: Registered device %s\n", dev->name); + + /* Check if user has supplied a valid dongle id or not */ + if ((dongle_id <= 0) || + (dongle_id >= ARRAY_SIZE(dongle_types))) { + dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base); + + net_info_ratelimited("%s, Found dongle: %s\n", + driver_name, dongle_types[dongle_id]); + } else { + net_info_ratelimited("%s, Using dongle: %s\n", + driver_name, dongle_types[dongle_id]); + } + + self->io.dongle_id = dongle_id; + nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id); + + self->pldev = platform_device_register_simple(NSC_IRCC_DRIVER_NAME, + self->index, NULL, 0); + if (IS_ERR(self->pldev)) { + err = PTR_ERR(self->pldev); + goto out5; + } + platform_set_drvdata(self->pldev, self); + + return chip_index; + + out5: + unregister_netdev(dev); + out4: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + out3: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + out2: + release_region(self->io.fir_base, self->io.fir_ext); + out1: + free_netdev(dev); + dev_self[chip_index] = NULL; + return err; +} + +/* + * Function nsc_ircc_close (self) + * + * Close driver instance + * + */ +static int __exit nsc_ircc_close(struct nsc_ircc_cb *self) +{ + int iobase; + + IRDA_ASSERT(self != NULL, return -1;); + + iobase = self->io.fir_base; + + platform_device_unregister(self->pldev); + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the PORT that this driver is using */ + pr_debug("%s(), Releasing Region %03x\n", + __func__, self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + + if (self->tx_buff.head) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + dev_self[self->index] = NULL; + free_netdev(self->netdev); + + return 0; +} + +/* + * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma) + * + * Initialize the NSC '108 chip + * + */ +static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + __u8 temp=0; + + outb(2, cfg_base); /* Mode Control Register (MCTL) */ + outb(0x00, cfg_base+1); /* Disable device */ + + /* Base Address and Interrupt Control Register (BAIC) */ + outb(CFG_108_BAIC, cfg_base); + switch (info->fir_base) { + case 0x3e8: outb(0x14, cfg_base+1); break; + case 0x2e8: outb(0x15, cfg_base+1); break; + case 0x3f8: outb(0x16, cfg_base+1); break; + case 0x2f8: outb(0x17, cfg_base+1); break; + default: net_err_ratelimited("%s(), invalid base_address\n", __func__); + } + + /* Control Signal Routing Register (CSRT) */ + switch (info->irq) { + case 3: temp = 0x01; break; + case 4: temp = 0x02; break; + case 5: temp = 0x03; break; + case 7: temp = 0x04; break; + case 9: temp = 0x05; break; + case 11: temp = 0x06; break; + case 15: temp = 0x07; break; + default: net_err_ratelimited("%s(), invalid irq\n", __func__); + } + outb(CFG_108_CSRT, cfg_base); + + switch (info->dma) { + case 0: outb(0x08+temp, cfg_base+1); break; + case 1: outb(0x10+temp, cfg_base+1); break; + case 3: outb(0x18+temp, cfg_base+1); break; + default: net_err_ratelimited("%s(), invalid dma\n", __func__); + } + + outb(CFG_108_MCTL, cfg_base); /* Mode Control Register (MCTL) */ + outb(0x03, cfg_base+1); /* Enable device */ + + return 0; +} + +/* + * Function nsc_ircc_probe_108 (chip, info) + * + * + * + */ +static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int reg; + + /* Read address and interrupt control register (BAIC) */ + outb(CFG_108_BAIC, cfg_base); + reg = inb(cfg_base+1); + + switch (reg & 0x03) { + case 0: + info->fir_base = 0x3e8; + break; + case 1: + info->fir_base = 0x2e8; + break; + case 2: + info->fir_base = 0x3f8; + break; + case 3: + info->fir_base = 0x2f8; + break; + } + info->sir_base = info->fir_base; + pr_debug("%s(), probing fir_base=0x%03x\n", __func__, + info->fir_base); + + /* Read control signals routing register (CSRT) */ + outb(CFG_108_CSRT, cfg_base); + reg = inb(cfg_base+1); + + switch (reg & 0x07) { + case 0: + info->irq = -1; + break; + case 1: + info->irq = 3; + break; + case 2: + info->irq = 4; + break; + case 3: + info->irq = 5; + break; + case 4: + info->irq = 7; + break; + case 5: + info->irq = 9; + break; + case 6: + info->irq = 11; + break; + case 7: + info->irq = 15; + break; + } + pr_debug("%s(), probing irq=%d\n", __func__, info->irq); + + /* Currently we only read Rx DMA but it will also be used for Tx */ + switch ((reg >> 3) & 0x03) { + case 0: + info->dma = -1; + break; + case 1: + info->dma = 0; + break; + case 2: + info->dma = 1; + break; + case 3: + info->dma = 3; + break; + } + pr_debug("%s(), probing dma=%d\n", __func__, info->dma); + + /* Read mode control register (MCTL) */ + outb(CFG_108_MCTL, cfg_base); + reg = inb(cfg_base+1); + + info->enabled = reg & 0x01; + info->suspended = !((reg >> 1) & 0x01); + + return 0; +} + +/* + * Function nsc_ircc_init_338 (chip, info) + * + * Initialize the NSC '338 chip. Remember that the 87338 needs two + * consecutive writes to the data registers while CPU interrupts are + * disabled. The 97338 does not require this, but shouldn't be any + * harm if we do it anyway. + */ +static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info) +{ + /* No init yet */ + + return 0; +} + +/* + * Function nsc_ircc_probe_338 (chip, info) + * + * + * + */ +static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int reg, com = 0; + int pnp; + + /* Read function enable register (FER) */ + outb(CFG_338_FER, cfg_base); + reg = inb(cfg_base+1); + + info->enabled = (reg >> 2) & 0x01; + + /* Check if we are in Legacy or PnP mode */ + outb(CFG_338_PNP0, cfg_base); + reg = inb(cfg_base+1); + + pnp = (reg >> 3) & 0x01; + if (pnp) { + pr_debug("(), Chip is in PnP mode\n"); + outb(0x46, cfg_base); + reg = (inb(cfg_base+1) & 0xfe) << 2; + + outb(0x47, cfg_base); + reg |= ((inb(cfg_base+1) & 0xfc) << 8); + + info->fir_base = reg; + } else { + /* Read function address register (FAR) */ + outb(CFG_338_FAR, cfg_base); + reg = inb(cfg_base+1); + + switch ((reg >> 4) & 0x03) { + case 0: + info->fir_base = 0x3f8; + break; + case 1: + info->fir_base = 0x2f8; + break; + case 2: + com = 3; + break; + case 3: + com = 4; + break; + } + + if (com) { + switch ((reg >> 6) & 0x03) { + case 0: + if (com == 3) + info->fir_base = 0x3e8; + else + info->fir_base = 0x2e8; + break; + case 1: + if (com == 3) + info->fir_base = 0x338; + else + info->fir_base = 0x238; + break; + case 2: + if (com == 3) + info->fir_base = 0x2e8; + else + info->fir_base = 0x2e0; + break; + case 3: + if (com == 3) + info->fir_base = 0x220; + else + info->fir_base = 0x228; + break; + } + } + } + info->sir_base = info->fir_base; + + /* Read PnP register 1 (PNP1) */ + outb(CFG_338_PNP1, cfg_base); + reg = inb(cfg_base+1); + + info->irq = reg >> 4; + + /* Read PnP register 3 (PNP3) */ + outb(CFG_338_PNP3, cfg_base); + reg = inb(cfg_base+1); + + info->dma = (reg & 0x07) - 1; + + /* Read power and test register (PTR) */ + outb(CFG_338_PTR, cfg_base); + reg = inb(cfg_base+1); + + info->suspended = reg & 0x01; + + return 0; +} + + +/* + * Function nsc_ircc_init_39x (chip, info) + * + * Now that we know it's a '39x (see probe below), we need to + * configure it so we can use it. + * + * The NSC '338 chip is a Super I/O chip with a "bank" architecture, + * the configuration of the different functionality (serial, parallel, + * floppy...) are each in a different bank (Logical Device Number). + * The base address, irq and dma configuration registers are common + * to all functionalities (index 0x30 to 0x7F). + * There is only one configuration register specific to the + * serial port, CFG_39X_SPC. + * JeanII + * + * Note : this code was written by Jan Frey + */ +static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int enabled; + + /* User is sure about his config... accept it. */ + pr_debug("%s(): nsc_ircc_init_39x (user settings): io=0x%04x, irq=%d, dma=%d\n", + __func__, info->fir_base, info->irq, info->dma); + + /* Access bank for SP2 */ + outb(CFG_39X_LDN, cfg_base); + outb(0x02, cfg_base+1); + + /* Configure SP2 */ + + /* We want to enable the device if not enabled */ + outb(CFG_39X_ACT, cfg_base); + enabled = inb(cfg_base+1) & 0x01; + + if (!enabled) { + /* Enable the device */ + outb(CFG_39X_SIOCF1, cfg_base); + outb(0x01, cfg_base+1); + /* May want to update info->enabled. Jean II */ + } + + /* Enable UART bank switching (bit 7) ; Sets the chip to normal + * power mode (wake up from sleep mode) (bit 1) */ + outb(CFG_39X_SPC, cfg_base); + outb(0x82, cfg_base+1); + + return 0; +} + +/* + * Function nsc_ircc_probe_39x (chip, info) + * + * Test if we really have a '39x chip at the given address + * + * Note : this code was written by Jan Frey + */ +static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int reg1, reg2, irq, irqt, dma1, dma2; + int enabled, susp; + + pr_debug("%s(), nsc_ircc_probe_39x, base=%d\n", + __func__, cfg_base); + + /* This function should be executed with irq off to avoid + * another driver messing with the Super I/O bank - Jean II */ + + /* Access bank for SP2 */ + outb(CFG_39X_LDN, cfg_base); + outb(0x02, cfg_base+1); + + /* Read infos about SP2 ; store in info struct */ + outb(CFG_39X_BASEH, cfg_base); + reg1 = inb(cfg_base+1); + outb(CFG_39X_BASEL, cfg_base); + reg2 = inb(cfg_base+1); + info->fir_base = (reg1 << 8) | reg2; + + outb(CFG_39X_IRQNUM, cfg_base); + irq = inb(cfg_base+1); + outb(CFG_39X_IRQSEL, cfg_base); + irqt = inb(cfg_base+1); + info->irq = irq; + + outb(CFG_39X_DMA0, cfg_base); + dma1 = inb(cfg_base+1); + outb(CFG_39X_DMA1, cfg_base); + dma2 = inb(cfg_base+1); + info->dma = dma1 -1; + + outb(CFG_39X_ACT, cfg_base); + info->enabled = enabled = inb(cfg_base+1) & 0x01; + + outb(CFG_39X_SPC, cfg_base); + susp = 1 - ((inb(cfg_base+1) & 0x02) >> 1); + + pr_debug("%s(): io=0x%02x%02x, irq=%d (type %d), rxdma=%d, txdma=%d, enabled=%d (suspended=%d)\n", + __func__, reg1, reg2, irq, irqt, dma1, dma2, enabled, susp); + + /* Configure SP2 */ + + /* We want to enable the device if not enabled */ + outb(CFG_39X_ACT, cfg_base); + enabled = inb(cfg_base+1) & 0x01; + + if (!enabled) { + /* Enable the device */ + outb(CFG_39X_SIOCF1, cfg_base); + outb(0x01, cfg_base+1); + /* May want to update info->enabled. Jean II */ + } + + /* Enable UART bank switching (bit 7) ; Sets the chip to normal + * power mode (wake up from sleep mode) (bit 1) */ + outb(CFG_39X_SPC, cfg_base); + outb(0x82, cfg_base+1); + + return 0; +} + +#ifdef CONFIG_PNP +/* PNP probing */ +static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id) +{ + memset(&pnp_info, 0, sizeof(chipio_t)); + pnp_info.irq = -1; + pnp_info.dma = -1; + pnp_succeeded = 1; + + if (id->driver_data & NSC_FORCE_DONGLE_TYPE9) + dongle_id = 0x9; + + /* There doesn't seem to be any way of getting the cfg_base. + * On my box, cfg_base is in the PnP descriptor of the + * motherboard. Oh well... Jean II */ + + if (pnp_port_valid(dev, 0) && + !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) + pnp_info.fir_base = pnp_port_start(dev, 0); + + if (pnp_irq_valid(dev, 0) && + !(pnp_irq_flags(dev, 0) & IORESOURCE_DISABLED)) + pnp_info.irq = pnp_irq(dev, 0); + + if (pnp_dma_valid(dev, 0) && + !(pnp_dma_flags(dev, 0) & IORESOURCE_DISABLED)) + pnp_info.dma = pnp_dma(dev, 0); + + pr_debug("%s() : From PnP, found firbase 0x%03X ; irq %d ; dma %d.\n", + __func__, pnp_info.fir_base, pnp_info.irq, pnp_info.dma); + + if((pnp_info.fir_base == 0) || + (pnp_info.irq == -1) || (pnp_info.dma == -1)) { + /* Returning an error will disable the device. Yuck ! */ + //return -EINVAL; + pnp_succeeded = 0; + } + + return 0; +} +#endif + +/* + * Function nsc_ircc_setup (info) + * + * Returns non-negative on success. + * + */ +static int nsc_ircc_setup(chipio_t *info) +{ + int version; + int iobase = info->fir_base; + + /* Read the Module ID */ + switch_bank(iobase, BANK3); + version = inb(iobase+MID); + + pr_debug("%s() Driver %s Found chip version %02x\n", + __func__, driver_name, version); + + /* Should be 0x2? */ + if (0x20 != (version & 0xf0)) { + net_err_ratelimited("%s, Wrong chip version %02x\n", + driver_name, version); + return -1; + } + + /* Switch to advanced mode */ + switch_bank(iobase, BANK2); + outb(ECR1_EXT_SL, iobase+ECR1); + switch_bank(iobase, BANK0); + + /* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */ + switch_bank(iobase, BANK0); + outb(FCR_RXTH|FCR_TXTH|FCR_TXSR|FCR_RXSR|FCR_FIFO_EN, iobase+FCR); + + outb(0x03, iobase+LCR); /* 8 bit word length */ + outb(MCR_SIR, iobase+MCR); /* Start at SIR-mode, also clears LSR*/ + + /* Set FIFO size to 32 */ + switch_bank(iobase, BANK2); + outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2); + + /* IRCR2: FEND_MD is not set */ + switch_bank(iobase, BANK5); + outb(0x02, iobase+4); + + /* Make sure that some defaults are OK */ + switch_bank(iobase, BANK6); + outb(0x20, iobase+0); /* Set 32 bits FIR CRC */ + outb(0x0a, iobase+1); /* Set MIR pulse width */ + outb(0x0d, iobase+2); /* Set SIR pulse width to 1.6us */ + outb(0x2a, iobase+4); /* Set beginning frag, and preamble length */ + + /* Enable receive interrupts */ + switch_bank(iobase, BANK0); + outb(IER_RXHDL_IE, iobase+IER); + + return 0; +} + +/* + * Function nsc_ircc_read_dongle_id (void) + * + * Try to read dongle identification. This procedure needs to be executed + * once after power-on/reset. It also needs to be used whenever you suspect + * that the user may have plugged/unplugged the IrDA Dongle. + */ +static int nsc_ircc_read_dongle_id (int iobase) +{ + int dongle_id; + __u8 bank; + + bank = inb(iobase+BSR); + + /* Select Bank 7 */ + switch_bank(iobase, BANK7); + + /* IRCFG4: IRSL0_DS and IRSL21_DS are cleared */ + outb(0x00, iobase+7); + + /* ID0, 1, and 2 are pulled up/down very slowly */ + udelay(50); + + /* IRCFG1: read the ID bits */ + dongle_id = inb(iobase+4) & 0x0f; + +#ifdef BROKEN_DONGLE_ID + if (dongle_id == 0x0a) + dongle_id = 0x09; +#endif + /* Go back to bank 0 before returning */ + switch_bank(iobase, BANK0); + + outb(bank, iobase+BSR); + + return dongle_id; +} + +/* + * Function nsc_ircc_init_dongle_interface (iobase, dongle_id) + * + * This function initializes the dongle for the transceiver that is + * used. This procedure needs to be executed once after + * power-on/reset. It also needs to be used whenever you suspect that + * the dongle is changed. + */ +static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id) +{ + int bank; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Select Bank 7 */ + switch_bank(iobase, BANK7); + + /* IRCFG4: set according to dongle_id */ + switch (dongle_id) { + case 0x00: /* same as */ + case 0x01: /* Differential serial interface */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x02: /* same as */ + case 0x03: /* Reserved */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x04: /* Sharp RY5HD01 */ + break; + case 0x05: /* Reserved, but this is what the Thinkpad reports */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x06: /* Single-ended serial interface */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x07: /* Consumer-IR only */ + pr_debug("%s(), %s is not for IrDA mode\n", + __func__, dongle_types[dongle_id]); + break; + case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ + pr_debug("%s(), %s\n", + __func__, dongle_types[dongle_id]); + break; + case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ + outb(0x28, iobase+7); /* Set irsl[0-2] as output */ + break; + case 0x0A: /* same as */ + case 0x0B: /* Reserved */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x0C: /* same as */ + case 0x0D: /* HP HSDL-1100/HSDL-2100 */ + /* + * Set irsl0 as input, irsl[1-2] as output, and separate + * inputs are used for SIR and MIR/FIR + */ + outb(0x48, iobase+7); + break; + case 0x0E: /* Supports SIR Mode only */ + outb(0x28, iobase+7); /* Set irsl[0-2] as output */ + break; + case 0x0F: /* No dongle connected */ + pr_debug("%s(), %s\n", + __func__, dongle_types[dongle_id]); + + switch_bank(iobase, BANK0); + outb(0x62, iobase+MCR); + break; + default: + pr_debug("%s(), invalid dongle_id %#x", + __func__, dongle_id); + } + + /* IRCFG1: IRSL1 and 2 are set to IrDA mode */ + outb(0x00, iobase+4); + + /* Restore bank register */ + outb(bank, iobase+BSR); + +} /* set_up_dongle_interface */ + +/* + * Function nsc_ircc_change_dongle_speed (iobase, speed, dongle_id) + * + * Change speed of the attach dongle + * + */ +static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id) +{ + __u8 bank; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Select Bank 7 */ + switch_bank(iobase, BANK7); + + /* IRCFG1: set according to dongle_id */ + switch (dongle_id) { + case 0x00: /* same as */ + case 0x01: /* Differential serial interface */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x02: /* same as */ + case 0x03: /* Reserved */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x04: /* Sharp RY5HD01 */ + break; + case 0x05: /* Reserved */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x06: /* Single-ended serial interface */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x07: /* Consumer-IR only */ + pr_debug("%s(), %s is not for IrDA mode\n", + __func__, dongle_types[dongle_id]); + break; + case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ + pr_debug("%s(), %s\n", + __func__, dongle_types[dongle_id]); + outb(0x00, iobase+4); + if (speed > 115200) + outb(0x01, iobase+4); + break; + case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ + outb(0x01, iobase+4); + + if (speed == 4000000) { + /* There was a cli() there, but we now are already + * under spin_lock_irqsave() - JeanII */ + outb(0x81, iobase+4); + outb(0x80, iobase+4); + } else + outb(0x00, iobase+4); + break; + case 0x0A: /* same as */ + case 0x0B: /* Reserved */ + pr_debug("%s(), %s not defined by irda yet\n", + __func__, dongle_types[dongle_id]); + break; + case 0x0C: /* same as */ + case 0x0D: /* HP HSDL-1100/HSDL-2100 */ + break; + case 0x0E: /* Supports SIR Mode only */ + break; + case 0x0F: /* No dongle connected */ + pr_debug("%s(), %s is not for IrDA mode\n", + __func__, dongle_types[dongle_id]); + + switch_bank(iobase, BANK0); + outb(0x62, iobase+MCR); + break; + default: + pr_debug("%s(), invalid data_rate\n", __func__); + } + /* Restore bank register */ + outb(bank, iobase+BSR); +} + +/* + * Function nsc_ircc_change_speed (self, baud) + * + * Change the speed of the device + * + * This function *must* be called with irq off and spin-lock. + */ +static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed) +{ + struct net_device *dev; + __u8 mcr = MCR_SIR; + int iobase; + __u8 bank; + __u8 ier; /* Interrupt enable register */ + + pr_debug("%s(), speed=%d\n", __func__, speed); + + IRDA_ASSERT(self != NULL, return 0;); + + dev = self->netdev; + iobase = self->io.fir_base; + + /* Update accounting for new speed */ + self->io.speed = speed; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Disable interrupts */ + switch_bank(iobase, BANK0); + outb(0, iobase+IER); + + /* Select Bank 2 */ + switch_bank(iobase, BANK2); + + outb(0x00, iobase+BGDH); + switch (speed) { + case 9600: outb(0x0c, iobase+BGDL); break; + case 19200: outb(0x06, iobase+BGDL); break; + case 38400: outb(0x03, iobase+BGDL); break; + case 57600: outb(0x02, iobase+BGDL); break; + case 115200: outb(0x01, iobase+BGDL); break; + case 576000: + switch_bank(iobase, BANK5); + + /* IRCR2: MDRS is set */ + outb(inb(iobase+4) | 0x04, iobase+4); + + mcr = MCR_MIR; + pr_debug("%s(), handling baud of 576000\n", __func__); + break; + case 1152000: + mcr = MCR_MIR; + pr_debug("%s(), handling baud of 1152000\n", __func__); + break; + case 4000000: + mcr = MCR_FIR; + pr_debug("%s(), handling baud of 4000000\n", __func__); + break; + default: + mcr = MCR_FIR; + pr_debug("%s(), unknown baud rate of %d\n", + __func__, speed); + break; + } + + /* Set appropriate speed mode */ + switch_bank(iobase, BANK0); + outb(mcr | MCR_TX_DFR, iobase+MCR); + + /* Give some hits to the transceiver */ + nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); + + /* Set FIFO threshold to TX17, RX16 */ + switch_bank(iobase, BANK0); + outb(0x00, iobase+FCR); + outb(FCR_FIFO_EN, iobase+FCR); + outb(FCR_RXTH| /* Set Rx FIFO threshold */ + FCR_TXTH| /* Set Tx FIFO threshold */ + FCR_TXSR| /* Reset Tx FIFO */ + FCR_RXSR| /* Reset Rx FIFO */ + FCR_FIFO_EN, /* Enable FIFOs */ + iobase+FCR); + + /* Set FIFO size to 32 */ + switch_bank(iobase, BANK2); + outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2); + + /* Enable some interrupts so we can receive frames */ + switch_bank(iobase, BANK0); + if (speed > 115200) { + /* Install FIR xmit handler */ + dev->netdev_ops = &nsc_ircc_fir_ops; + ier = IER_SFIF_IE; + nsc_ircc_dma_receive(self); + } else { + /* Install SIR xmit handler */ + dev->netdev_ops = &nsc_ircc_sir_ops; + ier = IER_RXHDL_IE; + } + /* Set our current interrupt mask */ + outb(ier, iobase+IER); + + /* Restore BSR */ + outb(bank, iobase+BSR); + + /* Make sure interrupt handlers keep the proper interrupt mask */ + return ier; +} + +/* + * Function nsc_ircc_hard_xmit (skb, dev) + * + * Transmit the frame! + * + */ +static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb, + struct net_device *dev) +{ + struct nsc_ircc_cb *self; + unsigned long flags; + int iobase; + __s32 speed; + __u8 bank; + + self = netdev_priv(dev); + + IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); + + iobase = self->io.fir_base; + + netif_stop_queue(dev); + + /* Make sure tests *& speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame. */ + if (!skb->len) { + /* If we just sent a frame, we get called before + * the last bytes get out (because of the SIR FIFO). + * If this is the case, let interrupt handler change + * the speed itself... Jean II */ + if (self->io.direction == IO_RECV) { + nsc_ircc_change_speed(self, speed); + /* TODO : For SIR->SIR, the next packet + * may get corrupted - Jean II */ + netif_wake_queue(dev); + } else { + self->new_speed = speed; + /* Queue will be restarted after speed change + * to make sure packets gets through the + * proper xmit handler - Jean II */ + } + netif_trans_update(dev); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } else + self->new_speed = speed; + } + + /* Save current bank */ + bank = inb(iobase+BSR); + + self->tx_buff.data = self->tx_buff.head; + + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + dev->stats.tx_bytes += self->tx_buff.len; + + /* Add interrupt on tx low level (will fire immediately) */ + switch_bank(iobase, BANK0); + outb(IER_TXLDL_IE, iobase+IER); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + netif_trans_update(dev); + spin_unlock_irqrestore(&self->lock, flags); + + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, + struct net_device *dev) +{ + struct nsc_ircc_cb *self; + unsigned long flags; + int iobase; + __s32 speed; + __u8 bank; + int mtt, diff; + + self = netdev_priv(dev); + iobase = self->io.fir_base; + + netif_stop_queue(dev); + + /* Make sure tests *& speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame. */ + if (!skb->len) { + /* If we are currently transmitting, defer to + * interrupt handler. - Jean II */ + if(self->tx_fifo.len == 0) { + nsc_ircc_change_speed(self, speed); + netif_wake_queue(dev); + } else { + self->new_speed = speed; + /* Keep queue stopped : + * the speed change operation may change the + * xmit handler, and we want to make sure + * the next packet get through the proper + * Tx path, so block the Tx queue until + * the speed change has been done. + * Jean II */ + } + netif_trans_update(dev); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } else { + /* Change speed after current frame */ + self->new_speed = speed; + } + } + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Register and copy this frame to DMA memory */ + self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; + self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; + self->tx_fifo.tail += skb->len; + + dev->stats.tx_bytes += skb->len; + + skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, + skb->len); + self->tx_fifo.len++; + self->tx_fifo.free++; + + /* Start transmit only if there is currently no transmit going on */ + if (self->tx_fifo.len == 1) { + /* Check if we must wait the min turn time or not */ + mtt = irda_get_mtt(skb); + if (mtt) { + /* Check how much time we have used already */ + diff = ktime_us_delta(ktime_get(), self->stamp); + + /* Check if the mtt is larger than the time we have + * already used by all the protocol processing + */ + if (mtt > diff) { + mtt -= diff; + + /* + * Use timer if delay larger than 125 us, and + * use udelay for smaller values which should + * be acceptable + */ + if (mtt > 125) { + /* Adjust for timer resolution */ + mtt = mtt / 125; + + /* Setup timer */ + switch_bank(iobase, BANK4); + outb(mtt & 0xff, iobase+TMRL); + outb((mtt >> 8) & 0x0f, iobase+TMRH); + + /* Start timer */ + outb(IRCR1_TMR_EN, iobase+IRCR1); + self->io.direction = IO_XMIT; + + /* Enable timer interrupt */ + switch_bank(iobase, BANK0); + outb(IER_TMR_IE, iobase+IER); + + /* Timer will take care of the rest */ + goto out; + } else + udelay(mtt); + } + } + /* Enable DMA interrupt */ + switch_bank(iobase, BANK0); + outb(IER_DMA_IE, iobase+IER); + + /* Transmit frame */ + nsc_ircc_dma_xmit(self, iobase); + } + out: + /* Not busy transmitting anymore if window is not full, + * and if we don't need to change speed */ + if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) + netif_wake_queue(self->netdev); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + netif_trans_update(dev); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +/* + * Function nsc_ircc_dma_xmit (self, iobase) + * + * Transmit data using DMA + * + */ +static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase) +{ + int bsr; + + /* Save current bank */ + bsr = inb(iobase+BSR); + + /* Disable DMA */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); + + self->io.direction = IO_XMIT; + + /* Choose transmit DMA channel */ + switch_bank(iobase, BANK2); + outb(ECR1_DMASWP|ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1); + + irda_setup_dma(self->io.dma, + ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - + self->tx_buff.head) + self->tx_buff_dma, + self->tx_fifo.queue[self->tx_fifo.ptr].len, + DMA_TX_MODE); + + /* Enable DMA and SIR interaction pulse */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR)|MCR_TX_DFR|MCR_DMA_EN|MCR_IR_PLS, iobase+MCR); + + /* Restore bank register */ + outb(bsr, iobase+BSR); +} + +/* + * Function nsc_ircc_pio_xmit (self, iobase) + * + * Transmit data using PIO. Returns the number of bytes that actually + * got transferred + * + */ +static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size) +{ + int actual = 0; + __u8 bank; + + /* Save current bank */ + bank = inb(iobase+BSR); + + switch_bank(iobase, BANK0); + if (!(inb_p(iobase+LSR) & LSR_TXEMP)) { + pr_debug("%s(), warning, FIFO not empty yet!\n", + __func__); + + /* FIFO may still be filled to the Tx interrupt threshold */ + fifo_size -= 17; + } + + /* Fill FIFO with current frame */ + while ((fifo_size-- > 0) && (actual < len)) { + /* Transmit next byte */ + outb(buf[actual++], iobase+TXD); + } + + pr_debug("%s(), fifo_size %d ; %d sent of %d\n", + __func__, fifo_size, actual, len); + + /* Restore bank */ + outb(bank, iobase+BSR); + + return actual; +} + +/* + * Function nsc_ircc_dma_xmit_complete (self) + * + * The transfer of a frame in finished. This function will only be called + * by the interrupt handler + * + */ +static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self) +{ + int iobase; + __u8 bank; + int ret = TRUE; + + iobase = self->io.fir_base; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Disable DMA */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); + + /* Check for underrun! */ + if (inb(iobase+ASCR) & ASCR_TXUR) { + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; + + /* Clear bit, by writing 1 into it */ + outb(ASCR_TXUR, iobase+ASCR); + } else { + self->netdev->stats.tx_packets++; + } + + /* Finished with this frame, so prepare for next */ + self->tx_fifo.ptr++; + self->tx_fifo.len--; + + /* Any frames to be sent back-to-back? */ + if (self->tx_fifo.len) { + nsc_ircc_dma_xmit(self, iobase); + + /* Not finished yet! */ + ret = FALSE; + } else { + /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + } + + /* Make sure we have room for more frames and + * that we don't need to change speed */ + if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) { + /* Not busy transmitting anymore */ + /* Tell the network layer, that we can accept more frames */ + netif_wake_queue(self->netdev); + } + + /* Restore bank */ + outb(bank, iobase+BSR); + + return ret; +} + +/* + * Function nsc_ircc_dma_receive (self) + * + * Get ready for receiving a frame. The device will initiate a DMA + * if it starts to receive a frame. + * + */ +static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self) +{ + int iobase; + __u8 bsr; + + iobase = self->io.fir_base; + + /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Save current bank */ + bsr = inb(iobase+BSR); + + /* Disable DMA */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); + + /* Choose DMA Rx, DMA Fairness, and Advanced mode */ + switch_bank(iobase, BANK2); + outb(ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1); + + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + + /* Reset Rx FIFO. This will also flush the ST_FIFO */ + switch_bank(iobase, BANK0); + outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR); + + self->st_fifo.len = self->st_fifo.pending_bytes = 0; + self->st_fifo.tail = self->st_fifo.head = 0; + + irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, + DMA_RX_MODE); + + /* Enable DMA */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR)|MCR_DMA_EN, iobase+MCR); + + /* Restore bank register */ + outb(bsr, iobase+BSR); + + return 0; +} + +/* + * Function nsc_ircc_dma_receive_complete (self) + * + * Finished with receiving frames + * + * + */ +static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) +{ + struct st_fifo *st_fifo; + struct sk_buff *skb; + __u8 status; + __u8 bank; + int len; + + st_fifo = &self->st_fifo; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Read all entries in status FIFO */ + switch_bank(iobase, BANK5); + while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) { + /* We must empty the status FIFO no matter what */ + len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8); + + if (st_fifo->tail >= MAX_RX_WINDOW) { + pr_debug("%s(), window is full!\n", __func__); + continue; + } + + st_fifo->entries[st_fifo->tail].status = status; + st_fifo->entries[st_fifo->tail].len = len; + st_fifo->pending_bytes += len; + st_fifo->tail++; + st_fifo->len++; + } + /* Try to process all entries in status FIFO */ + while (st_fifo->len > 0) { + /* Get first entry */ + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->pending_bytes -= len; + st_fifo->head++; + st_fifo->len--; + + /* Check for errors */ + if (status & FRM_ST_ERR_MSK) { + if (status & FRM_ST_LOST_FR) { + /* Add number of lost frames to stats */ + self->netdev->stats.rx_errors += len; + } else { + /* Skip frame */ + self->netdev->stats.rx_errors++; + + self->rx_buff.data += len; + + if (status & FRM_ST_MAX_LEN) + self->netdev->stats.rx_length_errors++; + + if (status & FRM_ST_PHY_ERR) + self->netdev->stats.rx_frame_errors++; + + if (status & FRM_ST_BAD_CRC) + self->netdev->stats.rx_crc_errors++; + } + /* The errors below can be reported in both cases */ + if (status & FRM_ST_OVR1) + self->netdev->stats.rx_fifo_errors++; + + if (status & FRM_ST_OVR2) + self->netdev->stats.rx_fifo_errors++; + } else { + /* + * First we must make sure that the frame we + * want to deliver is all in main memory. If we + * cannot tell, then we check if the Rx FIFO is + * empty. If not then we will have to take a nap + * and try again later. + */ + if (st_fifo->pending_bytes < self->io.fifo_size) { + switch_bank(iobase, BANK0); + if (inb(iobase+LSR) & LSR_RXDA) { + /* Put this entry back in fifo */ + st_fifo->head--; + st_fifo->len++; + st_fifo->pending_bytes += len; + st_fifo->entries[st_fifo->head].status = status; + st_fifo->entries[st_fifo->head].len = len; + /* + * DMA not finished yet, so try again + * later, set timer value, resolution + * 125 us + */ + switch_bank(iobase, BANK4); + outb(0x02, iobase+TMRL); /* x 125 us */ + outb(0x00, iobase+TMRH); + + /* Start timer */ + outb(IRCR1_TMR_EN, iobase+IRCR1); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + return FALSE; /* I'll be back! */ + } + } + + /* + * Remember the time we received this frame, so we can + * reduce the min turn time a bit since we will know + * how much time we have used for protocol processing + */ + self->stamp = ktime_get(); + + skb = dev_alloc_skb(len+1); + if (skb == NULL) { + self->netdev->stats.rx_dropped++; + + /* Restore bank register */ + outb(bank, iobase+BSR); + + return FALSE; + } + + /* Make sure IP header gets aligned */ + skb_reserve(skb, 1); + + /* Copy frame without CRC */ + if (self->io.speed < 4000000) { + skb_put(skb, len-2); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 2); + } else { + skb_put(skb, len-4); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 4); + } + + /* Move to next frame */ + self->rx_buff.data += len; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; + + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + } + } + /* Restore bank register */ + outb(bank, iobase+BSR); + + return TRUE; +} + +/* + * Function nsc_ircc_pio_receive (self) + * + * Receive all data in receiver FIFO + * + */ +static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self) +{ + __u8 byte; + int iobase; + + iobase = self->io.fir_base; + + /* Receive all characters in Rx FIFO */ + do { + byte = inb(iobase+RXD); + async_unwrap_char(self->netdev, &self->netdev->stats, + &self->rx_buff, byte); + } while (inb(iobase+LSR) & LSR_RXDA); /* Data available */ +} + +/* + * Function nsc_ircc_sir_interrupt (self, eir) + * + * Handle SIR interrupt + * + */ +static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir) +{ + int actual; + + /* Check if transmit FIFO is low on data */ + if (eir & EIR_TXLDL_EV) { + /* Write data left in transmit buffer */ + actual = nsc_ircc_pio_write(self->io.fir_base, + self->tx_buff.data, + self->tx_buff.len, + self->io.fifo_size); + self->tx_buff.data += actual; + self->tx_buff.len -= actual; + + self->io.direction = IO_XMIT; + + /* Check if finished */ + if (self->tx_buff.len > 0) + self->ier = IER_TXLDL_IE; + else { + + self->netdev->stats.tx_packets++; + netif_wake_queue(self->netdev); + self->ier = IER_TXEMP_IE; + } + + } + /* Check if transmission has completed */ + if (eir & EIR_TXEMP_EV) { + /* Turn around and get ready to receive some data */ + self->io.direction = IO_RECV; + self->ier = IER_RXHDL_IE; + /* Check if we need to change the speed? + * Need to be after self->io.direction to avoid race with + * nsc_ircc_hard_xmit_sir() - Jean II */ + if (self->new_speed) { + pr_debug("%s(), Changing speed!\n", __func__); + self->ier = nsc_ircc_change_speed(self, + self->new_speed); + self->new_speed = 0; + netif_wake_queue(self->netdev); + + /* Check if we are going to FIR */ + if (self->io.speed > 115200) { + /* No need to do anymore SIR stuff */ + return; + } + } + } + + /* Rx FIFO threshold or timeout */ + if (eir & EIR_RXHDL_EV) { + nsc_ircc_pio_receive(self); + + /* Keep receiving */ + self->ier = IER_RXHDL_IE; + } +} + +/* + * Function nsc_ircc_fir_interrupt (self, eir) + * + * Handle MIR/FIR interrupt + * + */ +static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase, + int eir) +{ + __u8 bank; + + bank = inb(iobase+BSR); + + /* Status FIFO event*/ + if (eir & EIR_SFIF_EV) { + /* Check if DMA has finished */ + if (nsc_ircc_dma_receive_complete(self, iobase)) { + /* Wait for next status FIFO interrupt */ + self->ier = IER_SFIF_IE; + } else { + self->ier = IER_SFIF_IE | IER_TMR_IE; + } + } else if (eir & EIR_TMR_EV) { /* Timer finished */ + /* Disable timer */ + switch_bank(iobase, BANK4); + outb(0, iobase+IRCR1); + + /* Clear timer event */ + switch_bank(iobase, BANK0); + outb(ASCR_CTE, iobase+ASCR); + + /* Check if this is a Tx timer interrupt */ + if (self->io.direction == IO_XMIT) { + nsc_ircc_dma_xmit(self, iobase); + + /* Interrupt on DMA */ + self->ier = IER_DMA_IE; + } else { + /* Check (again) if DMA has finished */ + if (nsc_ircc_dma_receive_complete(self, iobase)) { + self->ier = IER_SFIF_IE; + } else { + self->ier = IER_SFIF_IE | IER_TMR_IE; + } + } + } else if (eir & EIR_DMA_EV) { + /* Finished with all transmissions? */ + if (nsc_ircc_dma_xmit_complete(self)) { + if(self->new_speed != 0) { + /* As we stop the Tx queue, the speed change + * need to be done when the Tx fifo is + * empty. Ask for a Tx done interrupt */ + self->ier = IER_TXEMP_IE; + } else { + /* Check if there are more frames to be + * transmitted */ + if (irda_device_txqueue_empty(self->netdev)) { + /* Prepare for receive */ + nsc_ircc_dma_receive(self); + self->ier = IER_SFIF_IE; + } else + net_warn_ratelimited("%s(), potential Tx queue lockup !\n", + __func__); + } + } else { + /* Not finished yet, so interrupt on DMA again */ + self->ier = IER_DMA_IE; + } + } else if (eir & EIR_TXEMP_EV) { + /* The Tx FIFO has totally drained out, so now we can change + * the speed... - Jean II */ + self->ier = nsc_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + netif_wake_queue(self->netdev); + /* Note : nsc_ircc_change_speed() restarted Rx fifo */ + } + + outb(bank, iobase+BSR); +} + +/* + * Function nsc_ircc_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t nsc_ircc_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct nsc_ircc_cb *self; + __u8 bsr, eir; + int iobase; + + self = netdev_priv(dev); + + spin_lock(&self->lock); + + iobase = self->io.fir_base; + + bsr = inb(iobase+BSR); /* Save current bank */ + + switch_bank(iobase, BANK0); + self->ier = inb(iobase+IER); + eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */ + + outb(0, iobase+IER); /* Disable interrupts */ + + if (eir) { + /* Dispatch interrupt handler for the current speed */ + if (self->io.speed > 115200) + nsc_ircc_fir_interrupt(self, iobase, eir); + else + nsc_ircc_sir_interrupt(self, eir); + } + + outb(self->ier, iobase+IER); /* Restore interrupts */ + outb(bsr, iobase+BSR); /* Restore bank register */ + + spin_unlock(&self->lock); + return IRQ_RETVAL(eir); +} + +/* + * Function nsc_ircc_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self) +{ + unsigned long flags; + int status = FALSE; + int iobase; + __u8 bank; + + IRDA_ASSERT(self != NULL, return FALSE;); + + spin_lock_irqsave(&self->lock, flags); + + if (self->io.speed > 115200) { + iobase = self->io.fir_base; + + /* Check if rx FIFO is not empty */ + bank = inb(iobase+BSR); + switch_bank(iobase, BANK2); + if ((inb(iobase+RXFLV) & 0x3f) != 0) { + /* We are receiving something */ + status = TRUE; + } + outb(bank, iobase+BSR); + } else + status = (self->rx_buff.state != OUTSIDE_FRAME); + + spin_unlock_irqrestore(&self->lock, flags); + + return status; +} + +/* + * Function nsc_ircc_net_open (dev) + * + * Start the device + * + */ +static int nsc_ircc_net_open(struct net_device *dev) +{ + struct nsc_ircc_cb *self; + int iobase; + char hwname[32]; + __u8 bank; + + + IRDA_ASSERT(dev != NULL, return -1;); + self = netdev_priv(dev); + + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) { + net_warn_ratelimited("%s, unable to allocate irq=%d\n", + driver_name, self->io.irq); + return -EAGAIN; + } + /* + * Always allocate the DMA channel after the IRQ, and clean up on + * failure. + */ + if (request_dma(self->io.dma, dev->name)) { + net_warn_ratelimited("%s, unable to allocate dma=%d\n", + driver_name, self->io.dma); + free_irq(self->io.irq, dev); + return -EAGAIN; + } + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* turn on interrupts */ + switch_bank(iobase, BANK0); + outb(IER_LS_IE | IER_RXHDL_IE, iobase+IER); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + /* Ready to play! */ + netif_start_queue(dev); + + /* Give self a hardware name */ + sprintf(hwname, "NSC-FIR @ 0x%03x", self->io.fir_base); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + self->irlap = irlap_open(dev, &self->qos, hwname); + + return 0; +} + +/* + * Function nsc_ircc_net_close (dev) + * + * Stop the device + * + */ +static int nsc_ircc_net_close(struct net_device *dev) +{ + struct nsc_ircc_cb *self; + int iobase; + __u8 bank; + + + IRDA_ASSERT(dev != NULL, return -1;); + + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return 0;); + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + iobase = self->io.fir_base; + + disable_dma(self->io.dma); + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Disable interrupts */ + switch_bank(iobase, BANK0); + outb(0, iobase+IER); + + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + return 0; +} + +/* + * Function nsc_ircc_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct nsc_ircc_cb *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + + self = netdev_priv(dev); + + IRDA_ASSERT(self != NULL, return -1;); + + pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + spin_lock_irqsave(&self->lock, flags); + nsc_ircc_change_speed(self, irq->ifr_baudrate); + spin_unlock_irqrestore(&self->lock, flags); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + /* This is already protected */ + irq->ifr_receiving = nsc_ircc_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + return ret; +} + +static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state) +{ + struct nsc_ircc_cb *self = platform_get_drvdata(dev); + int bank; + unsigned long flags; + int iobase = self->io.fir_base; + + if (self->io.suspended) + return 0; + + pr_debug("%s, Suspending\n", driver_name); + + rtnl_lock(); + if (netif_running(self->netdev)) { + netif_device_detach(self->netdev); + spin_lock_irqsave(&self->lock, flags); + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Disable interrupts */ + switch_bank(iobase, BANK0); + outb(0, iobase+IER); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + spin_unlock_irqrestore(&self->lock, flags); + free_irq(self->io.irq, self->netdev); + disable_dma(self->io.dma); + } + self->io.suspended = 1; + rtnl_unlock(); + + return 0; +} + +static int nsc_ircc_resume(struct platform_device *dev) +{ + struct nsc_ircc_cb *self = platform_get_drvdata(dev); + unsigned long flags; + + if (!self->io.suspended) + return 0; + + pr_debug("%s, Waking up\n", driver_name); + + rtnl_lock(); + nsc_ircc_setup(&self->io); + nsc_ircc_init_dongle_interface(self->io.fir_base, self->io.dongle_id); + + if (netif_running(self->netdev)) { + if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, + self->netdev->name, self->netdev)) { + net_warn_ratelimited("%s, unable to allocate irq=%d\n", + driver_name, self->io.irq); + + /* + * Don't fail resume process, just kill this + * network interface + */ + unregister_netdevice(self->netdev); + } else { + spin_lock_irqsave(&self->lock, flags); + nsc_ircc_change_speed(self, self->io.speed); + spin_unlock_irqrestore(&self->lock, flags); + netif_device_attach(self->netdev); + } + + } else { + spin_lock_irqsave(&self->lock, flags); + nsc_ircc_change_speed(self, 9600); + spin_unlock_irqrestore(&self->lock, flags); + } + self->io.suspended = 0; + rtnl_unlock(); + + return 0; +} + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("NSC IrDA Device Driver"); +MODULE_LICENSE("GPL"); + + +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); +module_param_hw_array(io, int, ioport, NULL, 0); +MODULE_PARM_DESC(io, "Base I/O addresses"); +module_param_hw_array(irq, int, irq, NULL, 0); +MODULE_PARM_DESC(irq, "IRQ lines"); +module_param_hw_array(dma, int, dma, NULL, 0); +MODULE_PARM_DESC(dma, "DMA channels"); +module_param(dongle_id, int, 0); +MODULE_PARM_DESC(dongle_id, "Type-id of used dongle"); + +module_init(nsc_ircc_init); +module_exit(nsc_ircc_cleanup); + diff --git a/drivers/staging/irda/drivers/nsc-ircc.h b/drivers/staging/irda/drivers/nsc-ircc.h new file mode 100644 index 000000000000..7be5acb56532 --- /dev/null +++ b/drivers/staging/irda/drivers/nsc-ircc.h @@ -0,0 +1,281 @@ +/********************************************************************* + * + * Filename: nsc-ircc.h + * Version: + * Description: + * Status: Experimental. + * Author: Dag Brattli + * Created at: Fri Nov 13 14:37:40 1998 + * Modified at: Sun Jan 23 17:47:00 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-2000 Dag Brattli + * Copyright (c) 1998 Lichen Wang, + * Copyright (c) 1998 Actisys Corp., www.actisys.com + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#ifndef NSC_IRCC_H +#define NSC_IRCC_H + +#include + +#include +#include +#include +#include + +/* Features for chips (set in driver_data) */ +#define NSC_FORCE_DONGLE_TYPE9 0x00000001 + +/* DMA modes needed */ +#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ +#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ + +/* Config registers for the '108 */ +#define CFG_108_BAIC 0x00 +#define CFG_108_CSRT 0x01 +#define CFG_108_MCTL 0x02 + +/* Config registers for the '338 */ +#define CFG_338_FER 0x00 +#define CFG_338_FAR 0x01 +#define CFG_338_PTR 0x02 +#define CFG_338_PNP0 0x1b +#define CFG_338_PNP1 0x1c +#define CFG_338_PNP3 0x4f + +/* Config registers for the '39x (in the logical device bank) */ +#define CFG_39X_LDN 0x07 /* Logical device number (Super I/O bank) */ +#define CFG_39X_SIOCF1 0x21 /* SuperI/O Config */ +#define CFG_39X_ACT 0x30 /* Device activation */ +#define CFG_39X_BASEH 0x60 /* Device base address (high bits) */ +#define CFG_39X_BASEL 0x61 /* Device base address (low bits) */ +#define CFG_39X_IRQNUM 0x70 /* Interrupt number & wake up enable */ +#define CFG_39X_IRQSEL 0x71 /* Interrupt select (edge/level + polarity) */ +#define CFG_39X_DMA0 0x74 /* DMA 0 configuration */ +#define CFG_39X_DMA1 0x75 /* DMA 1 configuration */ +#define CFG_39X_SPC 0xF0 /* Serial port configuration register */ + +/* Flags for configuration register CRF0 */ +#define APEDCRC 0x02 +#define ENBNKSEL 0x01 + +/* Set 0 */ +#define TXD 0x00 /* Transmit data port */ +#define RXD 0x00 /* Receive data port */ + +/* Register 1 */ +#define IER 0x01 /* Interrupt Enable Register*/ +#define IER_RXHDL_IE 0x01 /* Receiver high data level interrupt */ +#define IER_TXLDL_IE 0x02 /* Transeiver low data level interrupt */ +#define IER_LS_IE 0x04//* Link Status Interrupt */ +#define IER_ETXURI 0x04 /* Tx underrun */ +#define IER_DMA_IE 0x10 /* DMA finished interrupt */ +#define IER_TXEMP_IE 0x20 +#define IER_SFIF_IE 0x40 /* Frame status FIFO intr */ +#define IER_TMR_IE 0x80 /* Timer event */ + +#define FCR 0x02 /* (write only) */ +#define FCR_FIFO_EN 0x01 /* Enable FIFO's */ +#define FCR_RXSR 0x02 /* Rx FIFO soft reset */ +#define FCR_TXSR 0x04 /* Tx FIFO soft reset */ +#define FCR_RXTH 0x40 /* Rx FIFO threshold (set to 16) */ +#define FCR_TXTH 0x20 /* Tx FIFO threshold (set to 17) */ + +#define EIR 0x02 /* (read only) */ +#define EIR_RXHDL_EV 0x01 +#define EIR_TXLDL_EV 0x02 +#define EIR_LS_EV 0x04 +#define EIR_DMA_EV 0x10 +#define EIR_TXEMP_EV 0x20 +#define EIR_SFIF_EV 0x40 +#define EIR_TMR_EV 0x80 + +#define LCR 0x03 /* Link control register */ +#define LCR_WLS_8 0x03 /* 8 bits */ + +#define BSR 0x03 /* Bank select register */ +#define BSR_BKSE 0x80 +#define BANK0 LCR_WLS_8 /* Must make sure that we set 8N1 */ +#define BANK1 0x80 +#define BANK2 0xe0 +#define BANK3 0xe4 +#define BANK4 0xe8 +#define BANK5 0xec +#define BANK6 0xf0 +#define BANK7 0xf4 + +#define MCR 0x04 /* Mode Control Register */ +#define MCR_MODE_MASK ~(0xd0) +#define MCR_UART 0x00 +#define MCR_RESERVED 0x20 +#define MCR_SHARP_IR 0x40 +#define MCR_SIR 0x60 +#define MCR_MIR 0x80 +#define MCR_FIR 0xa0 +#define MCR_CEIR 0xb0 +#define MCR_IR_PLS 0x10 +#define MCR_DMA_EN 0x04 +#define MCR_EN_IRQ 0x08 +#define MCR_TX_DFR 0x08 + +#define LSR 0x05 /* Link status register */ +#define LSR_RXDA 0x01 /* Receiver data available */ +#define LSR_TXRDY 0x20 /* Transmitter ready */ +#define LSR_TXEMP 0x40 /* Transmitter empty */ + +#define ASCR 0x07 /* Auxiliary Status and Control Register */ +#define ASCR_RXF_TOUT 0x01 /* Rx FIFO timeout */ +#define ASCR_FEND_INF 0x02 /* Frame end bytes in rx FIFO */ +#define ASCR_S_EOT 0x04 /* Set end of transmission */ +#define ASCT_RXBSY 0x20 /* Rx busy */ +#define ASCR_TXUR 0x40 /* Transeiver underrun */ +#define ASCR_CTE 0x80 /* Clear timer event */ + +/* Bank 2 */ +#define BGDL 0x00 /* Baud Generator Divisor Port (Low Byte) */ +#define BGDH 0x01 /* Baud Generator Divisor Port (High Byte) */ + +#define ECR1 0x02 /* Extended Control Register 1 */ +#define ECR1_EXT_SL 0x01 /* Extended Mode Select */ +#define ECR1_DMANF 0x02 /* DMA Fairness */ +#define ECR1_DMATH 0x04 /* DMA Threshold */ +#define ECR1_DMASWP 0x08 /* DMA Swap */ + +#define EXCR2 0x04 +#define EXCR2_TFSIZ 0x01 /* Rx FIFO size = 32 */ +#define EXCR2_RFSIZ 0x04 /* Tx FIFO size = 32 */ + +#define TXFLV 0x06 /* Tx FIFO level */ +#define RXFLV 0x07 /* Rx FIFO level */ + +/* Bank 3 */ +#define MID 0x00 + +/* Bank 4 */ +#define TMRL 0x00 /* Timer low byte */ +#define TMRH 0x01 /* Timer high byte */ +#define IRCR1 0x02 /* Infrared control register 1 */ +#define IRCR1_TMR_EN 0x01 /* Timer enable */ + +#define TFRLL 0x04 +#define TFRLH 0x05 +#define RFRLL 0x06 +#define RFRLH 0x07 + +/* Bank 5 */ +#define IRCR2 0x04 /* Infrared control register 2 */ +#define IRCR2_MDRS 0x04 /* MIR data rate select */ +#define IRCR2_FEND_MD 0x20 /* */ + +#define FRM_ST 0x05 /* Frame status FIFO */ +#define FRM_ST_VLD 0x80 /* Frame status FIFO data valid */ +#define FRM_ST_ERR_MSK 0x5f +#define FRM_ST_LOST_FR 0x40 /* Frame lost */ +#define FRM_ST_MAX_LEN 0x10 /* Max frame len exceeded */ +#define FRM_ST_PHY_ERR 0x08 /* Physical layer error */ +#define FRM_ST_BAD_CRC 0x04 +#define FRM_ST_OVR1 0x02 /* Rx FIFO overrun */ +#define FRM_ST_OVR2 0x01 /* Frame status FIFO overrun */ + +#define RFLFL 0x06 +#define RFLFH 0x07 + +/* Bank 6 */ +#define IR_CFG2 0x00 +#define IR_CFG2_DIS_CRC 0x02 + +/* Bank 7 */ +#define IRM_CR 0x07 /* Infrared module control register */ +#define IRM_CR_IRX_MSL 0x40 +#define IRM_CR_AF_MNT 0x80 /* Automatic format */ + +/* NSC chip information */ +struct nsc_chip { + char *name; /* Name of chipset */ + int cfg[3]; /* Config registers */ + u_int8_t cid_index; /* Chip identification index reg */ + u_int8_t cid_value; /* Chip identification expected value */ + u_int8_t cid_mask; /* Chip identification revision mask */ + + /* Functions for probing and initializing the specific chip */ + int (*probe)(struct nsc_chip *chip, chipio_t *info); + int (*init)(struct nsc_chip *chip, chipio_t *info); +}; +typedef struct nsc_chip nsc_chip_t; + +/* For storing entries in the status FIFO */ +struct st_fifo_entry { + int status; + int len; +}; + +#define MAX_TX_WINDOW 7 +#define MAX_RX_WINDOW 7 + +struct st_fifo { + struct st_fifo_entry entries[MAX_RX_WINDOW]; + int pending_bytes; + int head; + int tail; + int len; +}; + +struct frame_cb { + void *start; /* Start of frame in DMA mem */ + int len; /* Length of frame in DMA mem */ +}; + +struct tx_fifo { + struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */ + int ptr; /* Currently being sent */ + int len; /* Length of queue */ + int free; /* Next free slot */ + void *tail; /* Next free start in DMA mem */ +}; + +/* Private data for each instance */ +struct nsc_ircc_cb { + struct st_fifo st_fifo; /* Info about received frames */ + struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ + + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct qos_info qos; /* QoS capabilities for this device */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + __u8 ier; /* Interrupt enable register */ + + ktime_t stamp; + + spinlock_t lock; /* For serializing operations */ + + __u32 new_speed; + int index; /* Instance index */ + + struct platform_device *pldev; +}; + +static inline void switch_bank(int iobase, int bank) +{ + outb(bank, iobase+BSR); +} + +#endif /* NSC_IRCC_H */ diff --git a/drivers/staging/irda/drivers/old_belkin-sir.c b/drivers/staging/irda/drivers/old_belkin-sir.c new file mode 100644 index 000000000000..a7c2e990ae69 --- /dev/null +++ b/drivers/staging/irda/drivers/old_belkin-sir.c @@ -0,0 +1,146 @@ +/********************************************************************* + * + * Filename: old_belkin.c + * Version: 1.1 + * Description: Driver for the Belkin (old) SmartBeam dongle + * Status: Experimental... + * Author: Jean Tourrilhes + * Created at: 22/11/99 + * Modified at: Fri Dec 17 09:13:32 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1999 Jean Tourrilhes, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + ********************************************************************/ + +#include +#include +#include + +#include +// #include + +#include "sir-dev.h" + +/* + * Belkin is selling a dongle called the SmartBeam. + * In fact, there is two hardware version of this dongle, of course with + * the same name and looking the exactly same (grrr...). + * I guess that I've got the old one, because inside I don't have + * a jumper for IrDA/ASK... + * + * As far as I can make it from info on their web site, the old dongle + * support only 9600 b/s, which make our life much simpler as far as + * the driver is concerned, but you might not like it very much ;-) + * The new SmartBeam does 115 kb/s, and I've not tested it... + * + * Belkin claim that the correct driver for the old dongle (in Windows) + * is the generic Parallax 9500a driver, but the Linux LiteLink driver + * fails for me (probably because Linux-IrDA doesn't rate fallback), + * so I created this really dumb driver... + * + * In fact, this driver doesn't do much. The only thing it does is to + * prevent Linux-IrDA to use any other speed than 9600 b/s ;-) This + * driver is called "old_belkin" so that when the new SmartBeam is supported + * its driver can be called "belkin" instead of "new_belkin". + * + * Note : this driver was written without any info/help from Belkin, + * so a lot of info here might be totally wrong. Blame me ;-) + */ + +static int old_belkin_open(struct sir_dev *dev); +static int old_belkin_close(struct sir_dev *dev); +static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed); +static int old_belkin_reset(struct sir_dev *dev); + +static struct dongle_driver old_belkin = { + .owner = THIS_MODULE, + .driver_name = "Old Belkin SmartBeam", + .type = IRDA_OLD_BELKIN_DONGLE, + .open = old_belkin_open, + .close = old_belkin_close, + .reset = old_belkin_reset, + .set_speed = old_belkin_change_speed, +}; + +static int __init old_belkin_sir_init(void) +{ + return irda_register_dongle(&old_belkin); +} + +static void __exit old_belkin_sir_cleanup(void) +{ + irda_unregister_dongle(&old_belkin); +} + +static int old_belkin_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + /* Power on dongle */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Not too fast, please... */ + qos->baud_rate.bits &= IR_9600; + /* Needs at least 10 ms (totally wild guess, can do probably better) */ + qos->min_turn_time.bits = 0x01; + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int old_belkin_close(struct sir_dev *dev) +{ + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function old_belkin_change_speed (task) + * + * With only one speed available, not much to do... + */ +static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed) +{ + dev->speed = 9600; + return (speed==dev->speed) ? 0 : -EINVAL; +} + +/* + * Function old_belkin_reset (task) + * + * Reset the Old-Belkin type dongle. + * + */ +static int old_belkin_reset(struct sir_dev *dev) +{ + /* This dongles speed "defaults" to 9600 bps ;-) */ + dev->speed = 9600; + + return 0; +} + +MODULE_AUTHOR("Jean Tourrilhes "); +MODULE_DESCRIPTION("Belkin (old) SmartBeam dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-7"); /* IRDA_OLD_BELKIN_DONGLE */ + +module_init(old_belkin_sir_init); +module_exit(old_belkin_sir_cleanup); diff --git a/drivers/staging/irda/drivers/pxaficp_ir.c b/drivers/staging/irda/drivers/pxaficp_ir.c new file mode 100644 index 000000000000..1dba16bc7f8d --- /dev/null +++ b/drivers/staging/irda/drivers/pxaficp_ir.c @@ -0,0 +1,1076 @@ +/* + * linux/drivers/net/irda/pxaficp_ir.c + * + * Based on sa1100_ir.c by Russell King + * + * Changes copyright (C) 2003-2005 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Infra-red driver (SIR/FIR) for the PXA2xx embedded microprocessor + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#undef __REG +#define __REG(x) ((x) & 0xffff) +#include + +#define ICCR0 0x0000 /* ICP Control Register 0 */ +#define ICCR1 0x0004 /* ICP Control Register 1 */ +#define ICCR2 0x0008 /* ICP Control Register 2 */ +#define ICDR 0x000c /* ICP Data Register */ +#define ICSR0 0x0014 /* ICP Status Register 0 */ +#define ICSR1 0x0018 /* ICP Status Register 1 */ + +#define ICCR0_AME (1 << 7) /* Address match enable */ +#define ICCR0_TIE (1 << 6) /* Transmit FIFO interrupt enable */ +#define ICCR0_RIE (1 << 5) /* Receive FIFO interrupt enable */ +#define ICCR0_RXE (1 << 4) /* Receive enable */ +#define ICCR0_TXE (1 << 3) /* Transmit enable */ +#define ICCR0_TUS (1 << 2) /* Transmit FIFO underrun select */ +#define ICCR0_LBM (1 << 1) /* Loopback mode */ +#define ICCR0_ITR (1 << 0) /* IrDA transmission */ + +#define ICCR2_RXP (1 << 3) /* Receive Pin Polarity select */ +#define ICCR2_TXP (1 << 2) /* Transmit Pin Polarity select */ +#define ICCR2_TRIG (3 << 0) /* Receive FIFO Trigger threshold */ +#define ICCR2_TRIG_8 (0 << 0) /* >= 8 bytes */ +#define ICCR2_TRIG_16 (1 << 0) /* >= 16 bytes */ +#define ICCR2_TRIG_32 (2 << 0) /* >= 32 bytes */ + +#define ICSR0_EOC (1 << 6) /* DMA End of Descriptor Chain */ +#define ICSR0_FRE (1 << 5) /* Framing error */ +#define ICSR0_RFS (1 << 4) /* Receive FIFO service request */ +#define ICSR0_TFS (1 << 3) /* Transnit FIFO service request */ +#define ICSR0_RAB (1 << 2) /* Receiver abort */ +#define ICSR0_TUR (1 << 1) /* Trunsmit FIFO underun */ +#define ICSR0_EIF (1 << 0) /* End/Error in FIFO */ + +#define ICSR1_ROR (1 << 6) /* Receiver FIFO underrun */ +#define ICSR1_CRE (1 << 5) /* CRC error */ +#define ICSR1_EOF (1 << 4) /* End of frame */ +#define ICSR1_TNF (1 << 3) /* Transmit FIFO not full */ +#define ICSR1_RNE (1 << 2) /* Receive FIFO not empty */ +#define ICSR1_TBY (1 << 1) /* Tramsmiter busy flag */ +#define ICSR1_RSY (1 << 0) /* Recevier synchronized flag */ + +#define IrSR_RXPL_NEG_IS_ZERO (1<<4) +#define IrSR_RXPL_POS_IS_ZERO 0x0 +#define IrSR_TXPL_NEG_IS_ZERO (1<<3) +#define IrSR_TXPL_POS_IS_ZERO 0x0 +#define IrSR_XMODE_PULSE_1_6 (1<<2) +#define IrSR_XMODE_PULSE_3_16 0x0 +#define IrSR_RCVEIR_IR_MODE (1<<1) +#define IrSR_RCVEIR_UART_MODE 0x0 +#define IrSR_XMITIR_IR_MODE (1<<0) +#define IrSR_XMITIR_UART_MODE 0x0 + +#define IrSR_IR_RECEIVE_ON (\ + IrSR_RXPL_NEG_IS_ZERO | \ + IrSR_TXPL_POS_IS_ZERO | \ + IrSR_XMODE_PULSE_3_16 | \ + IrSR_RCVEIR_IR_MODE | \ + IrSR_XMITIR_UART_MODE) + +#define IrSR_IR_TRANSMIT_ON (\ + IrSR_RXPL_NEG_IS_ZERO | \ + IrSR_TXPL_POS_IS_ZERO | \ + IrSR_XMODE_PULSE_3_16 | \ + IrSR_RCVEIR_UART_MODE | \ + IrSR_XMITIR_IR_MODE) + +/* macros for registers read/write */ +#define ficp_writel(irda, val, off) \ + do { \ + dev_vdbg(irda->dev, \ + "%s():%d ficp_writel(0x%x, %s)\n", \ + __func__, __LINE__, (val), #off); \ + writel_relaxed((val), (irda)->irda_base + (off)); \ + } while (0) + +#define ficp_readl(irda, off) \ + ({ \ + unsigned int _v; \ + _v = readl_relaxed((irda)->irda_base + (off)); \ + dev_vdbg(irda->dev, \ + "%s():%d ficp_readl(%s): 0x%x\n", \ + __func__, __LINE__, #off, _v); \ + _v; \ + }) + +#define stuart_writel(irda, val, off) \ + do { \ + dev_vdbg(irda->dev, \ + "%s():%d stuart_writel(0x%x, %s)\n", \ + __func__, __LINE__, (val), #off); \ + writel_relaxed((val), (irda)->stuart_base + (off)); \ + } while (0) + +#define stuart_readl(irda, off) \ + ({ \ + unsigned int _v; \ + _v = readl_relaxed((irda)->stuart_base + (off)); \ + dev_vdbg(irda->dev, \ + "%s():%d stuart_readl(%s): 0x%x\n", \ + __func__, __LINE__, #off, _v); \ + _v; \ + }) + +struct pxa_irda { + int speed; + int newspeed; + unsigned long long last_clk; + + void __iomem *stuart_base; + void __iomem *irda_base; + unsigned char *dma_rx_buff; + unsigned char *dma_tx_buff; + dma_addr_t dma_rx_buff_phy; + dma_addr_t dma_tx_buff_phy; + unsigned int dma_tx_buff_len; + struct dma_chan *txdma; + struct dma_chan *rxdma; + dma_cookie_t rx_cookie; + dma_cookie_t tx_cookie; + int drcmr_rx; + int drcmr_tx; + + int uart_irq; + int icp_irq; + + struct irlap_cb *irlap; + struct qos_info qos; + + iobuff_t tx_buff; + iobuff_t rx_buff; + + struct device *dev; + struct pxaficp_platform_data *pdata; + struct clk *fir_clk; + struct clk *sir_clk; + struct clk *cur_clk; +}; + +static int pxa_irda_set_speed(struct pxa_irda *si, int speed); + +static inline void pxa_irda_disable_clk(struct pxa_irda *si) +{ + if (si->cur_clk) + clk_disable_unprepare(si->cur_clk); + si->cur_clk = NULL; +} + +static inline void pxa_irda_enable_firclk(struct pxa_irda *si) +{ + si->cur_clk = si->fir_clk; + clk_prepare_enable(si->fir_clk); +} + +static inline void pxa_irda_enable_sirclk(struct pxa_irda *si) +{ + si->cur_clk = si->sir_clk; + clk_prepare_enable(si->sir_clk); +} + + +#define IS_FIR(si) ((si)->speed >= 4000000) +#define IRDA_FRAME_SIZE_LIMIT 2047 + +static void pxa_irda_fir_dma_rx_irq(void *data); +static void pxa_irda_fir_dma_tx_irq(void *data); + +inline static void pxa_irda_fir_dma_rx_start(struct pxa_irda *si) +{ + struct dma_async_tx_descriptor *tx; + + tx = dmaengine_prep_slave_single(si->rxdma, si->dma_rx_buff_phy, + IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(si->dev, "prep_slave_sg() failed\n"); + return; + } + tx->callback = pxa_irda_fir_dma_rx_irq; + tx->callback_param = si; + si->rx_cookie = dmaengine_submit(tx); + dma_async_issue_pending(si->rxdma); +} + +inline static void pxa_irda_fir_dma_tx_start(struct pxa_irda *si) +{ + struct dma_async_tx_descriptor *tx; + + tx = dmaengine_prep_slave_single(si->txdma, si->dma_tx_buff_phy, + si->dma_tx_buff_len, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(si->dev, "prep_slave_sg() failed\n"); + return; + } + tx->callback = pxa_irda_fir_dma_tx_irq; + tx->callback_param = si; + si->tx_cookie = dmaengine_submit(tx); + dma_async_issue_pending(si->rxdma); +} + +/* + * Set the IrDA communications mode. + */ +static void pxa_irda_set_mode(struct pxa_irda *si, int mode) +{ + if (si->pdata->transceiver_mode) + si->pdata->transceiver_mode(si->dev, mode); + else { + if (gpio_is_valid(si->pdata->gpio_pwdown)) + gpio_set_value(si->pdata->gpio_pwdown, + !(mode & IR_OFF) ^ + !si->pdata->gpio_pwdown_inverted); + pxa2xx_transceiver_mode(si->dev, mode); + } +} + +/* + * Set the IrDA communications speed. + */ +static int pxa_irda_set_speed(struct pxa_irda *si, int speed) +{ + unsigned long flags; + unsigned int divisor; + + switch (speed) { + case 9600: case 19200: case 38400: + case 57600: case 115200: + + /* refer to PXA250/210 Developer's Manual 10-7 */ + /* BaudRate = 14.7456 MHz / (16*Divisor) */ + divisor = 14745600 / (16 * speed); + + local_irq_save(flags); + + if (IS_FIR(si)) { + /* stop RX DMA */ + dmaengine_terminate_all(si->rxdma); + /* disable FICP */ + ficp_writel(si, 0, ICCR0); + pxa_irda_disable_clk(si); + + /* set board transceiver to SIR mode */ + pxa_irda_set_mode(si, IR_SIRMODE); + + /* enable the STUART clock */ + pxa_irda_enable_sirclk(si); + } + + /* disable STUART first */ + stuart_writel(si, 0, STIER); + + /* access DLL & DLH */ + stuart_writel(si, stuart_readl(si, STLCR) | LCR_DLAB, STLCR); + stuart_writel(si, divisor & 0xff, STDLL); + stuart_writel(si, divisor >> 8, STDLH); + stuart_writel(si, stuart_readl(si, STLCR) & ~LCR_DLAB, STLCR); + + si->speed = speed; + stuart_writel(si, IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6, + STISR); + stuart_writel(si, IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE, + STIER); + + local_irq_restore(flags); + break; + + case 4000000: + local_irq_save(flags); + + /* disable STUART */ + stuart_writel(si, 0, STIER); + stuart_writel(si, 0, STISR); + pxa_irda_disable_clk(si); + + /* disable FICP first */ + ficp_writel(si, 0, ICCR0); + + /* set board transceiver to FIR mode */ + pxa_irda_set_mode(si, IR_FIRMODE); + + /* enable the FICP clock */ + pxa_irda_enable_firclk(si); + + si->speed = speed; + pxa_irda_fir_dma_rx_start(si); + ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); + + local_irq_restore(flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* SIR interrupt service routine. */ +static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct pxa_irda *si = netdev_priv(dev); + int iir, lsr, data; + + iir = stuart_readl(si, STIIR); + + switch (iir & 0x0F) { + case 0x06: /* Receiver Line Status */ + lsr = stuart_readl(si, STLSR); + while (lsr & LSR_FIFOE) { + data = stuart_readl(si, STRBR); + if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) { + printk(KERN_DEBUG "pxa_ir: sir receiving error\n"); + dev->stats.rx_errors++; + if (lsr & LSR_FE) + dev->stats.rx_frame_errors++; + if (lsr & LSR_OE) + dev->stats.rx_fifo_errors++; + } else { + dev->stats.rx_bytes++; + async_unwrap_char(dev, &dev->stats, + &si->rx_buff, data); + } + lsr = stuart_readl(si, STLSR); + } + si->last_clk = sched_clock(); + break; + + case 0x04: /* Received Data Available */ + /* forth through */ + + case 0x0C: /* Character Timeout Indication */ + do { + dev->stats.rx_bytes++; + async_unwrap_char(dev, &dev->stats, &si->rx_buff, + stuart_readl(si, STRBR)); + } while (stuart_readl(si, STLSR) & LSR_DR); + si->last_clk = sched_clock(); + break; + + case 0x02: /* Transmit FIFO Data Request */ + while ((si->tx_buff.len) && + (stuart_readl(si, STLSR) & LSR_TDRQ)) { + stuart_writel(si, *si->tx_buff.data++, STTHR); + si->tx_buff.len -= 1; + } + + if (si->tx_buff.len == 0) { + dev->stats.tx_packets++; + dev->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head; + + /* We need to ensure that the transmitter has finished. */ + while ((stuart_readl(si, STLSR) & LSR_TEMT) == 0) + cpu_relax(); + si->last_clk = sched_clock(); + + /* + * Ok, we've finished transmitting. Now enable + * the receiver. Sometimes we get a receive IRQ + * immediately after a transmit... + */ + if (si->newspeed) { + pxa_irda_set_speed(si, si->newspeed); + si->newspeed = 0; + } else { + /* enable IR Receiver, disable IR Transmitter */ + stuart_writel(si, IrSR_IR_RECEIVE_ON | + IrSR_XMODE_PULSE_1_6, STISR); + /* enable STUART and receive interrupts */ + stuart_writel(si, IER_UUE | IER_RLSE | + IER_RAVIE | IER_RTIOE, STIER); + } + /* I'm hungry! */ + netif_wake_queue(dev); + } + break; + } + + return IRQ_HANDLED; +} + +/* FIR Receive DMA interrupt handler */ +static void pxa_irda_fir_dma_rx_irq(void *data) +{ + struct net_device *dev = data; + struct pxa_irda *si = netdev_priv(dev); + + dmaengine_terminate_all(si->rxdma); + netdev_dbg(dev, "pxa_ir: fir rx dma bus error\n"); +} + +/* FIR Transmit DMA interrupt handler */ +static void pxa_irda_fir_dma_tx_irq(void *data) +{ + struct net_device *dev = data; + struct pxa_irda *si = netdev_priv(dev); + + dmaengine_terminate_all(si->txdma); + if (dmaengine_tx_status(si->txdma, si->tx_cookie, NULL) == DMA_ERROR) { + dev->stats.tx_errors++; + } else { + dev->stats.tx_packets++; + dev->stats.tx_bytes += si->dma_tx_buff_len; + } + + while (ficp_readl(si, ICSR1) & ICSR1_TBY) + cpu_relax(); + si->last_clk = sched_clock(); + + /* + * HACK: It looks like the TBY bit is dropped too soon. + * Without this delay things break. + */ + udelay(120); + + if (si->newspeed) { + pxa_irda_set_speed(si, si->newspeed); + si->newspeed = 0; + } else { + int i = 64; + + ficp_writel(si, 0, ICCR0); + pxa_irda_fir_dma_rx_start(si); + while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--) + ficp_readl(si, ICDR); + ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); + + if (i < 0) + printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); + } + netif_wake_queue(dev); +} + +/* EIF(Error in FIFO/End in Frame) handler for FIR */ +static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, int icsr0) +{ + unsigned int len, stat, data; + struct dma_tx_state state; + + /* Get the current data position. */ + + dmaengine_tx_status(si->rxdma, si->rx_cookie, &state); + len = IRDA_FRAME_SIZE_LIMIT - state.residue; + + do { + /* Read Status, and then Data. */ + stat = ficp_readl(si, ICSR1); + rmb(); + data = ficp_readl(si, ICDR); + + if (stat & (ICSR1_CRE | ICSR1_ROR)) { + dev->stats.rx_errors++; + if (stat & ICSR1_CRE) { + printk(KERN_DEBUG "pxa_ir: fir receive CRC error\n"); + dev->stats.rx_crc_errors++; + } + if (stat & ICSR1_ROR) { + printk(KERN_DEBUG "pxa_ir: fir receive overrun\n"); + dev->stats.rx_over_errors++; + } + } else { + si->dma_rx_buff[len++] = data; + } + /* If we hit the end of frame, there's no point in continuing. */ + if (stat & ICSR1_EOF) + break; + } while (ficp_readl(si, ICSR0) & ICSR0_EIF); + + if (stat & ICSR1_EOF) { + /* end of frame. */ + struct sk_buff *skb; + + if (icsr0 & ICSR0_FRE) { + printk(KERN_ERR "pxa_ir: dropping erroneous frame\n"); + dev->stats.rx_dropped++; + return; + } + + skb = alloc_skb(len+1,GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "pxa_ir: fir out of memory for receive skb\n"); + dev->stats.rx_dropped++; + return; + } + + /* Align IP header to 20 bytes */ + skb_reserve(skb, 1); + skb_copy_to_linear_data(skb, si->dma_rx_buff, len); + skb_put(skb, len); + + /* Feed it to IrLAP */ + skb->dev = dev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; + } +} + +/* FIR interrupt handler */ +static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct pxa_irda *si = netdev_priv(dev); + int icsr0, i = 64; + + /* stop RX DMA */ + dmaengine_terminate_all(si->rxdma); + si->last_clk = sched_clock(); + icsr0 = ficp_readl(si, ICSR0); + + if (icsr0 & (ICSR0_FRE | ICSR0_RAB)) { + if (icsr0 & ICSR0_FRE) { + printk(KERN_DEBUG "pxa_ir: fir receive frame error\n"); + dev->stats.rx_frame_errors++; + } else { + printk(KERN_DEBUG "pxa_ir: fir receive abort\n"); + dev->stats.rx_errors++; + } + ficp_writel(si, icsr0 & (ICSR0_FRE | ICSR0_RAB), ICSR0); + } + + if (icsr0 & ICSR0_EIF) { + /* An error in FIFO occurred, or there is a end of frame */ + pxa_irda_fir_irq_eif(si, dev, icsr0); + } + + ficp_writel(si, 0, ICCR0); + pxa_irda_fir_dma_rx_start(si); + while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--) + ficp_readl(si, ICDR); + ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); + + if (i < 0) + printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); + + return IRQ_HANDLED; +} + +/* hard_xmit interface of irda device */ +static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + int speed = irda_get_next_speed(skb); + + /* + * Does this packet contain a request to change the interface + * speed? If so, remember it until we complete the transmission + * of this frame. + */ + if (speed != si->speed && speed != -1) + si->newspeed = speed; + + /* + * If this is an empty frame, we can bypass a lot. + */ + if (skb->len == 0) { + if (si->newspeed) { + si->newspeed = 0; + pxa_irda_set_speed(si, speed); + } + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + netif_stop_queue(dev); + + if (!IS_FIR(si)) { + si->tx_buff.data = si->tx_buff.head; + si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, si->tx_buff.truesize); + + /* Disable STUART interrupts and switch to transmit mode. */ + stuart_writel(si, 0, STIER); + stuart_writel(si, IrSR_IR_TRANSMIT_ON | IrSR_XMODE_PULSE_1_6, + STISR); + + /* enable STUART and transmit interrupts */ + stuart_writel(si, IER_UUE | IER_TIE, STIER); + } else { + unsigned long mtt = irda_get_mtt(skb); + + si->dma_tx_buff_len = skb->len; + skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len); + + if (mtt) + while ((sched_clock() - si->last_clk) * 1000 < mtt) + cpu_relax(); + + /* stop RX DMA, disable FICP */ + dmaengine_terminate_all(si->rxdma); + ficp_writel(si, 0, ICCR0); + + pxa_irda_fir_dma_tx_start(si); + ficp_writel(si, ICCR0_ITR | ICCR0_TXE, ICCR0); + } + + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static int pxa_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) +{ + struct if_irda_req *rq = (struct if_irda_req *)ifreq; + struct pxa_irda *si = netdev_priv(dev); + int ret; + + switch (cmd) { + case SIOCSBANDWIDTH: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + /* + * We are unable to set the speed if the + * device is not running. + */ + if (netif_running(dev)) { + ret = pxa_irda_set_speed(si, + rq->ifr_baudrate); + } else { + printk(KERN_INFO "pxa_ir: SIOCSBANDWIDTH: !netif_running\n"); + ret = 0; + } + } + break; + + case SIOCSMEDIABUSY: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + irda_device_set_media_busy(dev, TRUE); + ret = 0; + } + break; + + case SIOCGRECEIVING: + ret = 0; + rq->ifr_receiving = IS_FIR(si) ? 0 + : si->rx_buff.state != OUTSIDE_FRAME; + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static void pxa_irda_startup(struct pxa_irda *si) +{ + /* Disable STUART interrupts */ + stuart_writel(si, 0, STIER); + /* enable STUART interrupt to the processor */ + stuart_writel(si, MCR_OUT2, STMCR); + /* configure SIR frame format: StartBit - Data 7 ... Data 0 - Stop Bit */ + stuart_writel(si, LCR_WLS0 | LCR_WLS1, STLCR); + /* enable FIFO, we use FIFO to improve performance */ + stuart_writel(si, FCR_TRFIFOE | FCR_ITL_32, STFCR); + + /* disable FICP */ + ficp_writel(si, 0, ICCR0); + /* configure FICP ICCR2 */ + ficp_writel(si, ICCR2_TXP | ICCR2_TRIG_32, ICCR2); + + /* force SIR reinitialization */ + si->speed = 4000000; + pxa_irda_set_speed(si, 9600); + + printk(KERN_DEBUG "pxa_ir: irda startup\n"); +} + +static void pxa_irda_shutdown(struct pxa_irda *si) +{ + unsigned long flags; + + local_irq_save(flags); + + /* disable STUART and interrupt */ + stuart_writel(si, 0, STIER); + /* disable STUART SIR mode */ + stuart_writel(si, 0, STISR); + + /* disable DMA */ + dmaengine_terminate_all(si->rxdma); + dmaengine_terminate_all(si->txdma); + /* disable FICP */ + ficp_writel(si, 0, ICCR0); + + /* disable the STUART or FICP clocks */ + pxa_irda_disable_clk(si); + + local_irq_restore(flags); + + /* power off board transceiver */ + pxa_irda_set_mode(si, IR_OFF); + + printk(KERN_DEBUG "pxa_ir: irda shutdown\n"); +} + +static int pxa_irda_start(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + dma_cap_mask_t mask; + struct dma_slave_config config; + struct pxad_param param; + int err; + + si->speed = 9600; + + err = request_irq(si->uart_irq, pxa_irda_sir_irq, 0, dev->name, dev); + if (err) + goto err_irq1; + + err = request_irq(si->icp_irq, pxa_irda_fir_irq, 0, dev->name, dev); + if (err) + goto err_irq2; + + /* + * The interrupt must remain disabled for now. + */ + disable_irq(si->uart_irq); + disable_irq(si->icp_irq); + + err = -EBUSY; + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + param.prio = PXAD_PRIO_LOWEST; + + memset(&config, 0, sizeof(config)); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.src_addr = (dma_addr_t)si->irda_base + ICDR; + config.dst_addr = (dma_addr_t)si->irda_base + ICDR; + config.src_maxburst = 32; + config.dst_maxburst = 32; + + param.drcmr = si->drcmr_rx; + si->rxdma = dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m, &dev->dev, "rx"); + if (!si->rxdma) + goto err_rx_dma; + + param.drcmr = si->drcmr_tx; + si->txdma = dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m, &dev->dev, "tx"); + if (!si->txdma) + goto err_tx_dma; + + err = dmaengine_slave_config(si->rxdma, &config); + if (err) + goto err_dma_rx_buff; + err = dmaengine_slave_config(si->txdma, &config); + if (err) + goto err_dma_rx_buff; + + err = -ENOMEM; + si->dma_rx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, + &si->dma_rx_buff_phy, GFP_KERNEL); + if (!si->dma_rx_buff) + goto err_dma_rx_buff; + + si->dma_tx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, + &si->dma_tx_buff_phy, GFP_KERNEL); + if (!si->dma_tx_buff) + goto err_dma_tx_buff; + + /* Setup the serial port for the initial speed. */ + pxa_irda_startup(si); + + /* + * Open a new IrLAP layer instance. + */ + si->irlap = irlap_open(dev, &si->qos, "pxa"); + err = -ENOMEM; + if (!si->irlap) + goto err_irlap; + + /* + * Now enable the interrupt and start the queue + */ + enable_irq(si->uart_irq); + enable_irq(si->icp_irq); + netif_start_queue(dev); + + printk(KERN_DEBUG "pxa_ir: irda driver opened\n"); + + return 0; + +err_irlap: + pxa_irda_shutdown(si); + dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy); +err_dma_tx_buff: + dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy); +err_dma_rx_buff: + dma_release_channel(si->txdma); +err_tx_dma: + dma_release_channel(si->rxdma); +err_rx_dma: + free_irq(si->icp_irq, dev); +err_irq2: + free_irq(si->uart_irq, dev); +err_irq1: + + return err; +} + +static int pxa_irda_stop(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + + netif_stop_queue(dev); + + pxa_irda_shutdown(si); + + /* Stop IrLAP */ + if (si->irlap) { + irlap_close(si->irlap); + si->irlap = NULL; + } + + free_irq(si->uart_irq, dev); + free_irq(si->icp_irq, dev); + + dmaengine_terminate_all(si->rxdma); + dmaengine_terminate_all(si->txdma); + dma_release_channel(si->rxdma); + dma_release_channel(si->txdma); + + if (si->dma_rx_buff) + dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy); + if (si->dma_tx_buff) + dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy); + + printk(KERN_DEBUG "pxa_ir: irda driver closed\n"); + return 0; +} + +static int pxa_irda_suspend(struct platform_device *_dev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(_dev); + struct pxa_irda *si; + + if (dev && netif_running(dev)) { + si = netdev_priv(dev); + netif_device_detach(dev); + pxa_irda_shutdown(si); + } + + return 0; +} + +static int pxa_irda_resume(struct platform_device *_dev) +{ + struct net_device *dev = platform_get_drvdata(_dev); + struct pxa_irda *si; + + if (dev && netif_running(dev)) { + si = netdev_priv(dev); + pxa_irda_startup(si); + netif_device_attach(dev); + netif_wake_queue(dev); + } + + return 0; +} + + +static int pxa_irda_init_iobuf(iobuff_t *io, int size) +{ + io->head = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (io->head != NULL) { + io->truesize = size; + io->in_frame = FALSE; + io->state = OUTSIDE_FRAME; + io->data = io->head; + } + return io->head ? 0 : -ENOMEM; +} + +static const struct net_device_ops pxa_irda_netdev_ops = { + .ndo_open = pxa_irda_start, + .ndo_stop = pxa_irda_stop, + .ndo_start_xmit = pxa_irda_hard_xmit, + .ndo_do_ioctl = pxa_irda_ioctl, +}; + +static int pxa_irda_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct resource *res; + struct pxa_irda *si; + void __iomem *ficp, *stuart; + unsigned int baudrate_mask; + int err; + + if (!pdev->dev.platform_data) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ficp = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ficp)) { + dev_err(&pdev->dev, "resource ficp not defined\n"); + return PTR_ERR(ficp); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + stuart = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(stuart)) { + dev_err(&pdev->dev, "resource stuart not defined\n"); + return PTR_ERR(stuart); + } + + dev = alloc_irdadev(sizeof(struct pxa_irda)); + if (!dev) { + err = -ENOMEM; + goto err_mem_1; + } + + SET_NETDEV_DEV(dev, &pdev->dev); + si = netdev_priv(dev); + si->dev = &pdev->dev; + si->pdata = pdev->dev.platform_data; + + si->irda_base = ficp; + si->stuart_base = stuart; + si->uart_irq = platform_get_irq(pdev, 0); + si->icp_irq = platform_get_irq(pdev, 1); + + si->sir_clk = devm_clk_get(&pdev->dev, "UARTCLK"); + si->fir_clk = devm_clk_get(&pdev->dev, "FICPCLK"); + if (IS_ERR(si->sir_clk) || IS_ERR(si->fir_clk)) { + err = PTR_ERR(IS_ERR(si->sir_clk) ? si->sir_clk : si->fir_clk); + goto err_mem_4; + } + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res) + si->drcmr_rx = res->start; + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res) + si->drcmr_tx = res->start; + + /* + * Initialise the SIR buffers + */ + err = pxa_irda_init_iobuf(&si->rx_buff, 14384); + if (err) + goto err_mem_4; + err = pxa_irda_init_iobuf(&si->tx_buff, 4000); + if (err) + goto err_mem_5; + + if (gpio_is_valid(si->pdata->gpio_pwdown)) { + err = gpio_request(si->pdata->gpio_pwdown, "IrDA switch"); + if (err) + goto err_startup; + err = gpio_direction_output(si->pdata->gpio_pwdown, + !si->pdata->gpio_pwdown_inverted); + if (err) { + gpio_free(si->pdata->gpio_pwdown); + goto err_startup; + } + } + + if (si->pdata->startup) { + err = si->pdata->startup(si->dev); + if (err) + goto err_startup; + } + + if (gpio_is_valid(si->pdata->gpio_pwdown) && si->pdata->startup) + dev_warn(si->dev, "gpio_pwdown and startup() both defined!\n"); + + dev->netdev_ops = &pxa_irda_netdev_ops; + + irda_init_max_qos_capabilies(&si->qos); + + baudrate_mask = 0; + if (si->pdata->transceiver_cap & IR_SIRMODE) + baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + if (si->pdata->transceiver_cap & IR_FIRMODE) + baudrate_mask |= IR_4000000 << 8; + + si->qos.baud_rate.bits &= baudrate_mask; + si->qos.min_turn_time.bits = 7; /* 1ms or more */ + + irda_qos_bits_to_value(&si->qos); + + err = register_netdev(dev); + + if (err == 0) + platform_set_drvdata(pdev, dev); + + if (err) { + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); +err_startup: + kfree(si->tx_buff.head); +err_mem_5: + kfree(si->rx_buff.head); +err_mem_4: + free_netdev(dev); + } +err_mem_1: + return err; +} + +static int pxa_irda_remove(struct platform_device *_dev) +{ + struct net_device *dev = platform_get_drvdata(_dev); + + if (dev) { + struct pxa_irda *si = netdev_priv(dev); + unregister_netdev(dev); + if (gpio_is_valid(si->pdata->gpio_pwdown)) + gpio_free(si->pdata->gpio_pwdown); + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + free_netdev(dev); + } + + return 0; +} + +static struct platform_driver pxa_ir_driver = { + .driver = { + .name = "pxa2xx-ir", + }, + .probe = pxa_irda_probe, + .remove = pxa_irda_remove, + .suspend = pxa_irda_suspend, + .resume = pxa_irda_resume, +}; + +module_platform_driver(pxa_ir_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa2xx-ir"); diff --git a/drivers/staging/irda/drivers/sa1100_ir.c b/drivers/staging/irda/drivers/sa1100_ir.c new file mode 100644 index 000000000000..b6e44ff4e373 --- /dev/null +++ b/drivers/staging/irda/drivers/sa1100_ir.c @@ -0,0 +1,1150 @@ +/* + * linux/drivers/net/irda/sa1100_ir.c + * + * Copyright (C) 2000-2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Infra-red driver for the StrongARM SA1100 embedded microprocessor + * + * Note that we don't have to worry about the SA1111's DMA bugs in here, + * so we use the straight forward dma_map_* functions with a null pointer. + * + * This driver takes one kernel command line parameter, sa1100ir=, with + * the following options: + * max_rate:baudrate - set the maximum baud rate + * power_level:level - set the transmitter power level + * tx_lpm:0|1 - set transmit low power mode + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static int power_level = 3; +static int tx_lpm; +static int max_rate = 4000000; + +struct sa1100_buf { + struct device *dev; + struct sk_buff *skb; + struct scatterlist sg; + struct dma_chan *chan; + dma_cookie_t cookie; +}; + +struct sa1100_irda { + unsigned char utcr4; + unsigned char power; + unsigned char open; + + int speed; + int newspeed; + + struct sa1100_buf dma_rx; + struct sa1100_buf dma_tx; + + struct device *dev; + struct irda_platform_data *pdata; + struct irlap_cb *irlap; + struct qos_info qos; + + iobuff_t tx_buff; + iobuff_t rx_buff; + + int (*tx_start)(struct sk_buff *, struct net_device *, struct sa1100_irda *); + irqreturn_t (*irq)(struct net_device *, struct sa1100_irda *); +}; + +static int sa1100_irda_set_speed(struct sa1100_irda *, int); + +#define IS_FIR(si) ((si)->speed >= 4000000) + +#define HPSIR_MAX_RXLEN 2047 + +static struct dma_slave_config sa1100_irda_sir_tx = { + .direction = DMA_TO_DEVICE, + .dst_addr = __PREG(Ser2UTDR), + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_maxburst = 4, +}; + +static struct dma_slave_config sa1100_irda_fir_rx = { + .direction = DMA_FROM_DEVICE, + .src_addr = __PREG(Ser2HSDR), + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .src_maxburst = 8, +}; + +static struct dma_slave_config sa1100_irda_fir_tx = { + .direction = DMA_TO_DEVICE, + .dst_addr = __PREG(Ser2HSDR), + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_maxburst = 8, +}; + +static unsigned sa1100_irda_dma_xferred(struct sa1100_buf *buf) +{ + struct dma_chan *chan = buf->chan; + struct dma_tx_state state; + enum dma_status status; + + status = chan->device->device_tx_status(chan, buf->cookie, &state); + if (status != DMA_PAUSED) + return 0; + + return sg_dma_len(&buf->sg) - state.residue; +} + +static int sa1100_irda_dma_request(struct device *dev, struct sa1100_buf *buf, + const char *name, struct dma_slave_config *cfg) +{ + dma_cap_mask_t m; + int ret; + + dma_cap_zero(m); + dma_cap_set(DMA_SLAVE, m); + + buf->chan = dma_request_channel(m, sa11x0_dma_filter_fn, (void *)name); + if (!buf->chan) { + dev_err(dev, "unable to request DMA channel for %s\n", + name); + return -ENOENT; + } + + ret = dmaengine_slave_config(buf->chan, cfg); + if (ret) + dev_warn(dev, "DMA slave_config for %s returned %d\n", + name, ret); + + buf->dev = buf->chan->device->dev; + + return 0; +} + +static void sa1100_irda_dma_start(struct sa1100_buf *buf, + enum dma_transfer_direction dir, dma_async_tx_callback cb, void *cb_p) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = buf->chan; + + desc = dmaengine_prep_slave_sg(chan, &buf->sg, 1, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (desc) { + desc->callback = cb; + desc->callback_param = cb_p; + buf->cookie = dmaengine_submit(desc); + dma_async_issue_pending(chan); + } +} + +/* + * Allocate and map the receive buffer, unless it is already allocated. + */ +static int sa1100_irda_rx_alloc(struct sa1100_irda *si) +{ + if (si->dma_rx.skb) + return 0; + + si->dma_rx.skb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); + if (!si->dma_rx.skb) { + printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n"); + return -ENOMEM; + } + + /* + * Align any IP headers that may be contained + * within the frame. + */ + skb_reserve(si->dma_rx.skb, 1); + + sg_set_buf(&si->dma_rx.sg, si->dma_rx.skb->data, HPSIR_MAX_RXLEN); + if (dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE) == 0) { + dev_kfree_skb_any(si->dma_rx.skb); + return -ENOMEM; + } + + return 0; +} + +/* + * We want to get here as soon as possible, and get the receiver setup. + * We use the existing buffer. + */ +static void sa1100_irda_rx_dma_start(struct sa1100_irda *si) +{ + if (!si->dma_rx.skb) { + printk(KERN_ERR "sa1100_ir: rx buffer went missing\n"); + return; + } + + /* + * First empty receive FIFO + */ + Ser2HSCR0 = HSCR0_HSSP; + + /* + * Enable the DMA, receiver and receive interrupt. + */ + dmaengine_terminate_all(si->dma_rx.chan); + sa1100_irda_dma_start(&si->dma_rx, DMA_DEV_TO_MEM, NULL, NULL); + + Ser2HSCR0 = HSCR0_HSSP | HSCR0_RXE; +} + +static void sa1100_irda_check_speed(struct sa1100_irda *si) +{ + if (si->newspeed) { + sa1100_irda_set_speed(si, si->newspeed); + si->newspeed = 0; + } +} + +/* + * HP-SIR format support. + */ +static void sa1100_irda_sirtxdma_irq(void *id) +{ + struct net_device *dev = id; + struct sa1100_irda *si = netdev_priv(dev); + + dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE); + dev_kfree_skb(si->dma_tx.skb); + si->dma_tx.skb = NULL; + + dev->stats.tx_packets++; + dev->stats.tx_bytes += sg_dma_len(&si->dma_tx.sg); + + /* We need to ensure that the transmitter has finished. */ + do + rmb(); + while (Ser2UTSR1 & UTSR1_TBY); + + /* + * Ok, we've finished transmitting. Now enable the receiver. + * Sometimes we get a receive IRQ immediately after a transmit... + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; + + sa1100_irda_check_speed(si); + + /* I'm hungry! */ + netif_wake_queue(dev); +} + +static int sa1100_irda_sir_tx_start(struct sk_buff *skb, struct net_device *dev, + struct sa1100_irda *si) +{ + si->tx_buff.data = si->tx_buff.head; + si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, + si->tx_buff.truesize); + + si->dma_tx.skb = skb; + sg_set_buf(&si->dma_tx.sg, si->tx_buff.data, si->tx_buff.len); + if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { + si->dma_tx.skb = NULL; + netif_wake_queue(dev); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_sirtxdma_irq, dev); + + /* + * The mean turn-around time is enforced by XBOF padding, + * so we don't have to do anything special here. + */ + Ser2UTCR3 = UTCR3_TXE; + + return NETDEV_TX_OK; +} + +static irqreturn_t sa1100_irda_sir_irq(struct net_device *dev, struct sa1100_irda *si) +{ + int status; + + status = Ser2UTSR0; + + /* + * Deal with any receive errors first. The bytes in error may be + * the only bytes in the receive FIFO, so we do this first. + */ + while (status & UTSR0_EIF) { + int stat, data; + + stat = Ser2UTSR1; + data = Ser2UTDR; + + if (stat & (UTSR1_FRE | UTSR1_ROR)) { + dev->stats.rx_errors++; + if (stat & UTSR1_FRE) + dev->stats.rx_frame_errors++; + if (stat & UTSR1_ROR) + dev->stats.rx_fifo_errors++; + } else + async_unwrap_char(dev, &dev->stats, &si->rx_buff, data); + + status = Ser2UTSR0; + } + + /* + * We must clear certain bits. + */ + Ser2UTSR0 = status & (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + + if (status & UTSR0_RFS) { + /* + * There are at least 4 bytes in the FIFO. Read 3 bytes + * and leave the rest to the block below. + */ + async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); + async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); + async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); + } + + if (status & (UTSR0_RFS | UTSR0_RID)) { + /* + * Fifo contains more than 1 character. + */ + do { + async_unwrap_char(dev, &dev->stats, &si->rx_buff, + Ser2UTDR); + } while (Ser2UTSR1 & UTSR1_RNE); + + } + + return IRQ_HANDLED; +} + +/* + * FIR format support. + */ +static void sa1100_irda_firtxdma_irq(void *id) +{ + struct net_device *dev = id; + struct sa1100_irda *si = netdev_priv(dev); + struct sk_buff *skb; + + /* + * Wait for the transmission to complete. Unfortunately, + * the hardware doesn't give us an interrupt to indicate + * "end of frame". + */ + do + rmb(); + while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY); + + /* + * Clear the transmit underrun bit. + */ + Ser2HSSR0 = HSSR0_TUR; + + /* + * Do we need to change speed? Note that we're lazy + * here - we don't free the old dma_rx.skb. We don't need + * to allocate a buffer either. + */ + sa1100_irda_check_speed(si); + + /* + * Start reception. This disables the transmitter for + * us. This will be using the existing RX buffer. + */ + sa1100_irda_rx_dma_start(si); + + /* Account and free the packet. */ + skb = si->dma_tx.skb; + if (skb) { + dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, + DMA_TO_DEVICE); + dev->stats.tx_packets ++; + dev->stats.tx_bytes += skb->len; + dev_kfree_skb_irq(skb); + si->dma_tx.skb = NULL; + } + + /* + * Make sure that the TX queue is available for sending + * (for retries). TX has priority over RX at all times. + */ + netif_wake_queue(dev); +} + +static int sa1100_irda_fir_tx_start(struct sk_buff *skb, struct net_device *dev, + struct sa1100_irda *si) +{ + int mtt = irda_get_mtt(skb); + + si->dma_tx.skb = skb; + sg_set_buf(&si->dma_tx.sg, skb->data, skb->len); + if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { + si->dma_tx.skb = NULL; + netif_wake_queue(dev); + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_firtxdma_irq, dev); + + /* + * If we have a mean turn-around time, impose the specified + * specified delay. We could shorten this by timing from + * the point we received the packet. + */ + if (mtt) + udelay(mtt); + + Ser2HSCR0 = HSCR0_HSSP | HSCR0_TXE; + + return NETDEV_TX_OK; +} + +static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev) +{ + struct sk_buff *skb = si->dma_rx.skb; + unsigned int len, stat, data; + + if (!skb) { + printk(KERN_ERR "sa1100_ir: SKB is NULL!\n"); + return; + } + + /* + * Get the current data position. + */ + len = sa1100_irda_dma_xferred(&si->dma_rx); + if (len > HPSIR_MAX_RXLEN) + len = HPSIR_MAX_RXLEN; + dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); + + do { + /* + * Read Status, and then Data. + */ + stat = Ser2HSSR1; + rmb(); + data = Ser2HSDR; + + if (stat & (HSSR1_CRE | HSSR1_ROR)) { + dev->stats.rx_errors++; + if (stat & HSSR1_CRE) + dev->stats.rx_crc_errors++; + if (stat & HSSR1_ROR) + dev->stats.rx_frame_errors++; + } else + skb->data[len++] = data; + + /* + * If we hit the end of frame, there's + * no point in continuing. + */ + if (stat & HSSR1_EOF) + break; + } while (Ser2HSSR0 & HSSR0_EIF); + + if (stat & HSSR1_EOF) { + si->dma_rx.skb = NULL; + + skb_put(skb, len); + skb->dev = dev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; + + /* + * Before we pass the buffer up, allocate a new one. + */ + sa1100_irda_rx_alloc(si); + + netif_rx(skb); + } else { + /* + * Remap the buffer - it was previously mapped, and we + * hope that this succeeds. + */ + dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); + } +} + +/* + * We only have to handle RX events here; transmit events go via the TX + * DMA handler. We disable RX, process, and the restart RX. + */ +static irqreturn_t sa1100_irda_fir_irq(struct net_device *dev, struct sa1100_irda *si) +{ + /* + * Stop RX DMA + */ + dmaengine_pause(si->dma_rx.chan); + + /* + * Framing error - we throw away the packet completely. + * Clearing RXE flushes the error conditions and data + * from the fifo. + */ + if (Ser2HSSR0 & (HSSR0_FRE | HSSR0_RAB)) { + dev->stats.rx_errors++; + + if (Ser2HSSR0 & HSSR0_FRE) + dev->stats.rx_frame_errors++; + + /* + * Clear out the DMA... + */ + Ser2HSCR0 = HSCR0_HSSP; + + /* + * Clear selected status bits now, so we + * don't miss them next time around. + */ + Ser2HSSR0 = HSSR0_FRE | HSSR0_RAB; + } + + /* + * Deal with any receive errors. The any of the lowest + * 8 bytes in the FIFO may contain an error. We must read + * them one by one. The "error" could even be the end of + * packet! + */ + if (Ser2HSSR0 & HSSR0_EIF) + sa1100_irda_fir_error(si, dev); + + /* + * No matter what happens, we must restart reception. + */ + sa1100_irda_rx_dma_start(si); + + return IRQ_HANDLED; +} + +/* + * Set the IrDA communications speed. + */ +static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) +{ + unsigned long flags; + int brd, ret = -EINVAL; + + switch (speed) { + case 9600: case 19200: case 38400: + case 57600: case 115200: + brd = 3686400 / (16 * speed) - 1; + + /* Stop the receive DMA, and configure transmit. */ + if (IS_FIR(si)) { + dmaengine_terminate_all(si->dma_rx.chan); + dmaengine_slave_config(si->dma_tx.chan, + &sa1100_irda_sir_tx); + } + + local_irq_save(flags); + + Ser2UTCR3 = 0; + Ser2HSCR0 = HSCR0_UART; + + Ser2UTCR1 = brd >> 8; + Ser2UTCR2 = brd; + + /* + * Clear status register + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; + + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); + + si->speed = speed; + si->tx_start = sa1100_irda_sir_tx_start; + si->irq = sa1100_irda_sir_irq; + + local_irq_restore(flags); + ret = 0; + break; + + case 4000000: + if (!IS_FIR(si)) + dmaengine_slave_config(si->dma_tx.chan, + &sa1100_irda_fir_tx); + + local_irq_save(flags); + + Ser2HSSR0 = 0xff; + Ser2HSCR0 = HSCR0_HSSP; + Ser2UTCR3 = 0; + + si->speed = speed; + si->tx_start = sa1100_irda_fir_tx_start; + si->irq = sa1100_irda_fir_irq; + + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); + + sa1100_irda_rx_alloc(si); + sa1100_irda_rx_dma_start(si); + + local_irq_restore(flags); + + break; + + default: + break; + } + + return ret; +} + +/* + * Control the power state of the IrDA transmitter. + * State: + * 0 - off + * 1 - short range, lowest power + * 2 - medium range, medium power + * 3 - maximum range, high power + * + * Currently, only assabet is known to support this. + */ +static int +__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state) +{ + int ret = 0; + if (si->pdata->set_power) + ret = si->pdata->set_power(si->dev, state); + return ret; +} + +static inline int +sa1100_set_power(struct sa1100_irda *si, unsigned int state) +{ + int ret; + + ret = __sa1100_irda_set_power(si, state); + if (ret == 0) + si->power = state; + + return ret; +} + +static irqreturn_t sa1100_irda_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct sa1100_irda *si = netdev_priv(dev); + + return si->irq(dev, si); +} + +static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct sa1100_irda *si = netdev_priv(dev); + int speed = irda_get_next_speed(skb); + + /* + * Does this packet contain a request to change the interface + * speed? If so, remember it until we complete the transmission + * of this frame. + */ + if (speed != si->speed && speed != -1) + si->newspeed = speed; + + /* If this is an empty frame, we can bypass a lot. */ + if (skb->len == 0) { + sa1100_irda_check_speed(si); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + netif_stop_queue(dev); + + /* We must not already have a skb to transmit... */ + BUG_ON(si->dma_tx.skb); + + return si->tx_start(skb, dev, si); +} + +static int +sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) +{ + struct if_irda_req *rq = (struct if_irda_req *)ifreq; + struct sa1100_irda *si = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (cmd) { + case SIOCSBANDWIDTH: + if (capable(CAP_NET_ADMIN)) { + /* + * We are unable to set the speed if the + * device is not running. + */ + if (si->open) { + ret = sa1100_irda_set_speed(si, + rq->ifr_baudrate); + } else { + printk("sa1100_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n"); + ret = 0; + } + } + break; + + case SIOCSMEDIABUSY: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + irda_device_set_media_busy(dev, TRUE); + ret = 0; + } + break; + + case SIOCGRECEIVING: + rq->ifr_receiving = IS_FIR(si) ? 0 + : si->rx_buff.state != OUTSIDE_FRAME; + break; + + default: + break; + } + + return ret; +} + +static int sa1100_irda_startup(struct sa1100_irda *si) +{ + int ret; + + /* + * Ensure that the ports for this device are setup correctly. + */ + if (si->pdata->startup) { + ret = si->pdata->startup(si->dev); + if (ret) + return ret; + } + + /* + * Configure PPC for IRDA - we want to drive TXD2 low. + * We also want to drive this pin low during sleep. + */ + PPSR &= ~PPC_TXD2; + PSDR &= ~PPC_TXD2; + PPDR |= PPC_TXD2; + + /* + * Enable HP-SIR modulation, and ensure that the port is disabled. + */ + Ser2UTCR3 = 0; + Ser2HSCR0 = HSCR0_UART; + Ser2UTCR4 = si->utcr4; + Ser2UTCR0 = UTCR0_8BitData; + Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL; + + /* + * Clear status register + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + + ret = sa1100_irda_set_speed(si, si->speed = 9600); + if (ret) { + Ser2UTCR3 = 0; + Ser2HSCR0 = 0; + + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); + } + + return ret; +} + +static void sa1100_irda_shutdown(struct sa1100_irda *si) +{ + /* + * Stop all DMA activity. + */ + dmaengine_terminate_all(si->dma_rx.chan); + dmaengine_terminate_all(si->dma_tx.chan); + + /* Disable the port. */ + Ser2UTCR3 = 0; + Ser2HSCR0 = 0; + + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); +} + +static int sa1100_irda_start(struct net_device *dev) +{ + struct sa1100_irda *si = netdev_priv(dev); + int err; + + si->speed = 9600; + + err = sa1100_irda_dma_request(si->dev, &si->dma_rx, "Ser2ICPRc", + &sa1100_irda_fir_rx); + if (err) + goto err_rx_dma; + + err = sa1100_irda_dma_request(si->dev, &si->dma_tx, "Ser2ICPTr", + &sa1100_irda_sir_tx); + if (err) + goto err_tx_dma; + + /* + * Setup the serial port for the specified speed. + */ + err = sa1100_irda_startup(si); + if (err) + goto err_startup; + + /* + * Open a new IrLAP layer instance. + */ + si->irlap = irlap_open(dev, &si->qos, "sa1100"); + err = -ENOMEM; + if (!si->irlap) + goto err_irlap; + + err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev); + if (err) + goto err_irq; + + /* + * Now enable the interrupt and start the queue + */ + si->open = 1; + sa1100_set_power(si, power_level); /* low power mode */ + + netif_start_queue(dev); + return 0; + +err_irq: + irlap_close(si->irlap); +err_irlap: + si->open = 0; + sa1100_irda_shutdown(si); +err_startup: + dma_release_channel(si->dma_tx.chan); +err_tx_dma: + dma_release_channel(si->dma_rx.chan); +err_rx_dma: + return err; +} + +static int sa1100_irda_stop(struct net_device *dev) +{ + struct sa1100_irda *si = netdev_priv(dev); + struct sk_buff *skb; + + netif_stop_queue(dev); + + si->open = 0; + sa1100_irda_shutdown(si); + + /* + * If we have been doing any DMA activity, make sure we + * tidy that up cleanly. + */ + skb = si->dma_rx.skb; + if (skb) { + dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, + DMA_FROM_DEVICE); + dev_kfree_skb(skb); + si->dma_rx.skb = NULL; + } + + skb = si->dma_tx.skb; + if (skb) { + dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, + DMA_TO_DEVICE); + dev_kfree_skb(skb); + si->dma_tx.skb = NULL; + } + + /* Stop IrLAP */ + if (si->irlap) { + irlap_close(si->irlap); + si->irlap = NULL; + } + + /* + * Free resources + */ + dma_release_channel(si->dma_tx.chan); + dma_release_channel(si->dma_rx.chan); + free_irq(dev->irq, dev); + + sa1100_set_power(si, 0); + + return 0; +} + +static int sa1100_irda_init_iobuf(iobuff_t *io, int size) +{ + io->head = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (io->head != NULL) { + io->truesize = size; + io->in_frame = FALSE; + io->state = OUTSIDE_FRAME; + io->data = io->head; + } + return io->head ? 0 : -ENOMEM; +} + +static const struct net_device_ops sa1100_irda_netdev_ops = { + .ndo_open = sa1100_irda_start, + .ndo_stop = sa1100_irda_stop, + .ndo_start_xmit = sa1100_irda_hard_xmit, + .ndo_do_ioctl = sa1100_irda_ioctl, +}; + +static int sa1100_irda_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct sa1100_irda *si; + unsigned int baudrate_mask; + int err, irq; + + if (!pdev->dev.platform_data) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq < 0 ? irq : -ENXIO; + + err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_1; + err = request_mem_region(__PREG(Ser2HSCR0), 0x1c, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_2; + err = request_mem_region(__PREG(Ser2HSCR2), 0x04, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_3; + + dev = alloc_irdadev(sizeof(struct sa1100_irda)); + if (!dev) { + err = -ENOMEM; + goto err_mem_4; + } + + SET_NETDEV_DEV(dev, &pdev->dev); + + si = netdev_priv(dev); + si->dev = &pdev->dev; + si->pdata = pdev->dev.platform_data; + + sg_init_table(&si->dma_rx.sg, 1); + sg_init_table(&si->dma_tx.sg, 1); + + /* + * Initialise the HP-SIR buffers + */ + err = sa1100_irda_init_iobuf(&si->rx_buff, 14384); + if (err) + goto err_mem_5; + err = sa1100_irda_init_iobuf(&si->tx_buff, IRDA_SIR_MAX_FRAME); + if (err) + goto err_mem_5; + + dev->netdev_ops = &sa1100_irda_netdev_ops; + dev->irq = irq; + + irda_init_max_qos_capabilies(&si->qos); + + /* + * We support original IRDA up to 115k2. (we don't currently + * support 4Mbps). Min Turn Time set to 1ms or greater. + */ + baudrate_mask = IR_9600; + + switch (max_rate) { + case 4000000: baudrate_mask |= IR_4000000 << 8; + case 115200: baudrate_mask |= IR_115200; + case 57600: baudrate_mask |= IR_57600; + case 38400: baudrate_mask |= IR_38400; + case 19200: baudrate_mask |= IR_19200; + } + + si->qos.baud_rate.bits &= baudrate_mask; + si->qos.min_turn_time.bits = 7; + + irda_qos_bits_to_value(&si->qos); + + si->utcr4 = UTCR4_HPSIR; + if (tx_lpm) + si->utcr4 |= UTCR4_Z1_6us; + + /* + * Initially enable HP-SIR modulation, and ensure that the port + * is disabled. + */ + Ser2UTCR3 = 0; + Ser2UTCR4 = si->utcr4; + Ser2HSCR0 = HSCR0_UART; + + err = register_netdev(dev); + if (err == 0) + platform_set_drvdata(pdev, dev); + + if (err) { + err_mem_5: + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + free_netdev(dev); + err_mem_4: + release_mem_region(__PREG(Ser2HSCR2), 0x04); + err_mem_3: + release_mem_region(__PREG(Ser2HSCR0), 0x1c); + err_mem_2: + release_mem_region(__PREG(Ser2UTCR0), 0x24); + } + err_mem_1: + return err; +} + +static int sa1100_irda_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + if (dev) { + struct sa1100_irda *si = netdev_priv(dev); + unregister_netdev(dev); + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + free_netdev(dev); + } + + release_mem_region(__PREG(Ser2HSCR2), 0x04); + release_mem_region(__PREG(Ser2HSCR0), 0x1c); + release_mem_region(__PREG(Ser2UTCR0), 0x24); + + return 0; +} + +#ifdef CONFIG_PM +/* + * Suspend the IrDA interface. + */ +static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct sa1100_irda *si; + + if (!dev) + return 0; + + si = netdev_priv(dev); + if (si->open) { + /* + * Stop the transmit queue + */ + netif_device_detach(dev); + disable_irq(dev->irq); + sa1100_irda_shutdown(si); + __sa1100_irda_set_power(si, 0); + } + + return 0; +} + +/* + * Resume the IrDA interface. + */ +static int sa1100_irda_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct sa1100_irda *si; + + if (!dev) + return 0; + + si = netdev_priv(dev); + if (si->open) { + /* + * If we missed a speed change, initialise at the new speed + * directly. It is debatable whether this is actually + * required, but in the interests of continuing from where + * we left off it is desirable. The converse argument is + * that we should re-negotiate at 9600 baud again. + */ + if (si->newspeed) { + si->speed = si->newspeed; + si->newspeed = 0; + } + + sa1100_irda_startup(si); + __sa1100_irda_set_power(si, si->power); + enable_irq(dev->irq); + + /* + * This automatically wakes up the queue + */ + netif_device_attach(dev); + } + + return 0; +} +#else +#define sa1100_irda_suspend NULL +#define sa1100_irda_resume NULL +#endif + +static struct platform_driver sa1100ir_driver = { + .probe = sa1100_irda_probe, + .remove = sa1100_irda_remove, + .suspend = sa1100_irda_suspend, + .resume = sa1100_irda_resume, + .driver = { + .name = "sa11x0-ir", + }, +}; + +static int __init sa1100_irda_init(void) +{ + /* + * Limit power level a sensible range. + */ + if (power_level < 1) + power_level = 1; + if (power_level > 3) + power_level = 3; + + return platform_driver_register(&sa1100ir_driver); +} + +static void __exit sa1100_irda_exit(void) +{ + platform_driver_unregister(&sa1100ir_driver); +} + +module_init(sa1100_irda_init); +module_exit(sa1100_irda_exit); +module_param(power_level, int, 0); +module_param(tx_lpm, int, 0); +module_param(max_rate, int, 0); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("StrongARM SA1100 IrDA driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM_DESC(power_level, "IrDA power level, 1 (low) to 3 (high)"); +MODULE_PARM_DESC(tx_lpm, "Enable transmitter low power (1.6us) mode"); +MODULE_PARM_DESC(max_rate, "Maximum baud rate (4000000, 115200, 57600, 38400, 19200, 9600)"); +MODULE_ALIAS("platform:sa11x0-ir"); diff --git a/drivers/staging/irda/drivers/sh_sir.c b/drivers/staging/irda/drivers/sh_sir.c new file mode 100644 index 000000000000..fede6864c737 --- /dev/null +++ b/drivers/staging/irda/drivers/sh_sir.c @@ -0,0 +1,810 @@ +/* + * SuperH IrDA Driver + * + * Copyright (C) 2009 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on bfin_sir.c + * Copyright 2006-2009 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "sh_sir" + +#define RX_PHASE (1 << 0) +#define TX_PHASE (1 << 1) +#define TX_COMP_PHASE (1 << 2) /* tx complete */ +#define NONE_PHASE (1 << 31) + +#define IRIF_RINTCLR 0x0016 /* DMA rx interrupt source clear */ +#define IRIF_TINTCLR 0x0018 /* DMA tx interrupt source clear */ +#define IRIF_SIR0 0x0020 /* IrDA-SIR10 control */ +#define IRIF_SIR1 0x0022 /* IrDA-SIR10 baudrate error correction */ +#define IRIF_SIR2 0x0024 /* IrDA-SIR10 baudrate count */ +#define IRIF_SIR3 0x0026 /* IrDA-SIR10 status */ +#define IRIF_SIR_FRM 0x0028 /* Hardware frame processing set */ +#define IRIF_SIR_EOF 0x002A /* EOF value */ +#define IRIF_SIR_FLG 0x002C /* Flag clear */ +#define IRIF_UART_STS2 0x002E /* UART status 2 */ +#define IRIF_UART0 0x0030 /* UART control */ +#define IRIF_UART1 0x0032 /* UART status */ +#define IRIF_UART2 0x0034 /* UART mode */ +#define IRIF_UART3 0x0036 /* UART transmit data */ +#define IRIF_UART4 0x0038 /* UART receive data */ +#define IRIF_UART5 0x003A /* UART interrupt mask */ +#define IRIF_UART6 0x003C /* UART baud rate error correction */ +#define IRIF_UART7 0x003E /* UART baud rate count set */ +#define IRIF_CRC0 0x0040 /* CRC engine control */ +#define IRIF_CRC1 0x0042 /* CRC engine input data */ +#define IRIF_CRC2 0x0044 /* CRC engine calculation */ +#define IRIF_CRC3 0x0046 /* CRC engine output data 1 */ +#define IRIF_CRC4 0x0048 /* CRC engine output data 2 */ + +/* IRIF_SIR0 */ +#define IRTPW (1 << 1) /* transmit pulse width select */ +#define IRERRC (1 << 0) /* Clear receive pulse width error */ + +/* IRIF_SIR3 */ +#define IRERR (1 << 0) /* received pulse width Error */ + +/* IRIF_SIR_FRM */ +#define EOFD (1 << 9) /* EOF detection flag */ +#define FRER (1 << 8) /* Frame Error bit */ +#define FRP (1 << 0) /* Frame processing set */ + +/* IRIF_UART_STS2 */ +#define IRSME (1 << 6) /* Receive Sum Error flag */ +#define IROVE (1 << 5) /* Receive Overrun Error flag */ +#define IRFRE (1 << 4) /* Receive Framing Error flag */ +#define IRPRE (1 << 3) /* Receive Parity Error flag */ + +/* IRIF_UART0_*/ +#define TBEC (1 << 2) /* Transmit Data Clear */ +#define RIE (1 << 1) /* Receive Enable */ +#define TIE (1 << 0) /* Transmit Enable */ + +/* IRIF_UART1 */ +#define URSME (1 << 6) /* Receive Sum Error Flag */ +#define UROVE (1 << 5) /* Receive Overrun Error Flag */ +#define URFRE (1 << 4) /* Receive Framing Error Flag */ +#define URPRE (1 << 3) /* Receive Parity Error Flag */ +#define RBF (1 << 2) /* Receive Buffer Full Flag */ +#define TSBE (1 << 1) /* Transmit Shift Buffer Empty Flag */ +#define TBE (1 << 0) /* Transmit Buffer Empty flag */ +#define TBCOMP (TSBE | TBE) + +/* IRIF_UART5 */ +#define RSEIM (1 << 6) /* Receive Sum Error Flag IRQ Mask */ +#define RBFIM (1 << 2) /* Receive Buffer Full Flag IRQ Mask */ +#define TSBEIM (1 << 1) /* Transmit Shift Buffer Empty Flag IRQ Mask */ +#define TBEIM (1 << 0) /* Transmit Buffer Empty Flag IRQ Mask */ +#define RX_MASK (RSEIM | RBFIM) + +/* IRIF_CRC0 */ +#define CRC_RST (1 << 15) /* CRC Engine Reset */ +#define CRC_CT_MASK 0x0FFF + +/************************************************************************ + + + structure + + +************************************************************************/ +struct sh_sir_self { + void __iomem *membase; + unsigned int irq; + struct clk *clk; + + struct net_device *ndev; + + struct irlap_cb *irlap; + struct qos_info qos; + + iobuff_t tx_buff; + iobuff_t rx_buff; +}; + +/************************************************************************ + + + common function + + +************************************************************************/ +static void sh_sir_write(struct sh_sir_self *self, u32 offset, u16 data) +{ + iowrite16(data, self->membase + offset); +} + +static u16 sh_sir_read(struct sh_sir_self *self, u32 offset) +{ + return ioread16(self->membase + offset); +} + +static void sh_sir_update_bits(struct sh_sir_self *self, u32 offset, + u16 mask, u16 data) +{ + u16 old, new; + + old = sh_sir_read(self, offset); + new = (old & ~mask) | data; + if (old != new) + sh_sir_write(self, offset, new); +} + +/************************************************************************ + + + CRC function + + +************************************************************************/ +static void sh_sir_crc_reset(struct sh_sir_self *self) +{ + sh_sir_write(self, IRIF_CRC0, CRC_RST); +} + +static void sh_sir_crc_add(struct sh_sir_self *self, u8 data) +{ + sh_sir_write(self, IRIF_CRC1, (u16)data); +} + +static u16 sh_sir_crc_cnt(struct sh_sir_self *self) +{ + return CRC_CT_MASK & sh_sir_read(self, IRIF_CRC0); +} + +static u16 sh_sir_crc_out(struct sh_sir_self *self) +{ + return sh_sir_read(self, IRIF_CRC4); +} + +static int sh_sir_crc_init(struct sh_sir_self *self) +{ + struct device *dev = &self->ndev->dev; + int ret = -EIO; + u16 val; + + sh_sir_crc_reset(self); + + sh_sir_crc_add(self, 0xCC); + sh_sir_crc_add(self, 0xF5); + sh_sir_crc_add(self, 0xF1); + sh_sir_crc_add(self, 0xA7); + + val = sh_sir_crc_cnt(self); + if (4 != val) { + dev_err(dev, "CRC count error %x\n", val); + goto crc_init_out; + } + + val = sh_sir_crc_out(self); + if (0x51DF != val) { + dev_err(dev, "CRC result error%x\n", val); + goto crc_init_out; + } + + ret = 0; + +crc_init_out: + + sh_sir_crc_reset(self); + return ret; +} + +/************************************************************************ + + + baud rate functions + + +************************************************************************/ +#define SCLK_BASE 1843200 /* 1.8432MHz */ + +static u32 sh_sir_find_sclk(struct clk *irda_clk) +{ + struct cpufreq_frequency_table *freq_table = irda_clk->freq_table; + struct cpufreq_frequency_table *pos; + struct clk *pclk = clk_get(NULL, "peripheral_clk"); + u32 limit, min = 0xffffffff, tmp; + int index = 0; + + limit = clk_get_rate(pclk); + clk_put(pclk); + + /* IrDA can not set over peripheral_clk */ + cpufreq_for_each_valid_entry(pos, freq_table) { + u32 freq = pos->frequency; + + /* IrDA should not over peripheral_clk */ + if (freq > limit) + continue; + + tmp = freq % SCLK_BASE; + if (tmp < min) { + min = tmp; + index = pos - freq_table; + } + } + + return freq_table[index].frequency; +} + +#define ERR_ROUNDING(a) ((a + 5000) / 10000) +static int sh_sir_set_baudrate(struct sh_sir_self *self, u32 baudrate) +{ + struct clk *clk; + struct device *dev = &self->ndev->dev; + u32 rate; + u16 uabca, uabc; + u16 irbca, irbc; + u32 min, rerr, tmp; + int i; + + /* Baud Rate Error Correction x 10000 */ + u32 rate_err_array[] = { + 0, 625, 1250, 1875, + 2500, 3125, 3750, 4375, + 5000, 5625, 6250, 6875, + 7500, 8125, 8750, 9375, + }; + + /* + * FIXME + * + * it support 9600 only now + */ + switch (baudrate) { + case 9600: + break; + default: + dev_err(dev, "un-supported baudrate %d\n", baudrate); + return -EIO; + } + + clk = clk_get(NULL, "irda_clk"); + if (IS_ERR(clk)) { + dev_err(dev, "can not get irda_clk\n"); + return -EIO; + } + + clk_set_rate(clk, sh_sir_find_sclk(clk)); + rate = clk_get_rate(clk); + clk_put(clk); + + dev_dbg(dev, "selected sclk = %d\n", rate); + + /* + * CALCULATION + * + * 1843200 = system rate / (irbca + (irbc + 1)) + */ + + irbc = rate / SCLK_BASE; + + tmp = rate - (SCLK_BASE * irbc); + tmp *= 10000; + + rerr = tmp / SCLK_BASE; + + min = 0xffffffff; + irbca = 0; + for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) { + tmp = abs(rate_err_array[i] - rerr); + if (min > tmp) { + min = tmp; + irbca = i; + } + } + + tmp = rate / (irbc + ERR_ROUNDING(rate_err_array[irbca])); + if ((SCLK_BASE / 100) < abs(tmp - SCLK_BASE)) + dev_warn(dev, "IrDA freq error margin over %d\n", tmp); + + dev_dbg(dev, "target = %d, result = %d, infrared = %d.%d\n", + SCLK_BASE, tmp, irbc, rate_err_array[irbca]); + + irbca = (irbca & 0xF) << 4; + irbc = (irbc - 1) & 0xF; + + if (!irbc) { + dev_err(dev, "sh_sir can not set 0 in IRIF_SIR2\n"); + return -EIO; + } + + sh_sir_write(self, IRIF_SIR0, IRTPW | IRERRC); + sh_sir_write(self, IRIF_SIR1, irbca); + sh_sir_write(self, IRIF_SIR2, irbc); + + /* + * CALCULATION + * + * BaudRate[bps] = system rate / (uabca + (uabc + 1) x 16) + */ + + uabc = rate / baudrate; + uabc = (uabc / 16) - 1; + uabc = (uabc + 1) * 16; + + tmp = rate - (uabc * baudrate); + tmp *= 10000; + + rerr = tmp / baudrate; + + min = 0xffffffff; + uabca = 0; + for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) { + tmp = abs(rate_err_array[i] - rerr); + if (min > tmp) { + min = tmp; + uabca = i; + } + } + + tmp = rate / (uabc + ERR_ROUNDING(rate_err_array[uabca])); + if ((baudrate / 100) < abs(tmp - baudrate)) + dev_warn(dev, "UART freq error margin over %d\n", tmp); + + dev_dbg(dev, "target = %d, result = %d, uart = %d.%d\n", + baudrate, tmp, + uabc, rate_err_array[uabca]); + + uabca = (uabca & 0xF) << 4; + uabc = (uabc / 16) - 1; + + sh_sir_write(self, IRIF_UART6, uabca); + sh_sir_write(self, IRIF_UART7, uabc); + + return 0; +} + +/************************************************************************ + + + iobuf function + + +************************************************************************/ +static int __sh_sir_init_iobuf(iobuff_t *io, int size) +{ + io->head = kmalloc(size, GFP_KERNEL); + if (!io->head) + return -ENOMEM; + + io->truesize = size; + io->in_frame = FALSE; + io->state = OUTSIDE_FRAME; + io->data = io->head; + + return 0; +} + +static void sh_sir_remove_iobuf(struct sh_sir_self *self) +{ + kfree(self->rx_buff.head); + kfree(self->tx_buff.head); + + self->rx_buff.head = NULL; + self->tx_buff.head = NULL; +} + +static int sh_sir_init_iobuf(struct sh_sir_self *self, int rxsize, int txsize) +{ + int err = -ENOMEM; + + if (self->rx_buff.head || + self->tx_buff.head) { + dev_err(&self->ndev->dev, "iobuff has already existed."); + return err; + } + + err = __sh_sir_init_iobuf(&self->rx_buff, rxsize); + if (err) + goto iobuf_err; + + err = __sh_sir_init_iobuf(&self->tx_buff, txsize); + +iobuf_err: + if (err) + sh_sir_remove_iobuf(self); + + return err; +} + +/************************************************************************ + + + status function + + +************************************************************************/ +static void sh_sir_clear_all_err(struct sh_sir_self *self) +{ + /* Clear error flag for receive pulse width */ + sh_sir_update_bits(self, IRIF_SIR0, IRERRC, IRERRC); + + /* Clear frame / EOF error flag */ + sh_sir_write(self, IRIF_SIR_FLG, 0xffff); + + /* Clear all status error */ + sh_sir_write(self, IRIF_UART_STS2, 0); +} + +static void sh_sir_set_phase(struct sh_sir_self *self, int phase) +{ + u16 uart5 = 0; + u16 uart0 = 0; + + switch (phase) { + case TX_PHASE: + uart5 = TBEIM; + uart0 = TBEC | TIE; + break; + case TX_COMP_PHASE: + uart5 = TSBEIM; + uart0 = TIE; + break; + case RX_PHASE: + uart5 = RX_MASK; + uart0 = RIE; + break; + default: + break; + } + + sh_sir_write(self, IRIF_UART5, uart5); + sh_sir_write(self, IRIF_UART0, uart0); +} + +static int sh_sir_is_which_phase(struct sh_sir_self *self) +{ + u16 val = sh_sir_read(self, IRIF_UART5); + + if (val & TBEIM) + return TX_PHASE; + + if (val & TSBEIM) + return TX_COMP_PHASE; + + if (val & RX_MASK) + return RX_PHASE; + + return NONE_PHASE; +} + +static void sh_sir_tx(struct sh_sir_self *self, int phase) +{ + switch (phase) { + case TX_PHASE: + if (0 >= self->tx_buff.len) { + sh_sir_set_phase(self, TX_COMP_PHASE); + } else { + sh_sir_write(self, IRIF_UART3, self->tx_buff.data[0]); + self->tx_buff.len--; + self->tx_buff.data++; + } + break; + case TX_COMP_PHASE: + sh_sir_set_phase(self, RX_PHASE); + netif_wake_queue(self->ndev); + break; + default: + dev_err(&self->ndev->dev, "should not happen\n"); + break; + } +} + +static int sh_sir_read_data(struct sh_sir_self *self) +{ + u16 val = 0; + int timeout = 1024; + + while (timeout--) { + val = sh_sir_read(self, IRIF_UART1); + + /* data get */ + if (val & RBF) { + if (val & (URSME | UROVE | URFRE | URPRE)) + break; + + return (int)sh_sir_read(self, IRIF_UART4); + } + + udelay(1); + } + + dev_err(&self->ndev->dev, "UART1 %04x : STATUS %04x\n", + val, sh_sir_read(self, IRIF_UART_STS2)); + + /* read data register for clear error */ + sh_sir_read(self, IRIF_UART4); + + return -1; +} + +static void sh_sir_rx(struct sh_sir_self *self) +{ + int timeout = 1024; + int data; + + while (timeout--) { + data = sh_sir_read_data(self); + if (data < 0) + break; + + async_unwrap_char(self->ndev, &self->ndev->stats, + &self->rx_buff, (u8)data); + + if (EOFD & sh_sir_read(self, IRIF_SIR_FRM)) + continue; + + break; + } +} + +static irqreturn_t sh_sir_irq(int irq, void *dev_id) +{ + struct sh_sir_self *self = dev_id; + struct device *dev = &self->ndev->dev; + int phase = sh_sir_is_which_phase(self); + + switch (phase) { + case TX_COMP_PHASE: + case TX_PHASE: + sh_sir_tx(self, phase); + break; + case RX_PHASE: + if (sh_sir_read(self, IRIF_SIR3)) + dev_err(dev, "rcv pulse width error occurred\n"); + + sh_sir_rx(self); + sh_sir_clear_all_err(self); + break; + default: + dev_err(dev, "unknown interrupt\n"); + } + + return IRQ_HANDLED; +} + +/************************************************************************ + + + net_device_ops function + + +************************************************************************/ +static int sh_sir_hard_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct sh_sir_self *self = netdev_priv(ndev); + int speed = irda_get_next_speed(skb); + + if ((0 < speed) && + (9600 != speed)) { + dev_err(&ndev->dev, "support 9600 only (%d)\n", speed); + return -EIO; + } + + netif_stop_queue(ndev); + + self->tx_buff.data = self->tx_buff.head; + self->tx_buff.len = 0; + if (skb->len) + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + sh_sir_set_phase(self, TX_PHASE); + dev_kfree_skb(skb); + + return 0; +} + +static int sh_sir_ioctl(struct net_device *ndev, struct ifreq *ifreq, int cmd) +{ + /* + * FIXME + * + * This function is needed for irda framework. + * But nothing to do now + */ + return 0; +} + +static struct net_device_stats *sh_sir_stats(struct net_device *ndev) +{ + struct sh_sir_self *self = netdev_priv(ndev); + + return &self->ndev->stats; +} + +static int sh_sir_open(struct net_device *ndev) +{ + struct sh_sir_self *self = netdev_priv(ndev); + int err; + + clk_enable(self->clk); + err = sh_sir_crc_init(self); + if (err) + goto open_err; + + sh_sir_set_baudrate(self, 9600); + + self->irlap = irlap_open(ndev, &self->qos, DRIVER_NAME); + if (!self->irlap) { + err = -ENODEV; + goto open_err; + } + + /* + * Now enable the interrupt then start the queue + */ + sh_sir_update_bits(self, IRIF_SIR_FRM, FRP, FRP); + sh_sir_read(self, IRIF_UART1); /* flag clear */ + sh_sir_read(self, IRIF_UART4); /* flag clear */ + sh_sir_set_phase(self, RX_PHASE); + + netif_start_queue(ndev); + + dev_info(&self->ndev->dev, "opened\n"); + + return 0; + +open_err: + clk_disable(self->clk); + + return err; +} + +static int sh_sir_stop(struct net_device *ndev) +{ + struct sh_sir_self *self = netdev_priv(ndev); + + /* Stop IrLAP */ + if (self->irlap) { + irlap_close(self->irlap); + self->irlap = NULL; + } + + netif_stop_queue(ndev); + + dev_info(&ndev->dev, "stopped\n"); + + return 0; +} + +static const struct net_device_ops sh_sir_ndo = { + .ndo_open = sh_sir_open, + .ndo_stop = sh_sir_stop, + .ndo_start_xmit = sh_sir_hard_xmit, + .ndo_do_ioctl = sh_sir_ioctl, + .ndo_get_stats = sh_sir_stats, +}; + +/************************************************************************ + + + platform_driver function + + +************************************************************************/ +static int sh_sir_probe(struct platform_device *pdev) +{ + struct net_device *ndev; + struct sh_sir_self *self; + struct resource *res; + char clk_name[8]; + int irq; + int err = -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || irq < 0) { + dev_err(&pdev->dev, "Not enough platform resources.\n"); + goto exit; + } + + ndev = alloc_irdadev(sizeof(*self)); + if (!ndev) + goto exit; + + self = netdev_priv(ndev); + self->membase = ioremap_nocache(res->start, resource_size(res)); + if (!self->membase) { + err = -ENXIO; + dev_err(&pdev->dev, "Unable to ioremap.\n"); + goto err_mem_1; + } + + err = sh_sir_init_iobuf(self, IRDA_SKB_MAX_MTU, IRDA_SIR_MAX_FRAME); + if (err) + goto err_mem_2; + + snprintf(clk_name, sizeof(clk_name), "irda%d", pdev->id); + self->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(self->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + err = -ENODEV; + goto err_mem_3; + } + + irda_init_max_qos_capabilies(&self->qos); + + ndev->netdev_ops = &sh_sir_ndo; + ndev->irq = irq; + + self->ndev = ndev; + self->qos.baud_rate.bits &= IR_9600; /* FIXME */ + self->qos.min_turn_time.bits = 1; /* 10 ms or more */ + + irda_qos_bits_to_value(&self->qos); + + err = register_netdev(ndev); + if (err) + goto err_mem_4; + + platform_set_drvdata(pdev, ndev); + err = devm_request_irq(&pdev->dev, irq, sh_sir_irq, 0, "sh_sir", self); + if (err) { + dev_warn(&pdev->dev, "Unable to attach sh_sir interrupt\n"); + goto err_mem_4; + } + + dev_info(&pdev->dev, "SuperH IrDA probed\n"); + + goto exit; + +err_mem_4: + clk_put(self->clk); +err_mem_3: + sh_sir_remove_iobuf(self); +err_mem_2: + iounmap(self->membase); +err_mem_1: + free_netdev(ndev); +exit: + return err; +} + +static int sh_sir_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct sh_sir_self *self = netdev_priv(ndev); + + if (!self) + return 0; + + unregister_netdev(ndev); + clk_put(self->clk); + sh_sir_remove_iobuf(self); + iounmap(self->membase); + free_netdev(ndev); + + return 0; +} + +static struct platform_driver sh_sir_driver = { + .probe = sh_sir_probe, + .remove = sh_sir_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; + +module_platform_driver(sh_sir_driver); + +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_DESCRIPTION("SuperH IrDA driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/sir-dev.h b/drivers/staging/irda/drivers/sir-dev.h new file mode 100644 index 000000000000..f50b9c1c0639 --- /dev/null +++ b/drivers/staging/irda/drivers/sir-dev.h @@ -0,0 +1,191 @@ +/********************************************************************* + * + * sir.h: include file for irda-sir device abstraction layer + * + * Copyright (c) 2002 Martin Diehl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + ********************************************************************/ + +#ifndef IRDA_SIR_H +#define IRDA_SIR_H + +#include +#include + +#include +#include // iobuff_t + +struct sir_fsm { + struct semaphore sem; + struct delayed_work work; + unsigned state, substate; + int param; + int result; +}; + +#define SIRDEV_STATE_WAIT_TX_COMPLETE 0x0100 + +/* substates for wait_tx_complete */ +#define SIRDEV_STATE_WAIT_XMIT 0x0101 +#define SIRDEV_STATE_WAIT_UNTIL_SENT 0x0102 +#define SIRDEV_STATE_TX_DONE 0x0103 + +#define SIRDEV_STATE_DONGLE_OPEN 0x0300 + +/* 0x0301-0x03ff reserved for individual dongle substates */ + +#define SIRDEV_STATE_DONGLE_CLOSE 0x0400 + +/* 0x0401-0x04ff reserved for individual dongle substates */ + +#define SIRDEV_STATE_SET_DTR_RTS 0x0500 + +#define SIRDEV_STATE_SET_SPEED 0x0700 +#define SIRDEV_STATE_DONGLE_CHECK 0x0800 +#define SIRDEV_STATE_DONGLE_RESET 0x0900 + +/* 0x0901-0x09ff reserved for individual dongle substates */ + +#define SIRDEV_STATE_DONGLE_SPEED 0x0a00 +/* 0x0a01-0x0aff reserved for individual dongle substates */ + +#define SIRDEV_STATE_PORT_SPEED 0x0b00 +#define SIRDEV_STATE_DONE 0x0c00 +#define SIRDEV_STATE_ERROR 0x0d00 +#define SIRDEV_STATE_COMPLETE 0x0e00 + +#define SIRDEV_STATE_DEAD 0xffff + + +struct sir_dev; + +struct dongle_driver { + + struct module *owner; + + const char *driver_name; + + IRDA_DONGLE type; + + int (*open)(struct sir_dev *dev); + int (*close)(struct sir_dev *dev); + int (*reset)(struct sir_dev *dev); + int (*set_speed)(struct sir_dev *dev, unsigned speed); + + struct list_head dongle_list; +}; + +struct sir_driver { + + struct module *owner; + + const char *driver_name; + + int qos_mtt_bits; + + int (*chars_in_buffer)(struct sir_dev *dev); + void (*wait_until_sent)(struct sir_dev *dev); + int (*set_speed)(struct sir_dev *dev, unsigned speed); + int (*set_dtr_rts)(struct sir_dev *dev, int dtr, int rts); + + int (*do_write)(struct sir_dev *dev, const unsigned char *ptr, size_t len); + + int (*start_dev)(struct sir_dev *dev); + int (*stop_dev)(struct sir_dev *dev); +}; + + +/* exported */ + +int irda_register_dongle(struct dongle_driver *new); +int irda_unregister_dongle(struct dongle_driver *drv); + +struct sir_dev *sirdev_get_instance(const struct sir_driver *drv, + const char *name); +int sirdev_put_instance(struct sir_dev *self); + +int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type); +void sirdev_write_complete(struct sir_dev *dev); +int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count); + +/* low level helpers for SIR device/dongle setup */ +int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len); +int sirdev_raw_read(struct sir_dev *dev, char *buf, int len); +int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts); + +/* not exported */ + +int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type); +int sirdev_put_dongle(struct sir_dev *self); + +void sirdev_enable_rx(struct sir_dev *dev); +int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param); + +/* inline helpers */ + +static inline int sirdev_schedule_speed(struct sir_dev *dev, unsigned speed) +{ + return sirdev_schedule_request(dev, SIRDEV_STATE_SET_SPEED, speed); +} + +static inline int sirdev_schedule_dongle_open(struct sir_dev *dev, int dongle_id) +{ + return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_OPEN, dongle_id); +} + +static inline int sirdev_schedule_dongle_close(struct sir_dev *dev) +{ + return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_CLOSE, 0); +} + +static inline int sirdev_schedule_dtr_rts(struct sir_dev *dev, int dtr, int rts) +{ + int dtrrts; + + dtrrts = ((dtr) ? 0x02 : 0x00) | ((rts) ? 0x01 : 0x00); + return sirdev_schedule_request(dev, SIRDEV_STATE_SET_DTR_RTS, dtrrts); +} + +#if 0 +static inline int sirdev_schedule_mode(struct sir_dev *dev, int mode) +{ + return sirdev_schedule_request(dev, SIRDEV_STATE_SET_MODE, mode); +} +#endif + + +struct sir_dev { + struct net_device *netdev; + + struct irlap_cb *irlap; + + struct qos_info qos; + + char hwname[32]; + + struct sir_fsm fsm; + atomic_t enable_rx; + int raw_tx; + spinlock_t tx_lock; + + u32 new_speed; + u32 flags; + + unsigned speed; + + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + struct sk_buff *tx_skb; + + const struct dongle_driver * dongle_drv; + const struct sir_driver * drv; + void *priv; + +}; + +#endif /* IRDA_SIR_H */ diff --git a/drivers/staging/irda/drivers/sir_dev.c b/drivers/staging/irda/drivers/sir_dev.c new file mode 100644 index 000000000000..6af26a7d787c --- /dev/null +++ b/drivers/staging/irda/drivers/sir_dev.c @@ -0,0 +1,987 @@ +/********************************************************************* + * + * sir_dev.c: irda sir network device + * + * Copyright (c) 2002 Martin Diehl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sir-dev.h" + + +static struct workqueue_struct *irda_sir_wq; + +/* STATE MACHINE */ + +/* substate handler of the config-fsm to handle the cases where we want + * to wait for transmit completion before changing the port configuration + */ + +static int sirdev_tx_complete_fsm(struct sir_dev *dev) +{ + struct sir_fsm *fsm = &dev->fsm; + unsigned next_state, delay; + unsigned bytes_left; + + do { + next_state = fsm->substate; /* default: stay in current substate */ + delay = 0; + + switch(fsm->substate) { + + case SIRDEV_STATE_WAIT_XMIT: + if (dev->drv->chars_in_buffer) + bytes_left = dev->drv->chars_in_buffer(dev); + else + bytes_left = 0; + if (!bytes_left) { + next_state = SIRDEV_STATE_WAIT_UNTIL_SENT; + break; + } + + if (dev->speed > 115200) + delay = (bytes_left*8*10000) / (dev->speed/100); + else if (dev->speed > 0) + delay = (bytes_left*10*10000) / (dev->speed/100); + else + delay = 0; + /* expected delay (usec) until remaining bytes are sent */ + if (delay < 100) { + udelay(delay); + delay = 0; + break; + } + /* sleep some longer delay (msec) */ + delay = (delay+999) / 1000; + break; + + case SIRDEV_STATE_WAIT_UNTIL_SENT: + /* block until underlaying hardware buffer are empty */ + if (dev->drv->wait_until_sent) + dev->drv->wait_until_sent(dev); + next_state = SIRDEV_STATE_TX_DONE; + break; + + case SIRDEV_STATE_TX_DONE: + return 0; + + default: + net_err_ratelimited("%s - undefined state\n", __func__); + return -EINVAL; + } + fsm->substate = next_state; + } while (delay == 0); + return delay; +} + +/* + * Function sirdev_config_fsm + * + * State machine to handle the configuration of the device (and attached dongle, if any). + * This handler is scheduled for execution in kIrDAd context, so we can sleep. + * however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too + * long. Instead, for longer delays we start a timer to reschedule us later. + * On entry, fsm->sem is always locked and the netdev xmit queue stopped. + * Both must be unlocked/restarted on completion - but only on final exit. + */ + +static void sirdev_config_fsm(struct work_struct *work) +{ + struct sir_dev *dev = container_of(work, struct sir_dev, fsm.work.work); + struct sir_fsm *fsm = &dev->fsm; + int next_state; + int ret = -1; + unsigned delay; + + pr_debug("%s(), <%ld>\n", __func__, jiffies); + + do { + pr_debug("%s - state=0x%04x / substate=0x%04x\n", + __func__, fsm->state, fsm->substate); + + next_state = fsm->state; + delay = 0; + + switch(fsm->state) { + + case SIRDEV_STATE_DONGLE_OPEN: + if (dev->dongle_drv != NULL) { + ret = sirdev_put_dongle(dev); + if (ret) { + fsm->result = -EINVAL; + next_state = SIRDEV_STATE_ERROR; + break; + } + } + + /* Initialize dongle */ + ret = sirdev_get_dongle(dev, fsm->param); + if (ret) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + + /* Dongles are powered through the modem control lines which + * were just set during open. Before resetting, let's wait for + * the power to stabilize. This is what some dongle drivers did + * in open before, while others didn't - should be safe anyway. + */ + + delay = 50; + fsm->substate = SIRDEV_STATE_DONGLE_RESET; + next_state = SIRDEV_STATE_DONGLE_RESET; + + fsm->param = 9600; + + break; + + case SIRDEV_STATE_DONGLE_CLOSE: + /* shouldn't we just treat this as success=? */ + if (dev->dongle_drv == NULL) { + fsm->result = -EINVAL; + next_state = SIRDEV_STATE_ERROR; + break; + } + + ret = sirdev_put_dongle(dev); + if (ret) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + next_state = SIRDEV_STATE_DONE; + break; + + case SIRDEV_STATE_SET_DTR_RTS: + ret = sirdev_set_dtr_rts(dev, + (fsm->param&0x02) ? TRUE : FALSE, + (fsm->param&0x01) ? TRUE : FALSE); + next_state = SIRDEV_STATE_DONE; + break; + + case SIRDEV_STATE_SET_SPEED: + fsm->substate = SIRDEV_STATE_WAIT_XMIT; + next_state = SIRDEV_STATE_DONGLE_CHECK; + break; + + case SIRDEV_STATE_DONGLE_CHECK: + ret = sirdev_tx_complete_fsm(dev); + if (ret < 0) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + if ((delay=ret) != 0) + break; + + if (dev->dongle_drv) { + fsm->substate = SIRDEV_STATE_DONGLE_RESET; + next_state = SIRDEV_STATE_DONGLE_RESET; + } + else { + dev->speed = fsm->param; + next_state = SIRDEV_STATE_PORT_SPEED; + } + break; + + case SIRDEV_STATE_DONGLE_RESET: + if (dev->dongle_drv->reset) { + ret = dev->dongle_drv->reset(dev); + if (ret < 0) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + } + else + ret = 0; + if ((delay=ret) == 0) { + /* set serial port according to dongle default speed */ + if (dev->drv->set_speed) + dev->drv->set_speed(dev, dev->speed); + fsm->substate = SIRDEV_STATE_DONGLE_SPEED; + next_state = SIRDEV_STATE_DONGLE_SPEED; + } + break; + + case SIRDEV_STATE_DONGLE_SPEED: + if (dev->dongle_drv->set_speed) { + ret = dev->dongle_drv->set_speed(dev, fsm->param); + if (ret < 0) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + } + else + ret = 0; + if ((delay=ret) == 0) + next_state = SIRDEV_STATE_PORT_SPEED; + break; + + case SIRDEV_STATE_PORT_SPEED: + /* Finally we are ready to change the serial port speed */ + if (dev->drv->set_speed) + dev->drv->set_speed(dev, dev->speed); + dev->new_speed = 0; + next_state = SIRDEV_STATE_DONE; + break; + + case SIRDEV_STATE_DONE: + /* Signal network layer so it can send more frames */ + netif_wake_queue(dev->netdev); + next_state = SIRDEV_STATE_COMPLETE; + break; + + default: + net_err_ratelimited("%s - undefined state\n", __func__); + fsm->result = -EINVAL; + /* fall thru */ + + case SIRDEV_STATE_ERROR: + net_err_ratelimited("%s - error: %d\n", + __func__, fsm->result); + +#if 0 /* don't enable this before we have netdev->tx_timeout to recover */ + netif_stop_queue(dev->netdev); +#else + netif_wake_queue(dev->netdev); +#endif + /* fall thru */ + + case SIRDEV_STATE_COMPLETE: + /* config change finished, so we are not busy any longer */ + sirdev_enable_rx(dev); + up(&fsm->sem); + return; + } + fsm->state = next_state; + } while(!delay); + + queue_delayed_work(irda_sir_wq, &fsm->work, msecs_to_jiffies(delay)); +} + +/* schedule some device configuration task for execution by kIrDAd + * on behalf of the above state machine. + * can be called from process or interrupt/tasklet context. + */ + +int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param) +{ + struct sir_fsm *fsm = &dev->fsm; + + pr_debug("%s - state=0x%04x / param=%u\n", __func__, + initial_state, param); + + if (down_trylock(&fsm->sem)) { + if (in_interrupt() || in_atomic() || irqs_disabled()) { + pr_debug("%s(), state machine busy!\n", __func__); + return -EWOULDBLOCK; + } else + down(&fsm->sem); + } + + if (fsm->state == SIRDEV_STATE_DEAD) { + /* race with sirdev_close should never happen */ + net_err_ratelimited("%s(), instance staled!\n", __func__); + up(&fsm->sem); + return -ESTALE; /* or better EPIPE? */ + } + + netif_stop_queue(dev->netdev); + atomic_set(&dev->enable_rx, 0); + + fsm->state = initial_state; + fsm->param = param; + fsm->result = 0; + + INIT_DELAYED_WORK(&fsm->work, sirdev_config_fsm); + queue_delayed_work(irda_sir_wq, &fsm->work, 0); + return 0; +} + + +/***************************************************************************/ + +void sirdev_enable_rx(struct sir_dev *dev) +{ + if (unlikely(atomic_read(&dev->enable_rx))) + return; + + /* flush rx-buffer - should also help in case of problems with echo cancelation */ + dev->rx_buff.data = dev->rx_buff.head; + dev->rx_buff.len = 0; + dev->rx_buff.in_frame = FALSE; + dev->rx_buff.state = OUTSIDE_FRAME; + atomic_set(&dev->enable_rx, 1); +} + +static int sirdev_is_receiving(struct sir_dev *dev) +{ + if (!atomic_read(&dev->enable_rx)) + return 0; + + return dev->rx_buff.state != OUTSIDE_FRAME; +} + +int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type) +{ + int err; + + pr_debug("%s : requesting dongle %d.\n", __func__, type); + + err = sirdev_schedule_dongle_open(dev, type); + if (unlikely(err)) + return err; + down(&dev->fsm.sem); /* block until config change completed */ + err = dev->fsm.result; + up(&dev->fsm.sem); + return err; +} +EXPORT_SYMBOL(sirdev_set_dongle); + +/* used by dongle drivers for dongle programming */ + +int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len) +{ + unsigned long flags; + int ret; + + if (unlikely(len > dev->tx_buff.truesize)) + return -ENOSPC; + + spin_lock_irqsave(&dev->tx_lock, flags); /* serialize with other tx operations */ + while (dev->tx_buff.len > 0) { /* wait until tx idle */ + spin_unlock_irqrestore(&dev->tx_lock, flags); + msleep(10); + spin_lock_irqsave(&dev->tx_lock, flags); + } + + dev->tx_buff.data = dev->tx_buff.head; + memcpy(dev->tx_buff.data, buf, len); + dev->tx_buff.len = len; + + ret = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); + if (ret > 0) { + pr_debug("%s(), raw-tx started\n", __func__); + + dev->tx_buff.data += ret; + dev->tx_buff.len -= ret; + dev->raw_tx = 1; + ret = len; /* all data is going to be sent */ + } + spin_unlock_irqrestore(&dev->tx_lock, flags); + return ret; +} +EXPORT_SYMBOL(sirdev_raw_write); + +/* seems some dongle drivers may need this */ + +int sirdev_raw_read(struct sir_dev *dev, char *buf, int len) +{ + int count; + + if (atomic_read(&dev->enable_rx)) + return -EIO; /* fail if we expect irda-frames */ + + count = (len < dev->rx_buff.len) ? len : dev->rx_buff.len; + + if (count > 0) { + memcpy(buf, dev->rx_buff.data, count); + dev->rx_buff.data += count; + dev->rx_buff.len -= count; + } + + /* remaining stuff gets flushed when re-enabling normal rx */ + + return count; +} +EXPORT_SYMBOL(sirdev_raw_read); + +int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) +{ + int ret = -ENXIO; + if (dev->drv->set_dtr_rts) + ret = dev->drv->set_dtr_rts(dev, dtr, rts); + return ret; +} +EXPORT_SYMBOL(sirdev_set_dtr_rts); + +/**********************************************************************/ + +/* called from client driver - likely with bh-context - to indicate + * it made some progress with transmission. Hence we send the next + * chunk, if any, or complete the skb otherwise + */ + +void sirdev_write_complete(struct sir_dev *dev) +{ + unsigned long flags; + struct sk_buff *skb; + int actual = 0; + int err; + + spin_lock_irqsave(&dev->tx_lock, flags); + + pr_debug("%s() - dev->tx_buff.len = %d\n", + __func__, dev->tx_buff.len); + + if (likely(dev->tx_buff.len > 0)) { + /* Write data left in transmit buffer */ + actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); + + if (likely(actual>0)) { + dev->tx_buff.data += actual; + dev->tx_buff.len -= actual; + } + else if (unlikely(actual<0)) { + /* could be dropped later when we have tx_timeout to recover */ + net_err_ratelimited("%s: drv->do_write failed (%d)\n", + __func__, actual); + if ((skb=dev->tx_skb) != NULL) { + dev->tx_skb = NULL; + dev_kfree_skb_any(skb); + dev->netdev->stats.tx_errors++; + dev->netdev->stats.tx_dropped++; + } + dev->tx_buff.len = 0; + } + if (dev->tx_buff.len > 0) + goto done; /* more data to send later */ + } + + if (unlikely(dev->raw_tx != 0)) { + /* in raw mode we are just done now after the buffer was sent + * completely. Since this was requested by some dongle driver + * running under the control of the irda-thread we must take + * care here not to re-enable the queue. The queue will be + * restarted when the irda-thread has completed the request. + */ + + pr_debug("%s(), raw-tx done\n", __func__); + dev->raw_tx = 0; + goto done; /* no post-frame handling in raw mode */ + } + + /* we have finished now sending this skb. + * update statistics and free the skb. + * finally we check and trigger a pending speed change, if any. + * if not we switch to rx mode and wake the queue for further + * packets. + * note the scheduled speed request blocks until the lower + * client driver and the corresponding hardware has really + * finished sending all data (xmit fifo drained f.e.) + * before the speed change gets finally done and the queue + * re-activated. + */ + + pr_debug("%s(), finished with frame!\n", __func__); + + if ((skb=dev->tx_skb) != NULL) { + dev->tx_skb = NULL; + dev->netdev->stats.tx_packets++; + dev->netdev->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + } + + if (unlikely(dev->new_speed > 0)) { + pr_debug("%s(), Changing speed!\n", __func__); + err = sirdev_schedule_speed(dev, dev->new_speed); + if (unlikely(err)) { + /* should never happen + * forget the speed change and hope the stack recovers + */ + net_err_ratelimited("%s - schedule speed change failed: %d\n", + __func__, err); + netif_wake_queue(dev->netdev); + } + /* else: success + * speed change in progress now + * on completion dev->new_speed gets cleared, + * rx-reenabled and the queue restarted + */ + } + else { + sirdev_enable_rx(dev); + netif_wake_queue(dev->netdev); + } + +done: + spin_unlock_irqrestore(&dev->tx_lock, flags); +} +EXPORT_SYMBOL(sirdev_write_complete); + +/* called from client driver - likely with bh-context - to give us + * some more received bytes. We put them into the rx-buffer, + * normally unwrapping and building LAP-skb's (unless rx disabled) + */ + +int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count) +{ + if (!dev || !dev->netdev) { + net_warn_ratelimited("%s(), not ready yet!\n", __func__); + return -1; + } + + if (!dev->irlap) { + net_warn_ratelimited("%s - too early: %p / %zd!\n", + __func__, cp, count); + return -1; + } + + if (cp==NULL) { + /* error already at lower level receive + * just update stats and set media busy + */ + irda_device_set_media_busy(dev->netdev, TRUE); + dev->netdev->stats.rx_dropped++; + pr_debug("%s; rx-drop: %zd\n", __func__, count); + return 0; + } + + /* Read the characters into the buffer */ + if (likely(atomic_read(&dev->enable_rx))) { + while (count--) + /* Unwrap and destuff one byte */ + async_unwrap_char(dev->netdev, &dev->netdev->stats, + &dev->rx_buff, *cp++); + } else { + while (count--) { + /* rx not enabled: save the raw bytes and never + * trigger any netif_rx. The received bytes are flushed + * later when we re-enable rx but might be read meanwhile + * by the dongle driver. + */ + dev->rx_buff.data[dev->rx_buff.len++] = *cp++; + + /* What should we do when the buffer is full? */ + if (unlikely(dev->rx_buff.len == dev->rx_buff.truesize)) + dev->rx_buff.len = 0; + } + } + + return 0; +} +EXPORT_SYMBOL(sirdev_receive); + +/**********************************************************************/ + +/* callbacks from network layer */ + +static netdev_tx_t sirdev_hard_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct sir_dev *dev = netdev_priv(ndev); + unsigned long flags; + int actual = 0; + int err; + s32 speed; + + IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); + + netif_stop_queue(ndev); + + pr_debug("%s(), skb->len = %d\n", __func__, skb->len); + + speed = irda_get_next_speed(skb); + if ((speed != dev->speed) && (speed != -1)) { + if (!skb->len) { + err = sirdev_schedule_speed(dev, speed); + if (unlikely(err == -EWOULDBLOCK)) { + /* Failed to initiate the speed change, likely the fsm + * is still busy (pretty unlikely, but...) + * We refuse to accept the skb and return with the queue + * stopped so the network layer will retry after the + * fsm completes and wakes the queue. + */ + return NETDEV_TX_BUSY; + } + else if (unlikely(err)) { + /* other fatal error - forget the speed change and + * hope the stack will recover somehow + */ + netif_start_queue(ndev); + } + /* else: success + * speed change in progress now + * on completion the queue gets restarted + */ + + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } else + dev->new_speed = speed; + } + + /* Init tx buffer*/ + dev->tx_buff.data = dev->tx_buff.head; + + /* Check problems */ + if(spin_is_locked(&dev->tx_lock)) { + pr_debug("%s(), write not completed\n", __func__); + } + + /* serialize with write completion */ + spin_lock_irqsave(&dev->tx_lock, flags); + + /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ + dev->tx_buff.len = async_wrap_skb(skb, dev->tx_buff.data, dev->tx_buff.truesize); + + /* transmission will start now - disable receive. + * if we are just in the middle of an incoming frame, + * treat it as collision. probably it's a good idea to + * reset the rx_buf OUTSIDE_FRAME in this case too? + */ + atomic_set(&dev->enable_rx, 0); + if (unlikely(sirdev_is_receiving(dev))) + dev->netdev->stats.collisions++; + + actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); + + if (likely(actual > 0)) { + dev->tx_skb = skb; + dev->tx_buff.data += actual; + dev->tx_buff.len -= actual; + } + else if (unlikely(actual < 0)) { + /* could be dropped later when we have tx_timeout to recover */ + net_err_ratelimited("%s: drv->do_write failed (%d)\n", + __func__, actual); + dev_kfree_skb_any(skb); + dev->netdev->stats.tx_errors++; + dev->netdev->stats.tx_dropped++; + netif_wake_queue(ndev); + } + spin_unlock_irqrestore(&dev->tx_lock, flags); + + return NETDEV_TX_OK; +} + +/* called from network layer with rtnl hold */ + +static int sirdev_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct sir_dev *dev = netdev_priv(ndev); + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + + pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, ndev->name, cmd); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + ret = sirdev_schedule_speed(dev, irq->ifr_baudrate); + /* cannot sleep here for completion + * we are called from network layer with rtnl hold + */ + break; + + case SIOCSDONGLE: /* Set dongle */ + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + ret = sirdev_schedule_dongle_open(dev, irq->ifr_dongle); + /* cannot sleep here for completion + * we are called from network layer with rtnl hold + */ + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + irda_device_set_media_busy(dev->netdev, TRUE); + break; + + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = sirdev_is_receiving(dev); + break; + + case SIOCSDTRRTS: + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + ret = sirdev_schedule_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); + /* cannot sleep here for completion + * we are called from network layer with rtnl hold + */ + break; + + case SIOCSMODE: +#if 0 + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + ret = sirdev_schedule_mode(dev, irq->ifr_mode); + /* cannot sleep here for completion + * we are called from network layer with rtnl hold + */ + break; +#endif + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* ----------------------------------------------------------------------------- */ + +#define SIRBUF_ALLOCSIZE 4269 /* worst case size of a wrapped IrLAP frame */ + +static int sirdev_alloc_buffers(struct sir_dev *dev) +{ + dev->tx_buff.truesize = SIRBUF_ALLOCSIZE; + dev->rx_buff.truesize = IRDA_SKB_MAX_MTU; + + /* Bootstrap ZeroCopy Rx */ + dev->rx_buff.skb = __netdev_alloc_skb(dev->netdev, dev->rx_buff.truesize, + GFP_KERNEL); + if (dev->rx_buff.skb == NULL) + return -ENOMEM; + skb_reserve(dev->rx_buff.skb, 1); + dev->rx_buff.head = dev->rx_buff.skb->data; + + dev->tx_buff.head = kmalloc(dev->tx_buff.truesize, GFP_KERNEL); + if (dev->tx_buff.head == NULL) { + kfree_skb(dev->rx_buff.skb); + dev->rx_buff.skb = NULL; + dev->rx_buff.head = NULL; + return -ENOMEM; + } + + dev->tx_buff.data = dev->tx_buff.head; + dev->rx_buff.data = dev->rx_buff.head; + dev->tx_buff.len = 0; + dev->rx_buff.len = 0; + + dev->rx_buff.in_frame = FALSE; + dev->rx_buff.state = OUTSIDE_FRAME; + return 0; +}; + +static void sirdev_free_buffers(struct sir_dev *dev) +{ + kfree_skb(dev->rx_buff.skb); + kfree(dev->tx_buff.head); + dev->rx_buff.head = dev->tx_buff.head = NULL; + dev->rx_buff.skb = NULL; +} + +static int sirdev_open(struct net_device *ndev) +{ + struct sir_dev *dev = netdev_priv(ndev); + const struct sir_driver *drv = dev->drv; + + if (!drv) + return -ENODEV; + + /* increase the reference count of the driver module before doing serious stuff */ + if (!try_module_get(drv->owner)) + return -ESTALE; + + if (sirdev_alloc_buffers(dev)) + goto errout_dec; + + if (!dev->drv->start_dev || dev->drv->start_dev(dev)) + goto errout_free; + + sirdev_enable_rx(dev); + dev->raw_tx = 0; + + netif_start_queue(ndev); + dev->irlap = irlap_open(ndev, &dev->qos, dev->hwname); + if (!dev->irlap) + goto errout_stop; + + netif_wake_queue(ndev); + + pr_debug("%s - done, speed = %d\n", __func__, dev->speed); + + return 0; + +errout_stop: + atomic_set(&dev->enable_rx, 0); + if (dev->drv->stop_dev) + dev->drv->stop_dev(dev); +errout_free: + sirdev_free_buffers(dev); +errout_dec: + module_put(drv->owner); + return -EAGAIN; +} + +static int sirdev_close(struct net_device *ndev) +{ + struct sir_dev *dev = netdev_priv(ndev); + const struct sir_driver *drv; + +/* pr_debug("%s\n", __func__); */ + + netif_stop_queue(ndev); + + down(&dev->fsm.sem); /* block on pending config completion */ + + atomic_set(&dev->enable_rx, 0); + + if (unlikely(!dev->irlap)) + goto out; + irlap_close(dev->irlap); + dev->irlap = NULL; + + drv = dev->drv; + if (unlikely(!drv || !dev->priv)) + goto out; + + if (drv->stop_dev) + drv->stop_dev(dev); + + sirdev_free_buffers(dev); + module_put(drv->owner); + +out: + dev->speed = 0; + up(&dev->fsm.sem); + return 0; +} + +static const struct net_device_ops sirdev_ops = { + .ndo_start_xmit = sirdev_hard_xmit, + .ndo_open = sirdev_open, + .ndo_stop = sirdev_close, + .ndo_do_ioctl = sirdev_ioctl, +}; +/* ----------------------------------------------------------------------------- */ + +struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name) +{ + struct net_device *ndev; + struct sir_dev *dev; + + pr_debug("%s - %s\n", __func__, name); + + /* instead of adding tests to protect against drv->do_write==NULL + * at several places we refuse to create a sir_dev instance for + * drivers which don't implement do_write. + */ + if (!drv || !drv->do_write) + return NULL; + + /* + * Allocate new instance of the device + */ + ndev = alloc_irdadev(sizeof(*dev)); + if (ndev == NULL) { + net_err_ratelimited("%s - Can't allocate memory for IrDA control block!\n", + __func__); + goto out; + } + dev = netdev_priv(ndev); + + irda_init_max_qos_capabilies(&dev->qos); + dev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + dev->qos.min_turn_time.bits = drv->qos_mtt_bits; + irda_qos_bits_to_value(&dev->qos); + + strncpy(dev->hwname, name, sizeof(dev->hwname)-1); + + atomic_set(&dev->enable_rx, 0); + dev->tx_skb = NULL; + + spin_lock_init(&dev->tx_lock); + sema_init(&dev->fsm.sem, 1); + + dev->drv = drv; + dev->netdev = ndev; + + /* Override the network functions we need to use */ + ndev->netdev_ops = &sirdev_ops; + + if (register_netdev(ndev)) { + net_err_ratelimited("%s(), register_netdev() failed!\n", + __func__); + goto out_freenetdev; + } + + return dev; + +out_freenetdev: + free_netdev(ndev); +out: + return NULL; +} +EXPORT_SYMBOL(sirdev_get_instance); + +int sirdev_put_instance(struct sir_dev *dev) +{ + int err = 0; + + pr_debug("%s\n", __func__); + + atomic_set(&dev->enable_rx, 0); + + netif_carrier_off(dev->netdev); + netif_device_detach(dev->netdev); + + if (dev->dongle_drv) + err = sirdev_schedule_dongle_close(dev); + if (err) + net_err_ratelimited("%s - error %d\n", __func__, err); + + sirdev_close(dev->netdev); + + down(&dev->fsm.sem); + dev->fsm.state = SIRDEV_STATE_DEAD; /* mark staled */ + dev->dongle_drv = NULL; + dev->priv = NULL; + up(&dev->fsm.sem); + + /* Remove netdevice */ + unregister_netdev(dev->netdev); + + free_netdev(dev->netdev); + + return 0; +} +EXPORT_SYMBOL(sirdev_put_instance); + +static int __init sir_wq_init(void) +{ + irda_sir_wq = create_singlethread_workqueue("irda_sir_wq"); + if (!irda_sir_wq) + return -ENOMEM; + return 0; +} + +static void __exit sir_wq_exit(void) +{ + destroy_workqueue(irda_sir_wq); +} + +module_init(sir_wq_init); +module_exit(sir_wq_exit); + +MODULE_AUTHOR("Martin Diehl "); +MODULE_DESCRIPTION("IrDA SIR core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/sir_dongle.c b/drivers/staging/irda/drivers/sir_dongle.c new file mode 100644 index 000000000000..7436f73ff1bb --- /dev/null +++ b/drivers/staging/irda/drivers/sir_dongle.c @@ -0,0 +1,133 @@ +/********************************************************************* + * + * sir_dongle.c: manager for serial dongle protocol drivers + * + * Copyright (c) 2002 Martin Diehl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + ********************************************************************/ + +#include +#include +#include +#include + +#include + +#include "sir-dev.h" + +/************************************************************************** + * + * dongle registration and attachment + * + */ + +static LIST_HEAD(dongle_list); /* list of registered dongle drivers */ +static DEFINE_MUTEX(dongle_list_lock); /* protects the list */ + +int irda_register_dongle(struct dongle_driver *new) +{ + struct list_head *entry; + struct dongle_driver *drv; + + pr_debug("%s : registering dongle \"%s\" (%d).\n", + __func__, new->driver_name, new->type); + + mutex_lock(&dongle_list_lock); + list_for_each(entry, &dongle_list) { + drv = list_entry(entry, struct dongle_driver, dongle_list); + if (new->type == drv->type) { + mutex_unlock(&dongle_list_lock); + return -EEXIST; + } + } + list_add(&new->dongle_list, &dongle_list); + mutex_unlock(&dongle_list_lock); + return 0; +} +EXPORT_SYMBOL(irda_register_dongle); + +int irda_unregister_dongle(struct dongle_driver *drv) +{ + mutex_lock(&dongle_list_lock); + list_del(&drv->dongle_list); + mutex_unlock(&dongle_list_lock); + return 0; +} +EXPORT_SYMBOL(irda_unregister_dongle); + +int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type) +{ + struct list_head *entry; + const struct dongle_driver *drv = NULL; + int err = -EINVAL; + + request_module("irda-dongle-%d", type); + + if (dev->dongle_drv != NULL) + return -EBUSY; + + /* serialize access to the list of registered dongles */ + mutex_lock(&dongle_list_lock); + + list_for_each(entry, &dongle_list) { + drv = list_entry(entry, struct dongle_driver, dongle_list); + if (drv->type == type) + break; + else + drv = NULL; + } + + if (!drv) { + err = -ENODEV; + goto out_unlock; /* no such dongle */ + } + + /* handling of SMP races with dongle module removal - three cases: + * 1) dongle driver was already unregistered - then we haven't found the + * requested dongle above and are already out here + * 2) the module is already marked deleted but the driver is still + * registered - then the try_module_get() below will fail + * 3) the try_module_get() below succeeds before the module is marked + * deleted - then sys_delete_module() fails and prevents the removal + * because the module is in use. + */ + + if (!try_module_get(drv->owner)) { + err = -ESTALE; + goto out_unlock; /* rmmod already pending */ + } + dev->dongle_drv = drv; + + if (!drv->open || (err=drv->open(dev))!=0) + goto out_reject; /* failed to open driver */ + + mutex_unlock(&dongle_list_lock); + return 0; + +out_reject: + dev->dongle_drv = NULL; + module_put(drv->owner); +out_unlock: + mutex_unlock(&dongle_list_lock); + return err; +} + +int sirdev_put_dongle(struct sir_dev *dev) +{ + const struct dongle_driver *drv = dev->dongle_drv; + + if (drv) { + if (drv->close) + drv->close(dev); /* close this dongle instance */ + + dev->dongle_drv = NULL; /* unlink the dongle driver */ + module_put(drv->owner);/* decrement driver's module refcount */ + } + + return 0; +} diff --git a/drivers/staging/irda/drivers/smsc-ircc2.c b/drivers/staging/irda/drivers/smsc-ircc2.c new file mode 100644 index 000000000000..19a55cba6beb --- /dev/null +++ b/drivers/staging/irda/drivers/smsc-ircc2.c @@ -0,0 +1,3026 @@ +/********************************************************************* + * + * Description: Driver for the SMC Infrared Communications Controller + * Author: Daniele Peri (peri@csai.unipa.it) + * Created at: + * Modified at: + * Modified by: + * + * Copyright (c) 2002 Daniele Peri + * All Rights Reserved. + * Copyright (c) 2002 Jean Tourrilhes + * Copyright (c) 2006 Linus Walleij + * + * + * Based on smc-ircc.c: + * + * Copyright (c) 2001 Stefani Seibold + * Copyright (c) 1999-2001 Dag Brattli + * Copyright (c) 1998-1999 Thomas Davis, + * + * and irport.c: + * + * Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#ifdef CONFIG_PCI +#include +#endif + +#include +#include +#include + +#include "smsc-ircc2.h" +#include "smsc-sio.h" + + +MODULE_AUTHOR("Daniele Peri "); +MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver"); +MODULE_LICENSE("GPL"); + +static bool smsc_nopnp = true; +module_param_named(nopnp, smsc_nopnp, bool, 0); +MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings, defaults to true"); + +#define DMA_INVAL 255 +static int ircc_dma = DMA_INVAL; +module_param_hw(ircc_dma, int, dma, 0); +MODULE_PARM_DESC(ircc_dma, "DMA channel"); + +#define IRQ_INVAL 255 +static int ircc_irq = IRQ_INVAL; +module_param_hw(ircc_irq, int, irq, 0); +MODULE_PARM_DESC(ircc_irq, "IRQ line"); + +static int ircc_fir; +module_param_hw(ircc_fir, int, ioport, 0); +MODULE_PARM_DESC(ircc_fir, "FIR Base Address"); + +static int ircc_sir; +module_param_hw(ircc_sir, int, ioport, 0); +MODULE_PARM_DESC(ircc_sir, "SIR Base Address"); + +static int ircc_cfg; +module_param_hw(ircc_cfg, int, ioport, 0); +MODULE_PARM_DESC(ircc_cfg, "Configuration register base address"); + +static int ircc_transceiver; +module_param(ircc_transceiver, int, 0); +MODULE_PARM_DESC(ircc_transceiver, "Transceiver type"); + +/* Types */ + +#ifdef CONFIG_PCI +struct smsc_ircc_subsystem_configuration { + unsigned short vendor; /* PCI vendor ID */ + unsigned short device; /* PCI vendor ID */ + unsigned short subvendor; /* PCI subsystem vendor ID */ + unsigned short subdevice; /* PCI subsystem device ID */ + unsigned short sir_io; /* I/O port for SIR */ + unsigned short fir_io; /* I/O port for FIR */ + unsigned char fir_irq; /* FIR IRQ */ + unsigned char fir_dma; /* FIR DMA */ + unsigned short cfg_base; /* I/O port for chip configuration */ + int (*preconfigure)(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); /* Preconfig function */ + const char *name; /* name shown as info */ +}; +#endif + +struct smsc_transceiver { + char *name; + void (*set_for_speed)(int fir_base, u32 speed); + int (*probe)(int fir_base); +}; + +struct smsc_chip { + char *name; + #if 0 + u8 type; + #endif + u16 flags; + u8 devid; + u8 rev; +}; + +struct smsc_chip_address { + unsigned int cfg_base; + unsigned int type; +}; + +/* Private data for each instance */ +struct smsc_ircc_cb { + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + struct qos_info qos; /* QoS capabilities for this device */ + + spinlock_t lock; /* For serializing operations */ + + __u32 new_speed; + __u32 flags; /* Interface flags */ + + int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */ + int tx_len; /* Number of frames in tx_buff */ + + int transceiver; + struct platform_device *pldev; +}; + +/* Constants */ + +#define SMSC_IRCC2_DRIVER_NAME "smsc-ircc2" + +#define SMSC_IRCC2_C_IRDA_FALLBACK_SPEED 9600 +#define SMSC_IRCC2_C_DEFAULT_TRANSCEIVER 1 +#define SMSC_IRCC2_C_NET_TIMEOUT 0 +#define SMSC_IRCC2_C_SIR_STOP 0 + +static const char *driver_name = SMSC_IRCC2_DRIVER_NAME; + +/* Prototypes */ + +static int smsc_ircc_open(unsigned int firbase, unsigned int sirbase, u8 dma, u8 irq); +static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base); +static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq); +static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self); +static void smsc_ircc_init_chip(struct smsc_ircc_cb *self); +static int __exit smsc_ircc_close(struct smsc_ircc_cb *self); +static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self); +static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self); +static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self); +static netdev_tx_t smsc_ircc_hard_xmit_sir(struct sk_buff *skb, + struct net_device *dev); +static netdev_tx_t smsc_ircc_hard_xmit_fir(struct sk_buff *skb, + struct net_device *dev); +static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int bofs); +static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self); +static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed); +static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, u32 speed); +static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id); +static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev); +static void smsc_ircc_sir_start(struct smsc_ircc_cb *self); +#if SMSC_IRCC2_C_SIR_STOP +static void smsc_ircc_sir_stop(struct smsc_ircc_cb *self); +#endif +static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self); +static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len); +static int smsc_ircc_net_open(struct net_device *dev); +static int smsc_ircc_net_close(struct net_device *dev); +static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#if SMSC_IRCC2_C_NET_TIMEOUT +static void smsc_ircc_timeout(struct net_device *dev); +#endif +static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self); +static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self); +static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed); +static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self); + +/* Probing */ +static int __init smsc_ircc_look_for_chips(void); +static const struct smsc_chip * __init smsc_ircc_probe(unsigned short cfg_base, u8 reg, const struct smsc_chip *chip, char *type); +static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned short cfg_base, char *type); +static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type); +static int __init smsc_superio_fdc(unsigned short cfg_base); +static int __init smsc_superio_lpc(unsigned short cfg_base); +#ifdef CONFIG_PCI +static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuration *conf); +static int __init preconfigure_through_82801(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); +static void __init preconfigure_ali_port(struct pci_dev *dev, + unsigned short port); +static int __init preconfigure_through_ali(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); +static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg, + unsigned short ircc_fir, + unsigned short ircc_sir, + unsigned char ircc_dma, + unsigned char ircc_irq); +#endif + +/* Transceivers specific functions */ + +static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed); +static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base); +static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed); +static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base); +static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed); +static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base); + +/* Power Management */ + +static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state); +static int smsc_ircc_resume(struct platform_device *dev); + +static struct platform_driver smsc_ircc_driver = { + .suspend = smsc_ircc_suspend, + .resume = smsc_ircc_resume, + .driver = { + .name = SMSC_IRCC2_DRIVER_NAME, + }, +}; + +/* Transceivers for SMSC-ircc */ + +static struct smsc_transceiver smsc_transceivers[] = +{ + { "Toshiba Satellite 1800 (GP data pin select)", smsc_ircc_set_transceiver_toshiba_sat1800, smsc_ircc_probe_transceiver_toshiba_sat1800 }, + { "Fast pin select", smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select, smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select }, + { "ATC IRMode", smsc_ircc_set_transceiver_smsc_ircc_atc, smsc_ircc_probe_transceiver_smsc_ircc_atc }, + { NULL, NULL } +}; +#define SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS (ARRAY_SIZE(smsc_transceivers) - 1) + +/* SMC SuperIO chipsets definitions */ + +#define KEY55_1 0 /* SuperIO Configuration mode with Key <0x55> */ +#define KEY55_2 1 /* SuperIO Configuration mode with Key <0x55,0x55> */ +#define NoIRDA 2 /* SuperIO Chip has no IRDA Port */ +#define SIR 0 /* SuperIO Chip has only slow IRDA */ +#define FIR 4 /* SuperIO Chip has fast IRDA */ +#define SERx4 8 /* SuperIO Chip supports 115,2 KBaud * 4=460,8 KBaud */ + +static struct smsc_chip __initdata fdc_chips_flat[] = +{ + /* Base address 0x3f0 or 0x370 */ + { "37C44", KEY55_1|NoIRDA, 0x00, 0x00 }, /* This chip cannot be detected */ + { "37C665GT", KEY55_2|NoIRDA, 0x65, 0x01 }, + { "37C665GT", KEY55_2|NoIRDA, 0x66, 0x01 }, + { "37C669", KEY55_2|SIR|SERx4, 0x03, 0x02 }, + { "37C669", KEY55_2|SIR|SERx4, 0x04, 0x02 }, /* ID? */ + { "37C78", KEY55_2|NoIRDA, 0x78, 0x00 }, + { "37N769", KEY55_1|FIR|SERx4, 0x28, 0x00 }, + { "37N869", KEY55_1|FIR|SERx4, 0x29, 0x00 }, + { NULL } +}; + +static struct smsc_chip __initdata fdc_chips_paged[] = +{ + /* Base address 0x3f0 or 0x370 */ + { "37B72X", KEY55_1|SIR|SERx4, 0x4c, 0x00 }, + { "37B77X", KEY55_1|SIR|SERx4, 0x43, 0x00 }, + { "37B78X", KEY55_1|SIR|SERx4, 0x44, 0x00 }, + { "37B80X", KEY55_1|SIR|SERx4, 0x42, 0x00 }, + { "37C67X", KEY55_1|FIR|SERx4, 0x40, 0x00 }, + { "37C93X", KEY55_2|SIR|SERx4, 0x02, 0x01 }, + { "37C93XAPM", KEY55_1|SIR|SERx4, 0x30, 0x01 }, + { "37C93XFR", KEY55_2|FIR|SERx4, 0x03, 0x01 }, + { "37M707", KEY55_1|SIR|SERx4, 0x42, 0x00 }, + { "37M81X", KEY55_1|SIR|SERx4, 0x4d, 0x00 }, + { "37N958FR", KEY55_1|FIR|SERx4, 0x09, 0x04 }, + { "37N971", KEY55_1|FIR|SERx4, 0x0a, 0x00 }, + { "37N972", KEY55_1|FIR|SERx4, 0x0b, 0x00 }, + { NULL } +}; + +static struct smsc_chip __initdata lpc_chips_flat[] = +{ + /* Base address 0x2E or 0x4E */ + { "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 }, + { "47N227", KEY55_1|FIR|SERx4, 0x7a, 0x00 }, + { "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 }, + { NULL } +}; + +static struct smsc_chip __initdata lpc_chips_paged[] = +{ + /* Base address 0x2E or 0x4E */ + { "47B27X", KEY55_1|SIR|SERx4, 0x51, 0x00 }, + { "47B37X", KEY55_1|SIR|SERx4, 0x52, 0x00 }, + { "47M10X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, + { "47M120", KEY55_1|NoIRDA|SERx4, 0x5c, 0x00 }, + { "47M13X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, + { "47M14X", KEY55_1|SIR|SERx4, 0x5f, 0x00 }, + { "47N252", KEY55_1|FIR|SERx4, 0x0e, 0x00 }, + { "47S42X", KEY55_1|SIR|SERx4, 0x57, 0x00 }, + { NULL } +}; + +#define SMSCSIO_TYPE_FDC 1 +#define SMSCSIO_TYPE_LPC 2 +#define SMSCSIO_TYPE_FLAT 4 +#define SMSCSIO_TYPE_PAGED 8 + +static struct smsc_chip_address __initdata possible_addresses[] = +{ + { 0x3f0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, + { 0x370, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, + { 0xe0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, + { 0x2e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, + { 0x4e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, + { 0, 0 } +}; + +/* Globals */ + +static struct smsc_ircc_cb *dev_self[] = { NULL, NULL }; +static unsigned short dev_count; + +static inline void register_bank(int iobase, int bank) +{ + outb(((inb(iobase + IRCC_MASTER) & 0xf0) | (bank & 0x07)), + iobase + IRCC_MASTER); +} + +/* PNP hotplug support */ +static const struct pnp_device_id smsc_ircc_pnp_table[] = { + { .id = "SMCf010", .driver_data = 0 }, + /* and presumably others */ + { } +}; +MODULE_DEVICE_TABLE(pnp, smsc_ircc_pnp_table); + +static int pnp_driver_registered; + +#ifdef CONFIG_PNP +static int smsc_ircc_pnp_probe(struct pnp_dev *dev, + const struct pnp_device_id *dev_id) +{ + unsigned int firbase, sirbase; + u8 dma, irq; + + if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && + pnp_dma_valid(dev, 0) && pnp_irq_valid(dev, 0))) + return -EINVAL; + + sirbase = pnp_port_start(dev, 0); + firbase = pnp_port_start(dev, 1); + dma = pnp_dma(dev, 0); + irq = pnp_irq(dev, 0); + + if (smsc_ircc_open(firbase, sirbase, dma, irq)) + return -ENODEV; + + return 0; +} + +static struct pnp_driver smsc_ircc_pnp_driver = { + .name = "smsc-ircc2", + .id_table = smsc_ircc_pnp_table, + .probe = smsc_ircc_pnp_probe, +}; +#else /* CONFIG_PNP */ +static struct pnp_driver smsc_ircc_pnp_driver; +#endif + +/******************************************************************************* + * + * + * SMSC-ircc stuff + * + * + *******************************************************************************/ + +static int __init smsc_ircc_legacy_probe(void) +{ + int ret = 0; + +#ifdef CONFIG_PCI + if (smsc_ircc_preconfigure_subsystems(ircc_cfg, ircc_fir, ircc_sir, ircc_dma, ircc_irq) < 0) { + /* Ignore errors from preconfiguration */ + net_err_ratelimited("%s, Preconfiguration failed !\n", + driver_name); + } +#endif + + if (ircc_fir > 0 && ircc_sir > 0) { + net_info_ratelimited(" Overriding FIR address 0x%04x\n", + ircc_fir); + net_info_ratelimited(" Overriding SIR address 0x%04x\n", + ircc_sir); + + if (smsc_ircc_open(ircc_fir, ircc_sir, ircc_dma, ircc_irq)) + ret = -ENODEV; + } else { + ret = -ENODEV; + + /* try user provided configuration register base address */ + if (ircc_cfg > 0) { + net_info_ratelimited(" Overriding configuration address 0x%04x\n", + ircc_cfg); + if (!smsc_superio_fdc(ircc_cfg)) + ret = 0; + if (!smsc_superio_lpc(ircc_cfg)) + ret = 0; + } + + if (smsc_ircc_look_for_chips() > 0) + ret = 0; + } + return ret; +} + +/* + * Function smsc_ircc_init () + * + * Initialize chip. Just try to find out how many chips we are dealing with + * and where they are + */ +static int __init smsc_ircc_init(void) +{ + int ret; + + pr_debug("%s\n", __func__); + + ret = platform_driver_register(&smsc_ircc_driver); + if (ret) { + net_err_ratelimited("%s, Can't register driver!\n", + driver_name); + return ret; + } + + dev_count = 0; + + if (smsc_nopnp || !pnp_platform_devices || + ircc_cfg || ircc_fir || ircc_sir || + ircc_dma != DMA_INVAL || ircc_irq != IRQ_INVAL) { + ret = smsc_ircc_legacy_probe(); + } else { + if (pnp_register_driver(&smsc_ircc_pnp_driver) == 0) + pnp_driver_registered = 1; + } + + if (ret) { + if (pnp_driver_registered) + pnp_unregister_driver(&smsc_ircc_pnp_driver); + platform_driver_unregister(&smsc_ircc_driver); + } + + return ret; +} + +static netdev_tx_t smsc_ircc_net_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct smsc_ircc_cb *self = netdev_priv(dev); + + if (self->io.speed > 115200) + return smsc_ircc_hard_xmit_fir(skb, dev); + else + return smsc_ircc_hard_xmit_sir(skb, dev); +} + +static const struct net_device_ops smsc_ircc_netdev_ops = { + .ndo_open = smsc_ircc_net_open, + .ndo_stop = smsc_ircc_net_close, + .ndo_do_ioctl = smsc_ircc_net_ioctl, + .ndo_start_xmit = smsc_ircc_net_xmit, +#if SMSC_IRCC2_C_NET_TIMEOUT + .ndo_tx_timeout = smsc_ircc_timeout, +#endif +}; + +/* + * Function smsc_ircc_open (firbase, sirbase, dma, irq) + * + * Try to open driver instance + * + */ +static int smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq) +{ + struct smsc_ircc_cb *self; + struct net_device *dev; + int err; + + pr_debug("%s\n", __func__); + + err = smsc_ircc_present(fir_base, sir_base); + if (err) + goto err_out; + + err = -ENOMEM; + if (dev_count >= ARRAY_SIZE(dev_self)) { + net_warn_ratelimited("%s(), too many devices!\n", __func__); + goto err_out1; + } + + /* + * Allocate new instance of the driver + */ + dev = alloc_irdadev(sizeof(struct smsc_ircc_cb)); + if (!dev) { + net_warn_ratelimited("%s() can't allocate net device\n", + __func__); + goto err_out1; + } + +#if SMSC_IRCC2_C_NET_TIMEOUT + dev->watchdog_timeo = HZ * 2; /* Allow enough time for speed change */ +#endif + dev->netdev_ops = &smsc_ircc_netdev_ops; + + self = netdev_priv(dev); + self->netdev = dev; + + /* Make ifconfig display some details */ + dev->base_addr = self->io.fir_base = fir_base; + dev->irq = self->io.irq = irq; + + /* Need to store self somewhere */ + dev_self[dev_count] = self; + spin_lock_init(&self->lock); + + self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE; + self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE; + + self->rx_buff.head = + dma_zalloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) + goto err_out2; + + self->tx_buff.head = + dma_zalloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) + goto err_out3; + + self->rx_buff.in_frame = FALSE; + self->rx_buff.state = OUTSIDE_FRAME; + self->tx_buff.data = self->tx_buff.head; + self->rx_buff.data = self->rx_buff.head; + + smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq); + smsc_ircc_setup_qos(self); + smsc_ircc_init_chip(self); + + if (ircc_transceiver > 0 && + ircc_transceiver < SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS) + self->transceiver = ircc_transceiver; + else + smsc_ircc_probe_transceiver(self); + + err = register_netdev(self->netdev); + if (err) { + net_err_ratelimited("%s, Network device registration failed!\n", + driver_name); + goto err_out4; + } + + self->pldev = platform_device_register_simple(SMSC_IRCC2_DRIVER_NAME, + dev_count, NULL, 0); + if (IS_ERR(self->pldev)) { + err = PTR_ERR(self->pldev); + goto err_out5; + } + platform_set_drvdata(self->pldev, self); + + net_info_ratelimited("IrDA: Registered device %s\n", dev->name); + dev_count++; + + return 0; + + err_out5: + unregister_netdev(self->netdev); + + err_out4: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + err_out3: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + err_out2: + free_netdev(self->netdev); + dev_self[dev_count] = NULL; + err_out1: + release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); + release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); + err_out: + return err; +} + +/* + * Function smsc_ircc_present(fir_base, sir_base) + * + * Check the smsc-ircc chip presence + * + */ +static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base) +{ + unsigned char low, high, chip, config, dma, irq, version; + + if (!request_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT, + driver_name)) { + net_warn_ratelimited("%s: can't get fir_base of 0x%03x\n", + __func__, fir_base); + goto out1; + } + + if (!request_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT, + driver_name)) { + net_warn_ratelimited("%s: can't get sir_base of 0x%03x\n", + __func__, sir_base); + goto out2; + } + + register_bank(fir_base, 3); + + high = inb(fir_base + IRCC_ID_HIGH); + low = inb(fir_base + IRCC_ID_LOW); + chip = inb(fir_base + IRCC_CHIP_ID); + version = inb(fir_base + IRCC_VERSION); + config = inb(fir_base + IRCC_INTERFACE); + dma = config & IRCC_INTERFACE_DMA_MASK; + irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; + + if (high != 0x10 || low != 0xb8 || (chip != 0xf1 && chip != 0xf2)) { + net_warn_ratelimited("%s(), addr 0x%04x - no device found!\n", + __func__, fir_base); + goto out3; + } + net_info_ratelimited("SMsC IrDA Controller found\n IrCC version %d.%d, firport 0x%03x, sirport 0x%03x dma=%d, irq=%d\n", + chip & 0x0f, version, + fir_base, sir_base, dma, irq); + + return 0; + + out3: + release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); + out2: + release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); + out1: + return -ENODEV; +} + +/* + * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq) + * + * Setup I/O + * + */ +static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, + unsigned int fir_base, unsigned int sir_base, + u8 dma, u8 irq) +{ + unsigned char config, chip_dma, chip_irq; + + register_bank(fir_base, 3); + config = inb(fir_base + IRCC_INTERFACE); + chip_dma = config & IRCC_INTERFACE_DMA_MASK; + chip_irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; + + self->io.fir_base = fir_base; + self->io.sir_base = sir_base; + self->io.fir_ext = SMSC_IRCC2_FIR_CHIP_IO_EXTENT; + self->io.sir_ext = SMSC_IRCC2_SIR_CHIP_IO_EXTENT; + self->io.fifo_size = SMSC_IRCC2_FIFO_SIZE; + self->io.speed = SMSC_IRCC2_C_IRDA_FALLBACK_SPEED; + + if (irq != IRQ_INVAL) { + if (irq != chip_irq) + net_info_ratelimited("%s, Overriding IRQ - chip says %d, using %d\n", + driver_name, chip_irq, irq); + self->io.irq = irq; + } else + self->io.irq = chip_irq; + + if (dma != DMA_INVAL) { + if (dma != chip_dma) + net_info_ratelimited("%s, Overriding DMA - chip says %d, using %d\n", + driver_name, chip_dma, dma); + self->io.dma = dma; + } else + self->io.dma = chip_dma; + +} + +/* + * Function smsc_ircc_setup_qos(self) + * + * Setup qos + * + */ +static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self) +{ + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); + + self->qos.min_turn_time.bits = SMSC_IRCC2_MIN_TURN_TIME; + self->qos.window_size.bits = SMSC_IRCC2_WINDOW_SIZE; + irda_qos_bits_to_value(&self->qos); +} + +/* + * Function smsc_ircc_init_chip(self) + * + * Init chip + * + */ +static void smsc_ircc_init_chip(struct smsc_ircc_cb *self) +{ + int iobase = self->io.fir_base; + + register_bank(iobase, 0); + outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER); + outb(0x00, iobase + IRCC_MASTER); + + register_bank(iobase, 1); + outb(((inb(iobase + IRCC_SCE_CFGA) & 0x87) | IRCC_CFGA_IRDA_SIR_A), + iobase + IRCC_SCE_CFGA); + +#ifdef smsc_669 /* Uses pin 88/89 for Rx/Tx */ + outb(((inb(iobase + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), + iobase + IRCC_SCE_CFGB); +#else + outb(((inb(iobase + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), + iobase + IRCC_SCE_CFGB); +#endif + (void) inb(iobase + IRCC_FIFO_THRESHOLD); + outb(SMSC_IRCC2_FIFO_THRESHOLD, iobase + IRCC_FIFO_THRESHOLD); + + register_bank(iobase, 4); + outb((inb(iobase + IRCC_CONTROL) & 0x30), iobase + IRCC_CONTROL); + + register_bank(iobase, 0); + outb(0, iobase + IRCC_LCR_A); + + smsc_ircc_set_sir_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED); + + /* Power on device */ + outb(0x00, iobase + IRCC_MASTER); +} + +/* + * Function smsc_ircc_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct smsc_ircc_cb *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + + self = netdev_priv(dev); + + IRDA_ASSERT(self != NULL, return -1;); + + pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else { + /* Make sure we are the only one touching + * self->io.speed and the hardware - Jean II */ + spin_lock_irqsave(&self->lock, flags); + smsc_ircc_change_speed(self, irq->ifr_baudrate); + spin_unlock_irqrestore(&self->lock, flags); + } + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = smsc_ircc_is_receiving(self); + break; + #if 0 + case SIOCSDTRRTS: + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + smsc_ircc_sir_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); + break; + #endif + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +#if SMSC_IRCC2_C_NET_TIMEOUT +/* + * Function smsc_ircc_timeout (struct net_device *dev) + * + * The networking timeout management. + * + */ + +static void smsc_ircc_timeout(struct net_device *dev) +{ + struct smsc_ircc_cb *self = netdev_priv(dev); + unsigned long flags; + + net_warn_ratelimited("%s: transmit timed out, changing speed to: %d\n", + dev->name, self->io.speed); + spin_lock_irqsave(&self->lock, flags); + smsc_ircc_sir_start(self); + smsc_ircc_change_speed(self, self->io.speed); + netif_trans_update(dev); /* prevent tx timeout */ + netif_wake_queue(dev); + spin_unlock_irqrestore(&self->lock, flags); +} +#endif + +/* + * Function smsc_ircc_hard_xmit_sir (struct sk_buff *skb, struct net_device *dev) + * + * Transmits the current frame until FIFO is full, then + * waits until the next transmit interrupt, and continues until the + * frame is transmitted. + */ +static netdev_tx_t smsc_ircc_hard_xmit_sir(struct sk_buff *skb, + struct net_device *dev) +{ + struct smsc_ircc_cb *self; + unsigned long flags; + s32 speed; + + pr_debug("%s\n", __func__); + + IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); + + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); + + netif_stop_queue(dev); + + /* Make sure test of self->io.speed & speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if (speed != self->io.speed && speed != -1) { + /* Check for empty frame */ + if (!skb->len) { + /* + * We send frames one by one in SIR mode (no + * pipelining), so at this point, if we were sending + * a previous frame, we just received the interrupt + * telling us it is finished (UART_IIR_THRI). + * Therefore, waiting for the transmitter to really + * finish draining the fifo won't take too long. + * And the interrupt handler is not expected to run. + * - Jean II */ + smsc_ircc_sir_wait_hw_transmitter_finish(self); + smsc_ircc_change_speed(self, speed); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + self->new_speed = speed; + } + + /* Init tx buffer */ + self->tx_buff.data = self->tx_buff.head; + + /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + dev->stats.tx_bytes += self->tx_buff.len; + + /* Turn on transmit finished interrupt. Will fire immediately! */ + outb(UART_IER_THRI, self->io.sir_base + UART_IER); + + spin_unlock_irqrestore(&self->lock, flags); + + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +/* + * Function smsc_ircc_set_fir_speed (self, baud) + * + * Change the speed of the device + * + */ +static void smsc_ircc_set_fir_speed(struct smsc_ircc_cb *self, u32 speed) +{ + int fir_base, ir_mode, ctrl, fast; + + IRDA_ASSERT(self != NULL, return;); + fir_base = self->io.fir_base; + + self->io.speed = speed; + + switch (speed) { + default: + case 576000: + ir_mode = IRCC_CFGA_IRDA_HDLC; + ctrl = IRCC_CRC; + fast = 0; + pr_debug("%s(), handling baud of 576000\n", __func__); + break; + case 1152000: + ir_mode = IRCC_CFGA_IRDA_HDLC; + ctrl = IRCC_1152 | IRCC_CRC; + fast = IRCC_LCR_A_FAST | IRCC_LCR_A_GP_DATA; + pr_debug("%s(), handling baud of 1152000\n", + __func__); + break; + case 4000000: + ir_mode = IRCC_CFGA_IRDA_4PPM; + ctrl = IRCC_CRC; + fast = IRCC_LCR_A_FAST; + pr_debug("%s(), handling baud of 4000000\n", + __func__); + break; + } + #if 0 + Now in tranceiver! + /* This causes an interrupt */ + register_bank(fir_base, 0); + outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast, fir_base + IRCC_LCR_A); + #endif + + register_bank(fir_base, 1); + outb(((inb(fir_base + IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | ir_mode), fir_base + IRCC_SCE_CFGA); + + register_bank(fir_base, 4); + outb((inb(fir_base + IRCC_CONTROL) & 0x30) | ctrl, fir_base + IRCC_CONTROL); +} + +/* + * Function smsc_ircc_fir_start(self) + * + * Change the speed of the device + * + */ +static void smsc_ircc_fir_start(struct smsc_ircc_cb *self) +{ + struct net_device *dev; + int fir_base; + + pr_debug("%s\n", __func__); + + IRDA_ASSERT(self != NULL, return;); + dev = self->netdev; + IRDA_ASSERT(dev != NULL, return;); + + fir_base = self->io.fir_base; + + /* Reset everything */ + + /* Clear FIFO */ + outb(inb(fir_base + IRCC_LCR_A) | IRCC_LCR_A_FIFO_RESET, fir_base + IRCC_LCR_A); + + /* Enable interrupt */ + /*outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base + IRCC_IER);*/ + + register_bank(fir_base, 1); + + /* Select the TX/RX interface */ +#ifdef SMSC_669 /* Uses pin 88/89 for Rx/Tx */ + outb(((inb(fir_base + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), + fir_base + IRCC_SCE_CFGB); +#else + outb(((inb(fir_base + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), + fir_base + IRCC_SCE_CFGB); +#endif + (void) inb(fir_base + IRCC_FIFO_THRESHOLD); + + /* Enable SCE interrupts */ + outb(0, fir_base + IRCC_MASTER); + register_bank(fir_base, 0); + outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, fir_base + IRCC_IER); + outb(IRCC_MASTER_INT_EN, fir_base + IRCC_MASTER); +} + +/* + * Function smsc_ircc_fir_stop(self, baud) + * + * Change the speed of the device + * + */ +static void smsc_ircc_fir_stop(struct smsc_ircc_cb *self) +{ + int fir_base; + + pr_debug("%s\n", __func__); + + IRDA_ASSERT(self != NULL, return;); + + fir_base = self->io.fir_base; + register_bank(fir_base, 0); + /*outb(IRCC_MASTER_RESET, fir_base + IRCC_MASTER);*/ + outb(inb(fir_base + IRCC_LCR_B) & IRCC_LCR_B_SIP_ENABLE, fir_base + IRCC_LCR_B); +} + + +/* + * Function smsc_ircc_change_speed(self, baud) + * + * Change the speed of the device + * + * This function *must* be called with spinlock held, because it may + * be called from the irq handler. - Jean II + */ +static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed) +{ + struct net_device *dev; + int last_speed_was_sir; + + pr_debug("%s() changing speed to: %d\n", __func__, speed); + + IRDA_ASSERT(self != NULL, return;); + dev = self->netdev; + + last_speed_was_sir = self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED; + + #if 0 + /* Temp Hack */ + speed= 1152000; + self->io.speed = speed; + last_speed_was_sir = 0; + smsc_ircc_fir_start(self); + #endif + + if (self->io.speed == 0) + smsc_ircc_sir_start(self); + + #if 0 + if (!last_speed_was_sir) speed = self->io.speed; + #endif + + if (self->io.speed != speed) + smsc_ircc_set_transceiver_for_speed(self, speed); + + self->io.speed = speed; + + if (speed <= SMSC_IRCC2_MAX_SIR_SPEED) { + if (!last_speed_was_sir) { + smsc_ircc_fir_stop(self); + smsc_ircc_sir_start(self); + } + smsc_ircc_set_sir_speed(self, speed); + } else { + if (last_speed_was_sir) { + #if SMSC_IRCC2_C_SIR_STOP + smsc_ircc_sir_stop(self); + #endif + smsc_ircc_fir_start(self); + } + smsc_ircc_set_fir_speed(self, speed); + + #if 0 + self->tx_buff.len = 10; + self->tx_buff.data = self->tx_buff.head; + + smsc_ircc_dma_xmit(self, 4000); + #endif + /* Be ready for incoming frames */ + smsc_ircc_dma_receive(self); + } + + netif_wake_queue(dev); +} + +/* + * Function smsc_ircc_set_sir_speed (self, speed) + * + * Set speed of IrDA port to specified baudrate + * + */ +static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, __u32 speed) +{ + int iobase; + int fcr; /* FIFO control reg */ + int lcr; /* Line control reg */ + int divisor; + + pr_debug("%s(), Setting speed to: %d\n", __func__, speed); + + IRDA_ASSERT(self != NULL, return;); + iobase = self->io.sir_base; + + /* Update accounting for new speed */ + self->io.speed = speed; + + /* Turn off interrupts */ + outb(0, iobase + UART_IER); + + divisor = SMSC_IRCC2_MAX_SIR_SPEED / speed; + + fcr = UART_FCR_ENABLE_FIFO; + + /* + * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and + * almost 1,7 ms at 19200 bps. At speeds above that we can just forget + * about this timeout since it will always be fast enough. + */ + fcr |= self->io.speed < 38400 ? + UART_FCR_TRIGGER_1 : UART_FCR_TRIGGER_14; + + /* IrDA ports use 8N1 */ + lcr = UART_LCR_WLEN8; + + outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */ + outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */ + outb(divisor >> 8, iobase + UART_DLM); + outb(lcr, iobase + UART_LCR); /* Set 8N1 */ + outb(fcr, iobase + UART_FCR); /* Enable FIFO's */ + + /* Turn on interrups */ + outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); + + pr_debug("%s() speed changed to: %d\n", __func__, speed); +} + + +/* + * Function smsc_ircc_hard_xmit_fir (skb, dev) + * + * Transmit the frame! + * + */ +static netdev_tx_t smsc_ircc_hard_xmit_fir(struct sk_buff *skb, + struct net_device *dev) +{ + struct smsc_ircc_cb *self; + unsigned long flags; + s32 speed; + int mtt; + + IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); + + netif_stop_queue(dev); + + /* Make sure test of self->io.speed & speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* Check if we need to change the speed after this frame */ + speed = irda_get_next_speed(skb); + if (speed != self->io.speed && speed != -1) { + /* Check for empty frame */ + if (!skb->len) { + /* Note : you should make sure that speed changes + * are not going to corrupt any outgoing frame. + * Look at nsc-ircc for the gory details - Jean II */ + smsc_ircc_change_speed(self, speed); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + self->new_speed = speed; + } + + skb_copy_from_linear_data(skb, self->tx_buff.head, skb->len); + + self->tx_buff.len = skb->len; + self->tx_buff.data = self->tx_buff.head; + + mtt = irda_get_mtt(skb); + if (mtt) { + int bofs; + + /* + * Compute how many BOFs (STA or PA's) we need to waste the + * min turn time given the speed of the link. + */ + bofs = mtt * (self->io.speed / 1000) / 8000; + if (bofs > 4095) + bofs = 4095; + + smsc_ircc_dma_xmit(self, bofs); + } else { + /* Transmit frame */ + smsc_ircc_dma_xmit(self, 0); + } + + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +/* + * Function smsc_ircc_dma_xmit (self, bofs) + * + * Transmit data using DMA + * + */ +static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int bofs) +{ + int iobase = self->io.fir_base; + u8 ctrl; + + pr_debug("%s\n", __func__); +#if 1 + /* Disable Rx */ + register_bank(iobase, 0); + outb(0x00, iobase + IRCC_LCR_B); +#endif + register_bank(iobase, 1); + outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + iobase + IRCC_SCE_CFGB); + + self->io.direction = IO_XMIT; + + /* Set BOF additional count for generating the min turn time */ + register_bank(iobase, 4); + outb(bofs & 0xff, iobase + IRCC_BOF_COUNT_LO); + ctrl = inb(iobase + IRCC_CONTROL) & 0xf0; + outb(ctrl | ((bofs >> 8) & 0x0f), iobase + IRCC_BOF_COUNT_HI); + + /* Set max Tx frame size */ + outb(self->tx_buff.len >> 8, iobase + IRCC_TX_SIZE_HI); + outb(self->tx_buff.len & 0xff, iobase + IRCC_TX_SIZE_LO); + + /*outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR);*/ + + /* Enable burst mode chip Tx DMA */ + register_bank(iobase, 1); + outb(inb(iobase + IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | + IRCC_CFGB_DMA_BURST, iobase + IRCC_SCE_CFGB); + + /* Setup DMA controller (must be done after enabling chip DMA) */ + irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, + DMA_TX_MODE); + + /* Enable interrupt */ + + register_bank(iobase, 0); + outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER); + outb(IRCC_MASTER_INT_EN, iobase + IRCC_MASTER); + + /* Enable transmit */ + outb(IRCC_LCR_B_SCE_TRANSMIT | IRCC_LCR_B_SIP_ENABLE, iobase + IRCC_LCR_B); +} + +/* + * Function smsc_ircc_dma_xmit_complete (self) + * + * The transfer of a frame in finished. This function will only be called + * by the interrupt handler + * + */ +static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self) +{ + int iobase = self->io.fir_base; + + pr_debug("%s\n", __func__); +#if 0 + /* Disable Tx */ + register_bank(iobase, 0); + outb(0x00, iobase + IRCC_LCR_B); +#endif + register_bank(iobase, 1); + outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + iobase + IRCC_SCE_CFGB); + + /* Check for underrun! */ + register_bank(iobase, 0); + if (inb(iobase + IRCC_LSR) & IRCC_LSR_UNDERRUN) { + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; + + /* Reset error condition */ + register_bank(iobase, 0); + outb(IRCC_MASTER_ERROR_RESET, iobase + IRCC_MASTER); + outb(0x00, iobase + IRCC_MASTER); + } else { + self->netdev->stats.tx_packets++; + self->netdev->stats.tx_bytes += self->tx_buff.len; + } + + /* Check if it's time to change the speed */ + if (self->new_speed) { + smsc_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + netif_wake_queue(self->netdev); +} + +/* + * Function smsc_ircc_dma_receive(self) + * + * Get ready for receiving a frame. The device will initiate a DMA + * if it starts to receive a frame. + * + */ +static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self) +{ + int iobase = self->io.fir_base; +#if 0 + /* Turn off chip DMA */ + register_bank(iobase, 1); + outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + iobase + IRCC_SCE_CFGB); +#endif + + /* Disable Tx */ + register_bank(iobase, 0); + outb(0x00, iobase + IRCC_LCR_B); + + /* Turn off chip DMA */ + register_bank(iobase, 1); + outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + iobase + IRCC_SCE_CFGB); + + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + + /* Set max Rx frame size */ + register_bank(iobase, 4); + outb((2050 >> 8) & 0x0f, iobase + IRCC_RX_SIZE_HI); + outb(2050 & 0xff, iobase + IRCC_RX_SIZE_LO); + + /* Setup DMA controller */ + irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, + DMA_RX_MODE); + + /* Enable burst mode chip Rx DMA */ + register_bank(iobase, 1); + outb(inb(iobase + IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | + IRCC_CFGB_DMA_BURST, iobase + IRCC_SCE_CFGB); + + /* Enable interrupt */ + register_bank(iobase, 0); + outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER); + outb(IRCC_MASTER_INT_EN, iobase + IRCC_MASTER); + + /* Enable receiver */ + register_bank(iobase, 0); + outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE, + iobase + IRCC_LCR_B); + + return 0; +} + +/* + * Function smsc_ircc_dma_receive_complete(self) + * + * Finished with receiving frames + * + */ +static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self) +{ + struct sk_buff *skb; + int len, msgcnt, lsr; + int iobase = self->io.fir_base; + + register_bank(iobase, 0); + + pr_debug("%s\n", __func__); +#if 0 + /* Disable Rx */ + register_bank(iobase, 0); + outb(0x00, iobase + IRCC_LCR_B); +#endif + register_bank(iobase, 0); + outb(inb(iobase + IRCC_LSAR) & ~IRCC_LSAR_ADDRESS_MASK, iobase + IRCC_LSAR); + lsr= inb(iobase + IRCC_LSR); + msgcnt = inb(iobase + IRCC_LCR_B) & 0x08; + + pr_debug("%s: dma count = %d\n", __func__, + get_dma_residue(self->io.dma)); + + len = self->rx_buff.truesize - get_dma_residue(self->io.dma); + + /* Look for errors */ + if (lsr & (IRCC_LSR_FRAME_ERROR | IRCC_LSR_CRC_ERROR | IRCC_LSR_SIZE_ERROR)) { + self->netdev->stats.rx_errors++; + if (lsr & IRCC_LSR_FRAME_ERROR) + self->netdev->stats.rx_frame_errors++; + if (lsr & IRCC_LSR_CRC_ERROR) + self->netdev->stats.rx_crc_errors++; + if (lsr & IRCC_LSR_SIZE_ERROR) + self->netdev->stats.rx_length_errors++; + if (lsr & (IRCC_LSR_UNDERRUN | IRCC_LSR_OVERRUN)) + self->netdev->stats.rx_length_errors++; + return; + } + + /* Remove CRC */ + len -= self->io.speed < 4000000 ? 2 : 4; + + if (len < 2 || len > 2050) { + net_warn_ratelimited("%s(), bogus len=%d\n", __func__, len); + return; + } + pr_debug("%s: msgcnt = %d, len=%d\n", __func__, msgcnt, len); + + skb = dev_alloc_skb(len + 1); + if (!skb) + return; + + /* Make sure IP header gets aligned */ + skb_reserve(skb, 1); + + skb_put_data(skb, self->rx_buff.data, len); + self->netdev->stats.rx_packets++; + self->netdev->stats.rx_bytes += len; + + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); +} + +/* + * Function smsc_ircc_sir_receive (self) + * + * Receive one frame from the infrared port + * + */ +static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self) +{ + int boguscount = 0; + int iobase; + + IRDA_ASSERT(self != NULL, return;); + + iobase = self->io.sir_base; + + /* + * Receive all characters in Rx FIFO, unwrap and unstuff them. + * async_unwrap_char will deliver all found frames + */ + do { + async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, + inb(iobase + UART_RX)); + + /* Make sure we don't stay here to long */ + if (boguscount++ > 32) { + pr_debug("%s(), breaking!\n", __func__); + break; + } + } while (inb(iobase + UART_LSR) & UART_LSR_DR); +} + + +/* + * Function smsc_ircc_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t smsc_ircc_interrupt(int dummy, void *dev_id) +{ + struct net_device *dev = dev_id; + struct smsc_ircc_cb *self = netdev_priv(dev); + int iobase, iir, lcra, lsr; + irqreturn_t ret = IRQ_NONE; + + /* Serialise the interrupt handler in various CPUs, stop Tx path */ + spin_lock(&self->lock); + + /* Check if we should use the SIR interrupt handler */ + if (self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED) { + ret = smsc_ircc_interrupt_sir(dev); + goto irq_ret_unlock; + } + + iobase = self->io.fir_base; + + register_bank(iobase, 0); + iir = inb(iobase + IRCC_IIR); + if (iir == 0) + goto irq_ret_unlock; + ret = IRQ_HANDLED; + + /* Disable interrupts */ + outb(0, iobase + IRCC_IER); + lcra = inb(iobase + IRCC_LCR_A); + lsr = inb(iobase + IRCC_LSR); + + pr_debug("%s(), iir = 0x%02x\n", __func__, iir); + + if (iir & IRCC_IIR_EOM) { + if (self->io.direction == IO_RECV) + smsc_ircc_dma_receive_complete(self); + else + smsc_ircc_dma_xmit_complete(self); + + smsc_ircc_dma_receive(self); + } + + if (iir & IRCC_IIR_ACTIVE_FRAME) { + /*printk(KERN_WARNING "%s(): Active Frame\n", __func__);*/ + } + + /* Enable interrupts again */ + + register_bank(iobase, 0); + outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER); + + irq_ret_unlock: + spin_unlock(&self->lock); + + return ret; +} + +/* + * Function irport_interrupt_sir (irq, dev_id) + * + * Interrupt handler for SIR modes + */ +static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev) +{ + struct smsc_ircc_cb *self = netdev_priv(dev); + int boguscount = 0; + int iobase; + int iir, lsr; + + /* Already locked coming here in smsc_ircc_interrupt() */ + /*spin_lock(&self->lock);*/ + + iobase = self->io.sir_base; + + iir = inb(iobase + UART_IIR) & UART_IIR_ID; + if (iir == 0) + return IRQ_NONE; + while (iir) { + /* Clear interrupt */ + lsr = inb(iobase + UART_LSR); + + pr_debug("%s(), iir=%02x, lsr=%02x, iobase=%#x\n", + __func__, iir, lsr, iobase); + + switch (iir) { + case UART_IIR_RLSI: + pr_debug("%s(), RLSI\n", __func__); + break; + case UART_IIR_RDI: + /* Receive interrupt */ + smsc_ircc_sir_receive(self); + break; + case UART_IIR_THRI: + if (lsr & UART_LSR_THRE) + /* Transmitter ready for data */ + smsc_ircc_sir_write_wakeup(self); + break; + default: + pr_debug("%s(), unhandled IIR=%#x\n", + __func__, iir); + break; + } + + /* Make sure we don't stay here to long */ + if (boguscount++ > 100) + break; + + iir = inb(iobase + UART_IIR) & UART_IIR_ID; + } + /*spin_unlock(&self->lock);*/ + return IRQ_HANDLED; +} + + +#if 0 /* unused */ +/* + * Function ircc_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int ircc_is_receiving(struct smsc_ircc_cb *self) +{ + int status = FALSE; + /* int iobase; */ + + pr_debug("%s\n", __func__); + + IRDA_ASSERT(self != NULL, return FALSE;); + + pr_debug("%s: dma count = %d\n", __func__, + get_dma_residue(self->io.dma)); + + status = (self->rx_buff.state != OUTSIDE_FRAME); + + return status; +} +#endif /* unused */ + +static int smsc_ircc_request_irq(struct smsc_ircc_cb *self) +{ + int error; + + error = request_irq(self->io.irq, smsc_ircc_interrupt, 0, + self->netdev->name, self->netdev); + if (error) + pr_debug("%s(), unable to allocate irq=%d, err=%d\n", + __func__, self->io.irq, error); + + return error; +} + +static void smsc_ircc_start_interrupts(struct smsc_ircc_cb *self) +{ + unsigned long flags; + + spin_lock_irqsave(&self->lock, flags); + + self->io.speed = 0; + smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED); + + spin_unlock_irqrestore(&self->lock, flags); +} + +static void smsc_ircc_stop_interrupts(struct smsc_ircc_cb *self) +{ + int iobase = self->io.fir_base; + unsigned long flags; + + spin_lock_irqsave(&self->lock, flags); + + register_bank(iobase, 0); + outb(0, iobase + IRCC_IER); + outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER); + outb(0x00, iobase + IRCC_MASTER); + + spin_unlock_irqrestore(&self->lock, flags); +} + + +/* + * Function smsc_ircc_net_open (dev) + * + * Start the device + * + */ +static int smsc_ircc_net_open(struct net_device *dev) +{ + struct smsc_ircc_cb *self; + char hwname[16]; + + pr_debug("%s\n", __func__); + + IRDA_ASSERT(dev != NULL, return -1;); + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return 0;); + + if (self->io.suspended) { + pr_debug("%s(), device is suspended\n", __func__); + return -EAGAIN; + } + + if (request_irq(self->io.irq, smsc_ircc_interrupt, 0, dev->name, + (void *) dev)) { + pr_debug("%s(), unable to allocate irq=%d\n", + __func__, self->io.irq); + return -EAGAIN; + } + + smsc_ircc_start_interrupts(self); + + /* Give self a hardware name */ + /* It would be cool to offer the chip revision here - Jean II */ + sprintf(hwname, "SMSC @ 0x%03x", self->io.fir_base); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + self->irlap = irlap_open(dev, &self->qos, hwname); + + /* + * Always allocate the DMA channel after the IRQ, + * and clean up on failure. + */ + if (request_dma(self->io.dma, dev->name)) { + smsc_ircc_net_close(dev); + + net_warn_ratelimited("%s(), unable to allocate DMA=%d\n", + __func__, self->io.dma); + return -EAGAIN; + } + + netif_start_queue(dev); + + return 0; +} + +/* + * Function smsc_ircc_net_close (dev) + * + * Stop the device + * + */ +static int smsc_ircc_net_close(struct net_device *dev) +{ + struct smsc_ircc_cb *self; + + pr_debug("%s\n", __func__); + + IRDA_ASSERT(dev != NULL, return -1;); + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return 0;); + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + smsc_ircc_stop_interrupts(self); + + /* if we are called from smsc_ircc_resume we don't have IRQ reserved */ + if (!self->io.suspended) + free_irq(self->io.irq, dev); + + disable_dma(self->io.dma); + free_dma(self->io.dma); + + return 0; +} + +static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state) +{ + struct smsc_ircc_cb *self = platform_get_drvdata(dev); + + if (!self->io.suspended) { + pr_debug("%s, Suspending\n", driver_name); + + rtnl_lock(); + if (netif_running(self->netdev)) { + netif_device_detach(self->netdev); + smsc_ircc_stop_interrupts(self); + free_irq(self->io.irq, self->netdev); + disable_dma(self->io.dma); + } + self->io.suspended = 1; + rtnl_unlock(); + } + + return 0; +} + +static int smsc_ircc_resume(struct platform_device *dev) +{ + struct smsc_ircc_cb *self = platform_get_drvdata(dev); + + if (self->io.suspended) { + pr_debug("%s, Waking up\n", driver_name); + + rtnl_lock(); + smsc_ircc_init_chip(self); + if (netif_running(self->netdev)) { + if (smsc_ircc_request_irq(self)) { + /* + * Don't fail resume process, just kill this + * network interface + */ + unregister_netdevice(self->netdev); + } else { + enable_dma(self->io.dma); + smsc_ircc_start_interrupts(self); + netif_device_attach(self->netdev); + } + } + self->io.suspended = 0; + rtnl_unlock(); + } + return 0; +} + +/* + * Function smsc_ircc_close (self) + * + * Close driver instance + * + */ +static int __exit smsc_ircc_close(struct smsc_ircc_cb *self) +{ + pr_debug("%s\n", __func__); + + IRDA_ASSERT(self != NULL, return -1;); + + platform_device_unregister(self->pldev); + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + smsc_ircc_stop_interrupts(self); + + /* Release the PORTS that this driver is using */ + pr_debug("%s(), releasing 0x%03x\n", __func__, + self->io.fir_base); + + release_region(self->io.fir_base, self->io.fir_ext); + + pr_debug("%s(), releasing 0x%03x\n", __func__, + self->io.sir_base); + + release_region(self->io.sir_base, self->io.sir_ext); + + if (self->tx_buff.head) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + free_netdev(self->netdev); + + return 0; +} + +static void __exit smsc_ircc_cleanup(void) +{ + int i; + + pr_debug("%s\n", __func__); + + for (i = 0; i < 2; i++) { + if (dev_self[i]) + smsc_ircc_close(dev_self[i]); + } + + if (pnp_driver_registered) + pnp_unregister_driver(&smsc_ircc_pnp_driver); + + platform_driver_unregister(&smsc_ircc_driver); +} + +/* + * Start SIR operations + * + * This function *must* be called with spinlock held, because it may + * be called from the irq handler (via smsc_ircc_change_speed()). - Jean II + */ +static void smsc_ircc_sir_start(struct smsc_ircc_cb *self) +{ + struct net_device *dev; + int fir_base, sir_base; + + pr_debug("%s\n", __func__); + + IRDA_ASSERT(self != NULL, return;); + dev = self->netdev; + IRDA_ASSERT(dev != NULL, return;); + + fir_base = self->io.fir_base; + sir_base = self->io.sir_base; + + /* Reset everything */ + outb(IRCC_MASTER_RESET, fir_base + IRCC_MASTER); + + #if SMSC_IRCC2_C_SIR_STOP + /*smsc_ircc_sir_stop(self);*/ + #endif + + register_bank(fir_base, 1); + outb(((inb(fir_base + IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | IRCC_CFGA_IRDA_SIR_A), fir_base + IRCC_SCE_CFGA); + + /* Initialize UART */ + outb(UART_LCR_WLEN8, sir_base + UART_LCR); /* Reset DLAB */ + outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), sir_base + UART_MCR); + + /* Turn on interrups */ + outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, sir_base + UART_IER); + + pr_debug("%s() - exit\n", __func__); + + outb(0x00, fir_base + IRCC_MASTER); +} + +#if SMSC_IRCC2_C_SIR_STOP +void smsc_ircc_sir_stop(struct smsc_ircc_cb *self) +{ + int iobase; + + pr_debug("%s\n", __func__); + iobase = self->io.sir_base; + + /* Reset UART */ + outb(0, iobase + UART_MCR); + + /* Turn off interrupts */ + outb(0, iobase + UART_IER); +} +#endif + +/* + * Function smsc_sir_write_wakeup (self) + * + * Called by the SIR interrupt handler when there's room for more data. + * If we have more packets to send, we send them here. + * + */ +static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self) +{ + int actual = 0; + int iobase; + int fcr; + + IRDA_ASSERT(self != NULL, return;); + + pr_debug("%s\n", __func__); + + iobase = self->io.sir_base; + + /* Finished with frame? */ + if (self->tx_buff.len > 0) { + /* Write data left in transmit buffer */ + actual = smsc_ircc_sir_write(iobase, self->io.fifo_size, + self->tx_buff.data, self->tx_buff.len); + self->tx_buff.data += actual; + self->tx_buff.len -= actual; + } else { + + /*if (self->tx_buff.len ==0) {*/ + + /* + * Now serial buffer is almost free & we can start + * transmission of another packet. But first we must check + * if we need to change the speed of the hardware + */ + if (self->new_speed) { + pr_debug("%s(), Changing speed to %d.\n", + __func__, self->new_speed); + smsc_ircc_sir_wait_hw_transmitter_finish(self); + smsc_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + } else { + /* Tell network layer that we want more frames */ + netif_wake_queue(self->netdev); + } + self->netdev->stats.tx_packets++; + + if (self->io.speed <= 115200) { + /* + * Reset Rx FIFO to make sure that all reflected transmit data + * is discarded. This is needed for half duplex operation + */ + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR; + fcr |= self->io.speed < 38400 ? + UART_FCR_TRIGGER_1 : UART_FCR_TRIGGER_14; + + outb(fcr, iobase + UART_FCR); + + /* Turn on receive interrupts */ + outb(UART_IER_RDI, iobase + UART_IER); + } + } +} + +/* + * Function smsc_ircc_sir_write (iobase, fifo_size, buf, len) + * + * Fill Tx FIFO with transmit data + * + */ +static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len) +{ + int actual = 0; + + /* Tx FIFO should be empty! */ + if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) { + net_warn_ratelimited("%s(), failed, fifo not empty!\n", + __func__); + return 0; + } + + /* Fill FIFO with current frame */ + while (fifo_size-- > 0 && actual < len) { + /* Transmit next byte */ + outb(buf[actual], iobase + UART_TX); + actual++; + } + return actual; +} + +/* + * Function smsc_ircc_is_receiving (self) + * + * Returns true is we are currently receiving data + * + */ +static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self) +{ + return self->rx_buff.state != OUTSIDE_FRAME; +} + + +/* + * Function smsc_ircc_probe_transceiver(self) + * + * Tries to find the used Transceiver + * + */ +static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self) +{ + unsigned int i; + + IRDA_ASSERT(self != NULL, return;); + + for (i = 0; smsc_transceivers[i].name != NULL; i++) + if (smsc_transceivers[i].probe(self->io.fir_base)) { + net_info_ratelimited(" %s transceiver found\n", + smsc_transceivers[i].name); + self->transceiver= i + 1; + return; + } + + net_info_ratelimited("No transceiver found. Defaulting to %s\n", + smsc_transceivers[SMSC_IRCC2_C_DEFAULT_TRANSCEIVER].name); + + self->transceiver = SMSC_IRCC2_C_DEFAULT_TRANSCEIVER; +} + + +/* + * Function smsc_ircc_set_transceiver_for_speed(self, speed) + * + * Set the transceiver according to the speed + * + */ +static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed) +{ + unsigned int trx; + + trx = self->transceiver; + if (trx > 0) + smsc_transceivers[trx - 1].set_for_speed(self->io.fir_base, speed); +} + +/* + * Function smsc_ircc_wait_hw_transmitter_finish () + * + * Wait for the real end of HW transmission + * + * The UART is a strict FIFO, and we get called only when we have finished + * pushing data to the FIFO, so the maximum amount of time we must wait + * is only for the FIFO to drain out. + * + * We use a simple calibrated loop. We may need to adjust the loop + * delay (udelay) to balance I/O traffic and latency. And we also need to + * adjust the maximum timeout. + * It would probably be better to wait for the proper interrupt, + * but it doesn't seem to be available. + * + * We can't use jiffies or kernel timers because : + * 1) We are called from the interrupt handler, which disable softirqs, + * so jiffies won't be increased + * 2) Jiffies granularity is usually very coarse (10ms), and we don't + * want to wait that long to detect stuck hardware. + * Jean II + */ + +static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self) +{ + int iobase = self->io.sir_base; + int count = SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US; + + /* Calibrated busy loop */ + while (count-- > 0 && !(inb(iobase + UART_LSR) & UART_LSR_TEMT)) + udelay(1); + + if (count < 0) + pr_debug("%s(): stuck transmitter\n", __func__); +} + + +/* PROBING + * + * REVISIT we can be told about the device by PNP, and should use that info + * instead of probing hardware and creating a platform_device ... + */ + +static int __init smsc_ircc_look_for_chips(void) +{ + struct smsc_chip_address *address; + char *type; + unsigned int cfg_base, found; + + found = 0; + address = possible_addresses; + + while (address->cfg_base) { + cfg_base = address->cfg_base; + + /*printk(KERN_WARNING "%s(): probing: 0x%02x for: 0x%02x\n", __func__, cfg_base, address->type);*/ + + if (address->type & SMSCSIO_TYPE_FDC) { + type = "FDC"; + if (address->type & SMSCSIO_TYPE_FLAT) + if (!smsc_superio_flat(fdc_chips_flat, cfg_base, type)) + found++; + + if (address->type & SMSCSIO_TYPE_PAGED) + if (!smsc_superio_paged(fdc_chips_paged, cfg_base, type)) + found++; + } + if (address->type & SMSCSIO_TYPE_LPC) { + type = "LPC"; + if (address->type & SMSCSIO_TYPE_FLAT) + if (!smsc_superio_flat(lpc_chips_flat, cfg_base, type)) + found++; + + if (address->type & SMSCSIO_TYPE_PAGED) + if (!smsc_superio_paged(lpc_chips_paged, cfg_base, type)) + found++; + } + address++; + } + return found; +} + +/* + * Function smsc_superio_flat (chip, base, type) + * + * Try to get configuration of a smc SuperIO chip with flat register model + * + */ +static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned short cfgbase, char *type) +{ + unsigned short firbase, sirbase; + u8 mode, dma, irq; + int ret = -ENODEV; + + pr_debug("%s\n", __func__); + + if (smsc_ircc_probe(cfgbase, SMSCSIOFLAT_DEVICEID_REG, chips, type) == NULL) + return ret; + + outb(SMSCSIOFLAT_UARTMODE0C_REG, cfgbase); + mode = inb(cfgbase + 1); + + /*printk(KERN_WARNING "%s(): mode: 0x%02x\n", __func__, mode);*/ + + if (!(mode & SMSCSIOFLAT_UART2MODE_VAL_IRDA)) + net_warn_ratelimited("%s(): IrDA not enabled\n", __func__); + + outb(SMSCSIOFLAT_UART2BASEADDR_REG, cfgbase); + sirbase = inb(cfgbase + 1) << 2; + + /* FIR iobase */ + outb(SMSCSIOFLAT_FIRBASEADDR_REG, cfgbase); + firbase = inb(cfgbase + 1) << 3; + + /* DMA */ + outb(SMSCSIOFLAT_FIRDMASELECT_REG, cfgbase); + dma = inb(cfgbase + 1) & SMSCSIOFLAT_FIRDMASELECT_MASK; + + /* IRQ */ + outb(SMSCSIOFLAT_UARTIRQSELECT_REG, cfgbase); + irq = inb(cfgbase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK; + + net_info_ratelimited("%s(): fir: 0x%02x, sir: 0x%02x, dma: %02d, irq: %d, mode: 0x%02x\n", + __func__, firbase, sirbase, dma, irq, mode); + + if (firbase && smsc_ircc_open(firbase, sirbase, dma, irq) == 0) + ret = 0; + + /* Exit configuration */ + outb(SMSCSIO_CFGEXITKEY, cfgbase); + + return ret; +} + +/* + * Function smsc_superio_paged (chip, base, type) + * + * Try to get configuration of a smc SuperIO chip with paged register model + * + */ +static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type) +{ + unsigned short fir_io, sir_io; + int ret = -ENODEV; + + pr_debug("%s\n", __func__); + + if (smsc_ircc_probe(cfg_base, 0x20, chips, type) == NULL) + return ret; + + /* Select logical device (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base + 1); + + /* SIR iobase */ + outb(0x60, cfg_base); + sir_io = inb(cfg_base + 1) << 8; + outb(0x61, cfg_base); + sir_io |= inb(cfg_base + 1); + + /* Read FIR base */ + outb(0x62, cfg_base); + fir_io = inb(cfg_base + 1) << 8; + outb(0x63, cfg_base); + fir_io |= inb(cfg_base + 1); + outb(0x2b, cfg_base); /* ??? */ + + if (fir_io && smsc_ircc_open(fir_io, sir_io, ircc_dma, ircc_irq) == 0) + ret = 0; + + /* Exit configuration */ + outb(SMSCSIO_CFGEXITKEY, cfg_base); + + return ret; +} + + +static int __init smsc_access(unsigned short cfg_base, unsigned char reg) +{ + pr_debug("%s\n", __func__); + + outb(reg, cfg_base); + return inb(cfg_base) != reg ? -1 : 0; +} + +static const struct smsc_chip * __init smsc_ircc_probe(unsigned short cfg_base, u8 reg, const struct smsc_chip *chip, char *type) +{ + u8 devid, xdevid, rev; + + pr_debug("%s\n", __func__); + + /* Leave configuration */ + + outb(SMSCSIO_CFGEXITKEY, cfg_base); + + if (inb(cfg_base) == SMSCSIO_CFGEXITKEY) /* not a smc superio chip */ + return NULL; + + outb(reg, cfg_base); + + xdevid = inb(cfg_base + 1); + + /* Enter configuration */ + + outb(SMSCSIO_CFGACCESSKEY, cfg_base); + + #if 0 + if (smsc_access(cfg_base,0x55)) /* send second key and check */ + return NULL; + #endif + + /* probe device ID */ + + if (smsc_access(cfg_base, reg)) + return NULL; + + devid = inb(cfg_base + 1); + + if (devid == 0 || devid == 0xff) /* typical values for unused port */ + return NULL; + + /* probe revision ID */ + + if (smsc_access(cfg_base, reg + 1)) + return NULL; + + rev = inb(cfg_base + 1); + + if (rev >= 128) /* i think this will make no sense */ + return NULL; + + if (devid == xdevid) /* protection against false positives */ + return NULL; + + /* Check for expected device ID; are there others? */ + + while (chip->devid != devid) { + + chip++; + + if (chip->name == NULL) + return NULL; + } + + net_info_ratelimited("found SMC SuperIO Chip (devid=0x%02x rev=%02X base=0x%04x): %s%s\n", + devid, rev, cfg_base, type, chip->name); + + if (chip->rev > rev) { + net_info_ratelimited("Revision higher than expected\n"); + return NULL; + } + + if (chip->flags & NoIRDA) + net_info_ratelimited("chipset does not support IRDA\n"); + + return chip; +} + +static int __init smsc_superio_fdc(unsigned short cfg_base) +{ + int ret = -1; + + if (!request_region(cfg_base, 2, driver_name)) { + net_warn_ratelimited("%s: can't get cfg_base of 0x%03x\n", + __func__, cfg_base); + } else { + if (!smsc_superio_flat(fdc_chips_flat, cfg_base, "FDC") || + !smsc_superio_paged(fdc_chips_paged, cfg_base, "FDC")) + ret = 0; + + release_region(cfg_base, 2); + } + + return ret; +} + +static int __init smsc_superio_lpc(unsigned short cfg_base) +{ + int ret = -1; + + if (!request_region(cfg_base, 2, driver_name)) { + net_warn_ratelimited("%s: can't get cfg_base of 0x%03x\n", + __func__, cfg_base); + } else { + if (!smsc_superio_flat(lpc_chips_flat, cfg_base, "LPC") || + !smsc_superio_paged(lpc_chips_paged, cfg_base, "LPC")) + ret = 0; + + release_region(cfg_base, 2); + } + return ret; +} + +/* + * Look for some specific subsystem setups that need + * pre-configuration not properly done by the BIOS (especially laptops) + * This code is based in part on smcinit.c, tosh1800-smcinit.c + * and tosh2450-smcinit.c. The table lists the device entries + * for ISA bridges with an LPC (Low Pin Count) controller which + * handles the communication with the SMSC device. After the LPC + * controller is initialized through PCI, the SMSC device is initialized + * through a dedicated port in the ISA port-mapped I/O area, this latter + * area is used to configure the SMSC device with default + * SIR and FIR I/O ports, DMA and IRQ. Different vendors have + * used different sets of parameters and different control port + * addresses making a subsystem device table necessary. + */ +#ifdef CONFIG_PCI +static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __initdata = { + /* + * Subsystems needing entries: + * 0x10b9:0x1533 0x103c:0x0850 HP nx9010 family + * 0x10b9:0x1533 0x0e11:0x005a Compaq nc4000 family + * 0x8086:0x24cc 0x0e11:0x002a HP nx9000 family + */ + { + /* Guessed entry */ + .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ + .device = 0x24cc, + .subvendor = 0x103c, + .subdevice = 0x08bc, + .sir_io = 0x02f8, + .fir_io = 0x0130, + .fir_irq = 0x05, + .fir_dma = 0x03, + .cfg_base = 0x004e, + .preconfigure = preconfigure_through_82801, + .name = "HP nx5000 family", + }, + { + .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ + .device = 0x24cc, + .subvendor = 0x103c, + .subdevice = 0x088c, + /* Quite certain these are the same for nc8000 as for nc6000 */ + .sir_io = 0x02f8, + .fir_io = 0x0130, + .fir_irq = 0x05, + .fir_dma = 0x03, + .cfg_base = 0x004e, + .preconfigure = preconfigure_through_82801, + .name = "HP nc8000 family", + }, + { + .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ + .device = 0x24cc, + .subvendor = 0x103c, + .subdevice = 0x0890, + .sir_io = 0x02f8, + .fir_io = 0x0130, + .fir_irq = 0x05, + .fir_dma = 0x03, + .cfg_base = 0x004e, + .preconfigure = preconfigure_through_82801, + .name = "HP nc6000 family", + }, + { + .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ + .device = 0x24cc, + .subvendor = 0x0e11, + .subdevice = 0x0860, + /* I assume these are the same for x1000 as for the others */ + .sir_io = 0x02e8, + .fir_io = 0x02f8, + .fir_irq = 0x07, + .fir_dma = 0x03, + .cfg_base = 0x002e, + .preconfigure = preconfigure_through_82801, + .name = "Compaq x1000 family", + }, + { + /* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */ + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x24c0, + .subvendor = 0x1179, + .subdevice = 0xffff, /* 0xffff is "any" */ + .sir_io = 0x03f8, + .fir_io = 0x0130, + .fir_irq = 0x07, + .fir_dma = 0x01, + .cfg_base = 0x002e, + .preconfigure = preconfigure_through_82801, + .name = "Toshiba laptop with Intel 82801DB/DBL LPC bridge", + }, + { + .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801CAM ISA bridge */ + .device = 0x248c, + .subvendor = 0x1179, + .subdevice = 0xffff, /* 0xffff is "any" */ + .sir_io = 0x03f8, + .fir_io = 0x0130, + .fir_irq = 0x03, + .fir_dma = 0x03, + .cfg_base = 0x002e, + .preconfigure = preconfigure_through_82801, + .name = "Toshiba laptop with Intel 82801CAM ISA bridge", + }, + { + /* 82801DBM (ICH4-M) LPC Interface Bridge */ + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x24cc, + .subvendor = 0x1179, + .subdevice = 0xffff, /* 0xffff is "any" */ + .sir_io = 0x03f8, + .fir_io = 0x0130, + .fir_irq = 0x03, + .fir_dma = 0x03, + .cfg_base = 0x002e, + .preconfigure = preconfigure_through_82801, + .name = "Toshiba laptop with Intel 8281DBM LPC bridge", + }, + { + /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */ + .vendor = PCI_VENDOR_ID_AL, + .device = 0x1533, + .subvendor = 0x1179, + .subdevice = 0xffff, /* 0xffff is "any" */ + .sir_io = 0x02e8, + .fir_io = 0x02f8, + .fir_irq = 0x07, + .fir_dma = 0x03, + .cfg_base = 0x002e, + .preconfigure = preconfigure_through_ali, + .name = "Toshiba laptop with ALi ISA bridge", + }, + { } // Terminator +}; + + +/* + * This sets up the basic SMSC parameters + * (FIR port, SIR port, FIR DMA, FIR IRQ) + * through the chip configuration port. + */ +static int __init preconfigure_smsc_chip(struct + smsc_ircc_subsystem_configuration + *conf) +{ + unsigned short iobase = conf->cfg_base; + unsigned char tmpbyte; + + outb(LPC47N227_CFGACCESSKEY, iobase); // enter configuration state + outb(SMSCSIOFLAT_DEVICEID_REG, iobase); // set for device ID + tmpbyte = inb(iobase +1); // Read device ID + pr_debug("Detected Chip id: 0x%02x, setting up registers...\n", + tmpbyte); + + /* Disable UART1 and set up SIR I/O port */ + outb(0x24, iobase); // select CR24 - UART1 base addr + outb(0x00, iobase + 1); // disable UART1 + outb(SMSCSIOFLAT_UART2BASEADDR_REG, iobase); // select CR25 - UART2 base addr + outb( (conf->sir_io >> 2), iobase + 1); // bits 2-9 of 0x3f8 + tmpbyte = inb(iobase + 1); + if (tmpbyte != (conf->sir_io >> 2) ) { + net_warn_ratelimited("ERROR: could not configure SIR ioport\n"); + net_warn_ratelimited("Try to supply ircc_cfg argument\n"); + return -ENXIO; + } + + /* Set up FIR IRQ channel for UART2 */ + outb(SMSCSIOFLAT_UARTIRQSELECT_REG, iobase); // select CR28 - UART1,2 IRQ select + tmpbyte = inb(iobase + 1); + tmpbyte &= SMSCSIOFLAT_UART1IRQSELECT_MASK; // Do not touch the UART1 portion + tmpbyte |= (conf->fir_irq & SMSCSIOFLAT_UART2IRQSELECT_MASK); + outb(tmpbyte, iobase + 1); + tmpbyte = inb(iobase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK; + if (tmpbyte != conf->fir_irq) { + net_warn_ratelimited("ERROR: could not configure FIR IRQ channel\n"); + return -ENXIO; + } + + /* Set up FIR I/O port */ + outb(SMSCSIOFLAT_FIRBASEADDR_REG, iobase); // CR2B - SCE (FIR) base addr + outb((conf->fir_io >> 3), iobase + 1); + tmpbyte = inb(iobase + 1); + if (tmpbyte != (conf->fir_io >> 3) ) { + net_warn_ratelimited("ERROR: could not configure FIR I/O port\n"); + return -ENXIO; + } + + /* Set up FIR DMA channel */ + outb(SMSCSIOFLAT_FIRDMASELECT_REG, iobase); // CR2C - SCE (FIR) DMA select + outb((conf->fir_dma & LPC47N227_FIRDMASELECT_MASK), iobase + 1); // DMA + tmpbyte = inb(iobase + 1) & LPC47N227_FIRDMASELECT_MASK; + if (tmpbyte != (conf->fir_dma & LPC47N227_FIRDMASELECT_MASK)) { + net_warn_ratelimited("ERROR: could not configure FIR DMA channel\n"); + return -ENXIO; + } + + outb(SMSCSIOFLAT_UARTMODE0C_REG, iobase); // CR0C - UART mode + tmpbyte = inb(iobase + 1); + tmpbyte &= ~SMSCSIOFLAT_UART2MODE_MASK | + SMSCSIOFLAT_UART2MODE_VAL_IRDA; + outb(tmpbyte, iobase + 1); // enable IrDA (HPSIR) mode, high speed + + outb(LPC47N227_APMBOOTDRIVE_REG, iobase); // CR07 - Auto Pwr Mgt/boot drive sel + tmpbyte = inb(iobase + 1); + outb(tmpbyte | LPC47N227_UART2AUTOPWRDOWN_MASK, iobase + 1); // enable UART2 autopower down + + /* This one was not part of tosh1800 */ + outb(0x0a, iobase); // CR0a - ecp fifo / ir mux + tmpbyte = inb(iobase + 1); + outb(tmpbyte | 0x40, iobase + 1); // send active device to ir port + + outb(LPC47N227_UART12POWER_REG, iobase); // CR02 - UART 1,2 power + tmpbyte = inb(iobase + 1); + outb(tmpbyte | LPC47N227_UART2POWERDOWN_MASK, iobase + 1); // UART2 power up mode, UART1 power down + + outb(LPC47N227_FDCPOWERVALIDCONF_REG, iobase); // CR00 - FDC Power/valid config cycle + tmpbyte = inb(iobase + 1); + outb(tmpbyte | LPC47N227_VALID_MASK, iobase + 1); // valid config cycle done + + outb(LPC47N227_CFGEXITKEY, iobase); // Exit configuration + + return 0; +} + +/* 82801CAM generic registers */ +#define VID 0x00 +#define DID 0x02 +#define PIRQ_A_D_ROUT 0x60 +#define SIRQ_CNTL 0x64 +#define PIRQ_E_H_ROUT 0x68 +#define PCI_DMA_C 0x90 +/* LPC-specific registers */ +#define COM_DEC 0xe0 +#define GEN1_DEC 0xe4 +#define LPC_EN 0xe6 +#define GEN2_DEC 0xec +/* + * Sets up the I/O range using the 82801CAM ISA bridge, 82801DBM LPC bridge + * or Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge. + * They all work the same way! + */ +static int __init preconfigure_through_82801(struct pci_dev *dev, + struct + smsc_ircc_subsystem_configuration + *conf) +{ + unsigned short tmpword; + unsigned char tmpbyte; + + net_info_ratelimited("Setting up Intel 82801 controller and SMSC device\n"); + /* + * Select the range for the COMA COM port (SIR) + * Register COM_DEC: + * Bit 7: reserved + * Bit 6-4, COMB decode range + * Bit 3: reserved + * Bit 2-0, COMA decode range + * + * Decode ranges: + * 000 = 0x3f8-0x3ff (COM1) + * 001 = 0x2f8-0x2ff (COM2) + * 010 = 0x220-0x227 + * 011 = 0x228-0x22f + * 100 = 0x238-0x23f + * 101 = 0x2e8-0x2ef (COM4) + * 110 = 0x338-0x33f + * 111 = 0x3e8-0x3ef (COM3) + */ + pci_read_config_byte(dev, COM_DEC, &tmpbyte); + tmpbyte &= 0xf8; /* mask COMA bits */ + switch(conf->sir_io) { + case 0x3f8: + tmpbyte |= 0x00; + break; + case 0x2f8: + tmpbyte |= 0x01; + break; + case 0x220: + tmpbyte |= 0x02; + break; + case 0x228: + tmpbyte |= 0x03; + break; + case 0x238: + tmpbyte |= 0x04; + break; + case 0x2e8: + tmpbyte |= 0x05; + break; + case 0x338: + tmpbyte |= 0x06; + break; + case 0x3e8: + tmpbyte |= 0x07; + break; + default: + tmpbyte |= 0x01; /* COM2 default */ + } + pr_debug("COM_DEC (write): 0x%02x\n", tmpbyte); + pci_write_config_byte(dev, COM_DEC, tmpbyte); + + /* Enable Low Pin Count interface */ + pci_read_config_word(dev, LPC_EN, &tmpword); + /* These seem to be set up at all times, + * just make sure it is properly set. + */ + switch(conf->cfg_base) { + case 0x04e: + tmpword |= 0x2000; + break; + case 0x02e: + tmpword |= 0x1000; + break; + case 0x062: + tmpword |= 0x0800; + break; + case 0x060: + tmpword |= 0x0400; + break; + default: + net_warn_ratelimited("Uncommon I/O base address: 0x%04x\n", + conf->cfg_base); + break; + } + tmpword &= 0xfffd; /* disable LPC COMB */ + tmpword |= 0x0001; /* set bit 0 : enable LPC COMA addr range (GEN2) */ + pr_debug("LPC_EN (write): 0x%04x\n", tmpword); + pci_write_config_word(dev, LPC_EN, tmpword); + + /* + * Configure LPC DMA channel + * PCI_DMA_C bits: + * Bit 15-14: DMA channel 7 select + * Bit 13-12: DMA channel 6 select + * Bit 11-10: DMA channel 5 select + * Bit 9-8: Reserved + * Bit 7-6: DMA channel 3 select + * Bit 5-4: DMA channel 2 select + * Bit 3-2: DMA channel 1 select + * Bit 1-0: DMA channel 0 select + * 00 = Reserved value + * 01 = PC/PCI DMA + * 10 = Reserved value + * 11 = LPC I/F DMA + */ + pci_read_config_word(dev, PCI_DMA_C, &tmpword); + switch(conf->fir_dma) { + case 0x07: + tmpword |= 0xc000; + break; + case 0x06: + tmpword |= 0x3000; + break; + case 0x05: + tmpword |= 0x0c00; + break; + case 0x03: + tmpword |= 0x00c0; + break; + case 0x02: + tmpword |= 0x0030; + break; + case 0x01: + tmpword |= 0x000c; + break; + case 0x00: + tmpword |= 0x0003; + break; + default: + break; /* do not change settings */ + } + pr_debug("PCI_DMA_C (write): 0x%04x\n", tmpword); + pci_write_config_word(dev, PCI_DMA_C, tmpword); + + /* + * GEN2_DEC bits: + * Bit 15-4: Generic I/O range + * Bit 3-1: reserved (read as 0) + * Bit 0: enable GEN2 range on LPC I/F + */ + tmpword = conf->fir_io & 0xfff8; + tmpword |= 0x0001; + pr_debug("GEN2_DEC (write): 0x%04x\n", tmpword); + pci_write_config_word(dev, GEN2_DEC, tmpword); + + /* Pre-configure chip */ + return preconfigure_smsc_chip(conf); +} + +/* + * Pre-configure a certain port on the ALi 1533 bridge. + * This is based on reverse-engineering since ALi does not + * provide any data sheet for the 1533 chip. + */ +static void __init preconfigure_ali_port(struct pci_dev *dev, + unsigned short port) +{ + unsigned char reg; + /* These bits obviously control the different ports */ + unsigned char mask; + unsigned char tmpbyte; + + switch(port) { + case 0x0130: + case 0x0178: + reg = 0xb0; + mask = 0x80; + break; + case 0x03f8: + reg = 0xb4; + mask = 0x80; + break; + case 0x02f8: + reg = 0xb4; + mask = 0x30; + break; + case 0x02e8: + reg = 0xb4; + mask = 0x08; + break; + default: + net_err_ratelimited("Failed to configure unsupported port on ALi 1533 bridge: 0x%04x\n", + port); + return; + } + + pci_read_config_byte(dev, reg, &tmpbyte); + /* Turn on the right bits */ + tmpbyte |= mask; + pci_write_config_byte(dev, reg, tmpbyte); + net_info_ratelimited("Activated ALi 1533 ISA bridge port 0x%04x\n", + port); +} + +static int __init preconfigure_through_ali(struct pci_dev *dev, + struct + smsc_ircc_subsystem_configuration + *conf) +{ + /* Configure the two ports on the ALi 1533 */ + preconfigure_ali_port(dev, conf->sir_io); + preconfigure_ali_port(dev, conf->fir_io); + + /* Pre-configure chip */ + return preconfigure_smsc_chip(conf); +} + +static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg, + unsigned short ircc_fir, + unsigned short ircc_sir, + unsigned char ircc_dma, + unsigned char ircc_irq) +{ + struct pci_dev *dev = NULL; + unsigned short ss_vendor = 0x0000; + unsigned short ss_device = 0x0000; + int ret = 0; + + for_each_pci_dev(dev) { + struct smsc_ircc_subsystem_configuration *conf; + + /* + * Cache the subsystem vendor/device: + * some manufacturers fail to set this for all components, + * so we save it in case there is just 0x0000 0x0000 on the + * device we want to check. + */ + if (dev->subsystem_vendor != 0x0000U) { + ss_vendor = dev->subsystem_vendor; + ss_device = dev->subsystem_device; + } + conf = subsystem_configurations; + for( ; conf->subvendor; conf++) { + if(conf->vendor == dev->vendor && + conf->device == dev->device && + conf->subvendor == ss_vendor && + /* Sometimes these are cached values */ + (conf->subdevice == ss_device || + conf->subdevice == 0xffff)) { + struct smsc_ircc_subsystem_configuration + tmpconf; + + memcpy(&tmpconf, conf, + sizeof(struct smsc_ircc_subsystem_configuration)); + + /* + * Override the default values with anything + * passed in as parameter + */ + if (ircc_cfg != 0) + tmpconf.cfg_base = ircc_cfg; + if (ircc_fir != 0) + tmpconf.fir_io = ircc_fir; + if (ircc_sir != 0) + tmpconf.sir_io = ircc_sir; + if (ircc_dma != DMA_INVAL) + tmpconf.fir_dma = ircc_dma; + if (ircc_irq != IRQ_INVAL) + tmpconf.fir_irq = ircc_irq; + + net_info_ratelimited("Detected unconfigured %s SMSC IrDA chip, pre-configuring device\n", + conf->name); + if (conf->preconfigure) + ret = conf->preconfigure(dev, &tmpconf); + else + ret = -ENODEV; + } + } + } + + return ret; +} +#endif // CONFIG_PCI + +/************************************************ + * + * Transceivers specific functions + * + ************************************************/ + + +/* + * Function smsc_ircc_set_transceiver_smsc_ircc_atc(fir_base, speed) + * + * Program transceiver through smsc-ircc ATC circuitry + * + */ + +static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed) +{ + unsigned long jiffies_now, jiffies_timeout; + u8 val; + + jiffies_now = jiffies; + jiffies_timeout = jiffies + SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES; + + /* ATC */ + register_bank(fir_base, 4); + outb((inb(fir_base + IRCC_ATC) & IRCC_ATC_MASK) | IRCC_ATC_nPROGREADY|IRCC_ATC_ENABLE, + fir_base + IRCC_ATC); + + while ((val = (inb(fir_base + IRCC_ATC) & IRCC_ATC_nPROGREADY)) && + !time_after(jiffies, jiffies_timeout)) + /* empty */; + + if (val) + net_warn_ratelimited("%s(): ATC: 0x%02x\n", + __func__, inb(fir_base + IRCC_ATC)); +} + +/* + * Function smsc_ircc_probe_transceiver_smsc_ircc_atc(fir_base) + * + * Probe transceiver smsc-ircc ATC circuitry + * + */ + +static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base) +{ + return 0; +} + +/* + * Function smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(self, speed) + * + * Set transceiver + * + */ + +static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed) +{ + u8 fast_mode; + + switch (speed) { + default: + case 576000 : + fast_mode = 0; + break; + case 1152000 : + case 4000000 : + fast_mode = IRCC_LCR_A_FAST; + break; + } + register_bank(fir_base, 0); + outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast_mode, fir_base + IRCC_LCR_A); +} + +/* + * Function smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(fir_base) + * + * Probe transceiver + * + */ + +static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base) +{ + return 0; +} + +/* + * Function smsc_ircc_set_transceiver_toshiba_sat1800(fir_base, speed) + * + * Set transceiver + * + */ + +static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed) +{ + u8 fast_mode; + + switch (speed) { + default: + case 576000 : + fast_mode = 0; + break; + case 1152000 : + case 4000000 : + fast_mode = /*IRCC_LCR_A_FAST |*/ IRCC_LCR_A_GP_DATA; + break; + + } + /* This causes an interrupt */ + register_bank(fir_base, 0); + outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast_mode, fir_base + IRCC_LCR_A); +} + +/* + * Function smsc_ircc_probe_transceiver_toshiba_sat1800(fir_base) + * + * Probe transceiver + * + */ + +static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base) +{ + return 0; +} + + +module_init(smsc_ircc_init); +module_exit(smsc_ircc_cleanup); diff --git a/drivers/staging/irda/drivers/smsc-ircc2.h b/drivers/staging/irda/drivers/smsc-ircc2.h new file mode 100644 index 000000000000..4829fa22cb29 --- /dev/null +++ b/drivers/staging/irda/drivers/smsc-ircc2.h @@ -0,0 +1,191 @@ +/********************************************************************* + * + * Description: Definitions for the SMC IrCC chipset + * Status: Experimental. + * Author: Daniele Peri (peri@csai.unipa.it) + * + * Copyright (c) 2002 Daniele Peri + * All Rights Reserved. + * + * Based on smc-ircc.h: + * + * Copyright (c) 1999-2000, Dag Brattli + * Copyright (c) 1998-1999, Thomas Davis (tadavis@jps.net> + * All Rights Reserved + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + ********************************************************************/ + +#ifndef SMSC_IRCC2_H +#define SMSC_IRCC2_H + +/* DMA modes needed */ +#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ +#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ + +/* Master Control Register */ +#define IRCC_MASTER 0x07 +#define IRCC_MASTER_POWERDOWN 0x80 +#define IRCC_MASTER_RESET 0x40 +#define IRCC_MASTER_INT_EN 0x20 +#define IRCC_MASTER_ERROR_RESET 0x10 + +/* Register block 0 */ + +/* Interrupt Identification */ +#define IRCC_IIR 0x01 +#define IRCC_IIR_ACTIVE_FRAME 0x80 +#define IRCC_IIR_EOM 0x40 +#define IRCC_IIR_RAW_MODE 0x20 +#define IRCC_IIR_FIFO 0x10 + +/* Interrupt Enable */ +#define IRCC_IER 0x02 +#define IRCC_IER_ACTIVE_FRAME 0x80 +#define IRCC_IER_EOM 0x40 +#define IRCC_IER_RAW_MODE 0x20 +#define IRCC_IER_FIFO 0x10 + +/* Line Status Register */ +#define IRCC_LSR 0x03 +#define IRCC_LSR_UNDERRUN 0x80 +#define IRCC_LSR_OVERRUN 0x40 +#define IRCC_LSR_FRAME_ERROR 0x20 +#define IRCC_LSR_SIZE_ERROR 0x10 +#define IRCC_LSR_CRC_ERROR 0x80 +#define IRCC_LSR_FRAME_ABORT 0x40 + +/* Line Status Address Register */ +#define IRCC_LSAR 0x03 +#define IRCC_LSAR_ADDRESS_MASK 0x07 + +/* Line Control Register A */ +#define IRCC_LCR_A 0x04 +#define IRCC_LCR_A_FIFO_RESET 0x80 +#define IRCC_LCR_A_FAST 0x40 +#define IRCC_LCR_A_GP_DATA 0x20 +#define IRCC_LCR_A_RAW_TX 0x10 +#define IRCC_LCR_A_RAW_RX 0x08 +#define IRCC_LCR_A_ABORT 0x04 +#define IRCC_LCR_A_DATA_DONE 0x02 + +/* Line Control Register B */ +#define IRCC_LCR_B 0x05 +#define IRCC_LCR_B_SCE_DISABLED 0x00 +#define IRCC_LCR_B_SCE_TRANSMIT 0x40 +#define IRCC_LCR_B_SCE_RECEIVE 0x80 +#define IRCC_LCR_B_SCE_UNDEFINED 0xc0 +#define IRCC_LCR_B_SIP_ENABLE 0x20 +#define IRCC_LCR_B_BRICK_WALL 0x10 + +/* Bus Status Register */ +#define IRCC_BSR 0x06 +#define IRCC_BSR_NOT_EMPTY 0x80 +#define IRCC_BSR_FIFO_FULL 0x40 +#define IRCC_BSR_TIMEOUT 0x20 + +/* Register block 1 */ + +#define IRCC_FIFO_THRESHOLD 0x02 + +#define IRCC_SCE_CFGA 0x00 +#define IRCC_CFGA_AUX_IR 0x80 +#define IRCC_CFGA_HALF_DUPLEX 0x04 +#define IRCC_CFGA_TX_POLARITY 0x02 +#define IRCC_CFGA_RX_POLARITY 0x01 + +#define IRCC_CFGA_COM 0x00 +#define IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK 0x87 +#define IRCC_CFGA_IRDA_SIR_A 0x08 +#define IRCC_CFGA_ASK_SIR 0x10 +#define IRCC_CFGA_IRDA_SIR_B 0x18 +#define IRCC_CFGA_IRDA_HDLC 0x20 +#define IRCC_CFGA_IRDA_4PPM 0x28 +#define IRCC_CFGA_CONSUMER 0x30 +#define IRCC_CFGA_RAW_IR 0x38 +#define IRCC_CFGA_OTHER 0x40 + +#define IRCC_IR_HDLC 0x04 +#define IRCC_IR_4PPM 0x01 +#define IRCC_IR_CONSUMER 0x02 + +#define IRCC_SCE_CFGB 0x01 +#define IRCC_CFGB_LOOPBACK 0x20 +#define IRCC_CFGB_LPBCK_TX_CRC 0x10 +#define IRCC_CFGB_NOWAIT 0x08 +#define IRCC_CFGB_STRING_MOVE 0x04 +#define IRCC_CFGB_DMA_BURST 0x02 +#define IRCC_CFGB_DMA_ENABLE 0x01 + +#define IRCC_CFGB_MUX_COM 0x00 +#define IRCC_CFGB_MUX_IR 0x40 +#define IRCC_CFGB_MUX_AUX 0x80 +#define IRCC_CFGB_MUX_INACTIVE 0xc0 + +/* Register block 3 - Identification Registers! */ +#define IRCC_ID_HIGH 0x00 /* 0x10 */ +#define IRCC_ID_LOW 0x01 /* 0xB8 */ +#define IRCC_CHIP_ID 0x02 /* 0xF1 */ +#define IRCC_VERSION 0x03 /* 0x01 */ +#define IRCC_INTERFACE 0x04 /* low 4 = DMA, high 4 = IRQ */ +#define IRCC_INTERFACE_DMA_MASK 0x0F /* low 4 = DMA, high 4 = IRQ */ +#define IRCC_INTERFACE_IRQ_MASK 0xF0 /* low 4 = DMA, high 4 = IRQ */ + +/* Register block 4 - IrDA */ +#define IRCC_CONTROL 0x00 +#define IRCC_BOF_COUNT_LO 0x01 /* Low byte */ +#define IRCC_BOF_COUNT_HI 0x00 /* High nibble (bit 0-3) */ +#define IRCC_BRICKWALL_CNT_LO 0x02 /* Low byte */ +#define IRCC_BRICKWALL_CNT_HI 0x03 /* High nibble (bit 4-7) */ +#define IRCC_TX_SIZE_LO 0x04 /* Low byte */ +#define IRCC_TX_SIZE_HI 0x03 /* High nibble (bit 0-3) */ +#define IRCC_RX_SIZE_HI 0x05 /* High nibble (bit 0-3) */ +#define IRCC_RX_SIZE_LO 0x06 /* Low byte */ + +#define IRCC_1152 0x80 +#define IRCC_CRC 0x40 + +/* Register block 5 - IrDA */ +#define IRCC_ATC 0x00 +#define IRCC_ATC_nPROGREADY 0x80 +#define IRCC_ATC_SPEED 0x40 +#define IRCC_ATC_ENABLE 0x20 +#define IRCC_ATC_MASK 0xE0 + + +#define IRCC_IRHALFDUPLEX_TIMEOUT 0x01 + +#define IRCC_SCE_TX_DELAY_TIMER 0x02 + +/* + * Other definitions + */ + +#define SMSC_IRCC2_MAX_SIR_SPEED 115200 +#define SMSC_IRCC2_FIR_CHIP_IO_EXTENT 8 +#define SMSC_IRCC2_SIR_CHIP_IO_EXTENT 8 +#define SMSC_IRCC2_FIFO_SIZE 16 +#define SMSC_IRCC2_FIFO_THRESHOLD 64 +/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ +#define SMSC_IRCC2_RX_BUFF_TRUESIZE 14384 +#define SMSC_IRCC2_TX_BUFF_TRUESIZE 14384 +#define SMSC_IRCC2_MIN_TURN_TIME 0x07 +#define SMSC_IRCC2_WINDOW_SIZE 0x07 +/* Maximum wait for hw transmitter to finish */ +#define SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US 1000 /* 1 ms */ +/* Maximum wait for ATC transceiver programming to finish */ +#define SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES 1 +#endif /* SMSC_IRCC2_H */ diff --git a/drivers/staging/irda/drivers/smsc-sio.h b/drivers/staging/irda/drivers/smsc-sio.h new file mode 100644 index 000000000000..59e20e653ebe --- /dev/null +++ b/drivers/staging/irda/drivers/smsc-sio.h @@ -0,0 +1,100 @@ +#ifndef SMSC_SIO_H +#define SMSC_SIO_H + +/****************************************** + Keys. They should work with every SMsC SIO + ******************************************/ + +#define SMSCSIO_CFGACCESSKEY 0x55 +#define SMSCSIO_CFGEXITKEY 0xaa + +/***************************** + * Generic SIO Flat (!?) * + *****************************/ + +/* Register 0x0d */ +#define SMSCSIOFLAT_DEVICEID_REG 0x0d + +/* Register 0x0c */ +#define SMSCSIOFLAT_UARTMODE0C_REG 0x0c +#define SMSCSIOFLAT_UART2MODE_MASK 0x38 +#define SMSCSIOFLAT_UART2MODE_VAL_COM 0x00 +#define SMSCSIOFLAT_UART2MODE_VAL_IRDA 0x08 +#define SMSCSIOFLAT_UART2MODE_VAL_ASKIR 0x10 + +/* Register 0x25 */ +#define SMSCSIOFLAT_UART2BASEADDR_REG 0x25 + +/* Register 0x2b */ +#define SMSCSIOFLAT_FIRBASEADDR_REG 0x2b + +/* Register 0x2c */ +#define SMSCSIOFLAT_FIRDMASELECT_REG 0x2c +#define SMSCSIOFLAT_FIRDMASELECT_MASK 0x0f + +/* Register 0x28 */ +#define SMSCSIOFLAT_UARTIRQSELECT_REG 0x28 +#define SMSCSIOFLAT_UART2IRQSELECT_MASK 0x0f +#define SMSCSIOFLAT_UART1IRQSELECT_MASK 0xf0 +#define SMSCSIOFLAT_UARTIRQSELECT_VAL_NONE 0x00 + + +/********************* + * LPC47N227 * + *********************/ + +#define LPC47N227_CFGACCESSKEY 0x55 +#define LPC47N227_CFGEXITKEY 0xaa + +/* Register 0x00 */ +#define LPC47N227_FDCPOWERVALIDCONF_REG 0x00 +#define LPC47N227_FDCPOWER_MASK 0x08 +#define LPC47N227_VALID_MASK 0x80 + +/* Register 0x02 */ +#define LPC47N227_UART12POWER_REG 0x02 +#define LPC47N227_UART1POWERDOWN_MASK 0x08 +#define LPC47N227_UART2POWERDOWN_MASK 0x80 + +/* Register 0x07 */ +#define LPC47N227_APMBOOTDRIVE_REG 0x07 +#define LPC47N227_PARPORT2AUTOPWRDOWN_MASK 0x10 /* auto power down on if set */ +#define LPC47N227_UART2AUTOPWRDOWN_MASK 0x20 /* auto power down on if set */ +#define LPC47N227_UART1AUTOPWRDOWN_MASK 0x40 /* auto power down on if set */ + +/* Register 0x0c */ +#define LPC47N227_UARTMODE0C_REG 0x0c +#define LPC47N227_UART2MODE_MASK 0x38 +#define LPC47N227_UART2MODE_VAL_COM 0x00 +#define LPC47N227_UART2MODE_VAL_IRDA 0x08 +#define LPC47N227_UART2MODE_VAL_ASKIR 0x10 + +/* Register 0x0d */ +#define LPC47N227_DEVICEID_REG 0x0d +#define LPC47N227_DEVICEID_DEFVAL 0x5a + +/* Register 0x0e */ +#define LPC47N227_REVISIONID_REG 0x0e + +/* Register 0x25 */ +#define LPC47N227_UART2BASEADDR_REG 0x25 + +/* Register 0x28 */ +#define LPC47N227_UARTIRQSELECT_REG 0x28 +#define LPC47N227_UART2IRQSELECT_MASK 0x0f +#define LPC47N227_UART1IRQSELECT_MASK 0xf0 +#define LPC47N227_UARTIRQSELECT_VAL_NONE 0x00 + +/* Register 0x2b */ +#define LPC47N227_FIRBASEADDR_REG 0x2b + +/* Register 0x2c */ +#define LPC47N227_FIRDMASELECT_REG 0x2c +#define LPC47N227_FIRDMASELECT_MASK 0x0f +#define LPC47N227_FIRDMASELECT_VAL_DMA1 0x01 /* 47n227 has three dma channels */ +#define LPC47N227_FIRDMASELECT_VAL_DMA2 0x02 +#define LPC47N227_FIRDMASELECT_VAL_DMA3 0x03 +#define LPC47N227_FIRDMASELECT_VAL_NONE 0x0f + + +#endif diff --git a/drivers/staging/irda/drivers/stir4200.c b/drivers/staging/irda/drivers/stir4200.c new file mode 100644 index 000000000000..ee2cb70b688d --- /dev/null +++ b/drivers/staging/irda/drivers/stir4200.c @@ -0,0 +1,1134 @@ +/***************************************************************************** +* +* Filename: stir4200.c +* Version: 0.4 +* Description: Irda SigmaTel USB Dongle +* Status: Experimental +* Author: Stephen Hemminger +* +* Based on earlier driver by Paul Stewart +* +* Copyright (C) 2000, Roman Weissgaerber +* Copyright (C) 2001, Dag Brattli +* Copyright (C) 2001, Jean Tourrilhes +* Copyright (C) 2004, Stephen Hemminger +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * This dongle does no framing, and requires polling to receive the + * data. The STIr4200 has bulk in and out endpoints just like + * usr-irda devices, but the data it sends and receives is raw; like + * irtty, it needs to call the wrap and unwrap functions to add and + * remove SOF/BOF and escape characters to/from the frame. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Stephen Hemminger "); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for SigmaTel STIr4200"); +MODULE_LICENSE("GPL"); + +static int qos_mtt_bits = 0x07; /* 1 ms or more */ +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); + +static int rx_sensitivity = 1; /* FIR 0..4, SIR 0..6 */ +module_param(rx_sensitivity, int, 0); +MODULE_PARM_DESC(rx_sensitivity, "Set Receiver sensitivity (0-6, 0 is most sensitive)"); + +static int tx_power = 0; /* 0 = highest ... 3 = lowest */ +module_param(tx_power, int, 0); +MODULE_PARM_DESC(tx_power, "Set Transmitter power (0-3, 0 is highest power)"); + +#define STIR_IRDA_HEADER 4 +#define CTRL_TIMEOUT 100 /* milliseconds */ +#define TRANSMIT_TIMEOUT 200 /* milliseconds */ +#define STIR_FIFO_SIZE 4096 +#define FIFO_REGS_SIZE 3 + +enum FirChars { + FIR_CE = 0x7d, + FIR_XBOF = 0x7f, + FIR_EOF = 0x7e, +}; + +enum StirRequests { + REQ_WRITE_REG = 0x00, + REQ_READ_REG = 0x01, + REQ_READ_ROM = 0x02, + REQ_WRITE_SINGLE = 0x03, +}; + +/* Register offsets */ +enum StirRegs { + REG_RSVD=0, + REG_MODE, + REG_PDCLK, + REG_CTRL1, + REG_CTRL2, + REG_FIFOCTL, + REG_FIFOLSB, + REG_FIFOMSB, + REG_DPLL, + REG_IRDIG, + REG_TEST=15, +}; + +enum StirModeMask { + MODE_FIR = 0x80, + MODE_SIR = 0x20, + MODE_ASK = 0x10, + MODE_FASTRX = 0x08, + MODE_FFRSTEN = 0x04, + MODE_NRESET = 0x02, + MODE_2400 = 0x01, +}; + +enum StirPdclkMask { + PDCLK_4000000 = 0x02, + PDCLK_115200 = 0x09, + PDCLK_57600 = 0x13, + PDCLK_38400 = 0x1D, + PDCLK_19200 = 0x3B, + PDCLK_9600 = 0x77, + PDCLK_2400 = 0xDF, +}; + +enum StirCtrl1Mask { + CTRL1_SDMODE = 0x80, + CTRL1_RXSLOW = 0x40, + CTRL1_TXPWD = 0x10, + CTRL1_RXPWD = 0x08, + CTRL1_SRESET = 0x01, +}; + +enum StirCtrl2Mask { + CTRL2_SPWIDTH = 0x08, + CTRL2_REVID = 0x03, +}; + +enum StirFifoCtlMask { + FIFOCTL_DIR = 0x10, + FIFOCTL_CLR = 0x08, + FIFOCTL_EMPTY = 0x04, +}; + +enum StirDiagMask { + IRDIG_RXHIGH = 0x80, + IRDIG_RXLOW = 0x40, +}; + +enum StirTestMask { + TEST_PLLDOWN = 0x80, + TEST_LOOPIR = 0x40, + TEST_LOOPUSB = 0x20, + TEST_TSTENA = 0x10, + TEST_TSTOSC = 0x0F, +}; + +struct stir_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + + struct qos_info qos; + unsigned speed; /* Current speed */ + + struct task_struct *thread; /* transmit thread */ + + struct sk_buff *tx_pending; + void *io_buf; /* transmit/receive buffer */ + __u8 *fifo_status; + + iobuff_t rx_buff; /* receive unwrap state machine */ + ktime_t rx_time; + int receiving; + struct urb *rx_urb; +}; + + +/* These are the currently known USB ids */ +static const struct usb_device_id dongles[] = { + /* SigmaTel, Inc, STIr4200 IrDA/USB Bridge */ + { USB_DEVICE(0x066f, 0x4200) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dongles); + +/* Send control message to set dongle register */ +static int write_reg(struct stir_cb *stir, __u16 reg, __u8 value) +{ + struct usb_device *dev = stir->usbdev; + + pr_debug("%s: write reg %d = 0x%x\n", + stir->netdev->name, reg, value); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + REQ_WRITE_SINGLE, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE, + value, reg, NULL, 0, + CTRL_TIMEOUT); +} + +/* Send control message to read multiple registers */ +static inline int read_reg(struct stir_cb *stir, __u16 reg, + __u8 *data, __u16 count) +{ + struct usb_device *dev = stir->usbdev; + + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + REQ_READ_REG, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, reg, data, count, + CTRL_TIMEOUT); +} + +static inline int isfir(u32 speed) +{ + return speed == 4000000; +} + +/* + * Prepare a FIR IrDA frame for transmission to the USB dongle. The + * FIR transmit frame is documented in the datasheet. It consists of + * a two byte 0x55 0xAA sequence, two little-endian length bytes, a + * sequence of exactly 16 XBOF bytes of 0x7E, two BOF bytes of 0x7E, + * then the data escaped as follows: + * + * 0x7D -> 0x7D 0x5D + * 0x7E -> 0x7D 0x5E + * 0x7F -> 0x7D 0x5F + * + * Then, 4 bytes of little endian (stuffed) FCS follow, then two + * trailing EOF bytes of 0x7E. + */ +static inline __u8 *stuff_fir(__u8 *p, __u8 c) +{ + switch(c) { + case 0x7d: + case 0x7e: + case 0x7f: + *p++ = 0x7d; + c ^= IRDA_TRANS; + /* fall through */ + default: + *p++ = c; + } + return p; +} + +/* Take raw data in skb and put it wrapped into buf */ +static unsigned wrap_fir_skb(const struct sk_buff *skb, __u8 *buf) +{ + __u8 *ptr = buf; + __u32 fcs = ~(crc32_le(~0, skb->data, skb->len)); + __u16 wraplen; + int i; + + /* Header */ + buf[0] = 0x55; + buf[1] = 0xAA; + + ptr = buf + STIR_IRDA_HEADER; + memset(ptr, 0x7f, 16); + ptr += 16; + + /* BOF */ + *ptr++ = 0x7e; + *ptr++ = 0x7e; + + /* Address / Control / Information */ + for (i = 0; i < skb->len; i++) + ptr = stuff_fir(ptr, skb->data[i]); + + /* FCS */ + ptr = stuff_fir(ptr, fcs & 0xff); + ptr = stuff_fir(ptr, (fcs >> 8) & 0xff); + ptr = stuff_fir(ptr, (fcs >> 16) & 0xff); + ptr = stuff_fir(ptr, (fcs >> 24) & 0xff); + + /* EOFs */ + *ptr++ = 0x7e; + *ptr++ = 0x7e; + + /* Total length, minus the header */ + wraplen = (ptr - buf) - STIR_IRDA_HEADER; + buf[2] = wraplen & 0xff; + buf[3] = (wraplen >> 8) & 0xff; + + return wraplen + STIR_IRDA_HEADER; +} + +static unsigned wrap_sir_skb(struct sk_buff *skb, __u8 *buf) +{ + __u16 wraplen; + + wraplen = async_wrap_skb(skb, buf + STIR_IRDA_HEADER, + STIR_FIFO_SIZE - STIR_IRDA_HEADER); + buf[0] = 0x55; + buf[1] = 0xAA; + buf[2] = wraplen & 0xff; + buf[3] = (wraplen >> 8) & 0xff; + + return wraplen + STIR_IRDA_HEADER; +} + +/* + * Frame is fully formed in the rx_buff so check crc + * and pass up to irlap + * setup for next receive + */ +static void fir_eof(struct stir_cb *stir) +{ + iobuff_t *rx_buff = &stir->rx_buff; + int len = rx_buff->len - 4; + struct sk_buff *skb, *nskb; + __u32 fcs; + + if (unlikely(len <= 0)) { + pr_debug("%s: short frame len %d\n", + stir->netdev->name, len); + + ++stir->netdev->stats.rx_errors; + ++stir->netdev->stats.rx_length_errors; + return; + } + + fcs = ~(crc32_le(~0, rx_buff->data, len)); + if (fcs != get_unaligned_le32(rx_buff->data + len)) { + pr_debug("crc error calc 0x%x len %d\n", fcs, len); + stir->netdev->stats.rx_errors++; + stir->netdev->stats.rx_crc_errors++; + return; + } + + /* if frame is short then just copy it */ + if (len < IRDA_RX_COPY_THRESHOLD) { + nskb = dev_alloc_skb(len + 1); + if (unlikely(!nskb)) { + ++stir->netdev->stats.rx_dropped; + return; + } + skb_reserve(nskb, 1); + skb = nskb; + skb_copy_to_linear_data(nskb, rx_buff->data, len); + } else { + nskb = dev_alloc_skb(rx_buff->truesize); + if (unlikely(!nskb)) { + ++stir->netdev->stats.rx_dropped; + return; + } + skb_reserve(nskb, 1); + skb = rx_buff->skb; + rx_buff->skb = nskb; + rx_buff->head = nskb->data; + } + + skb_put(skb, len); + + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + skb->dev = stir->netdev; + + netif_rx(skb); + + stir->netdev->stats.rx_packets++; + stir->netdev->stats.rx_bytes += len; + + rx_buff->data = rx_buff->head; + rx_buff->len = 0; +} + +/* Unwrap FIR stuffed data and bump it to IrLAP */ +static void stir_fir_chars(struct stir_cb *stir, + const __u8 *bytes, int len) +{ + iobuff_t *rx_buff = &stir->rx_buff; + int i; + + for (i = 0; i < len; i++) { + __u8 byte = bytes[i]; + + switch(rx_buff->state) { + case OUTSIDE_FRAME: + /* ignore garbage till start of frame */ + if (unlikely(byte != FIR_EOF)) + continue; + /* Now receiving frame */ + rx_buff->state = BEGIN_FRAME; + + /* Time to initialize receive buffer */ + rx_buff->data = rx_buff->head; + rx_buff->len = 0; + continue; + + case LINK_ESCAPE: + if (byte == FIR_EOF) { + pr_debug("%s: got EOF after escape\n", + stir->netdev->name); + goto frame_error; + } + rx_buff->state = INSIDE_FRAME; + byte ^= IRDA_TRANS; + break; + + case BEGIN_FRAME: + /* ignore multiple BOF/EOF */ + if (byte == FIR_EOF) + continue; + rx_buff->state = INSIDE_FRAME; + rx_buff->in_frame = TRUE; + + /* fall through */ + case INSIDE_FRAME: + switch(byte) { + case FIR_CE: + rx_buff->state = LINK_ESCAPE; + continue; + case FIR_XBOF: + /* 0x7f is not used in this framing */ + pr_debug("%s: got XBOF without escape\n", + stir->netdev->name); + goto frame_error; + case FIR_EOF: + rx_buff->state = OUTSIDE_FRAME; + rx_buff->in_frame = FALSE; + fir_eof(stir); + continue; + } + break; + } + + /* add byte to rx buffer */ + if (unlikely(rx_buff->len >= rx_buff->truesize)) { + pr_debug("%s: fir frame exceeds %d\n", + stir->netdev->name, rx_buff->truesize); + ++stir->netdev->stats.rx_over_errors; + goto error_recovery; + } + + rx_buff->data[rx_buff->len++] = byte; + continue; + + frame_error: + ++stir->netdev->stats.rx_frame_errors; + + error_recovery: + ++stir->netdev->stats.rx_errors; + rx_buff->state = OUTSIDE_FRAME; + rx_buff->in_frame = FALSE; + } +} + +/* Unwrap SIR stuffed data and bump it up to IrLAP */ +static void stir_sir_chars(struct stir_cb *stir, + const __u8 *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) + async_unwrap_char(stir->netdev, &stir->netdev->stats, + &stir->rx_buff, bytes[i]); +} + +static inline void unwrap_chars(struct stir_cb *stir, + const __u8 *bytes, int length) +{ + if (isfir(stir->speed)) + stir_fir_chars(stir, bytes, length); + else + stir_sir_chars(stir, bytes, length); +} + +/* Mode parameters for each speed */ +static const struct { + unsigned speed; + __u8 pdclk; +} stir_modes[] = { + { 2400, PDCLK_2400 }, + { 9600, PDCLK_9600 }, + { 19200, PDCLK_19200 }, + { 38400, PDCLK_38400 }, + { 57600, PDCLK_57600 }, + { 115200, PDCLK_115200 }, + { 4000000, PDCLK_4000000 }, +}; + + +/* + * Setup chip for speed. + * Called at startup to initialize the chip + * and on speed changes. + * + * Note: Write multiple registers doesn't appear to work + */ +static int change_speed(struct stir_cb *stir, unsigned speed) +{ + int i, err; + __u8 mode; + + for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) { + if (speed == stir_modes[i].speed) + goto found; + } + + dev_warn(&stir->netdev->dev, "invalid speed %d\n", speed); + return -EINVAL; + + found: + pr_debug("speed change from %d to %d\n", stir->speed, speed); + + /* Reset modulator */ + err = write_reg(stir, REG_CTRL1, CTRL1_SRESET); + if (err) + goto out; + + /* Undocumented magic to tweak the DPLL */ + err = write_reg(stir, REG_DPLL, 0x15); + if (err) + goto out; + + /* Set clock */ + err = write_reg(stir, REG_PDCLK, stir_modes[i].pdclk); + if (err) + goto out; + + mode = MODE_NRESET | MODE_FASTRX; + if (isfir(speed)) + mode |= MODE_FIR | MODE_FFRSTEN; + else + mode |= MODE_SIR; + + if (speed == 2400) + mode |= MODE_2400; + + err = write_reg(stir, REG_MODE, mode); + if (err) + goto out; + + /* This resets TEMIC style transceiver if any. */ + err = write_reg(stir, REG_CTRL1, + CTRL1_SDMODE | (tx_power & 3) << 1); + if (err) + goto out; + + err = write_reg(stir, REG_CTRL1, (tx_power & 3) << 1); + if (err) + goto out; + + /* Reset sensitivity */ + err = write_reg(stir, REG_CTRL2, (rx_sensitivity & 7) << 5); + out: + stir->speed = speed; + return err; +} + +/* + * Called from net/core when new frame is available. + */ +static netdev_tx_t stir_hard_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct stir_cb *stir = netdev_priv(netdev); + + netif_stop_queue(netdev); + + /* the IRDA wrapping routines don't deal with non linear skb */ + SKB_LINEAR_ASSERT(skb); + + skb = xchg(&stir->tx_pending, skb); + wake_up_process(stir->thread); + + /* this should never happen unless stop/wakeup problem */ + if (unlikely(skb)) { + WARN_ON(1); + dev_kfree_skb(skb); + } + + return NETDEV_TX_OK; +} + +/* + * Wait for the transmit FIFO to have space for next data + * + * If space < 0 then wait till FIFO completely drains. + * FYI: can take up to 13 seconds at 2400baud. + */ +static int fifo_txwait(struct stir_cb *stir, int space) +{ + int err; + unsigned long count, status; + unsigned long prev_count = 0x1fff; + + /* Read FIFO status and count */ + for (;; prev_count = count) { + err = read_reg(stir, REG_FIFOCTL, stir->fifo_status, + FIFO_REGS_SIZE); + if (unlikely(err != FIFO_REGS_SIZE)) { + dev_warn(&stir->netdev->dev, + "FIFO register read error: %d\n", err); + + return err; + } + + status = stir->fifo_status[0]; + count = (unsigned)(stir->fifo_status[2] & 0x1f) << 8 + | stir->fifo_status[1]; + + pr_debug("fifo status 0x%lx count %lu\n", status, count); + + /* is fifo receiving already, or empty */ + if (!(status & FIFOCTL_DIR) || + (status & FIFOCTL_EMPTY)) + return 0; + + if (signal_pending(current)) + return -EINTR; + + /* shutting down? */ + if (!netif_running(stir->netdev) || + !netif_device_present(stir->netdev)) + return -ESHUTDOWN; + + /* only waiting for some space */ + if (space >= 0 && STIR_FIFO_SIZE - 4 > space + count) + return 0; + + /* queue confused */ + if (prev_count < count) + break; + + /* estimate transfer time for remaining chars */ + msleep((count * 8000) / stir->speed); + } + + err = write_reg(stir, REG_FIFOCTL, FIFOCTL_CLR); + if (err) + return err; + err = write_reg(stir, REG_FIFOCTL, 0); + if (err) + return err; + + return 0; +} + + +/* Wait for turnaround delay before starting transmit. */ +static void turnaround_delay(const struct stir_cb *stir, long us) +{ + long ticks; + + if (us <= 0) + return; + + us -= ktime_us_delta(ktime_get(), stir->rx_time); + + if (us < 10) + return; + + ticks = us / (1000000 / HZ); + if (ticks > 0) + schedule_timeout_interruptible(1 + ticks); + else + udelay(us); +} + +/* + * Start receiver by submitting a request to the receive pipe. + * If nothing is available it will return after rx_interval. + */ +static int receive_start(struct stir_cb *stir) +{ + /* reset state */ + stir->receiving = 1; + + stir->rx_buff.in_frame = FALSE; + stir->rx_buff.state = OUTSIDE_FRAME; + + stir->rx_urb->status = 0; + return usb_submit_urb(stir->rx_urb, GFP_KERNEL); +} + +/* Stop all pending receive Urb's */ +static void receive_stop(struct stir_cb *stir) +{ + stir->receiving = 0; + usb_kill_urb(stir->rx_urb); + + if (stir->rx_buff.in_frame) + stir->netdev->stats.collisions++; +} +/* + * Wrap data in socket buffer and send it. + */ +static void stir_send(struct stir_cb *stir, struct sk_buff *skb) +{ + unsigned wraplen; + int first_frame = 0; + + /* if receiving, need to turnaround */ + if (stir->receiving) { + receive_stop(stir); + turnaround_delay(stir, irda_get_mtt(skb)); + first_frame = 1; + } + + if (isfir(stir->speed)) + wraplen = wrap_fir_skb(skb, stir->io_buf); + else + wraplen = wrap_sir_skb(skb, stir->io_buf); + + /* check for space available in fifo */ + if (!first_frame) + fifo_txwait(stir, wraplen); + + stir->netdev->stats.tx_packets++; + stir->netdev->stats.tx_bytes += skb->len; + netif_trans_update(stir->netdev); + pr_debug("send %d (%d)\n", skb->len, wraplen); + + if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1), + stir->io_buf, wraplen, + NULL, TRANSMIT_TIMEOUT)) + stir->netdev->stats.tx_errors++; +} + +/* + * Transmit state machine thread + */ +static int stir_transmit_thread(void *arg) +{ + struct stir_cb *stir = arg; + struct net_device *dev = stir->netdev; + struct sk_buff *skb; + + while (!kthread_should_stop()) { +#ifdef CONFIG_PM + /* if suspending, then power off and wait */ + if (unlikely(freezing(current))) { + if (stir->receiving) + receive_stop(stir); + else + fifo_txwait(stir, -1); + + write_reg(stir, REG_CTRL1, CTRL1_TXPWD|CTRL1_RXPWD); + + try_to_freeze(); + + if (change_speed(stir, stir->speed)) + break; + } +#endif + + /* if something to send? */ + skb = xchg(&stir->tx_pending, NULL); + if (skb) { + unsigned new_speed = irda_get_next_speed(skb); + netif_wake_queue(dev); + + if (skb->len > 0) + stir_send(stir, skb); + dev_kfree_skb(skb); + + if ((new_speed != -1) && (stir->speed != new_speed)) { + if (fifo_txwait(stir, -1) || + change_speed(stir, new_speed)) + break; + } + continue; + } + + /* nothing to send? start receiving */ + if (!stir->receiving && + irda_device_txqueue_empty(dev)) { + /* Wait otherwise chip gets confused. */ + if (fifo_txwait(stir, -1)) + break; + + if (unlikely(receive_start(stir))) { + if (net_ratelimit()) + dev_info(&dev->dev, + "%s: receive usb submit failed\n", + stir->netdev->name); + stir->receiving = 0; + msleep(10); + continue; + } + } + + /* sleep if nothing to send */ + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + } + return 0; +} + + +/* + * USB bulk receive completion callback. + * Wakes up every ms (usb round trip) with wrapped + * data. + */ +static void stir_rcv_irq(struct urb *urb) +{ + struct stir_cb *stir = urb->context; + int err; + + /* in process of stopping, just drop data */ + if (!netif_running(stir->netdev)) + return; + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) + return; + + if (urb->actual_length > 0) { + pr_debug("receive %d\n", urb->actual_length); + unwrap_chars(stir, urb->transfer_buffer, + urb->actual_length); + + stir->rx_time = ktime_get(); + } + + /* kernel thread is stopping receiver don't resubmit */ + if (!stir->receiving) + return; + + /* resubmit existing urb */ + err = usb_submit_urb(urb, GFP_ATOMIC); + + /* in case of error, the kernel thread will restart us */ + if (err) { + dev_warn(&stir->netdev->dev, "usb receive submit error: %d\n", + err); + stir->receiving = 0; + wake_up_process(stir->thread); + } +} + +/* + * Function stir_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + */ +static int stir_net_open(struct net_device *netdev) +{ + struct stir_cb *stir = netdev_priv(netdev); + int err; + char hwname[16]; + + err = usb_clear_halt(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1)); + if (err) + goto err_out1; + err = usb_clear_halt(stir->usbdev, usb_rcvbulkpipe(stir->usbdev, 2)); + if (err) + goto err_out1; + + err = change_speed(stir, 9600); + if (err) + goto err_out1; + + err = -ENOMEM; + + /* Initialize for SIR/FIR to copy data directly into skb. */ + stir->receiving = 0; + stir->rx_buff.truesize = IRDA_SKB_MAX_MTU; + stir->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!stir->rx_buff.skb) + goto err_out1; + + skb_reserve(stir->rx_buff.skb, 1); + stir->rx_buff.head = stir->rx_buff.skb->data; + stir->rx_time = ktime_get(); + + stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!stir->rx_urb) + goto err_out2; + + stir->io_buf = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL); + if (!stir->io_buf) + goto err_out3; + + usb_fill_bulk_urb(stir->rx_urb, stir->usbdev, + usb_rcvbulkpipe(stir->usbdev, 2), + stir->io_buf, STIR_FIFO_SIZE, + stir_rcv_irq, stir); + + stir->fifo_status = kmalloc(FIFO_REGS_SIZE, GFP_KERNEL); + if (!stir->fifo_status) + goto err_out4; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + * Note : will send immediately a speed change... + */ + sprintf(hwname, "usb#%d", stir->usbdev->devnum); + stir->irlap = irlap_open(netdev, &stir->qos, hwname); + if (!stir->irlap) { + dev_err(&stir->usbdev->dev, "irlap_open failed\n"); + goto err_out5; + } + + /** Start kernel thread for transmit. */ + stir->thread = kthread_run(stir_transmit_thread, stir, + "%s", stir->netdev->name); + if (IS_ERR(stir->thread)) { + err = PTR_ERR(stir->thread); + dev_err(&stir->usbdev->dev, "unable to start kernel thread\n"); + goto err_out6; + } + + netif_start_queue(netdev); + + return 0; + + err_out6: + irlap_close(stir->irlap); + err_out5: + kfree(stir->fifo_status); + err_out4: + kfree(stir->io_buf); + err_out3: + usb_free_urb(stir->rx_urb); + err_out2: + kfree_skb(stir->rx_buff.skb); + err_out1: + return err; +} + +/* + * Function stir_net_close (stir) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int stir_net_close(struct net_device *netdev) +{ + struct stir_cb *stir = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* Kill transmit thread */ + kthread_stop(stir->thread); + kfree(stir->fifo_status); + + /* Mop up receive urb's */ + usb_kill_urb(stir->rx_urb); + + kfree(stir->io_buf); + usb_free_urb(stir->rx_urb); + kfree_skb(stir->rx_buff.skb); + + /* Stop and remove instance of IrLAP */ + if (stir->irlap) + irlap_close(stir->irlap); + + stir->irlap = NULL; + + return 0; +} + +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int stir_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct stir_cb *stir = netdev_priv(netdev); + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the device is still there */ + if (netif_device_present(stir->netdev)) + ret = change_speed(stir, irq->ifr_baudrate); + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the IrDA stack is still there */ + if (netif_running(stir->netdev)) + irda_device_set_media_busy(stir->netdev, TRUE); + break; + + case SIOCGRECEIVING: + /* Only approximately true */ + irq->ifr_receiving = stir->receiving; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static const struct net_device_ops stir_netdev_ops = { + .ndo_open = stir_net_open, + .ndo_stop = stir_net_close, + .ndo_start_xmit = stir_hard_xmit, + .ndo_do_ioctl = stir_net_ioctl, +}; + +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + * Note : it might be worth protecting this function by a global + * spinlock... Or not, because maybe USB already deal with that... + */ +static int stir_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct stir_cb *stir = NULL; + struct net_device *net; + int ret = -ENOMEM; + + /* Allocate network device container. */ + net = alloc_irdadev(sizeof(*stir)); + if(!net) + goto err_out1; + + SET_NETDEV_DEV(net, &intf->dev); + stir = netdev_priv(net); + stir->netdev = net; + stir->usbdev = dev; + + ret = usb_reset_configuration(dev); + if (ret != 0) { + dev_err(&intf->dev, "usb reset configuration failed\n"); + goto err_out2; + } + + printk(KERN_INFO "SigmaTel STIr4200 IRDA/USB found at address %d, " + "Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&stir->qos); + + /* That's the Rx capability. */ + stir->qos.baud_rate.bits &= IR_2400 | IR_9600 | IR_19200 | + IR_38400 | IR_57600 | IR_115200 | + (IR_4000000 << 8); + stir->qos.min_turn_time.bits &= qos_mtt_bits; + irda_qos_bits_to_value(&stir->qos); + + /* Override the network functions we need to use */ + net->netdev_ops = &stir_netdev_ops; + + ret = register_netdev(net); + if (ret != 0) + goto err_out2; + + dev_info(&intf->dev, "IrDA: Registered SigmaTel device %s\n", + net->name); + + usb_set_intfdata(intf, stir); + + return 0; + +err_out2: + free_netdev(net); +err_out1: + return ret; +} + +/* + * The current device is removed, the USB layer tell us to shut it down... + */ +static void stir_disconnect(struct usb_interface *intf) +{ + struct stir_cb *stir = usb_get_intfdata(intf); + + if (!stir) + return; + + unregister_netdev(stir->netdev); + free_netdev(stir->netdev); + + usb_set_intfdata(intf, NULL); +} + +#ifdef CONFIG_PM +/* USB suspend, so power off the transmitter/receiver */ +static int stir_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct stir_cb *stir = usb_get_intfdata(intf); + + netif_device_detach(stir->netdev); + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int stir_resume(struct usb_interface *intf) +{ + struct stir_cb *stir = usb_get_intfdata(intf); + + netif_device_attach(stir->netdev); + + /* receiver restarted when send thread wakes up */ + return 0; +} +#endif + +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .name = "stir4200", + .probe = stir_probe, + .disconnect = stir_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = stir_suspend, + .resume = stir_resume, +#endif +}; + +module_usb_driver(irda_driver); diff --git a/drivers/staging/irda/drivers/tekram-sir.c b/drivers/staging/irda/drivers/tekram-sir.c new file mode 100644 index 000000000000..9dcf0c103b9d --- /dev/null +++ b/drivers/staging/irda/drivers/tekram-sir.c @@ -0,0 +1,225 @@ +/********************************************************************* + * + * Filename: tekram.c + * Version: 1.3 + * Description: Implementation of the Tekram IrMate IR-210B dongle + * Status: Experimental. + * Author: Dag Brattli + * Created at: Wed Oct 21 20:02:35 1998 + * Modified at: Sun Oct 27 22:02:38 2002 + * Modified by: Martin Diehl + * + * Copyright (c) 1998-1999 Dag Brattli, + * Copyright (c) 2002 Martin Diehl, + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +static int tekram_delay = 150; /* default is 150 ms */ +module_param(tekram_delay, int, 0); +MODULE_PARM_DESC(tekram_delay, "tekram dongle write complete delay"); + +static int tekram_open(struct sir_dev *); +static int tekram_close(struct sir_dev *); +static int tekram_change_speed(struct sir_dev *, unsigned); +static int tekram_reset(struct sir_dev *); + +#define TEKRAM_115200 0x00 +#define TEKRAM_57600 0x01 +#define TEKRAM_38400 0x02 +#define TEKRAM_19200 0x03 +#define TEKRAM_9600 0x04 + +#define TEKRAM_PW 0x10 /* Pulse select bit */ + +static struct dongle_driver tekram = { + .owner = THIS_MODULE, + .driver_name = "Tekram IR-210B", + .type = IRDA_TEKRAM_DONGLE, + .open = tekram_open, + .close = tekram_close, + .reset = tekram_reset, + .set_speed = tekram_change_speed, +}; + +static int __init tekram_sir_init(void) +{ + if (tekram_delay < 1 || tekram_delay > 500) + tekram_delay = 200; + pr_debug("%s - using %d ms delay\n", + tekram.driver_name, tekram_delay); + return irda_register_dongle(&tekram); +} + +static void __exit tekram_sir_cleanup(void) +{ + irda_unregister_dongle(&tekram); +} + +static int tekram_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int tekram_close(struct sir_dev *dev) +{ + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function tekram_change_speed (dev, state, speed) + * + * Set the speed for the Tekram IRMate 210 type dongle. Warning, this + * function must be called with a process context! + * + * Algorithm + * 1. clear DTR + * 2. set RTS, and wait at least 7 us + * 3. send Control Byte to the IR-210 through TXD to set new baud rate + * wait until the stop bit of Control Byte is sent (for 9600 baud rate, + * it takes about 100 msec) + * + * [oops, why 100 msec? sending 1 byte (10 bits) takes 1.05 msec + * - is this probably to compensate for delays in tty layer?] + * + * 5. clear RTS (return to NORMAL Operation) + * 6. wait at least 50 us, new setting (baud rate, etc) takes effect here + * after + */ + +#define TEKRAM_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) + +static int tekram_change_speed(struct sir_dev *dev, unsigned speed) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + u8 byte; + static int ret = 0; + + switch(state) { + case SIRDEV_STATE_DONGLE_SPEED: + + switch (speed) { + default: + speed = 9600; + ret = -EINVAL; + /* fall thru */ + case 9600: + byte = TEKRAM_PW|TEKRAM_9600; + break; + case 19200: + byte = TEKRAM_PW|TEKRAM_19200; + break; + case 38400: + byte = TEKRAM_PW|TEKRAM_38400; + break; + case 57600: + byte = TEKRAM_PW|TEKRAM_57600; + break; + case 115200: + byte = TEKRAM_115200; + break; + } + + /* Set DTR, Clear RTS */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + + /* Wait at least 7us */ + udelay(14); + + /* Write control byte */ + sirdev_raw_write(dev, &byte, 1); + + dev->speed = speed; + + state = TEKRAM_STATE_WAIT_SPEED; + delay = tekram_delay; + break; + + case TEKRAM_STATE_WAIT_SPEED: + /* Set DTR, Set RTS */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + udelay(50); + break; + + default: + net_err_ratelimited("%s - undefined state %d\n", + __func__, state); + ret = -EINVAL; + break; + } + + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +/* + * Function tekram_reset (driver) + * + * This function resets the tekram dongle. Warning, this function + * must be called with a process context!! + * + * Algorithm: + * 0. Clear RTS and DTR, and wait 50 ms (power off the IR-210 ) + * 1. clear RTS + * 2. set DTR, and wait at least 1 ms + * 3. clear DTR to SPACE state, wait at least 50 us for further + * operation + */ + +static int tekram_reset(struct sir_dev *dev) +{ + /* Clear DTR, Set RTS */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + /* Should sleep 1 ms */ + msleep(1); + + /* Set DTR, Set RTS */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Wait at least 50 us */ + udelay(75); + + dev->speed = 9600; + + return 0; +} + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-0"); /* IRDA_TEKRAM_DONGLE */ + +module_init(tekram_sir_init); +module_exit(tekram_sir_cleanup); diff --git a/drivers/staging/irda/drivers/toim3232-sir.c b/drivers/staging/irda/drivers/toim3232-sir.c new file mode 100644 index 000000000000..b977d6d33e74 --- /dev/null +++ b/drivers/staging/irda/drivers/toim3232-sir.c @@ -0,0 +1,358 @@ +/********************************************************************* + * + * Filename: toim3232-sir.c + * Version: 1.0 + * Description: Implementation of dongles based on the Vishay/Temic + * TOIM3232 SIR Endec chipset. Currently only the + * IRWave IR320ST-2 is tested, although it should work + * with any TOIM3232 or TOIM4232 chipset based RS232 + * dongle with minimal modification. + * Based heavily on the Tekram driver (tekram.c), + * with thanks to Dag Brattli and Martin Diehl. + * Status: Experimental. + * Author: David Basden + * Created at: Thu Feb 09 23:47:32 2006 + * + * Copyright (c) 2006 David Basden. + * Copyright (c) 1998-1999 Dag Brattli, + * Copyright (c) 2002 Martin Diehl, + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +/* + * This driver has currently only been tested on the IRWave IR320ST-2 + * + * PROTOCOL: + * + * The protocol for talking to the TOIM3232 is quite easy, and is + * designed to interface with RS232 with only level convertors. The + * BR/~D line on the chip is brought high to signal 'command mode', + * where a command byte is sent to select the baudrate of the RS232 + * interface and the pulse length of the IRDA output. When BR/~D + * is brought low, the dongle then changes to the selected baudrate, + * and the RS232 interface is used for data until BR/~D is brought + * high again. The initial speed for the TOIMx323 after RESET is + * 9600 baud. The baudrate for command-mode is the last selected + * baud-rate, or 9600 after a RESET. + * + * The dongle I have (below) adds some extra hardware on the front end, + * but this is mostly directed towards pariasitic power from the RS232 + * line rather than changing very much about how to communicate with + * the TOIM3232. + * + * The protocol to talk to the TOIM4232 chipset seems to be almost + * identical to the TOIM3232 (and the 4232 datasheet is more detailed) + * so this code will probably work on that as well, although I haven't + * tested it on that hardware. + * + * Target dongle variations that might be common: + * + * DTR and RTS function: + * The data sheet for the 4232 has a sample implementation that hooks the + * DTR and RTS lines to the RESET and BaudRate/~Data lines of the + * chip (through line-converters). Given both DTR and RTS would have to + * be held low in normal operation, and the TOIMx232 requires +5V to + * signal ground, most dongle designers would almost certainly choose + * an implementation that kept at least one of DTR or RTS high in + * normal operation to provide power to the dongle, but will likely + * vary between designs. + * + * User specified command bits: + * There are two user-controllable output lines from the TOIMx232 that + * can be set low or high by setting the appropriate bits in the + * high-nibble of the command byte (when setting speed and pulse length). + * These might be used to switch on and off added hardware or extra + * dongle features. + * + * + * Target hardware: IRWave IR320ST-2 + * + * The IRWave IR320ST-2 is a simple dongle based on the Vishay/Temic + * TOIM3232 SIR Endec and the Vishay/Temic TFDS4500 SIR IRDA transceiver. + * It uses a hex inverter and some discrete components to buffer and + * line convert the RS232 down to 5V. + * + * The dongle is powered through a voltage regulator, fed by a large + * capacitor. To switch the dongle on, DTR is brought high to charge + * the capacitor and drive the voltage regulator. DTR isn't associated + * with any control lines on the TOIM3232. Parisitic power is also taken + * from the RTS, TD and RD lines when brought high, but through resistors. + * When DTR is low, the circuit might lose power even with RTS high. + * + * RTS is inverted and attached to the BR/~D input pin. When RTS + * is high, BR/~D is low, and the TOIM3232 is in the normal 'data' mode. + * RTS is brought low, BR/~D is high, and the TOIM3232 is in 'command + * mode'. + * + * For some unknown reason, the RESET line isn't actually connected + * to anything. This means to reset the dongle to get it to a known + * state (9600 baud) you must drop DTR and RTS low, wait for the power + * capacitor to discharge, and then bring DTR (and RTS for data mode) + * high again, and wait for the capacitor to charge, the power supply + * to stabilise, and the oscillator clock to stabilise. + * + * Fortunately, if the current baudrate is known, the chipset can + * easily change speed by entering command mode without having to + * reset the dongle first. + * + * Major Components: + * + * - Vishay/Temic TOIM3232 SIR Endec to change RS232 pulse timings + * to IRDA pulse timings + * - 3.6864MHz crystal to drive TOIM3232 clock oscillator + * - DM74lS04M Inverting Hex line buffer for RS232 input buffering + * and level conversion + * - PJ2951AC 150mA voltage regulator + * - Vishay/Temic TFDS4500 SIR IRDA front-end transceiver + * + */ + +#include +#include +#include +#include + +#include + +#include "sir-dev.h" + +static int toim3232delay = 150; /* default is 150 ms */ +module_param(toim3232delay, int, 0); +MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay"); + +static int toim3232_open(struct sir_dev *); +static int toim3232_close(struct sir_dev *); +static int toim3232_change_speed(struct sir_dev *, unsigned); +static int toim3232_reset(struct sir_dev *); + +#define TOIM3232_115200 0x00 +#define TOIM3232_57600 0x01 +#define TOIM3232_38400 0x02 +#define TOIM3232_19200 0x03 +#define TOIM3232_9600 0x06 +#define TOIM3232_2400 0x0A + +#define TOIM3232_PW 0x10 /* Pulse select bit */ + +static struct dongle_driver toim3232 = { + .owner = THIS_MODULE, + .driver_name = "Vishay TOIM3232", + .type = IRDA_TOIM3232_DONGLE, + .open = toim3232_open, + .close = toim3232_close, + .reset = toim3232_reset, + .set_speed = toim3232_change_speed, +}; + +static int __init toim3232_sir_init(void) +{ + if (toim3232delay < 1 || toim3232delay > 500) + toim3232delay = 200; + pr_debug("%s - using %d ms delay\n", + toim3232.driver_name, toim3232delay); + return irda_register_dongle(&toim3232); +} + +static void __exit toim3232_sir_cleanup(void) +{ + irda_unregister_dongle(&toim3232); +} + +static int toim3232_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + /* Pull the lines high to start with. + * + * For the IR320ST-2, we need to charge the main supply capacitor to + * switch the device on. We keep DTR high throughout to do this. + * When RTS, TD and RD are high, they will also trickle-charge the + * cap. RTS is high for data transmission, and low for baud rate select. + * -- DGB + */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* The TOI3232 supports many speeds between 1200bps and 115000bps. + * We really only care about those supported by the IRDA spec, but + * 38400 seems to be implemented in many places */ + qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + + /* From the tekram driver. Not sure what a reasonable value is -- DGB */ + qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int toim3232_close(struct sir_dev *dev) +{ + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function toim3232change_speed (dev, state, speed) + * + * Set the speed for the TOIM3232 based dongle. Warning, this + * function must be called with a process context! + * + * Algorithm + * 1. keep DTR high but clear RTS to bring into baud programming mode + * 2. wait at least 7us to enter programming mode + * 3. send control word to set baud rate and timing + * 4. wait at least 1us + * 5. bring RTS high to enter DATA mode (RS232 is passed through to transceiver) + * 6. should take effect immediately (although probably worth waiting) + */ + +#define TOIM3232_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) + +static int toim3232_change_speed(struct sir_dev *dev, unsigned speed) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + u8 byte; + static int ret = 0; + + switch(state) { + case SIRDEV_STATE_DONGLE_SPEED: + + /* Figure out what we are going to send as a control byte */ + switch (speed) { + case 2400: + byte = TOIM3232_PW|TOIM3232_2400; + break; + default: + speed = 9600; + ret = -EINVAL; + /* fall thru */ + case 9600: + byte = TOIM3232_PW|TOIM3232_9600; + break; + case 19200: + byte = TOIM3232_PW|TOIM3232_19200; + break; + case 38400: + byte = TOIM3232_PW|TOIM3232_38400; + break; + case 57600: + byte = TOIM3232_PW|TOIM3232_57600; + break; + case 115200: + byte = TOIM3232_115200; + break; + } + + /* Set DTR, Clear RTS: Go into baud programming mode */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + + /* Wait at least 7us */ + udelay(14); + + /* Write control byte */ + sirdev_raw_write(dev, &byte, 1); + + dev->speed = speed; + + state = TOIM3232_STATE_WAIT_SPEED; + delay = toim3232delay; + break; + + case TOIM3232_STATE_WAIT_SPEED: + /* Have transmitted control byte * Wait for 'at least 1us' */ + udelay(14); + + /* Set DTR, Set RTS: Go into normal data mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Wait (TODO: check this is needed) */ + udelay(50); + break; + + default: + printk(KERN_ERR "%s - undefined state %d\n", __func__, state); + ret = -EINVAL; + break; + } + + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +/* + * Function toim3232reset (driver) + * + * This function resets the toim3232 dongle. Warning, this function + * must be called with a process context!! + * + * What we should do is: + * 0. Pull RESET high + * 1. Wait for at least 7us + * 2. Pull RESET low + * 3. Wait for at least 7us + * 4. Pull BR/~D high + * 5. Wait for at least 7us + * 6. Send control byte to set baud rate + * 7. Wait at least 1us after stop bit + * 8. Pull BR/~D low + * 9. Should then be in data mode + * + * Because the IR320ST-2 doesn't have the RESET line connected for some reason, + * we'll have to do something else. + * + * The default speed after a RESET is 9600, so lets try just bringing it up in + * data mode after switching it off, waiting for the supply capacitor to + * discharge, and then switch it back on. This isn't actually pulling RESET + * high, but it seems to have the same effect. + * + * This behaviour will probably work on dongles that have the RESET line connected, + * but if not, add a flag for the IR320ST-2, and implment the above-listed proper + * behaviour. + * + * RTS is inverted and then fed to BR/~D, so to put it in programming mode, we + * need to have pull RTS low + */ + +static int toim3232_reset(struct sir_dev *dev) +{ + /* Switch off both DTR and RTS to switch off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + /* Should sleep a while. This might be evil doing it this way.*/ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(50)); + + /* Set DTR, Set RTS (data mode) */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Wait at least 10 ms for power to stabilize again */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(10)); + + /* Speed should now be 9600 */ + dev->speed = 9600; + + return 0; +} + +MODULE_AUTHOR("David Basden "); +MODULE_DESCRIPTION("Vishay/Temic TOIM3232 based dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-12"); /* IRDA_TOIM3232_DONGLE */ + +module_init(toim3232_sir_init); +module_exit(toim3232_sir_cleanup); diff --git a/drivers/staging/irda/drivers/via-ircc.c b/drivers/staging/irda/drivers/via-ircc.c new file mode 100644 index 000000000000..ca4442a9d631 --- /dev/null +++ b/drivers/staging/irda/drivers/via-ircc.c @@ -0,0 +1,1593 @@ +/******************************************************************** + Filename: via-ircc.c + Version: 1.0 + Description: Driver for the VIA VT8231/VT8233 IrDA chipsets + Author: VIA Technologies,inc + Date : 08/06/2003 + +Copyright (c) 1998-2003 VIA Technologies, Inc. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTIES OR REPRESENTATIONS; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, see . + +F01 Oct/02/02: Modify code for V0.11(move out back to back transfer) +F02 Oct/28/02: Add SB device ID for 3147 and 3177. + Comment : + jul/09/2002 : only implement two kind of dongle currently. + Oct/02/2002 : work on VT8231 and VT8233 . + Aug/06/2003 : change driver format to pci driver . + +2004-02-16: +- Removed unneeded 'legacy' pci stuff. +- Make sure SIR mode is set (hw_init()) before calling mode-dependent stuff. +- On speed change from core, don't send SIR frame with new speed. + Use current speed and change speeds later. +- Make module-param dongle_id actually work. +- New dongle_id 17 (0x11): TDFS4500. Single-ended SIR only. + Tested with home-grown PCB on EPIA boards. +- Code cleanup. + + ********************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "via-ircc.h" + +#define VIA_MODULE_NAME "via-ircc" +#define CHIP_IO_EXTENT 0x40 + +static char *driver_name = VIA_MODULE_NAME; + +/* Module parameters */ +static int qos_mtt_bits = 0x07; /* 1 ms or more */ +static int dongle_id = 0; /* default: probe */ + +/* We can't guess the type of connected dongle, user *must* supply it. */ +module_param(dongle_id, int, 0); + +/* Some prototypes */ +static int via_ircc_open(struct pci_dev *pdev, chipio_t *info, + unsigned int id); +static int via_ircc_dma_receive(struct via_ircc_cb *self); +static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, + int iobase); +static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb, + struct net_device *dev); +static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb, + struct net_device *dev); +static void via_hw_init(struct via_ircc_cb *self); +static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 baud); +static irqreturn_t via_ircc_interrupt(int irq, void *dev_id); +static int via_ircc_is_receiving(struct via_ircc_cb *self); +static int via_ircc_read_dongle_id(int iobase); + +static int via_ircc_net_open(struct net_device *dev); +static int via_ircc_net_close(struct net_device *dev); +static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, + int cmd); +static void via_ircc_change_dongle_speed(int iobase, int speed, + int dongle_id); +static int RxTimerHandler(struct via_ircc_cb *self, int iobase); +static void hwreset(struct via_ircc_cb *self); +static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase); +static int upload_rxdata(struct via_ircc_cb *self, int iobase); +static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id); +static void via_remove_one(struct pci_dev *pdev); + +/* FIXME : Should use udelay() instead, even if we are x86 only - Jean II */ +static void iodelay(int udelay) +{ + u8 data; + int i; + + for (i = 0; i < udelay; i++) { + data = inb(0x80); + } +} + +static const struct pci_device_id via_pci_tbl[] = { + { PCI_VENDOR_ID_VIA, 0x8231, PCI_ANY_ID, PCI_ANY_ID,0,0,0 }, + { PCI_VENDOR_ID_VIA, 0x3109, PCI_ANY_ID, PCI_ANY_ID,0,0,1 }, + { PCI_VENDOR_ID_VIA, 0x3074, PCI_ANY_ID, PCI_ANY_ID,0,0,2 }, + { PCI_VENDOR_ID_VIA, 0x3147, PCI_ANY_ID, PCI_ANY_ID,0,0,3 }, + { PCI_VENDOR_ID_VIA, 0x3177, PCI_ANY_ID, PCI_ANY_ID,0,0,4 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci,via_pci_tbl); + + +static struct pci_driver via_driver = { + .name = VIA_MODULE_NAME, + .id_table = via_pci_tbl, + .probe = via_init_one, + .remove = via_remove_one, +}; + + +/* + * Function via_ircc_init () + * + * Initialize chip. Just find out chip type and resource. + */ +static int __init via_ircc_init(void) +{ + int rc; + + rc = pci_register_driver(&via_driver); + if (rc < 0) { + pr_debug("%s(): error rc = %d, returning -ENODEV...\n", + __func__, rc); + return -ENODEV; + } + return 0; +} + +static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id) +{ + int rc; + u8 temp,oldPCI_40,oldPCI_44,bTmp,bTmp1; + u16 Chipset,FirDRQ1,FirDRQ0,FirIRQ,FirIOBase; + chipio_t info; + + pr_debug("%s(): Device ID=(0X%X)\n", __func__, id->device); + + rc = pci_enable_device (pcidev); + if (rc) { + pr_debug("%s(): error rc = %d\n", __func__, rc); + return -ENODEV; + } + + // South Bridge exist + if ( ReadLPCReg(0x20) != 0x3C ) + Chipset=0x3096; + else + Chipset=0x3076; + + if (Chipset==0x3076) { + pr_debug("%s(): Chipset = 3076\n", __func__); + + WriteLPCReg(7,0x0c ); + temp=ReadLPCReg(0x30);//check if BIOS Enable Fir + if((temp&0x01)==1) { // BIOS close or no FIR + WriteLPCReg(0x1d, 0x82 ); + WriteLPCReg(0x23,0x18); + temp=ReadLPCReg(0xF0); + if((temp&0x01)==0) { + temp=(ReadLPCReg(0x74)&0x03); //DMA + FirDRQ0=temp + 4; + temp=(ReadLPCReg(0x74)&0x0C) >> 2; + FirDRQ1=temp + 4; + } else { + temp=(ReadLPCReg(0x74)&0x0C) >> 2; //DMA + FirDRQ0=temp + 4; + FirDRQ1=FirDRQ0; + } + FirIRQ=(ReadLPCReg(0x70)&0x0f); //IRQ + FirIOBase=ReadLPCReg(0x60 ) << 8; //IO Space :high byte + FirIOBase=FirIOBase| ReadLPCReg(0x61) ; //low byte + FirIOBase=FirIOBase ; + info.fir_base=FirIOBase; + info.irq=FirIRQ; + info.dma=FirDRQ1; + info.dma2=FirDRQ0; + pci_read_config_byte(pcidev,0x40,&bTmp); + pci_write_config_byte(pcidev,0x40,((bTmp | 0x08) & 0xfe)); + pci_read_config_byte(pcidev,0x42,&bTmp); + pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0)); + pci_write_config_byte(pcidev,0x5a,0xc0); + WriteLPCReg(0x28, 0x70 ); + rc = via_ircc_open(pcidev, &info, 0x3076); + } else + rc = -ENODEV; //IR not turn on + } else { //Not VT1211 + pr_debug("%s(): Chipset = 3096\n", __func__); + + pci_read_config_byte(pcidev,0x67,&bTmp);//check if BIOS Enable Fir + if((bTmp&0x01)==1) { // BIOS enable FIR + //Enable Double DMA clock + pci_read_config_byte(pcidev,0x42,&oldPCI_40); + pci_write_config_byte(pcidev,0x42,oldPCI_40 | 0x80); + pci_read_config_byte(pcidev,0x40,&oldPCI_40); + pci_write_config_byte(pcidev,0x40,oldPCI_40 & 0xf7); + pci_read_config_byte(pcidev,0x44,&oldPCI_44); + pci_write_config_byte(pcidev,0x44,0x4e); + //---------- read configuration from Function0 of south bridge + if((bTmp&0x02)==0) { + pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA + FirDRQ0 = (bTmp1 & 0x30) >> 4; + pci_read_config_byte(pcidev,0x44,&bTmp1); + FirDRQ1 = (bTmp1 & 0xc0) >> 6; + } else { + pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA + FirDRQ0 = (bTmp1 & 0x30) >> 4 ; + FirDRQ1=0; + } + pci_read_config_byte(pcidev,0x47,&bTmp1); //IRQ + FirIRQ = bTmp1 & 0x0f; + + pci_read_config_byte(pcidev,0x69,&bTmp); + FirIOBase = bTmp << 8;//hight byte + pci_read_config_byte(pcidev,0x68,&bTmp); + FirIOBase = (FirIOBase | bTmp ) & 0xfff0; + //------------------------- + info.fir_base=FirIOBase; + info.irq=FirIRQ; + info.dma=FirDRQ1; + info.dma2=FirDRQ0; + rc = via_ircc_open(pcidev, &info, 0x3096); + } else + rc = -ENODEV; //IR not turn on !!!!! + }//Not VT1211 + + pr_debug("%s(): End - rc = %d\n", __func__, rc); + return rc; +} + +static void __exit via_ircc_cleanup(void) +{ + /* Cleanup all instances of the driver */ + pci_unregister_driver (&via_driver); +} + +static const struct net_device_ops via_ircc_sir_ops = { + .ndo_start_xmit = via_ircc_hard_xmit_sir, + .ndo_open = via_ircc_net_open, + .ndo_stop = via_ircc_net_close, + .ndo_do_ioctl = via_ircc_net_ioctl, +}; +static const struct net_device_ops via_ircc_fir_ops = { + .ndo_start_xmit = via_ircc_hard_xmit_fir, + .ndo_open = via_ircc_net_open, + .ndo_stop = via_ircc_net_close, + .ndo_do_ioctl = via_ircc_net_ioctl, +}; + +/* + * Function via_ircc_open(pdev, iobase, irq) + * + * Open driver instance + * + */ +static int via_ircc_open(struct pci_dev *pdev, chipio_t *info, unsigned int id) +{ + struct net_device *dev; + struct via_ircc_cb *self; + int err; + + /* Allocate new instance of the driver */ + dev = alloc_irdadev(sizeof(struct via_ircc_cb)); + if (dev == NULL) + return -ENOMEM; + + self = netdev_priv(dev); + self->netdev = dev; + spin_lock_init(&self->lock); + + pci_set_drvdata(pdev, self); + + /* Initialize Resource */ + self->io.cfg_base = info->cfg_base; + self->io.fir_base = info->fir_base; + self->io.irq = info->irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = info->dma; + self->io.dma2 = info->dma2; + self->io.fifo_size = 32; + self->chip_id = id; + self->st_fifo.len = 0; + self->RxDataReady = 0; + + /* Reserve the ioports that we need */ + if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) { + pr_debug("%s(), can't get iobase of 0x%03x\n", + __func__, self->io.fir_base); + err = -ENODEV; + goto err_out1; + } + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* Check if user has supplied the dongle id or not */ + if (!dongle_id) + dongle_id = via_ircc_read_dongle_id(self->io.fir_base); + self->io.dongle_id = dongle_id; + + /* The only value we must override it the baudrate */ + /* Maximum speeds and capabilities are dongle-dependent. */ + switch( self->io.dongle_id ){ + case 0x0d: + self->qos.baud_rate.bits = + IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 | + IR_576000 | IR_1152000 | (IR_4000000 << 8); + break; + default: + self->qos.baud_rate.bits = + IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200; + break; + } + + /* Following was used for testing: + * + * self->qos.baud_rate.bits = IR_9600; + * + * Is is no good, as it prohibits (error-prone) speed-changes. + */ + + self->qos.min_turn_time.bits = qos_mtt_bits; + irda_qos_bits_to_value(&self->qos); + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384 + 2048; + self->tx_buff.truesize = 14384 + 2048; + + /* Allocate memory if needed */ + self->rx_buff.head = + dma_zalloc_coherent(&pdev->dev, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + err = -ENOMEM; + goto err_out2; + } + + self->tx_buff.head = + dma_zalloc_coherent(&pdev->dev, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + err = -ENOMEM; + goto err_out3; + } + + self->rx_buff.in_frame = FALSE; + self->rx_buff.state = OUTSIDE_FRAME; + self->tx_buff.data = self->tx_buff.head; + self->rx_buff.data = self->rx_buff.head; + + /* Reset Tx queue info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Override the network functions we need to use */ + dev->netdev_ops = &via_ircc_sir_ops; + + err = register_netdev(dev); + if (err) + goto err_out4; + + net_info_ratelimited("IrDA: Registered device %s (via-ircc)\n", + dev->name); + + /* Initialise the hardware.. + */ + self->io.speed = 9600; + via_hw_init(self); + return 0; + err_out4: + dma_free_coherent(&pdev->dev, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + err_out3: + dma_free_coherent(&pdev->dev, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + err_out2: + release_region(self->io.fir_base, self->io.fir_ext); + err_out1: + free_netdev(dev); + return err; +} + +/* + * Function via_remove_one(pdev) + * + * Close driver instance + * + */ +static void via_remove_one(struct pci_dev *pdev) +{ + struct via_ircc_cb *self = pci_get_drvdata(pdev); + int iobase; + + iobase = self->io.fir_base; + + ResetChip(iobase, 5); //hardware reset. + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the PORT that this driver is using */ + pr_debug("%s(), Releasing Region %03x\n", + __func__, self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + if (self->tx_buff.head) + dma_free_coherent(&pdev->dev, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + if (self->rx_buff.head) + dma_free_coherent(&pdev->dev, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + free_netdev(self->netdev); + + pci_disable_device(pdev); +} + +/* + * Function via_hw_init(self) + * + * Returns non-negative on success. + * + * Formerly via_ircc_setup + */ +static void via_hw_init(struct via_ircc_cb *self) +{ + int iobase = self->io.fir_base; + + SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095 + // FIFO Init + EnRXFIFOReadyInt(iobase, OFF); + EnRXFIFOHalfLevelInt(iobase, OFF); + EnTXFIFOHalfLevelInt(iobase, OFF); + EnTXFIFOUnderrunEOMInt(iobase, ON); + EnTXFIFOReadyInt(iobase, OFF); + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); + + if (ReadLPCReg(0x20) == 0x3c) + WriteLPCReg(0xF0, 0); // for VT1211 + /* Int Init */ + EnRXSpecInt(iobase, ON); + + /* The following is basically hwreset */ + /* If this is the case, why not just call hwreset() ? Jean II */ + ResetChip(iobase, 5); + EnableDMA(iobase, OFF); + EnableTX(iobase, OFF); + EnableRX(iobase, OFF); + EnRXDMA(iobase, OFF); + EnTXDMA(iobase, OFF); + RXStart(iobase, OFF); + TXStart(iobase, OFF); + InitCard(iobase); + CommonInit(iobase); + SIRFilter(iobase, ON); + SetSIR(iobase, ON); + CRC16(iobase, ON); + EnTXCRC(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x00); + SetBaudRate(iobase, 9600); + SetPulseWidth(iobase, 12); + SetSendPreambleCount(iobase, 0); + + self->io.speed = 9600; + self->st_fifo.len = 0; + + via_ircc_change_dongle_speed(iobase, self->io.speed, + self->io.dongle_id); + + WriteReg(iobase, I_ST_CT_0, 0x80); +} + +/* + * Function via_ircc_read_dongle_id (void) + * + */ +static int via_ircc_read_dongle_id(int iobase) +{ + net_err_ratelimited("via-ircc: dongle probing not supported, please specify dongle_id module parameter\n"); + return 9; /* Default to IBM */ +} + +/* + * Function via_ircc_change_dongle_speed (iobase, speed, dongle_id) + * Change speed of the attach dongle + * only implement two type of dongle currently. + */ +static void via_ircc_change_dongle_speed(int iobase, int speed, + int dongle_id) +{ + u8 mode = 0; + + /* speed is unused, as we use IsSIROn()/IsMIROn() */ + speed = speed; + + pr_debug("%s(): change_dongle_speed to %d for 0x%x, %d\n", + __func__, speed, iobase, dongle_id); + + switch (dongle_id) { + + /* Note: The dongle_id's listed here are derived from + * nsc-ircc.c */ + + case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ + UseOneRX(iobase, ON); // use one RX pin RX1,RX2 + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); + + EnRX2(iobase, ON); //sir to rx2 + EnGPIOtoRX2(iobase, OFF); + + if (IsSIROn(iobase)) { //sir + // Mode select Off + SlowIRRXLowActive(iobase, ON); + udelay(1000); + SlowIRRXLowActive(iobase, OFF); + } else { + if (IsMIROn(iobase)) { //mir + // Mode select On + SlowIRRXLowActive(iobase, OFF); + udelay(20); + } else { // fir + if (IsFIROn(iobase)) { //fir + // Mode select On + SlowIRRXLowActive(iobase, OFF); + udelay(20); + } + } + } + break; + + case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ + UseOneRX(iobase, ON); //use ONE RX....RX1 + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); // invert RX pin + + EnRX2(iobase, ON); + EnGPIOtoRX2(iobase, OFF); + if (IsSIROn(iobase)) { //sir + // Mode select On + SlowIRRXLowActive(iobase, ON); + udelay(20); + // Mode select Off + SlowIRRXLowActive(iobase, OFF); + } + if (IsMIROn(iobase)) { //mir + // Mode select On + SlowIRRXLowActive(iobase, OFF); + udelay(20); + // Mode select Off + SlowIRRXLowActive(iobase, ON); + } else { // fir + if (IsFIROn(iobase)) { //fir + // Mode select On + SlowIRRXLowActive(iobase, OFF); + // TX On + WriteTX(iobase, ON); + udelay(20); + // Mode select OFF + SlowIRRXLowActive(iobase, ON); + udelay(20); + // TX Off + WriteTX(iobase, OFF); + } + } + break; + + case 0x0d: + UseOneRX(iobase, OFF); // use two RX pin RX1,RX2 + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); + SlowIRRXLowActive(iobase, OFF); + if (IsSIROn(iobase)) { //sir + EnGPIOtoRX2(iobase, OFF); + WriteGIO(iobase, OFF); + EnRX2(iobase, OFF); //sir to rx2 + } else { // fir mir + EnGPIOtoRX2(iobase, OFF); + WriteGIO(iobase, OFF); + EnRX2(iobase, OFF); //fir to rx + } + break; + + case 0x11: /* Temic TFDS4500 */ + + pr_debug("%s: Temic TFDS4500: One RX pin, TX normal, RX inverted\n", + __func__); + + UseOneRX(iobase, ON); //use ONE RX....RX1 + InvertTX(iobase, OFF); + InvertRX(iobase, ON); // invert RX pin + + EnRX2(iobase, ON); //sir to rx2 + EnGPIOtoRX2(iobase, OFF); + + if( IsSIROn(iobase) ){ //sir + + // Mode select On + SlowIRRXLowActive(iobase, ON); + udelay(20); + // Mode select Off + SlowIRRXLowActive(iobase, OFF); + + } else{ + pr_debug("%s: Warning: TFDS4500 not running in SIR mode !\n", + __func__); + } + break; + + case 0x0ff: /* Vishay */ + if (IsSIROn(iobase)) + mode = 0; + else if (IsMIROn(iobase)) + mode = 1; + else if (IsFIROn(iobase)) + mode = 2; + else if (IsVFIROn(iobase)) + mode = 5; //VFIR-16 + SI_SetMode(iobase, mode); + break; + + default: + net_err_ratelimited("%s: Error: dongle_id %d unsupported !\n", + __func__, dongle_id); + } +} + +/* + * Function via_ircc_change_speed (self, baud) + * + * Change the speed of the device + * + */ +static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 speed) +{ + struct net_device *dev = self->netdev; + u16 iobase; + u8 value = 0, bTmp; + + iobase = self->io.fir_base; + /* Update accounting for new speed */ + self->io.speed = speed; + pr_debug("%s: change_speed to %d bps.\n", __func__, speed); + + WriteReg(iobase, I_ST_CT_0, 0x0); + + /* Controller mode sellection */ + switch (speed) { + case 2400: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + value = (115200/speed)-1; + SetSIR(iobase, ON); + CRC16(iobase, ON); + break; + case 576000: + /* FIXME: this can't be right, as it's the same as 115200, + * and 576000 is MIR, not SIR. */ + value = 0; + SetSIR(iobase, ON); + CRC16(iobase, ON); + break; + case 1152000: + value = 0; + SetMIR(iobase, ON); + /* FIXME: CRC ??? */ + break; + case 4000000: + value = 0; + SetFIR(iobase, ON); + SetPulseWidth(iobase, 0); + SetSendPreambleCount(iobase, 14); + CRC16(iobase, OFF); + EnTXCRC(iobase, ON); + break; + case 16000000: + value = 0; + SetVFIR(iobase, ON); + /* FIXME: CRC ??? */ + break; + default: + value = 0; + break; + } + + /* Set baudrate to 0x19[2..7] */ + bTmp = (ReadReg(iobase, I_CF_H_1) & 0x03); + bTmp |= value << 2; + WriteReg(iobase, I_CF_H_1, bTmp); + + /* Some dongles may need to be informed about speed changes. */ + via_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); + + /* Set FIFO size to 64 */ + SetFIFO(iobase, 64); + + /* Enable IR */ + WriteReg(iobase, I_ST_CT_0, 0x80); + + // EnTXFIFOHalfLevelInt(iobase,ON); + + /* Enable some interrupts so we can receive frames */ + //EnAllInt(iobase,ON); + + if (IsSIROn(iobase)) { + SIRFilter(iobase, ON); + SIRRecvAny(iobase, ON); + } else { + SIRFilter(iobase, OFF); + SIRRecvAny(iobase, OFF); + } + + if (speed > 115200) { + /* Install FIR xmit handler */ + dev->netdev_ops = &via_ircc_fir_ops; + via_ircc_dma_receive(self); + } else { + /* Install SIR xmit handler */ + dev->netdev_ops = &via_ircc_sir_ops; + } + netif_wake_queue(dev); +} + +/* + * Function via_ircc_hard_xmit (skb, dev) + * + * Transmit the frame! + * + */ +static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb, + struct net_device *dev) +{ + struct via_ircc_cb *self; + unsigned long flags; + u16 iobase; + __u32 speed; + + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); + iobase = self->io.fir_base; + + netif_stop_queue(dev); + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame */ + if (!skb->len) { + via_ircc_change_speed(self, speed); + netif_trans_update(dev); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } else + self->new_speed = speed; + } + InitCard(iobase); + CommonInit(iobase); + SIRFilter(iobase, ON); + SetSIR(iobase, ON); + CRC16(iobase, ON); + EnTXCRC(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x00); + + spin_lock_irqsave(&self->lock, flags); + self->tx_buff.data = self->tx_buff.head; + self->tx_buff.len = + async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + dev->stats.tx_bytes += self->tx_buff.len; + /* Send this frame with old speed */ + SetBaudRate(iobase, self->io.speed); + SetPulseWidth(iobase, 12); + SetSendPreambleCount(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x80); + + EnableTX(iobase, ON); + EnableRX(iobase, OFF); + + ResetChip(iobase, 0); + ResetChip(iobase, 1); + ResetChip(iobase, 2); + ResetChip(iobase, 3); + ResetChip(iobase, 4); + + EnAllInt(iobase, ON); + EnTXDMA(iobase, ON); + EnRXDMA(iobase, OFF); + + irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, + DMA_TX_MODE); + + SetSendByte(iobase, self->tx_buff.len); + RXStart(iobase, OFF); + TXStart(iobase, ON); + + netif_trans_update(dev); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb, + struct net_device *dev) +{ + struct via_ircc_cb *self; + u16 iobase; + __u32 speed; + unsigned long flags; + + self = netdev_priv(dev); + iobase = self->io.fir_base; + + if (self->st_fifo.len) + return NETDEV_TX_OK; + if (self->chip_id == 0x3076) + iodelay(1500); + else + udelay(1500); + netif_stop_queue(dev); + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + if (!skb->len) { + via_ircc_change_speed(self, speed); + netif_trans_update(dev); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } else + self->new_speed = speed; + } + spin_lock_irqsave(&self->lock, flags); + self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; + self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; + + self->tx_fifo.tail += skb->len; + dev->stats.tx_bytes += skb->len; + skb_copy_from_linear_data(skb, + self->tx_fifo.queue[self->tx_fifo.free].start, skb->len); + self->tx_fifo.len++; + self->tx_fifo.free++; +//F01 if (self->tx_fifo.len == 1) { + via_ircc_dma_xmit(self, iobase); +//F01 } +//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) netif_wake_queue(self->netdev); + netif_trans_update(dev); + dev_kfree_skb(skb); + spin_unlock_irqrestore(&self->lock, flags); + return NETDEV_TX_OK; + +} + +static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase) +{ + EnTXDMA(iobase, OFF); + self->io.direction = IO_XMIT; + EnPhys(iobase, ON); + EnableTX(iobase, ON); + EnableRX(iobase, OFF); + ResetChip(iobase, 0); + ResetChip(iobase, 1); + ResetChip(iobase, 2); + ResetChip(iobase, 3); + ResetChip(iobase, 4); + EnAllInt(iobase, ON); + EnTXDMA(iobase, ON); + EnRXDMA(iobase, OFF); + irda_setup_dma(self->io.dma, + ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - + self->tx_buff.head) + self->tx_buff_dma, + self->tx_fifo.queue[self->tx_fifo.ptr].len, DMA_TX_MODE); + pr_debug("%s: tx_fifo.ptr=%x,len=%x,tx_fifo.len=%x..\n", + __func__, self->tx_fifo.ptr, + self->tx_fifo.queue[self->tx_fifo.ptr].len, + self->tx_fifo.len); + + SetSendByte(iobase, self->tx_fifo.queue[self->tx_fifo.ptr].len); + RXStart(iobase, OFF); + TXStart(iobase, ON); + return 0; + +} + +/* + * Function via_ircc_dma_xmit_complete (self) + * + * The transfer of a frame in finished. This function will only be called + * by the interrupt handler + * + */ +static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self) +{ + int iobase; + u8 Tx_status; + + iobase = self->io.fir_base; + /* Disable DMA */ +// DisableDmaChannel(self->io.dma); + /* Check for underrun! */ + /* Clear bit, by writing 1 into it */ + Tx_status = GetTXStatus(iobase); + if (Tx_status & 0x08) { + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; + hwreset(self); + /* how to clear underrun? */ + } else { + self->netdev->stats.tx_packets++; + ResetChip(iobase, 3); + ResetChip(iobase, 4); + } + /* Check if we need to change the speed */ + if (self->new_speed) { + via_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Finished with this frame, so prepare for next */ + if (IsFIROn(iobase)) { + if (self->tx_fifo.len) { + self->tx_fifo.len--; + self->tx_fifo.ptr++; + } + } + pr_debug("%s: tx_fifo.len=%x ,tx_fifo.ptr=%x,tx_fifo.free=%x...\n", + __func__, + self->tx_fifo.len, self->tx_fifo.ptr, self->tx_fifo.free); +/* F01_S + // Any frames to be sent back-to-back? + if (self->tx_fifo.len) { + // Not finished yet! + via_ircc_dma_xmit(self, iobase); + ret = FALSE; + } else { +F01_E*/ + // Reset Tx FIFO info + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; +//F01 } + + // Make sure we have room for more frames +//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) { + // Not busy transmitting anymore + // Tell the network layer, that we can accept more frames + netif_wake_queue(self->netdev); +//F01 } + return TRUE; +} + +/* + * Function via_ircc_dma_receive (self) + * + * Set configuration for receive a frame. + * + */ +static int via_ircc_dma_receive(struct via_ircc_cb *self) +{ + int iobase; + + iobase = self->io.fir_base; + + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + self->RxDataReady = 0; + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + self->st_fifo.len = self->st_fifo.pending_bytes = 0; + self->st_fifo.tail = self->st_fifo.head = 0; + + EnPhys(iobase, ON); + EnableTX(iobase, OFF); + EnableRX(iobase, ON); + + ResetChip(iobase, 0); + ResetChip(iobase, 1); + ResetChip(iobase, 2); + ResetChip(iobase, 3); + ResetChip(iobase, 4); + + EnAllInt(iobase, ON); + EnTXDMA(iobase, OFF); + EnRXDMA(iobase, ON); + irda_setup_dma(self->io.dma2, self->rx_buff_dma, + self->rx_buff.truesize, DMA_RX_MODE); + TXStart(iobase, OFF); + RXStart(iobase, ON); + + return 0; +} + +/* + * Function via_ircc_dma_receive_complete (self) + * + * Controller Finished with receiving frames, + * and this routine is call by ISR + * + */ +static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, + int iobase) +{ + struct st_fifo *st_fifo; + struct sk_buff *skb; + int len, i; + u8 status = 0; + + iobase = self->io.fir_base; + st_fifo = &self->st_fifo; + + if (self->io.speed < 4000000) { //Speed below FIR + len = GetRecvByte(iobase, self); + skb = dev_alloc_skb(len + 1); + if (skb == NULL) + return FALSE; + // Make sure IP header gets aligned + skb_reserve(skb, 1); + skb_put(skb, len - 2); + if (self->chip_id == 0x3076) { + for (i = 0; i < len - 2; i++) + skb->data[i] = self->rx_buff.data[i * 2]; + } else { + if (self->chip_id == 0x3096) { + for (i = 0; i < len - 2; i++) + skb->data[i] = + self->rx_buff.data[i]; + } + } + // Move to next frame + self->rx_buff.data += len; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + return TRUE; + } + + else { //FIR mode + len = GetRecvByte(iobase, self); + if (len == 0) + return TRUE; //interrupt only, data maybe move by RxT + if (((len - 4) < 2) || ((len - 4) > 2048)) { + pr_debug("%s(): Trouble:len=%x,CurCount=%x,LastCount=%x\n", + __func__, len, RxCurCount(iobase, self), + self->RxLastCount); + hwreset(self); + return FALSE; + } + pr_debug("%s(): fifo.len=%x,len=%x,CurCount=%x..\n", + __func__, + st_fifo->len, len - 4, RxCurCount(iobase, self)); + + st_fifo->entries[st_fifo->tail].status = status; + st_fifo->entries[st_fifo->tail].len = len; + st_fifo->pending_bytes += len; + st_fifo->tail++; + st_fifo->len++; + if (st_fifo->tail > MAX_RX_WINDOW) + st_fifo->tail = 0; + self->RxDataReady = 0; + + // It maybe have MAX_RX_WINDOW package receive by + // receive_complete before Timer IRQ +/* F01_S + if (st_fifo->len < (MAX_RX_WINDOW+2 )) { + RXStart(iobase,ON); + SetTimer(iobase,4); + } + else { +F01_E */ + EnableRX(iobase, OFF); + EnRXDMA(iobase, OFF); + RXStart(iobase, OFF); +//F01_S + // Put this entry back in fifo + if (st_fifo->head > MAX_RX_WINDOW) + st_fifo->head = 0; + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->head++; + st_fifo->len--; + + skb = dev_alloc_skb(len + 1 - 4); + /* + * if frame size, data ptr, or skb ptr are wrong, then get next + * entry. + */ + if ((skb == NULL) || (skb->data == NULL) || + (self->rx_buff.data == NULL) || (len < 6)) { + self->netdev->stats.rx_dropped++; + kfree_skb(skb); + return TRUE; + } + skb_reserve(skb, 1); + skb_put(skb, len - 4); + + skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); + pr_debug("%s(): len=%x.rx_buff=%p\n", __func__, + len - 4, self->rx_buff.data); + + // Move to next frame + self->rx_buff.data += len; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + +//F01_E + } //FIR + return TRUE; + +} + +/* + * if frame is received , but no INT ,then use this routine to upload frame. + */ +static int upload_rxdata(struct via_ircc_cb *self, int iobase) +{ + struct sk_buff *skb; + int len; + struct st_fifo *st_fifo; + st_fifo = &self->st_fifo; + + len = GetRecvByte(iobase, self); + + pr_debug("%s(): len=%x\n", __func__, len); + + if ((len - 4) < 2) { + self->netdev->stats.rx_dropped++; + return FALSE; + } + + skb = dev_alloc_skb(len + 1); + if (skb == NULL) { + self->netdev->stats.rx_dropped++; + return FALSE; + } + skb_reserve(skb, 1); + skb_put(skb, len - 4 + 1); + skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4 + 1); + st_fifo->tail++; + st_fifo->len++; + if (st_fifo->tail > MAX_RX_WINDOW) + st_fifo->tail = 0; + // Move to next frame + self->rx_buff.data += len; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + if (st_fifo->len < (MAX_RX_WINDOW + 2)) { + RXStart(iobase, ON); + } else { + EnableRX(iobase, OFF); + EnRXDMA(iobase, OFF); + RXStart(iobase, OFF); + } + return TRUE; +} + +/* + * Implement back to back receive , use this routine to upload data. + */ + +static int RxTimerHandler(struct via_ircc_cb *self, int iobase) +{ + struct st_fifo *st_fifo; + struct sk_buff *skb; + int len; + u8 status; + + st_fifo = &self->st_fifo; + + if (CkRxRecv(iobase, self)) { + // if still receiving ,then return ,don't upload frame + self->RetryCount = 0; + SetTimer(iobase, 20); + self->RxDataReady++; + return FALSE; + } else + self->RetryCount++; + + if ((self->RetryCount >= 1) || + ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize) || + (st_fifo->len >= (MAX_RX_WINDOW))) { + while (st_fifo->len > 0) { //upload frame + // Put this entry back in fifo + if (st_fifo->head > MAX_RX_WINDOW) + st_fifo->head = 0; + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->head++; + st_fifo->len--; + + skb = dev_alloc_skb(len + 1 - 4); + /* + * if frame size, data ptr, or skb ptr are wrong, + * then get next entry. + */ + if ((skb == NULL) || (skb->data == NULL) || + (self->rx_buff.data == NULL) || (len < 6)) { + self->netdev->stats.rx_dropped++; + continue; + } + skb_reserve(skb, 1); + skb_put(skb, len - 4); + skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); + + pr_debug("%s(): len=%x.head=%x\n", __func__, + len - 4, st_fifo->head); + + // Move to next frame + self->rx_buff.data += len; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + } //while + self->RetryCount = 0; + + pr_debug("%s(): End of upload HostStatus=%x,RxStatus=%x\n", + __func__, GetHostStatus(iobase), GetRXStatus(iobase)); + + /* + * if frame is receive complete at this routine ,then upload + * frame. + */ + if ((GetRXStatus(iobase) & 0x10) && + (RxCurCount(iobase, self) != self->RxLastCount)) { + upload_rxdata(self, iobase); + if (irda_device_txqueue_empty(self->netdev)) + via_ircc_dma_receive(self); + } + } // timer detect complete + else + SetTimer(iobase, 4); + return TRUE; + +} + + + +/* + * Function via_ircc_interrupt (irq, dev_id) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t via_ircc_interrupt(int dummy, void *dev_id) +{ + struct net_device *dev = dev_id; + struct via_ircc_cb *self = netdev_priv(dev); + int iobase; + u8 iHostIntType, iRxIntType, iTxIntType; + + iobase = self->io.fir_base; + spin_lock(&self->lock); + iHostIntType = GetHostStatus(iobase); + + pr_debug("%s(): iHostIntType %02x: %s %s %s %02x\n", + __func__, iHostIntType, + (iHostIntType & 0x40) ? "Timer" : "", + (iHostIntType & 0x20) ? "Tx" : "", + (iHostIntType & 0x10) ? "Rx" : "", + (iHostIntType & 0x0e) >> 1); + + if ((iHostIntType & 0x40) != 0) { //Timer Event + self->EventFlag.TimeOut++; + ClearTimerInt(iobase, 1); + if (self->io.direction == IO_XMIT) { + via_ircc_dma_xmit(self, iobase); + } + if (self->io.direction == IO_RECV) { + /* + * frame ready hold too long, must reset. + */ + if (self->RxDataReady > 30) { + hwreset(self); + if (irda_device_txqueue_empty(self->netdev)) { + via_ircc_dma_receive(self); + } + } else { // call this to upload frame. + RxTimerHandler(self, iobase); + } + } //RECV + } //Timer Event + if ((iHostIntType & 0x20) != 0) { //Tx Event + iTxIntType = GetTXStatus(iobase); + + pr_debug("%s(): iTxIntType %02x: %s %s %s %s\n", + __func__, iTxIntType, + (iTxIntType & 0x08) ? "FIFO underr." : "", + (iTxIntType & 0x04) ? "EOM" : "", + (iTxIntType & 0x02) ? "FIFO ready" : "", + (iTxIntType & 0x01) ? "Early EOM" : ""); + + if (iTxIntType & 0x4) { + self->EventFlag.EOMessage++; // read and will auto clean + if (via_ircc_dma_xmit_complete(self)) { + if (irda_device_txqueue_empty + (self->netdev)) { + via_ircc_dma_receive(self); + } + } else { + self->EventFlag.Unknown++; + } + } //EOP + } //Tx Event + //---------------------------------------- + if ((iHostIntType & 0x10) != 0) { //Rx Event + /* Check if DMA has finished */ + iRxIntType = GetRXStatus(iobase); + + pr_debug("%s(): iRxIntType %02x: %s %s %s %s %s %s %s\n", + __func__, iRxIntType, + (iRxIntType & 0x80) ? "PHY err." : "", + (iRxIntType & 0x40) ? "CRC err" : "", + (iRxIntType & 0x20) ? "FIFO overr." : "", + (iRxIntType & 0x10) ? "EOF" : "", + (iRxIntType & 0x08) ? "RxData" : "", + (iRxIntType & 0x02) ? "RxMaxLen" : "", + (iRxIntType & 0x01) ? "SIR bad" : ""); + if (!iRxIntType) + pr_debug("%s(): RxIRQ =0\n", __func__); + + if (iRxIntType & 0x10) { + if (via_ircc_dma_receive_complete(self, iobase)) { +//F01 if(!(IsFIROn(iobase))) via_ircc_dma_receive(self); + via_ircc_dma_receive(self); + } + } // No ERR + else { //ERR + pr_debug("%s(): RxIRQ ERR:iRxIntType=%x,HostIntType=%x,CurCount=%x,RxLastCount=%x_____\n", + __func__, iRxIntType, iHostIntType, + RxCurCount(iobase, self), self->RxLastCount); + + if (iRxIntType & 0x20) { //FIFO OverRun ERR + ResetChip(iobase, 0); + ResetChip(iobase, 1); + } else { //PHY,CRC ERR + + if (iRxIntType != 0x08) + hwreset(self); //F01 + } + via_ircc_dma_receive(self); + } //ERR + + } //Rx Event + spin_unlock(&self->lock); + return IRQ_RETVAL(iHostIntType); +} + +static void hwreset(struct via_ircc_cb *self) +{ + int iobase; + iobase = self->io.fir_base; + + ResetChip(iobase, 5); + EnableDMA(iobase, OFF); + EnableTX(iobase, OFF); + EnableRX(iobase, OFF); + EnRXDMA(iobase, OFF); + EnTXDMA(iobase, OFF); + RXStart(iobase, OFF); + TXStart(iobase, OFF); + InitCard(iobase); + CommonInit(iobase); + SIRFilter(iobase, ON); + SetSIR(iobase, ON); + CRC16(iobase, ON); + EnTXCRC(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x00); + SetBaudRate(iobase, 9600); + SetPulseWidth(iobase, 12); + SetSendPreambleCount(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x80); + + /* Restore speed. */ + via_ircc_change_speed(self, self->io.speed); + + self->st_fifo.len = 0; +} + +/* + * Function via_ircc_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int via_ircc_is_receiving(struct via_ircc_cb *self) +{ + int status = FALSE; + int iobase; + + IRDA_ASSERT(self != NULL, return FALSE;); + + iobase = self->io.fir_base; + if (CkRxRecv(iobase, self)) + status = TRUE; + + pr_debug("%s(): status=%x....\n", __func__, status); + + return status; +} + + +/* + * Function via_ircc_net_open (dev) + * + * Start the device + * + */ +static int via_ircc_net_open(struct net_device *dev) +{ + struct via_ircc_cb *self; + int iobase; + char hwname[32]; + + IRDA_ASSERT(dev != NULL, return -1;); + self = netdev_priv(dev); + dev->stats.rx_packets = 0; + IRDA_ASSERT(self != NULL, return 0;); + iobase = self->io.fir_base; + if (request_irq(self->io.irq, via_ircc_interrupt, 0, dev->name, dev)) { + net_warn_ratelimited("%s, unable to allocate irq=%d\n", + driver_name, self->io.irq); + return -EAGAIN; + } + /* + * Always allocate the DMA channel after the IRQ, and clean up on + * failure. + */ + if (request_dma(self->io.dma, dev->name)) { + net_warn_ratelimited("%s, unable to allocate dma=%d\n", + driver_name, self->io.dma); + free_irq(self->io.irq, dev); + return -EAGAIN; + } + if (self->io.dma2 != self->io.dma) { + if (request_dma(self->io.dma2, dev->name)) { + net_warn_ratelimited("%s, unable to allocate dma2=%d\n", + driver_name, self->io.dma2); + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + return -EAGAIN; + } + } + + + /* turn on interrupts */ + EnAllInt(iobase, ON); + EnInternalLoop(iobase, OFF); + EnExternalLoop(iobase, OFF); + + /* */ + via_ircc_dma_receive(self); + + /* Ready to play! */ + netif_start_queue(dev); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + sprintf(hwname, "VIA @ 0x%x", iobase); + self->irlap = irlap_open(dev, &self->qos, hwname); + + self->RxLastCount = 0; + + return 0; +} + +/* + * Function via_ircc_net_close (dev) + * + * Stop the device + * + */ +static int via_ircc_net_close(struct net_device *dev) +{ + struct via_ircc_cb *self; + int iobase; + + IRDA_ASSERT(dev != NULL, return -1;); + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return 0;); + + /* Stop device */ + netif_stop_queue(dev); + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + iobase = self->io.fir_base; + EnTXDMA(iobase, OFF); + EnRXDMA(iobase, OFF); + DisableDmaChannel(self->io.dma); + + /* Disable interrupts */ + EnAllInt(iobase, OFF); + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + if (self->io.dma2 != self->io.dma) + free_dma(self->io.dma2); + + return 0; +} + +/* + * Function via_ircc_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, + int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct via_ircc_cb *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + self = netdev_priv(dev); + IRDA_ASSERT(self != NULL, return -1;); + pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, + cmd); + /* Disable interrupts & save flags */ + spin_lock_irqsave(&self->lock, flags); + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + via_ircc_change_speed(self, irq->ifr_baudrate); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = via_ircc_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + out: + spin_unlock_irqrestore(&self->lock, flags); + return ret; +} + +MODULE_AUTHOR("VIA Technologies,inc"); +MODULE_DESCRIPTION("VIA IrDA Device Driver"); +MODULE_LICENSE("GPL"); + +module_init(via_ircc_init); +module_exit(via_ircc_cleanup); diff --git a/drivers/staging/irda/drivers/via-ircc.h b/drivers/staging/irda/drivers/via-ircc.h new file mode 100644 index 000000000000..ac1525573398 --- /dev/null +++ b/drivers/staging/irda/drivers/via-ircc.h @@ -0,0 +1,846 @@ +/********************************************************************* + * + * Filename: via-ircc.h + * Version: 1.0 + * Description: Driver for the VIA VT8231/VT8233 IrDA chipsets + * Author: VIA Technologies, inc + * Date : 08/06/2003 + +Copyright (c) 1998-2003 VIA Technologies, Inc. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTIES OR REPRESENTATIONS; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, see . + + * Comment: + * jul/08/2002 : Rx buffer length should use Rx ring ptr. + * Oct/28/2002 : Add SB id for 3147 and 3177. + * jul/09/2002 : only implement two kind of dongle currently. + * Oct/02/2002 : work on VT8231 and VT8233 . + * Aug/06/2003 : change driver format to pci driver . + ********************************************************************/ +#ifndef via_IRCC_H +#define via_IRCC_H +#include +#include +#include +#include + +#define MAX_TX_WINDOW 7 +#define MAX_RX_WINDOW 7 + +struct st_fifo_entry { + int status; + int len; +}; + +struct st_fifo { + struct st_fifo_entry entries[MAX_RX_WINDOW + 2]; + int pending_bytes; + int head; + int tail; + int len; +}; + +struct frame_cb { + void *start; /* Start of frame in DMA mem */ + int len; /* Length of frame in DMA mem */ +}; + +struct tx_fifo { + struct frame_cb queue[MAX_TX_WINDOW + 2]; /* Info about frames in queue */ + int ptr; /* Currently being sent */ + int len; /* Length of queue */ + int free; /* Next free slot */ + void *tail; /* Next free start in DMA mem */ +}; + + +struct eventflag // for keeping track of Interrupt Events +{ + //--------tx part + unsigned char TxFIFOUnderRun; + unsigned char EOMessage; + unsigned char TxFIFOReady; + unsigned char EarlyEOM; + //--------rx part + unsigned char PHYErr; + unsigned char CRCErr; + unsigned char RxFIFOOverRun; + unsigned char EOPacket; + unsigned char RxAvail; + unsigned char TooLargePacket; + unsigned char SIRBad; + //--------unknown + unsigned char Unknown; + //---------- + unsigned char TimeOut; + unsigned char RxDMATC; + unsigned char TxDMATC; +}; + +/* Private data for each instance */ +struct via_ircc_cb { + struct st_fifo st_fifo; /* Info about received frames */ + struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ + + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct qos_info qos; /* QoS capabilities for this device */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + __u8 ier; /* Interrupt enable register */ + + spinlock_t lock; /* For serializing operations */ + + __u32 flags; /* Interface flags */ + __u32 new_speed; + int index; /* Instance index */ + + struct eventflag EventFlag; + unsigned int chip_id; /* to remember chip id */ + unsigned int RetryCount; + unsigned int RxDataReady; + unsigned int RxLastCount; +}; + + +//---------I=Infrared, H=Host, M=Misc, T=Tx, R=Rx, ST=Status, +// CF=Config, CT=Control, L=Low, H=High, C=Count +#define I_CF_L_0 0x10 +#define I_CF_H_0 0x11 +#define I_SIR_BOF 0x12 +#define I_SIR_EOF 0x13 +#define I_ST_CT_0 0x15 +#define I_ST_L_1 0x16 +#define I_ST_H_1 0x17 +#define I_CF_L_1 0x18 +#define I_CF_H_1 0x19 +#define I_CF_L_2 0x1a +#define I_CF_H_2 0x1b +#define I_CF_3 0x1e +#define H_CT 0x20 +#define H_ST 0x21 +#define M_CT 0x22 +#define TX_CT_1 0x23 +#define TX_CT_2 0x24 +#define TX_ST 0x25 +#define RX_CT 0x26 +#define RX_ST 0x27 +#define RESET 0x28 +#define P_ADDR 0x29 +#define RX_C_L 0x2a +#define RX_C_H 0x2b +#define RX_P_L 0x2c +#define RX_P_H 0x2d +#define TX_C_L 0x2e +#define TX_C_H 0x2f +#define TIMER 0x32 +#define I_CF_4 0x33 +#define I_T_C_L 0x34 +#define I_T_C_H 0x35 +#define VERSION 0x3f +//------------------------------- +#define StartAddr 0x10 // the first register address +#define EndAddr 0x3f // the last register address +#define GetBit(val,bit) val = (unsigned char) ((val>>bit) & 0x1) + // Returns the bit +#define SetBit(val,bit) val= (unsigned char ) (val | (0x1 << bit)) + // Sets bit to 1 +#define ResetBit(val,bit) val= (unsigned char ) (val & ~(0x1 << bit)) + // Sets bit to 0 + +#define OFF 0 +#define ON 1 +#define DMA_TX_MODE 0x08 +#define DMA_RX_MODE 0x04 + +#define DMA1 0 +#define DMA2 0xc0 +#define MASK1 DMA1+0x0a +#define MASK2 DMA2+0x14 + +#define Clk_bit 0x40 +#define Tx_bit 0x01 +#define Rd_Valid 0x08 +#define RxBit 0x08 + +static void DisableDmaChannel(unsigned int channel) +{ + switch (channel) { // 8 Bit DMA channels DMAC1 + case 0: + outb(4, MASK1); //mask channel 0 + break; + case 1: + outb(5, MASK1); //Mask channel 1 + break; + case 2: + outb(6, MASK1); //Mask channel 2 + break; + case 3: + outb(7, MASK1); //Mask channel 3 + break; + case 5: + outb(5, MASK2); //Mask channel 5 + break; + case 6: + outb(6, MASK2); //Mask channel 6 + break; + case 7: + outb(7, MASK2); //Mask channel 7 + break; + default: + break; + } +} + +static unsigned char ReadLPCReg(int iRegNum) +{ + unsigned char iVal; + + outb(0x87, 0x2e); + outb(0x87, 0x2e); + outb(iRegNum, 0x2e); + iVal = inb(0x2f); + outb(0xaa, 0x2e); + + return iVal; +} + +static void WriteLPCReg(int iRegNum, unsigned char iVal) +{ + + outb(0x87, 0x2e); + outb(0x87, 0x2e); + outb(iRegNum, 0x2e); + outb(iVal, 0x2f); + outb(0xAA, 0x2e); +} + +static __u8 ReadReg(unsigned int BaseAddr, int iRegNum) +{ + return (__u8) inb(BaseAddr + iRegNum); +} + +static void WriteReg(unsigned int BaseAddr, int iRegNum, unsigned char iVal) +{ + outb(iVal, BaseAddr + iRegNum); +} + +static int WriteRegBit(unsigned int BaseAddr, unsigned char RegNum, + unsigned char BitPos, unsigned char value) +{ + __u8 Rtemp, Wtemp; + + if (BitPos > 7) { + return -1; + } + if ((RegNum < StartAddr) || (RegNum > EndAddr)) + return -1; + Rtemp = ReadReg(BaseAddr, RegNum); + if (value == 0) + Wtemp = ResetBit(Rtemp, BitPos); + else { + if (value == 1) + Wtemp = SetBit(Rtemp, BitPos); + else + return -1; + } + WriteReg(BaseAddr, RegNum, Wtemp); + return 0; +} + +static __u8 CheckRegBit(unsigned int BaseAddr, unsigned char RegNum, + unsigned char BitPos) +{ + __u8 temp; + + if (BitPos > 7) + return 0xff; + if ((RegNum < StartAddr) || (RegNum > EndAddr)) { +// printf("what is the register %x!\n",RegNum); + } + temp = ReadReg(BaseAddr, RegNum); + return GetBit(temp, BitPos); +} + +static void SetMaxRxPacketSize(__u16 iobase, __u16 size) +{ + __u16 low, high; + if ((size & 0xe000) == 0) { + low = size & 0x00ff; + high = (size & 0x1f00) >> 8; + WriteReg(iobase, I_CF_L_2, low); + WriteReg(iobase, I_CF_H_2, high); + + } + +} + +//for both Rx and Tx + +static void SetFIFO(__u16 iobase, __u16 value) +{ + switch (value) { + case 128: + WriteRegBit(iobase, 0x11, 0, 0); + WriteRegBit(iobase, 0x11, 7, 1); + break; + case 64: + WriteRegBit(iobase, 0x11, 0, 0); + WriteRegBit(iobase, 0x11, 7, 0); + break; + case 32: + WriteRegBit(iobase, 0x11, 0, 1); + WriteRegBit(iobase, 0x11, 7, 0); + break; + default: + WriteRegBit(iobase, 0x11, 0, 0); + WriteRegBit(iobase, 0x11, 7, 0); + } + +} + +#define CRC16(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,7,val) //0 for 32 CRC +/* +#define SetVFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,5,val) +#define SetFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,6,val) +#define SetMIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,5,val) +#define SetSIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,4,val) +*/ +#define SIRFilter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,3,val) +#define Filter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,2,val) +#define InvertTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,1,val) +#define InvertRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,0,val) +//****************************I_CF_H_0 +#define EnableTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,4,val) +#define EnableRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,3,val) +#define EnableDMA(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,2,val) +#define SIRRecvAny(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,1,val) +#define DiableTrans(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,0,val) +//***************************I_SIR_BOF,I_SIR_EOF +#define SetSIRBOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_BOF,val) +#define SetSIREOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_EOF,val) +#define GetSIRBOF(BaseAddr) ReadReg(BaseAddr,I_SIR_BOF) +#define GetSIREOF(BaseAddr) ReadReg(BaseAddr,I_SIR_EOF) +//*******************I_ST_CT_0 +#define EnPhys(BaseAddr,val) WriteRegBit(BaseAddr,I_ST_CT_0,7,val) +#define IsModeError(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,6) //RO +#define IsVFIROn(BaseAddr) CheckRegBit(BaseAddr,0x14,0) //RO for VT1211 only +#define IsFIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,5) //RO +#define IsMIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,4) //RO +#define IsSIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,3) //RO +#define IsEnableTX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,2) //RO +#define IsEnableRX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,1) //RO +#define Is16CRC(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,0) //RO +//***************************I_CF_3 +#define DisableAdjacentPulseWidth(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,5,val) //1 disable +#define DisablePulseWidthAdjust(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,4,val) //1 disable +#define UseOneRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,1,val) //0 use two RX +#define SlowIRRXLowActive(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,0,val) //0 show RX high=1 in SIR +//***************************H_CT +#define EnAllInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,7,val) +#define TXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,6,val) +#define RXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,5,val) +#define ClearRXInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,4,val) // 1 clear +//*****************H_ST +#define IsRXInt(BaseAddr) CheckRegBit(BaseAddr,H_ST,4) +#define GetIntIndentify(BaseAddr) ((ReadReg(BaseAddr,H_ST)&0xf1) >>1) +#define IsHostBusy(BaseAddr) CheckRegBit(BaseAddr,H_ST,0) +#define GetHostStatus(BaseAddr) ReadReg(BaseAddr,H_ST) //RO +//**************************M_CT +#define EnTXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,7,val) +#define EnRXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,6,val) +#define SwapDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,5,val) +#define EnInternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,4,val) +#define EnExternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,3,val) +//**************************TX_CT_1 +#define EnTXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,4,val) //half empty int (1 half) +#define EnTXFIFOUnderrunEOMInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,5,val) +#define EnTXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,6,val) //int when reach it threshold (setting by bit 4) +//**************************TX_CT_2 +#define ForceUnderrun(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,7,val) // force an underrun int +#define EnTXCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,6,val) //1 for FIR,MIR...0 (not SIR) +#define ForceBADCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,5,val) //force an bad CRC +#define SendSIP(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,4,val) //send indication pulse for prevent SIR disturb +#define ClearEnTX(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,3,val) // opposite to EnTX +//*****************TX_ST +#define GetTXStatus(BaseAddr) ReadReg(BaseAddr,TX_ST) //RO +//**************************RX_CT +#define EnRXSpecInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,0,val) +#define EnRXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,1,val) //enable int when reach it threshold (setting by bit 7) +#define EnRXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,7,val) //enable int when (1) half full...or (0) just not full +//*****************RX_ST +#define GetRXStatus(BaseAddr) ReadReg(BaseAddr,RX_ST) //RO +//***********************P_ADDR +#define SetPacketAddr(BaseAddr,addr) WriteReg(BaseAddr,P_ADDR,addr) +//***********************I_CF_4 +#define EnGPIOtoRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,7,val) +#define EnTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,1,val) +#define ClearTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,0,val) +//***********************I_T_C_L +#define WriteGIO(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,7,val) +#define ReadGIO(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,7) +#define ReadRX(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,3) //RO +#define WriteTX(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,0,val) +//***********************I_T_C_H +#define EnRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_H,7,val) +#define ReadRX2(BaseAddr) CheckRegBit(BaseAddr,I_T_C_H,7) +//**********************Version +#define GetFIRVersion(BaseAddr) ReadReg(BaseAddr,VERSION) + + +static void SetTimer(__u16 iobase, __u8 count) +{ + EnTimerInt(iobase, OFF); + WriteReg(iobase, TIMER, count); + EnTimerInt(iobase, ON); +} + + +static void SetSendByte(__u16 iobase, __u32 count) +{ + __u32 low, high; + + if ((count & 0xf000) == 0) { + low = count & 0x00ff; + high = (count & 0x0f00) >> 8; + WriteReg(iobase, TX_C_L, low); + WriteReg(iobase, TX_C_H, high); + } +} + +static void ResetChip(__u16 iobase, __u8 type) +{ + __u8 value; + + value = (type + 2) << 4; + WriteReg(iobase, RESET, type); +} + +static int CkRxRecv(__u16 iobase, struct via_ircc_cb *self) +{ + __u8 low, high; + __u16 wTmp = 0, wTmp1 = 0, wTmp_new = 0; + + low = ReadReg(iobase, RX_C_L); + high = ReadReg(iobase, RX_C_H); + wTmp1 = high; + wTmp = (wTmp1 << 8) | low; + udelay(10); + low = ReadReg(iobase, RX_C_L); + high = ReadReg(iobase, RX_C_H); + wTmp1 = high; + wTmp_new = (wTmp1 << 8) | low; + if (wTmp_new != wTmp) + return 1; + else + return 0; + +} + +static __u16 RxCurCount(__u16 iobase, struct via_ircc_cb * self) +{ + __u8 low, high; + __u16 wTmp = 0, wTmp1 = 0; + + low = ReadReg(iobase, RX_P_L); + high = ReadReg(iobase, RX_P_H); + wTmp1 = high; + wTmp = (wTmp1 << 8) | low; + return wTmp; +} + +/* This Routine can only use in recevie_complete + * for it will update last count. + */ + +static __u16 GetRecvByte(__u16 iobase, struct via_ircc_cb * self) +{ + __u8 low, high; + __u16 wTmp, wTmp1, ret; + + low = ReadReg(iobase, RX_P_L); + high = ReadReg(iobase, RX_P_H); + wTmp1 = high; + wTmp = (wTmp1 << 8) | low; + + + if (wTmp >= self->RxLastCount) + ret = wTmp - self->RxLastCount; + else + ret = (0x8000 - self->RxLastCount) + wTmp; + self->RxLastCount = wTmp; + +/* RX_P is more actually the RX_C + low=ReadReg(iobase,RX_C_L); + high=ReadReg(iobase,RX_C_H); + + if(!(high&0xe000)) { + temp=(high<<8)+low; + return temp; + } + else return 0; +*/ + return ret; +} + +static void Sdelay(__u16 scale) +{ + __u8 bTmp; + int i, j; + + for (j = 0; j < scale; j++) { + for (i = 0; i < 0x20; i++) { + bTmp = inb(0xeb); + outb(bTmp, 0xeb); + } + } +} + +static void Tdelay(__u16 scale) +{ + __u8 bTmp; + int i, j; + + for (j = 0; j < scale; j++) { + for (i = 0; i < 0x50; i++) { + bTmp = inb(0xeb); + outb(bTmp, 0xeb); + } + } +} + + +static void ActClk(__u16 iobase, __u8 value) +{ + __u8 bTmp; + bTmp = ReadReg(iobase, 0x34); + if (value) + WriteReg(iobase, 0x34, bTmp | Clk_bit); + else + WriteReg(iobase, 0x34, bTmp & ~Clk_bit); +} + +static void ClkTx(__u16 iobase, __u8 Clk, __u8 Tx) +{ + __u8 bTmp; + + bTmp = ReadReg(iobase, 0x34); + if (Clk == 0) + bTmp &= ~Clk_bit; + else { + if (Clk == 1) + bTmp |= Clk_bit; + } + WriteReg(iobase, 0x34, bTmp); + Sdelay(1); + if (Tx == 0) + bTmp &= ~Tx_bit; + else { + if (Tx == 1) + bTmp |= Tx_bit; + } + WriteReg(iobase, 0x34, bTmp); +} + +static void Wr_Byte(__u16 iobase, __u8 data) +{ + __u8 bData = data; +// __u8 btmp; + int i; + + ClkTx(iobase, 0, 1); + + Tdelay(2); + ActClk(iobase, 1); + Tdelay(1); + + for (i = 0; i < 8; i++) { //LDN + + if ((bData >> i) & 0x01) { + ClkTx(iobase, 0, 1); //bit data = 1; + } else { + ClkTx(iobase, 0, 0); //bit data = 1; + } + Tdelay(2); + Sdelay(1); + ActClk(iobase, 1); //clk hi + Tdelay(1); + } +} + +static __u8 Rd_Indx(__u16 iobase, __u8 addr, __u8 index) +{ + __u8 data = 0, bTmp, data_bit; + int i; + + bTmp = addr | (index << 1) | 0; + ClkTx(iobase, 0, 0); + Tdelay(2); + ActClk(iobase, 1); + udelay(1); + Wr_Byte(iobase, bTmp); + Sdelay(1); + ClkTx(iobase, 0, 0); + Tdelay(2); + for (i = 0; i < 10; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(1); + ClkTx(iobase, 0, 1); + Tdelay(1); + bTmp = ReadReg(iobase, 0x34); + if (!(bTmp & Rd_Valid)) + break; + } + if (!(bTmp & Rd_Valid)) { + for (i = 0; i < 8; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + bTmp = ReadReg(iobase, 0x34); + data_bit = 1 << i; + if (bTmp & RxBit) + data |= data_bit; + else + data &= ~data_bit; + Tdelay(2); + } + } else { + for (i = 0; i < 2; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(2); + } + bTmp = ReadReg(iobase, 0x34); + } + for (i = 0; i < 1; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(2); + } + ClkTx(iobase, 0, 0); + Tdelay(1); + for (i = 0; i < 3; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(2); + } + return data; +} + +static void Wr_Indx(__u16 iobase, __u8 addr, __u8 index, __u8 data) +{ + int i; + __u8 bTmp; + + ClkTx(iobase, 0, 0); + udelay(2); + ActClk(iobase, 1); + udelay(1); + bTmp = addr | (index << 1) | 1; + Wr_Byte(iobase, bTmp); + Wr_Byte(iobase, data); + for (i = 0; i < 2; i++) { + ClkTx(iobase, 0, 0); + Tdelay(2); + ActClk(iobase, 1); + Tdelay(1); + } + ActClk(iobase, 0); +} + +static void ResetDongle(__u16 iobase) +{ + int i; + ClkTx(iobase, 0, 0); + Tdelay(1); + for (i = 0; i < 30; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(1); + } + ActClk(iobase, 0); +} + +static void SetSITmode(__u16 iobase) +{ + + __u8 bTmp; + + bTmp = ReadLPCReg(0x28); + WriteLPCReg(0x28, bTmp | 0x10); //select ITMOFF + bTmp = ReadReg(iobase, 0x35); + WriteReg(iobase, 0x35, bTmp | 0x40); // Driver ITMOFF + WriteReg(iobase, 0x28, bTmp | 0x80); // enable All interrupt +} + +static void SI_SetMode(__u16 iobase, int mode) +{ + //__u32 dTmp; + __u8 bTmp; + + WriteLPCReg(0x28, 0x70); // S/W Reset + SetSITmode(iobase); + ResetDongle(iobase); + udelay(10); + Wr_Indx(iobase, 0x40, 0x0, 0x17); //RX ,APEN enable,Normal power + Wr_Indx(iobase, 0x40, 0x1, mode); //Set Mode + Wr_Indx(iobase, 0x40, 0x2, 0xff); //Set power to FIR VFIR > 1m + bTmp = Rd_Indx(iobase, 0x40, 1); +} + +static void InitCard(__u16 iobase) +{ + ResetChip(iobase, 5); + WriteReg(iobase, I_ST_CT_0, 0x00); // open CHIP on + SetSIRBOF(iobase, 0xc0); // hardware default value + SetSIREOF(iobase, 0xc1); +} + +static void CommonInit(__u16 iobase) +{ +// EnTXCRC(iobase,0); + SwapDMA(iobase, OFF); + SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095 + EnRXFIFOReadyInt(iobase, OFF); + EnRXFIFOHalfLevelInt(iobase, OFF); + EnTXFIFOHalfLevelInt(iobase, OFF); + EnTXFIFOUnderrunEOMInt(iobase, ON); +// EnTXFIFOReadyInt(iobase,ON); + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); +// WriteLPCReg(0xF0,0); //(if VT1211 then do this) + if (IsSIROn(iobase)) { + SIRFilter(iobase, ON); + SIRRecvAny(iobase, ON); + } else { + SIRFilter(iobase, OFF); + SIRRecvAny(iobase, OFF); + } + EnRXSpecInt(iobase, ON); + WriteReg(iobase, I_ST_CT_0, 0x80); + EnableDMA(iobase, ON); +} + +static void SetBaudRate(__u16 iobase, __u32 rate) +{ + __u8 value = 11, temp; + + if (IsSIROn(iobase)) { + switch (rate) { + case (__u32) (2400L): + value = 47; + break; + case (__u32) (9600L): + value = 11; + break; + case (__u32) (19200L): + value = 5; + break; + case (__u32) (38400L): + value = 2; + break; + case (__u32) (57600L): + value = 1; + break; + case (__u32) (115200L): + value = 0; + break; + default: + break; + } + } else if (IsMIROn(iobase)) { + value = 0; // will automatically be fixed in 1.152M + } else if (IsFIROn(iobase)) { + value = 0; // will automatically be fixed in 4M + } + temp = (ReadReg(iobase, I_CF_H_1) & 0x03); + temp |= value << 2; + WriteReg(iobase, I_CF_H_1, temp); +} + +static void SetPulseWidth(__u16 iobase, __u8 width) +{ + __u8 temp, temp1, temp2; + + temp = (ReadReg(iobase, I_CF_L_1) & 0x1f); + temp1 = (ReadReg(iobase, I_CF_H_1) & 0xfc); + temp2 = (width & 0x07) << 5; + temp |= temp2; + temp2 = (width & 0x18) >> 3; + temp1 |= temp2; + WriteReg(iobase, I_CF_L_1, temp); + WriteReg(iobase, I_CF_H_1, temp1); +} + +static void SetSendPreambleCount(__u16 iobase, __u8 count) +{ + __u8 temp; + + temp = ReadReg(iobase, I_CF_L_1) & 0xe0; + temp |= count; + WriteReg(iobase, I_CF_L_1, temp); + +} + +static void SetVFIR(__u16 BaseAddr, __u8 val) +{ + __u8 tmp; + + tmp = ReadReg(BaseAddr, I_CF_L_0); + WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); + WriteRegBit(BaseAddr, I_CF_H_0, 5, val); +} + +static void SetFIR(__u16 BaseAddr, __u8 val) +{ + __u8 tmp; + + WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); + tmp = ReadReg(BaseAddr, I_CF_L_0); + WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); + WriteRegBit(BaseAddr, I_CF_L_0, 6, val); +} + +static void SetMIR(__u16 BaseAddr, __u8 val) +{ + __u8 tmp; + + WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); + tmp = ReadReg(BaseAddr, I_CF_L_0); + WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); + WriteRegBit(BaseAddr, I_CF_L_0, 5, val); +} + +static void SetSIR(__u16 BaseAddr, __u8 val) +{ + __u8 tmp; + + WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); + tmp = ReadReg(BaseAddr, I_CF_L_0); + WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); + WriteRegBit(BaseAddr, I_CF_L_0, 4, val); +} + +#endif /* via_IRCC_H */ diff --git a/drivers/staging/irda/drivers/vlsi_ir.c b/drivers/staging/irda/drivers/vlsi_ir.c new file mode 100644 index 000000000000..6638784c082e --- /dev/null +++ b/drivers/staging/irda/drivers/vlsi_ir.c @@ -0,0 +1,1872 @@ +/********************************************************************* + * + * vlsi_ir.c: VLSI82C147 PCI IrDA controller driver for Linux + * + * Copyright (c) 2001-2003 Martin Diehl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + ********************************************************************/ + +#include + +#define DRIVER_NAME "vlsi_ir" +#define DRIVER_VERSION "v0.5" +#define DRIVER_DESCRIPTION "IrDA SIR/MIR/FIR driver for VLSI 82C147" +#define DRIVER_AUTHOR "Martin Diehl " + +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); + +/********************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "vlsi_ir.h" + +/********************************************************/ + +static /* const */ char drivername[] = DRIVER_NAME; + +static const struct pci_device_id vlsi_irda_table[] = { + { + .class = PCI_CLASS_WIRELESS_IRDA << 8, + .class_mask = PCI_CLASS_SUBCLASS_MASK << 8, + .vendor = PCI_VENDOR_ID_VLSI, + .device = PCI_DEVICE_ID_VLSI_82C147, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { /* all zeroes */ } +}; + +MODULE_DEVICE_TABLE(pci, vlsi_irda_table); + +/********************************************************/ + +/* clksrc: which clock source to be used + * 0: auto - try PLL, fallback to 40MHz XCLK + * 1: on-chip 48MHz PLL + * 2: external 48MHz XCLK + * 3: external 40MHz XCLK (HP OB-800) + */ + +static int clksrc = 0; /* default is 0(auto) */ +module_param(clksrc, int, 0); +MODULE_PARM_DESC(clksrc, "clock input source selection"); + +/* ringsize: size of the tx and rx descriptor rings + * independent for tx and rx + * specify as ringsize=tx[,rx] + * allowed values: 4, 8, 16, 32, 64 + * Due to the IrDA 1.x max. allowed window size=7, + * there should be no gain when using rings larger than 8 + */ + +static int ringsize[] = {8,8}; /* default is tx=8 / rx=8 */ +module_param_array(ringsize, int, NULL, 0); +MODULE_PARM_DESC(ringsize, "TX, RX ring descriptor size"); + +/* sirpulse: tuning of the SIR pulse width within IrPHY 1.3 limits + * 0: very short, 1.5us (exception: 6us at 2.4 kbaud) + * 1: nominal 3/16 bittime width + * note: IrDA compliant peer devices should be happy regardless + * which one is used. Primary goal is to save some power + * on the sender's side - at 9.6kbaud for example the short + * pulse width saves more than 90% of the transmitted IR power. + */ + +static int sirpulse = 1; /* default is 3/16 bittime */ +module_param(sirpulse, int, 0); +MODULE_PARM_DESC(sirpulse, "SIR pulse width tuning"); + +/* qos_mtt_bits: encoded min-turn-time value we require the peer device + * to use before transmitting to us. "Type 1" (per-station) + * bitfield according to IrLAP definition (section 6.6.8) + * Don't know which transceiver is used by my OB800 - the + * pretty common HP HDLS-1100 requires 1 msec - so lets use this. + */ + +static int qos_mtt_bits = 0x07; /* default is 1 ms or more */ +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "IrLAP bitfield representing min-turn-time"); + +/********************************************************/ + +static void vlsi_reg_debug(unsigned iobase, const char *s) +{ + int i; + + printk(KERN_DEBUG "%s: ", s); + for (i = 0; i < 0x20; i++) + printk("%02x", (unsigned)inb((iobase+i))); + printk("\n"); +} + +static void vlsi_ring_debug(struct vlsi_ring *r) +{ + struct ring_descr *rd; + unsigned i; + + printk(KERN_DEBUG "%s - ring %p / size %u / mask 0x%04x / len %u / dir %d / hw %p\n", + __func__, r, r->size, r->mask, r->len, r->dir, r->rd[0].hw); + printk(KERN_DEBUG "%s - head = %d / tail = %d\n", __func__, + atomic_read(&r->head) & r->mask, atomic_read(&r->tail) & r->mask); + for (i = 0; i < r->size; i++) { + rd = &r->rd[i]; + printk(KERN_DEBUG "%s - ring descr %u: ", __func__, i); + printk("skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw); + printk(KERN_DEBUG "%s - hw: status=%02x count=%u addr=0x%08x\n", + __func__, (unsigned) rd_get_status(rd), + (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd)); + } +} + +/********************************************************/ + +/* needed regardless of CONFIG_PROC_FS */ +static struct proc_dir_entry *vlsi_proc_root = NULL; + +#ifdef CONFIG_PROC_FS + +static void vlsi_proc_pdev(struct seq_file *seq, struct pci_dev *pdev) +{ + unsigned iobase = pci_resource_start(pdev, 0); + unsigned i; + + seq_printf(seq, "\n%s (vid/did: [%04x:%04x])\n", + pci_name(pdev), (int)pdev->vendor, (int)pdev->device); + seq_printf(seq, "pci-power-state: %u\n", (unsigned) pdev->current_state); + seq_printf(seq, "resources: irq=%u / io=0x%04x / dma_mask=0x%016Lx\n", + pdev->irq, (unsigned)pci_resource_start(pdev, 0), (unsigned long long)pdev->dma_mask); + seq_printf(seq, "hw registers: "); + for (i = 0; i < 0x20; i++) + seq_printf(seq, "%02x", (unsigned)inb((iobase+i))); + seq_printf(seq, "\n"); +} + +static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = netdev_priv(ndev); + u8 byte; + u16 word; + s32 sec, usec; + unsigned iobase = ndev->base_addr; + + seq_printf(seq, "\n%s link state: %s / %s / %s / %s\n", ndev->name, + netif_device_present(ndev) ? "attached" : "detached", + netif_running(ndev) ? "running" : "not running", + netif_carrier_ok(ndev) ? "carrier ok" : "no carrier", + netif_queue_stopped(ndev) ? "queue stopped" : "queue running"); + + if (!netif_running(ndev)) + return; + + seq_printf(seq, "\nhw-state:\n"); + pci_read_config_byte(idev->pdev, VLSI_PCI_IRMISC, &byte); + seq_printf(seq, "IRMISC:%s%s%s uart%s", + (byte&IRMISC_IRRAIL) ? " irrail" : "", + (byte&IRMISC_IRPD) ? " irpd" : "", + (byte&IRMISC_UARTTST) ? " uarttest" : "", + (byte&IRMISC_UARTEN) ? "@" : " disabled\n"); + if (byte&IRMISC_UARTEN) { + seq_printf(seq, "0x%s\n", + (byte&2) ? ((byte&1) ? "3e8" : "2e8") + : ((byte&1) ? "3f8" : "2f8")); + } + pci_read_config_byte(idev->pdev, VLSI_PCI_CLKCTL, &byte); + seq_printf(seq, "CLKCTL: PLL %s%s%s / clock %s / wakeup %s\n", + (byte&CLKCTL_PD_INV) ? "powered" : "down", + (byte&CLKCTL_LOCK) ? " locked" : "", + (byte&CLKCTL_EXTCLK) ? ((byte&CLKCTL_XCKSEL)?" / 40 MHz XCLK":" / 48 MHz XCLK") : "", + (byte&CLKCTL_CLKSTP) ? "stopped" : "running", + (byte&CLKCTL_WAKE) ? "enabled" : "disabled"); + pci_read_config_byte(idev->pdev, VLSI_PCI_MSTRPAGE, &byte); + seq_printf(seq, "MSTRPAGE: 0x%02x\n", (unsigned)byte); + + byte = inb(iobase+VLSI_PIO_IRINTR); + seq_printf(seq, "IRINTR:%s%s%s%s%s%s%s%s\n", + (byte&IRINTR_ACTEN) ? " ACTEN" : "", + (byte&IRINTR_RPKTEN) ? " RPKTEN" : "", + (byte&IRINTR_TPKTEN) ? " TPKTEN" : "", + (byte&IRINTR_OE_EN) ? " OE_EN" : "", + (byte&IRINTR_ACTIVITY) ? " ACTIVITY" : "", + (byte&IRINTR_RPKTINT) ? " RPKTINT" : "", + (byte&IRINTR_TPKTINT) ? " TPKTINT" : "", + (byte&IRINTR_OE_INT) ? " OE_INT" : ""); + word = inw(iobase+VLSI_PIO_RINGPTR); + seq_printf(seq, "RINGPTR: rx=%u / tx=%u\n", RINGPTR_GET_RX(word), RINGPTR_GET_TX(word)); + word = inw(iobase+VLSI_PIO_RINGBASE); + seq_printf(seq, "RINGBASE: busmap=0x%08x\n", + ((unsigned)word << 10)|(MSTRPAGE_VALUE<<24)); + word = inw(iobase+VLSI_PIO_RINGSIZE); + seq_printf(seq, "RINGSIZE: rx=%u / tx=%u\n", RINGSIZE_TO_RXSIZE(word), + RINGSIZE_TO_TXSIZE(word)); + + word = inw(iobase+VLSI_PIO_IRCFG); + seq_printf(seq, "IRCFG:%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + (word&IRCFG_LOOP) ? " LOOP" : "", + (word&IRCFG_ENTX) ? " ENTX" : "", + (word&IRCFG_ENRX) ? " ENRX" : "", + (word&IRCFG_MSTR) ? " MSTR" : "", + (word&IRCFG_RXANY) ? " RXANY" : "", + (word&IRCFG_CRC16) ? " CRC16" : "", + (word&IRCFG_FIR) ? " FIR" : "", + (word&IRCFG_MIR) ? " MIR" : "", + (word&IRCFG_SIR) ? " SIR" : "", + (word&IRCFG_SIRFILT) ? " SIRFILT" : "", + (word&IRCFG_SIRTEST) ? " SIRTEST" : "", + (word&IRCFG_TXPOL) ? " TXPOL" : "", + (word&IRCFG_RXPOL) ? " RXPOL" : ""); + word = inw(iobase+VLSI_PIO_IRENABLE); + seq_printf(seq, "IRENABLE:%s%s%s%s%s%s%s%s\n", + (word&IRENABLE_PHYANDCLOCK) ? " PHYANDCLOCK" : "", + (word&IRENABLE_CFGER) ? " CFGERR" : "", + (word&IRENABLE_FIR_ON) ? " FIR_ON" : "", + (word&IRENABLE_MIR_ON) ? " MIR_ON" : "", + (word&IRENABLE_SIR_ON) ? " SIR_ON" : "", + (word&IRENABLE_ENTXST) ? " ENTXST" : "", + (word&IRENABLE_ENRXST) ? " ENRXST" : "", + (word&IRENABLE_CRC16_ON) ? " CRC16_ON" : ""); + word = inw(iobase+VLSI_PIO_PHYCTL); + seq_printf(seq, "PHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n", + (unsigned)PHYCTL_TO_BAUD(word), + (unsigned)PHYCTL_TO_PLSWID(word), + (unsigned)PHYCTL_TO_PREAMB(word)); + word = inw(iobase+VLSI_PIO_NPHYCTL); + seq_printf(seq, "NPHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n", + (unsigned)PHYCTL_TO_BAUD(word), + (unsigned)PHYCTL_TO_PLSWID(word), + (unsigned)PHYCTL_TO_PREAMB(word)); + word = inw(iobase+VLSI_PIO_MAXPKT); + seq_printf(seq, "MAXPKT: max. rx packet size = %u\n", word); + word = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + seq_printf(seq, "RCVBCNT: rx-fifo filling level = %u\n", word); + + seq_printf(seq, "\nsw-state:\n"); + seq_printf(seq, "IrPHY setup: %d baud - %s encoding\n", idev->baud, + (idev->mode==IFF_SIR)?"SIR":((idev->mode==IFF_MIR)?"MIR":"FIR")); + sec = div_s64_rem(ktime_us_delta(ktime_get(), idev->last_rx), + USEC_PER_SEC, &usec); + seq_printf(seq, "last rx: %ul.%06u sec\n", sec, usec); + + seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu", + ndev->stats.rx_packets, ndev->stats.rx_bytes, ndev->stats.rx_errors, + ndev->stats.rx_dropped); + seq_printf(seq, " / overrun=%lu / length=%lu / frame=%lu / crc=%lu\n", + ndev->stats.rx_over_errors, ndev->stats.rx_length_errors, + ndev->stats.rx_frame_errors, ndev->stats.rx_crc_errors); + seq_printf(seq, "TX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu / fifo=%lu\n", + ndev->stats.tx_packets, ndev->stats.tx_bytes, ndev->stats.tx_errors, + ndev->stats.tx_dropped, ndev->stats.tx_fifo_errors); + +} + +static void vlsi_proc_ring(struct seq_file *seq, struct vlsi_ring *r) +{ + struct ring_descr *rd; + unsigned i, j; + int h, t; + + seq_printf(seq, "size %u / mask 0x%04x / len %u / dir %d / hw %p\n", + r->size, r->mask, r->len, r->dir, r->rd[0].hw); + h = atomic_read(&r->head) & r->mask; + t = atomic_read(&r->tail) & r->mask; + seq_printf(seq, "head = %d / tail = %d ", h, t); + if (h == t) + seq_printf(seq, "(empty)\n"); + else { + if (((t+1)&r->mask) == h) + seq_printf(seq, "(full)\n"); + else + seq_printf(seq, "(level = %d)\n", ((unsigned)(t-h) & r->mask)); + rd = &r->rd[h]; + j = (unsigned) rd_get_count(rd); + seq_printf(seq, "current: rd = %d / status = %02x / len = %u\n", + h, (unsigned)rd_get_status(rd), j); + if (j > 0) { + seq_printf(seq, " data: %*ph\n", + min_t(unsigned, j, 20), rd->buf); + } + } + for (i = 0; i < r->size; i++) { + rd = &r->rd[i]; + seq_printf(seq, "> ring descr %u: ", i); + seq_printf(seq, "skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw); + seq_printf(seq, " hw: status=%02x count=%u busaddr=0x%08x\n", + (unsigned) rd_get_status(rd), + (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd)); + } +} + +static int vlsi_seq_show(struct seq_file *seq, void *v) +{ + struct net_device *ndev = seq->private; + vlsi_irda_dev_t *idev = netdev_priv(ndev); + unsigned long flags; + + seq_printf(seq, "\n%s %s\n\n", DRIVER_NAME, DRIVER_VERSION); + seq_printf(seq, "clksrc: %s\n", + (clksrc>=2) ? ((clksrc==3)?"40MHz XCLK":"48MHz XCLK") + : ((clksrc==1)?"48MHz PLL":"autodetect")); + seq_printf(seq, "ringsize: tx=%d / rx=%d\n", + ringsize[0], ringsize[1]); + seq_printf(seq, "sirpulse: %s\n", (sirpulse)?"3/16 bittime":"short"); + seq_printf(seq, "qos_mtt_bits: 0x%02x\n", (unsigned)qos_mtt_bits); + + spin_lock_irqsave(&idev->lock, flags); + if (idev->pdev != NULL) { + vlsi_proc_pdev(seq, idev->pdev); + + if (idev->pdev->current_state == 0) + vlsi_proc_ndev(seq, ndev); + else + seq_printf(seq, "\nPCI controller down - resume_ok = %d\n", + idev->resume_ok); + if (netif_running(ndev) && idev->rx_ring && idev->tx_ring) { + seq_printf(seq, "\n--------- RX ring -----------\n\n"); + vlsi_proc_ring(seq, idev->rx_ring); + seq_printf(seq, "\n--------- TX ring -----------\n\n"); + vlsi_proc_ring(seq, idev->tx_ring); + } + } + seq_printf(seq, "\n"); + spin_unlock_irqrestore(&idev->lock, flags); + + return 0; +} + +static int vlsi_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, vlsi_seq_show, PDE_DATA(inode)); +} + +static const struct file_operations vlsi_proc_fops = { + .owner = THIS_MODULE, + .open = vlsi_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define VLSI_PROC_FOPS (&vlsi_proc_fops) + +#else /* CONFIG_PROC_FS */ +#define VLSI_PROC_FOPS NULL +#endif + +/********************************************************/ + +static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr_hw *hwmap, + unsigned size, unsigned len, int dir) +{ + struct vlsi_ring *r; + struct ring_descr *rd; + unsigned i, j; + dma_addr_t busaddr; + + if (!size || ((size-1)&size)!=0) /* must be >0 and power of 2 */ + return NULL; + + r = kmalloc(sizeof(*r) + size * sizeof(struct ring_descr), GFP_KERNEL); + if (!r) + return NULL; + memset(r, 0, sizeof(*r)); + + r->pdev = pdev; + r->dir = dir; + r->len = len; + r->rd = (struct ring_descr *)(r+1); + r->mask = size - 1; + r->size = size; + atomic_set(&r->head, 0); + atomic_set(&r->tail, 0); + + for (i = 0; i < size; i++) { + rd = r->rd + i; + memset(rd, 0, sizeof(*rd)); + rd->hw = hwmap + i; + rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA); + if (rd->buf) + busaddr = pci_map_single(pdev, rd->buf, len, dir); + if (rd->buf == NULL || pci_dma_mapping_error(pdev, busaddr)) { + if (rd->buf) { + net_err_ratelimited("%s: failed to create PCI-MAP for %p\n", + __func__, rd->buf); + kfree(rd->buf); + rd->buf = NULL; + } + for (j = 0; j < i; j++) { + rd = r->rd + j; + busaddr = rd_get_addr(rd); + rd_set_addr_status(rd, 0, 0); + pci_unmap_single(pdev, busaddr, len, dir); + kfree(rd->buf); + rd->buf = NULL; + } + kfree(r); + return NULL; + } + rd_set_addr_status(rd, busaddr, 0); + /* initially, the dma buffer is owned by the CPU */ + rd->skb = NULL; + } + return r; +} + +static int vlsi_free_ring(struct vlsi_ring *r) +{ + struct ring_descr *rd; + unsigned i; + dma_addr_t busaddr; + + for (i = 0; i < r->size; i++) { + rd = r->rd + i; + if (rd->skb) + dev_kfree_skb_any(rd->skb); + busaddr = rd_get_addr(rd); + rd_set_addr_status(rd, 0, 0); + if (busaddr) + pci_unmap_single(r->pdev, busaddr, r->len, r->dir); + kfree(rd->buf); + } + kfree(r); + return 0; +} + +static int vlsi_create_hwif(vlsi_irda_dev_t *idev) +{ + char *ringarea; + struct ring_descr_hw *hwmap; + + idev->virtaddr = NULL; + idev->busaddr = 0; + + ringarea = pci_zalloc_consistent(idev->pdev, HW_RING_AREA_SIZE, + &idev->busaddr); + if (!ringarea) + goto out; + + hwmap = (struct ring_descr_hw *)ringarea; + idev->rx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[1], + XFER_BUF_SIZE, PCI_DMA_FROMDEVICE); + if (idev->rx_ring == NULL) + goto out_unmap; + + hwmap += MAX_RING_DESCR; + idev->tx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[0], + XFER_BUF_SIZE, PCI_DMA_TODEVICE); + if (idev->tx_ring == NULL) + goto out_free_rx; + + idev->virtaddr = ringarea; + return 0; + +out_free_rx: + vlsi_free_ring(idev->rx_ring); +out_unmap: + idev->rx_ring = idev->tx_ring = NULL; + pci_free_consistent(idev->pdev, HW_RING_AREA_SIZE, ringarea, idev->busaddr); + idev->busaddr = 0; +out: + return -ENOMEM; +} + +static int vlsi_destroy_hwif(vlsi_irda_dev_t *idev) +{ + vlsi_free_ring(idev->rx_ring); + vlsi_free_ring(idev->tx_ring); + idev->rx_ring = idev->tx_ring = NULL; + + if (idev->busaddr) + pci_free_consistent(idev->pdev,HW_RING_AREA_SIZE,idev->virtaddr,idev->busaddr); + + idev->virtaddr = NULL; + idev->busaddr = 0; + + return 0; +} + +/********************************************************/ + +static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd) +{ + u16 status; + int crclen, len = 0; + struct sk_buff *skb; + int ret = 0; + struct net_device *ndev = pci_get_drvdata(r->pdev); + vlsi_irda_dev_t *idev = netdev_priv(ndev); + + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); + /* dma buffer now owned by the CPU */ + status = rd_get_status(rd); + if (status & RD_RX_ERROR) { + if (status & RD_RX_OVER) + ret |= VLSI_RX_OVER; + if (status & RD_RX_LENGTH) + ret |= VLSI_RX_LENGTH; + if (status & RD_RX_PHYERR) + ret |= VLSI_RX_FRAME; + if (status & RD_RX_CRCERR) + ret |= VLSI_RX_CRC; + goto done; + } + + len = rd_get_count(rd); + crclen = (idev->mode==IFF_FIR) ? sizeof(u32) : sizeof(u16); + len -= crclen; /* remove trailing CRC */ + if (len <= 0) { + pr_debug("%s: strange frame (len=%d)\n", __func__, len); + ret |= VLSI_RX_DROP; + goto done; + } + + if (idev->mode == IFF_SIR) { /* hw checks CRC in MIR, FIR mode */ + + /* rd->buf is a streaming PCI_DMA_FROMDEVICE map. Doing the + * endian-adjustment there just in place will dirty a cache line + * which belongs to the map and thus we must be sure it will + * get flushed before giving the buffer back to hardware. + * vlsi_fill_rx() will do this anyway - but here we rely on. + */ + le16_to_cpus(rd->buf+len); + if (irda_calc_crc16(INIT_FCS,rd->buf,len+crclen) != GOOD_FCS) { + pr_debug("%s: crc error\n", __func__); + ret |= VLSI_RX_CRC; + goto done; + } + } + + if (!rd->skb) { + net_warn_ratelimited("%s: rx packet lost\n", __func__); + ret |= VLSI_RX_DROP; + goto done; + } + + skb = rd->skb; + rd->skb = NULL; + skb->dev = ndev; + skb_put_data(skb, rd->buf, len); + skb_reset_mac_header(skb); + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + +done: + rd_set_status(rd, 0); + rd_set_count(rd, 0); + /* buffer still owned by CPU */ + + return (ret) ? -ret : len; +} + +static void vlsi_fill_rx(struct vlsi_ring *r) +{ + struct ring_descr *rd; + + for (rd = ring_last(r); rd != NULL; rd = ring_put(r)) { + if (rd_is_active(rd)) { + net_warn_ratelimited("%s: driver bug: rx descr race with hw\n", + __func__); + vlsi_ring_debug(r); + break; + } + if (!rd->skb) { + rd->skb = dev_alloc_skb(IRLAP_SKB_ALLOCSIZE); + if (rd->skb) { + skb_reserve(rd->skb,1); + rd->skb->protocol = htons(ETH_P_IRDA); + } + else + break; /* probably not worth logging? */ + } + /* give dma buffer back to busmaster */ + pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); + rd_activate(rd); + } +} + +static void vlsi_rx_interrupt(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = netdev_priv(ndev); + struct vlsi_ring *r = idev->rx_ring; + struct ring_descr *rd; + int ret; + + for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { + + if (rd_is_active(rd)) + break; + + ret = vlsi_process_rx(r, rd); + + if (ret < 0) { + ret = -ret; + ndev->stats.rx_errors++; + if (ret & VLSI_RX_DROP) + ndev->stats.rx_dropped++; + if (ret & VLSI_RX_OVER) + ndev->stats.rx_over_errors++; + if (ret & VLSI_RX_LENGTH) + ndev->stats.rx_length_errors++; + if (ret & VLSI_RX_FRAME) + ndev->stats.rx_frame_errors++; + if (ret & VLSI_RX_CRC) + ndev->stats.rx_crc_errors++; + } + else if (ret > 0) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += ret; + } + } + + idev->last_rx = ktime_get(); /* remember "now" for later mtt delay */ + + vlsi_fill_rx(r); + + if (ring_first(r) == NULL) { + /* we are in big trouble, if this should ever happen */ + net_err_ratelimited("%s: rx ring exhausted!\n", __func__); + vlsi_ring_debug(r); + } + else + outw(0, ndev->base_addr+VLSI_PIO_PROMPT); +} + +/* caller must have stopped the controller from busmastering */ + +static void vlsi_unarm_rx(vlsi_irda_dev_t *idev) +{ + struct net_device *ndev = pci_get_drvdata(idev->pdev); + struct vlsi_ring *r = idev->rx_ring; + struct ring_descr *rd; + int ret; + + for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { + + ret = 0; + if (rd_is_active(rd)) { + rd_set_status(rd, 0); + if (rd_get_count(rd)) { + pr_debug("%s - dropping rx packet\n", __func__); + ret = -VLSI_RX_DROP; + } + rd_set_count(rd, 0); + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); + if (rd->skb) { + dev_kfree_skb_any(rd->skb); + rd->skb = NULL; + } + } + else + ret = vlsi_process_rx(r, rd); + + if (ret < 0) { + ret = -ret; + ndev->stats.rx_errors++; + if (ret & VLSI_RX_DROP) + ndev->stats.rx_dropped++; + if (ret & VLSI_RX_OVER) + ndev->stats.rx_over_errors++; + if (ret & VLSI_RX_LENGTH) + ndev->stats.rx_length_errors++; + if (ret & VLSI_RX_FRAME) + ndev->stats.rx_frame_errors++; + if (ret & VLSI_RX_CRC) + ndev->stats.rx_crc_errors++; + } + else if (ret > 0) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += ret; + } + } +} + +/********************************************************/ + +static int vlsi_process_tx(struct vlsi_ring *r, struct ring_descr *rd) +{ + u16 status; + int len; + int ret; + + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); + /* dma buffer now owned by the CPU */ + status = rd_get_status(rd); + if (status & RD_TX_UNDRN) + ret = VLSI_TX_FIFO; + else + ret = 0; + rd_set_status(rd, 0); + + if (rd->skb) { + len = rd->skb->len; + dev_kfree_skb_any(rd->skb); + rd->skb = NULL; + } + else /* tx-skb already freed? - should never happen */ + len = rd_get_count(rd); /* incorrect for SIR! (due to wrapping) */ + + rd_set_count(rd, 0); + /* dma buffer still owned by the CPU */ + + return (ret) ? -ret : len; +} + +static int vlsi_set_baud(vlsi_irda_dev_t *idev, unsigned iobase) +{ + u16 nphyctl; + u16 config; + unsigned mode; + int ret; + int baudrate; + int fifocnt; + + baudrate = idev->new_baud; + pr_debug("%s: %d -> %d\n", __func__, idev->baud, idev->new_baud); + if (baudrate == 4000000) { + mode = IFF_FIR; + config = IRCFG_FIR; + nphyctl = PHYCTL_FIR; + } + else if (baudrate == 1152000) { + mode = IFF_MIR; + config = IRCFG_MIR | IRCFG_CRC16; + nphyctl = PHYCTL_MIR(clksrc==3); + } + else { + mode = IFF_SIR; + config = IRCFG_SIR | IRCFG_SIRFILT | IRCFG_RXANY; + switch(baudrate) { + default: + net_warn_ratelimited("%s: undefined baudrate %d - fallback to 9600!\n", + __func__, baudrate); + baudrate = 9600; + /* fallthru */ + case 2400: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + nphyctl = PHYCTL_SIR(baudrate,sirpulse,clksrc==3); + break; + } + } + config |= IRCFG_MSTR | IRCFG_ENRX; + + fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + if (fifocnt != 0) { + pr_debug("%s: rx fifo not empty(%d)\n", __func__, fifocnt); + } + + outw(0, iobase+VLSI_PIO_IRENABLE); + outw(config, iobase+VLSI_PIO_IRCFG); + outw(nphyctl, iobase+VLSI_PIO_NPHYCTL); + wmb(); + outw(IRENABLE_PHYANDCLOCK, iobase+VLSI_PIO_IRENABLE); + mb(); + + udelay(1); /* chip applies IRCFG on next rising edge of its 8MHz clock */ + + /* read back settings for validation */ + + config = inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_MASK; + + if (mode == IFF_FIR) + config ^= IRENABLE_FIR_ON; + else if (mode == IFF_MIR) + config ^= (IRENABLE_MIR_ON|IRENABLE_CRC16_ON); + else + config ^= IRENABLE_SIR_ON; + + if (config != (IRENABLE_PHYANDCLOCK|IRENABLE_ENRXST)) { + net_warn_ratelimited("%s: failed to set %s mode!\n", + __func__, + mode == IFF_SIR ? "SIR" : + mode == IFF_MIR ? "MIR" : "FIR"); + ret = -1; + } + else { + if (inw(iobase+VLSI_PIO_PHYCTL) != nphyctl) { + net_warn_ratelimited("%s: failed to apply baudrate %d\n", + __func__, baudrate); + ret = -1; + } + else { + idev->mode = mode; + idev->baud = baudrate; + idev->new_baud = 0; + ret = 0; + } + } + + if (ret) + vlsi_reg_debug(iobase,__func__); + + return ret; +} + +static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = netdev_priv(ndev); + struct vlsi_ring *r = idev->tx_ring; + struct ring_descr *rd; + unsigned long flags; + unsigned iobase = ndev->base_addr; + u8 status; + u16 config; + int mtt, diff; + int len, speed; + char *msg = NULL; + + speed = irda_get_next_speed(skb); + spin_lock_irqsave(&idev->lock, flags); + if (speed != -1 && speed != idev->baud) { + netif_stop_queue(ndev); + idev->new_baud = speed; + status = RD_TX_CLRENTX; /* stop tx-ring after this frame */ + } + else + status = 0; + + if (skb->len == 0) { + /* handle zero packets - should be speed change */ + if (status == 0) { + msg = "bogus zero-length packet"; + goto drop_unlock; + } + + /* due to the completely asynch tx operation we might have + * IrLAP racing with the hardware here, f.e. if the controller + * is just sending the last packet with current speed while + * the LAP is already switching the speed using synchronous + * len=0 packet. Immediate execution would lead to hw lockup + * requiring a powercycle to reset. Good candidate to trigger + * this is the final UA:RSP packet after receiving a DISC:CMD + * when getting the LAP down. + * Note that we are not protected by the queue_stop approach + * because the final UA:RSP arrives _without_ request to apply + * new-speed-after-this-packet - hence the driver doesn't know + * this was the last packet and doesn't stop the queue. So the + * forced switch to default speed from LAP gets through as fast + * as only some 10 usec later while the UA:RSP is still processed + * by the hardware and we would get screwed. + */ + + if (ring_first(idev->tx_ring) == NULL) { + /* no race - tx-ring already empty */ + vlsi_set_baud(idev, iobase); + netif_wake_queue(ndev); + } + else + ; + /* keep the speed change pending like it would + * for any len>0 packet. tx completion interrupt + * will apply it when the tx ring becomes empty. + */ + spin_unlock_irqrestore(&idev->lock, flags); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + /* sanity checks - simply drop the packet */ + + rd = ring_last(r); + if (!rd) { + msg = "ring full, but queue wasn't stopped"; + goto drop_unlock; + } + + if (rd_is_active(rd)) { + msg = "entry still owned by hw"; + goto drop_unlock; + } + + if (!rd->buf) { + msg = "tx ring entry without pci buffer"; + goto drop_unlock; + } + + if (rd->skb) { + msg = "ring entry with old skb still attached"; + goto drop_unlock; + } + + /* no need for serialization or interrupt disable during mtt */ + spin_unlock_irqrestore(&idev->lock, flags); + + if ((mtt = irda_get_mtt(skb)) > 0) { + diff = ktime_us_delta(ktime_get(), idev->last_rx); + if (mtt > diff) + udelay(mtt - diff); + /* must not sleep here - called under netif_tx_lock! */ + } + + /* tx buffer already owned by CPU due to pci_dma_sync_single_for_cpu() + * after subsequent tx-completion + */ + + if (idev->mode == IFF_SIR) { + status |= RD_TX_DISCRC; /* no hw-crc creation */ + len = async_wrap_skb(skb, rd->buf, r->len); + + /* Some rare worst case situation in SIR mode might lead to + * potential buffer overflow. The wrapper detects this, returns + * with a shortened frame (without FCS/EOF) but doesn't provide + * any error indication about the invalid packet which we are + * going to transmit. + * Therefore we log if the buffer got filled to the point, where the + * wrapper would abort, i.e. when there are less than 5 bytes left to + * allow appending the FCS/EOF. + */ + + if (len >= r->len-5) + net_warn_ratelimited("%s: possible buffer overflow with SIR wrapping!\n", + __func__); + } + else { + /* hw deals with MIR/FIR mode wrapping */ + status |= RD_TX_PULSE; /* send 2 us highspeed indication pulse */ + len = skb->len; + if (len > r->len) { + msg = "frame exceeds tx buffer length"; + goto drop; + } + else + skb_copy_from_linear_data(skb, rd->buf, len); + } + + rd->skb = skb; /* remember skb for tx-complete stats */ + + rd_set_count(rd, len); + rd_set_status(rd, status); /* not yet active! */ + + /* give dma buffer back to busmaster-hw (flush caches to make + * CPU-driven changes visible from the pci bus). + */ + + pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); + +/* Switching to TX mode here races with the controller + * which may stop TX at any time when fetching an inactive descriptor + * or one with CLR_ENTX set. So we switch on TX only, if TX was not running + * _after_ the new descriptor was activated on the ring. This ensures + * we will either find TX already stopped or we can be sure, there + * will be a TX-complete interrupt even if the chip stopped doing + * TX just after we found it still running. The ISR will then find + * the non-empty ring and restart TX processing. The enclosing + * spinlock provides the correct serialization to prevent race with isr. + */ + + spin_lock_irqsave(&idev->lock,flags); + + rd_activate(rd); + + if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) { + int fifocnt; + + fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + if (fifocnt != 0) { + pr_debug("%s: rx fifo not empty(%d)\n", + __func__, fifocnt); + } + + config = inw(iobase+VLSI_PIO_IRCFG); + mb(); + outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG); + wmb(); + outw(0, iobase+VLSI_PIO_PROMPT); + } + + if (ring_put(r) == NULL) { + netif_stop_queue(ndev); + pr_debug("%s: tx ring full - queue stopped\n", __func__); + } + spin_unlock_irqrestore(&idev->lock, flags); + + return NETDEV_TX_OK; + +drop_unlock: + spin_unlock_irqrestore(&idev->lock, flags); +drop: + net_warn_ratelimited("%s: dropping packet - %s\n", __func__, msg); + dev_kfree_skb_any(skb); + ndev->stats.tx_errors++; + ndev->stats.tx_dropped++; + /* Don't even think about returning NET_XMIT_DROP (=1) here! + * In fact any retval!=0 causes the packet scheduler to requeue the + * packet for later retry of transmission - which isn't exactly + * what we want after we've just called dev_kfree_skb_any ;-) + */ + return NETDEV_TX_OK; +} + +static void vlsi_tx_interrupt(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = netdev_priv(ndev); + struct vlsi_ring *r = idev->tx_ring; + struct ring_descr *rd; + unsigned iobase; + int ret; + u16 config; + + for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { + + if (rd_is_active(rd)) + break; + + ret = vlsi_process_tx(r, rd); + + if (ret < 0) { + ret = -ret; + ndev->stats.tx_errors++; + if (ret & VLSI_TX_DROP) + ndev->stats.tx_dropped++; + if (ret & VLSI_TX_FIFO) + ndev->stats.tx_fifo_errors++; + } + else if (ret > 0){ + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += ret; + } + } + + iobase = ndev->base_addr; + + if (idev->new_baud && rd == NULL) /* tx ring empty and speed change pending */ + vlsi_set_baud(idev, iobase); + + config = inw(iobase+VLSI_PIO_IRCFG); + if (rd == NULL) /* tx ring empty: re-enable rx */ + outw((config & ~IRCFG_ENTX) | IRCFG_ENRX, iobase+VLSI_PIO_IRCFG); + + else if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) { + int fifocnt; + + fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + if (fifocnt != 0) { + pr_debug("%s: rx fifo not empty(%d)\n", + __func__, fifocnt); + } + outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG); + } + + outw(0, iobase+VLSI_PIO_PROMPT); + + if (netif_queue_stopped(ndev) && !idev->new_baud) { + netif_wake_queue(ndev); + pr_debug("%s: queue awoken\n", __func__); + } +} + +/* caller must have stopped the controller from busmastering */ + +static void vlsi_unarm_tx(vlsi_irda_dev_t *idev) +{ + struct net_device *ndev = pci_get_drvdata(idev->pdev); + struct vlsi_ring *r = idev->tx_ring; + struct ring_descr *rd; + int ret; + + for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { + + ret = 0; + if (rd_is_active(rd)) { + rd_set_status(rd, 0); + rd_set_count(rd, 0); + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); + if (rd->skb) { + dev_kfree_skb_any(rd->skb); + rd->skb = NULL; + } + pr_debug("%s - dropping tx packet\n", __func__); + ret = -VLSI_TX_DROP; + } + else + ret = vlsi_process_tx(r, rd); + + if (ret < 0) { + ret = -ret; + ndev->stats.tx_errors++; + if (ret & VLSI_TX_DROP) + ndev->stats.tx_dropped++; + if (ret & VLSI_TX_FIFO) + ndev->stats.tx_fifo_errors++; + } + else if (ret > 0){ + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += ret; + } + } + +} + +/********************************************************/ + +static int vlsi_start_clock(struct pci_dev *pdev) +{ + u8 clkctl, lock; + int i, count; + + if (clksrc < 2) { /* auto or PLL: try PLL */ + clkctl = CLKCTL_PD_INV | CLKCTL_CLKSTP; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + + /* procedure to detect PLL lock synchronisation: + * after 0.5 msec initial delay we expect to find 3 PLL lock + * indications within 10 msec for successful PLL detection. + */ + udelay(500); + count = 0; + for (i = 500; i <= 10000; i += 50) { /* max 10 msec */ + pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &lock); + if (lock&CLKCTL_LOCK) { + if (++count >= 3) + break; + } + udelay(50); + } + if (count < 3) { + if (clksrc == 1) { /* explicitly asked for PLL hence bail out */ + net_err_ratelimited("%s: no PLL or failed to lock!\n", + __func__); + clkctl = CLKCTL_CLKSTP; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + return -1; + } + else /* was: clksrc=0(auto) */ + clksrc = 3; /* fallback to 40MHz XCLK (OB800) */ + + pr_debug("%s: PLL not locked, fallback to clksrc=%d\n", + __func__, clksrc); + } + else + clksrc = 1; /* got successful PLL lock */ + } + + if (clksrc != 1) { + /* we get here if either no PLL detected in auto-mode or + an external clock source was explicitly specified */ + + clkctl = CLKCTL_EXTCLK | CLKCTL_CLKSTP; + if (clksrc == 3) + clkctl |= CLKCTL_XCKSEL; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + + /* no way to test for working XCLK */ + } + else + pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); + + /* ok, now going to connect the chip with the clock source */ + + clkctl &= ~CLKCTL_CLKSTP; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + + return 0; +} + +static void vlsi_stop_clock(struct pci_dev *pdev) +{ + u8 clkctl; + + /* disconnect chip from clock source */ + pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); + clkctl |= CLKCTL_CLKSTP; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + + /* disable all clock sources */ + clkctl &= ~(CLKCTL_EXTCLK | CLKCTL_PD_INV); + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); +} + +/********************************************************/ + +/* writing all-zero to the VLSI PCI IO register area seems to prevent + * some occasional situations where the hardware fails (symptoms are + * what appears as stalled tx/rx state machines, i.e. everything ok for + * receive or transmit but hw makes no progress or is unable to access + * the bus memory locations). + * Best place to call this is immediately after/before the internal clock + * gets started/stopped. + */ + +static inline void vlsi_clear_regs(unsigned iobase) +{ + unsigned i; + const unsigned chip_io_extent = 32; + + for (i = 0; i < chip_io_extent; i += sizeof(u16)) + outw(0, iobase + i); +} + +static int vlsi_init_chip(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + vlsi_irda_dev_t *idev = netdev_priv(ndev); + unsigned iobase; + u16 ptr; + + /* start the clock and clean the registers */ + + if (vlsi_start_clock(pdev)) { + net_err_ratelimited("%s: no valid clock source\n", __func__); + return -1; + } + iobase = ndev->base_addr; + vlsi_clear_regs(iobase); + + outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* w/c pending IRQ, disable all INT */ + + outw(0, iobase+VLSI_PIO_IRENABLE); /* disable IrPHY-interface */ + + /* disable everything, particularly IRCFG_MSTR - (also resetting the RING_PTR) */ + + outw(0, iobase+VLSI_PIO_IRCFG); + wmb(); + + outw(MAX_PACKET_LENGTH, iobase+VLSI_PIO_MAXPKT); /* max possible value=0x0fff */ + + outw(BUS_TO_RINGBASE(idev->busaddr), iobase+VLSI_PIO_RINGBASE); + + outw(TX_RX_TO_RINGSIZE(idev->tx_ring->size, idev->rx_ring->size), + iobase+VLSI_PIO_RINGSIZE); + + ptr = inw(iobase+VLSI_PIO_RINGPTR); + atomic_set(&idev->rx_ring->head, RINGPTR_GET_RX(ptr)); + atomic_set(&idev->rx_ring->tail, RINGPTR_GET_RX(ptr)); + atomic_set(&idev->tx_ring->head, RINGPTR_GET_TX(ptr)); + atomic_set(&idev->tx_ring->tail, RINGPTR_GET_TX(ptr)); + + vlsi_set_baud(idev, iobase); /* idev->new_baud used as provided by caller */ + + outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* just in case - w/c pending IRQ's */ + wmb(); + + /* DO NOT BLINDLY ENABLE IRINTR_ACTEN! + * basically every received pulse fires an ACTIVITY-INT + * leading to >>1000 INT's per second instead of few 10 + */ + + outb(IRINTR_RPKTEN|IRINTR_TPKTEN, iobase+VLSI_PIO_IRINTR); + + return 0; +} + +static int vlsi_start_hw(vlsi_irda_dev_t *idev) +{ + struct pci_dev *pdev = idev->pdev; + struct net_device *ndev = pci_get_drvdata(pdev); + unsigned iobase = ndev->base_addr; + u8 byte; + + /* we don't use the legacy UART, disable its address decoding */ + + pci_read_config_byte(pdev, VLSI_PCI_IRMISC, &byte); + byte &= ~(IRMISC_UARTEN | IRMISC_UARTTST); + pci_write_config_byte(pdev, VLSI_PCI_IRMISC, byte); + + /* enable PCI busmaster access to our 16MB page */ + + pci_write_config_byte(pdev, VLSI_PCI_MSTRPAGE, MSTRPAGE_VALUE); + pci_set_master(pdev); + + if (vlsi_init_chip(pdev) < 0) { + pci_disable_device(pdev); + return -1; + } + + vlsi_fill_rx(idev->rx_ring); + + idev->last_rx = ktime_get(); /* first mtt may start from now on */ + + outw(0, iobase+VLSI_PIO_PROMPT); /* kick hw state machine */ + + return 0; +} + +static int vlsi_stop_hw(vlsi_irda_dev_t *idev) +{ + struct pci_dev *pdev = idev->pdev; + struct net_device *ndev = pci_get_drvdata(pdev); + unsigned iobase = ndev->base_addr; + unsigned long flags; + + spin_lock_irqsave(&idev->lock,flags); + outw(0, iobase+VLSI_PIO_IRENABLE); + outw(0, iobase+VLSI_PIO_IRCFG); /* disable everything */ + + /* disable and w/c irqs */ + outb(0, iobase+VLSI_PIO_IRINTR); + wmb(); + outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); + spin_unlock_irqrestore(&idev->lock,flags); + + vlsi_unarm_tx(idev); + vlsi_unarm_rx(idev); + + vlsi_clear_regs(iobase); + vlsi_stop_clock(pdev); + + pci_disable_device(pdev); + + return 0; +} + +/**************************************************************/ + +static void vlsi_tx_timeout(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = netdev_priv(ndev); + + + vlsi_reg_debug(ndev->base_addr, __func__); + vlsi_ring_debug(idev->tx_ring); + + if (netif_running(ndev)) + netif_stop_queue(ndev); + + vlsi_stop_hw(idev); + + /* now simply restart the whole thing */ + + if (!idev->new_baud) + idev->new_baud = idev->baud; /* keep current baudrate */ + + if (vlsi_start_hw(idev)) + net_err_ratelimited("%s: failed to restart hw - %s(%s) unusable!\n", + __func__, pci_name(idev->pdev), ndev->name); + else + netif_start_queue(ndev); +} + +static int vlsi_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +{ + vlsi_irda_dev_t *idev = netdev_priv(ndev); + struct if_irda_req *irq = (struct if_irda_req *) rq; + unsigned long flags; + u16 fifocnt; + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + spin_lock_irqsave(&idev->lock, flags); + idev->new_baud = irq->ifr_baudrate; + /* when called from userland there might be a minor race window here + * if the stack tries to change speed concurrently - which would be + * pretty strange anyway with the userland having full control... + */ + vlsi_set_baud(idev, ndev->base_addr); + spin_unlock_irqrestore(&idev->lock, flags); + break; + case SIOCSMEDIABUSY: + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + irda_device_set_media_busy(ndev, TRUE); + break; + case SIOCGRECEIVING: + /* the best we can do: check whether there are any bytes in rx fifo. + * The trustable window (in case some data arrives just afterwards) + * may be as short as 1usec or so at 4Mbps. + */ + fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + irq->ifr_receiving = (fifocnt!=0) ? 1 : 0; + break; + default: + net_warn_ratelimited("%s: notsupp - cmd=%04x\n", + __func__, cmd); + ret = -EOPNOTSUPP; + } + + return ret; +} + +/********************************************************/ + +static irqreturn_t vlsi_interrupt(int irq, void *dev_instance) +{ + struct net_device *ndev = dev_instance; + vlsi_irda_dev_t *idev = netdev_priv(ndev); + unsigned iobase; + u8 irintr; + int boguscount = 5; + unsigned long flags; + int handled = 0; + + iobase = ndev->base_addr; + spin_lock_irqsave(&idev->lock,flags); + do { + irintr = inb(iobase+VLSI_PIO_IRINTR); + mb(); + outb(irintr, iobase+VLSI_PIO_IRINTR); /* acknowledge asap */ + + if (!(irintr&=IRINTR_INT_MASK)) /* not our INT - probably shared */ + break; + + handled = 1; + + if (unlikely(!(irintr & ~IRINTR_ACTIVITY))) + break; /* nothing todo if only activity */ + + if (irintr&IRINTR_RPKTINT) + vlsi_rx_interrupt(ndev); + + if (irintr&IRINTR_TPKTINT) + vlsi_tx_interrupt(ndev); + + } while (--boguscount > 0); + spin_unlock_irqrestore(&idev->lock,flags); + + if (boguscount <= 0) + net_info_ratelimited("%s: too much work in interrupt!\n", + __func__); + return IRQ_RETVAL(handled); +} + +/********************************************************/ + +static int vlsi_open(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = netdev_priv(ndev); + int err = -EAGAIN; + char hwname[32]; + + if (pci_request_regions(idev->pdev, drivername)) { + net_warn_ratelimited("%s: io resource busy\n", __func__); + goto errout; + } + ndev->base_addr = pci_resource_start(idev->pdev,0); + ndev->irq = idev->pdev->irq; + + /* under some rare occasions the chip apparently comes up with + * IRQ's pending. We better w/c pending IRQ and disable them all + */ + + outb(IRINTR_INT_MASK, ndev->base_addr+VLSI_PIO_IRINTR); + + if (request_irq(ndev->irq, vlsi_interrupt, IRQF_SHARED, + drivername, ndev)) { + net_warn_ratelimited("%s: couldn't get IRQ: %d\n", + __func__, ndev->irq); + goto errout_io; + } + + if ((err = vlsi_create_hwif(idev)) != 0) + goto errout_irq; + + sprintf(hwname, "VLSI-FIR @ 0x%04x", (unsigned)ndev->base_addr); + idev->irlap = irlap_open(ndev,&idev->qos,hwname); + if (!idev->irlap) + goto errout_free_ring; + + idev->last_rx = ktime_get(); /* first mtt may start from now on */ + + idev->new_baud = 9600; /* start with IrPHY using 9600(SIR) mode */ + + if ((err = vlsi_start_hw(idev)) != 0) + goto errout_close_irlap; + + netif_start_queue(ndev); + + net_info_ratelimited("%s: device %s operational\n", + __func__, ndev->name); + + return 0; + +errout_close_irlap: + irlap_close(idev->irlap); +errout_free_ring: + vlsi_destroy_hwif(idev); +errout_irq: + free_irq(ndev->irq,ndev); +errout_io: + pci_release_regions(idev->pdev); +errout: + return err; +} + +static int vlsi_close(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = netdev_priv(ndev); + + netif_stop_queue(ndev); + + if (idev->irlap) + irlap_close(idev->irlap); + idev->irlap = NULL; + + vlsi_stop_hw(idev); + + vlsi_destroy_hwif(idev); + + free_irq(ndev->irq,ndev); + + pci_release_regions(idev->pdev); + + net_info_ratelimited("%s: device %s stopped\n", __func__, ndev->name); + + return 0; +} + +static const struct net_device_ops vlsi_netdev_ops = { + .ndo_open = vlsi_open, + .ndo_stop = vlsi_close, + .ndo_start_xmit = vlsi_hard_start_xmit, + .ndo_do_ioctl = vlsi_ioctl, + .ndo_tx_timeout = vlsi_tx_timeout, +}; + +static int vlsi_irda_init(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = netdev_priv(ndev); + struct pci_dev *pdev = idev->pdev; + + ndev->irq = pdev->irq; + ndev->base_addr = pci_resource_start(pdev,0); + + /* PCI busmastering + * see include file for details why we need these 2 masks, in this order! + */ + + if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW) || + pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) { + net_err_ratelimited("%s: aborting due to PCI BM-DMA address limitations\n", + __func__); + return -1; + } + + irda_init_max_qos_capabilies(&idev->qos); + + /* the VLSI82C147 does not support 576000! */ + + idev->qos.baud_rate.bits = IR_2400 | IR_9600 + | IR_19200 | IR_38400 | IR_57600 | IR_115200 + | IR_1152000 | (IR_4000000 << 8); + + idev->qos.min_turn_time.bits = qos_mtt_bits; + + irda_qos_bits_to_value(&idev->qos); + + /* currently no public media definitions for IrDA */ + + ndev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; + ndev->if_port = IF_PORT_UNKNOWN; + + ndev->netdev_ops = &vlsi_netdev_ops; + ndev->watchdog_timeo = 500*HZ/1000; /* max. allowed turn time for IrLAP */ + + SET_NETDEV_DEV(ndev, &pdev->dev); + + return 0; +} + +/**************************************************************/ + +static int +vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net_device *ndev; + vlsi_irda_dev_t *idev; + + if (pci_enable_device(pdev)) + goto out; + else + pdev->current_state = 0; /* hw must be running now */ + + net_info_ratelimited("%s: IrDA PCI controller %s detected\n", + drivername, pci_name(pdev)); + + if ( !pci_resource_start(pdev,0) || + !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) { + net_err_ratelimited("%s: bar 0 invalid", __func__); + goto out_disable; + } + + ndev = alloc_irdadev(sizeof(*idev)); + if (ndev==NULL) { + net_err_ratelimited("%s: Unable to allocate device memory.\n", + __func__); + goto out_disable; + } + + idev = netdev_priv(ndev); + + spin_lock_init(&idev->lock); + mutex_init(&idev->mtx); + mutex_lock(&idev->mtx); + idev->pdev = pdev; + + if (vlsi_irda_init(ndev) < 0) + goto out_freedev; + + if (register_netdev(ndev) < 0) { + net_err_ratelimited("%s: register_netdev failed\n", __func__); + goto out_freedev; + } + + if (vlsi_proc_root != NULL) { + struct proc_dir_entry *ent; + + ent = proc_create_data(ndev->name, S_IFREG|S_IRUGO, + vlsi_proc_root, VLSI_PROC_FOPS, ndev); + if (!ent) { + net_warn_ratelimited("%s: failed to create proc entry\n", + __func__); + } else { + proc_set_size(ent, 0); + } + idev->proc_entry = ent; + } + net_info_ratelimited("%s: registered device %s\n", + drivername, ndev->name); + + pci_set_drvdata(pdev, ndev); + mutex_unlock(&idev->mtx); + + return 0; + +out_freedev: + mutex_unlock(&idev->mtx); + free_netdev(ndev); +out_disable: + pci_disable_device(pdev); +out: + return -ENODEV; +} + +static void vlsi_irda_remove(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + vlsi_irda_dev_t *idev; + + if (!ndev) { + net_err_ratelimited("%s: lost netdevice?\n", drivername); + return; + } + + unregister_netdev(ndev); + + idev = netdev_priv(ndev); + mutex_lock(&idev->mtx); + if (idev->proc_entry) { + remove_proc_entry(ndev->name, vlsi_proc_root); + idev->proc_entry = NULL; + } + mutex_unlock(&idev->mtx); + + free_netdev(ndev); + + net_info_ratelimited("%s: %s removed\n", drivername, pci_name(pdev)); +} + +#ifdef CONFIG_PM + +/* The Controller doesn't provide PCI PM capabilities as defined by PCI specs. + * Some of the Linux PCI-PM code however depends on this, for example in + * pci_set_power_state(). So we have to take care to perform the required + * operations on our own (particularly reflecting the pdev->current_state) + * otherwise we might get cheated by pci-pm. + */ + + +static int vlsi_irda_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + vlsi_irda_dev_t *idev; + + if (!ndev) { + net_err_ratelimited("%s - %s: no netdevice\n", + __func__, pci_name(pdev)); + return 0; + } + idev = netdev_priv(ndev); + mutex_lock(&idev->mtx); + if (pdev->current_state != 0) { /* already suspended */ + if (state.event > pdev->current_state) { /* simply go deeper */ + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + pdev->current_state = state.event; + } + else + net_err_ratelimited("%s - %s: invalid suspend request %u -> %u\n", + __func__, pci_name(pdev), + pdev->current_state, state.event); + mutex_unlock(&idev->mtx); + return 0; + } + + if (netif_running(ndev)) { + netif_device_detach(ndev); + vlsi_stop_hw(idev); + pci_save_state(pdev); + if (!idev->new_baud) + /* remember speed settings to restore on resume */ + idev->new_baud = idev->baud; + } + + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + pdev->current_state = state.event; + idev->resume_ok = 1; + mutex_unlock(&idev->mtx); + return 0; +} + +static int vlsi_irda_resume(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + vlsi_irda_dev_t *idev; + + if (!ndev) { + net_err_ratelimited("%s - %s: no netdevice\n", + __func__, pci_name(pdev)); + return 0; + } + idev = netdev_priv(ndev); + mutex_lock(&idev->mtx); + if (pdev->current_state == 0) { + mutex_unlock(&idev->mtx); + net_warn_ratelimited("%s - %s: already resumed\n", + __func__, pci_name(pdev)); + return 0; + } + + pci_set_power_state(pdev, PCI_D0); + pdev->current_state = PM_EVENT_ON; + + if (!idev->resume_ok) { + /* should be obsolete now - but used to happen due to: + * - pci layer initially setting pdev->current_state = 4 (unknown) + * - pci layer did not walk the save_state-tree (might be APM problem) + * so we could not refuse to suspend from undefined state + * - vlsi_irda_suspend detected invalid state and refused to save + * configuration for resume - but was too late to stop suspending + * - vlsi_irda_resume got screwed when trying to resume from garbage + * + * now we explicitly set pdev->current_state = 0 after enabling the + * device and independently resume_ok should catch any garbage config. + */ + net_warn_ratelimited("%s - hm, nothing to resume?\n", __func__); + mutex_unlock(&idev->mtx); + return 0; + } + + if (netif_running(ndev)) { + pci_restore_state(pdev); + vlsi_start_hw(idev); + netif_device_attach(ndev); + } + idev->resume_ok = 0; + mutex_unlock(&idev->mtx); + return 0; +} + +#endif /* CONFIG_PM */ + +/*********************************************************/ + +static struct pci_driver vlsi_irda_driver = { + .name = drivername, + .id_table = vlsi_irda_table, + .probe = vlsi_irda_probe, + .remove = vlsi_irda_remove, +#ifdef CONFIG_PM + .suspend = vlsi_irda_suspend, + .resume = vlsi_irda_resume, +#endif +}; + +#define PROC_DIR ("driver/" DRIVER_NAME) + +static int __init vlsi_mod_init(void) +{ + int i, ret; + + if (clksrc < 0 || clksrc > 3) { + net_err_ratelimited("%s: invalid clksrc=%d\n", + drivername, clksrc); + return -1; + } + + for (i = 0; i < 2; i++) { + switch(ringsize[i]) { + case 4: + case 8: + case 16: + case 32: + case 64: + break; + default: + net_warn_ratelimited("%s: invalid %s ringsize %d, using default=8\n", + drivername, + i ? "rx" : "tx", + ringsize[i]); + ringsize[i] = 8; + break; + } + } + + sirpulse = !!sirpulse; + + /* proc_mkdir returns NULL if !CONFIG_PROC_FS. + * Failure to create the procfs entry is handled like running + * without procfs - it's not required for the driver to work. + */ + vlsi_proc_root = proc_mkdir(PROC_DIR, NULL); + + ret = pci_register_driver(&vlsi_irda_driver); + + if (ret && vlsi_proc_root) + remove_proc_entry(PROC_DIR, NULL); + return ret; + +} + +static void __exit vlsi_mod_exit(void) +{ + pci_unregister_driver(&vlsi_irda_driver); + if (vlsi_proc_root) + remove_proc_entry(PROC_DIR, NULL); +} + +module_init(vlsi_mod_init); +module_exit(vlsi_mod_exit); diff --git a/drivers/staging/irda/drivers/vlsi_ir.h b/drivers/staging/irda/drivers/vlsi_ir.h new file mode 100644 index 000000000000..f9db2ce4c5c6 --- /dev/null +++ b/drivers/staging/irda/drivers/vlsi_ir.h @@ -0,0 +1,757 @@ + +/********************************************************************* + * + * vlsi_ir.h: VLSI82C147 PCI IrDA controller driver for Linux + * + * Version: 0.5 + * + * Copyright (c) 2001-2003 Martin Diehl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + ********************************************************************/ + +#ifndef IRDA_VLSI_FIR_H +#define IRDA_VLSI_FIR_H + +/* ================================================================ + * compatibility stuff + */ + +/* definitions not present in pci_ids.h */ + +#ifndef PCI_CLASS_WIRELESS_IRDA +#define PCI_CLASS_WIRELESS_IRDA 0x0d00 +#endif + +#ifndef PCI_CLASS_SUBCLASS_MASK +#define PCI_CLASS_SUBCLASS_MASK 0xffff +#endif + +/* ================================================================ */ + +/* non-standard PCI registers */ + +enum vlsi_pci_regs { + VLSI_PCI_CLKCTL = 0x40, /* chip clock input control */ + VLSI_PCI_MSTRPAGE = 0x41, /* addr [31:24] for all busmaster cycles */ + VLSI_PCI_IRMISC = 0x42 /* mainly legacy UART related */ +}; + +/* ------------------------------------------ */ + +/* VLSI_PCI_CLKCTL: Clock Control Register (u8, rw) */ + +/* Three possible clock sources: either on-chip 48MHz PLL or + * external clock applied to EXTCLK pin. External clock may + * be either 48MHz or 40MHz, which is indicated by XCKSEL. + * CLKSTP controls whether the selected clock source gets + * connected to the IrDA block. + * + * On my HP OB-800 the BIOS sets external 40MHz clock as source + * when IrDA enabled and I've never detected any PLL lock success. + * Apparently the 14.3...MHz OSC input required for the PLL to work + * is not connected and the 40MHz EXTCLK is provided externally. + * At least this is what makes the driver working for me. + */ + +enum vlsi_pci_clkctl { + + /* PLL control */ + + CLKCTL_PD_INV = 0x04, /* PD#: inverted power down signal, + * i.e. PLL is powered, if PD_INV set */ + CLKCTL_LOCK = 0x40, /* (ro) set, if PLL is locked */ + + /* clock source selection */ + + CLKCTL_EXTCLK = 0x20, /* set to select external clock input, not PLL */ + CLKCTL_XCKSEL = 0x10, /* set to indicate EXTCLK is 40MHz, not 48MHz */ + + /* IrDA block control */ + + CLKCTL_CLKSTP = 0x80, /* set to disconnect from selected clock source */ + CLKCTL_WAKE = 0x08 /* set to enable wakeup feature: whenever IR activity + * is detected, PD_INV gets set(?) and CLKSTP cleared */ +}; + +/* ------------------------------------------ */ + +/* VLSI_PCI_MSTRPAGE: Master Page Register (u8, rw) and busmastering stuff */ + +#define DMA_MASK_USED_BY_HW 0xffffffff +#define DMA_MASK_MSTRPAGE 0x00ffffff +#define MSTRPAGE_VALUE (DMA_MASK_MSTRPAGE >> 24) + + /* PCI busmastering is somewhat special for this guy - in short: + * + * We select to operate using fixed MSTRPAGE=0, use ISA DMA + * address restrictions to make the PCI BM api aware of this, + * but ensure the hardware is dealing with real 32bit access. + * + * In detail: + * The chip executes normal 32bit busmaster cycles, i.e. + * drives all 32 address lines. These addresses however are + * composed of [0:23] taken from various busaddr-pointers + * and [24:31] taken from the MSTRPAGE register in the VLSI82C147 + * config space. Therefore _all_ busmastering must be + * targeted to/from one single 16MB (busaddr-) superpage! + * The point is to make sure all the allocations for memory + * locations with busmaster access (ring descriptors, buffers) + * are indeed bus-mappable to the same 16MB range (for x86 this + * means they must reside in the same 16MB physical memory address + * range). The only constraint we have which supports "several objects + * mappable to common 16MB range" paradigma, is the old ISA DMA + * restriction to the first 16MB of physical address range. + * Hence the approach here is to enable PCI busmaster support using + * the correct 32bit dma-mask used by the chip. Afterwards the device's + * dma-mask gets restricted to 24bit, which must be honoured somehow by + * all allocations for memory areas to be exposed to the chip ... + * + * Note: + * Don't be surprised to get "Setting latency timer..." messages every + * time when PCI busmastering is enabled for the chip. + * The chip has its PCI latency timer RO fixed at 0 - which is not a + * problem here, because it is never requesting _burst_ transactions. + */ + +/* ------------------------------------------ */ + +/* VLSI_PCIIRMISC: IR Miscellaneous Register (u8, rw) */ + +/* legacy UART emulation - not used by this driver - would require: + * (see below for some register-value definitions) + * + * - IRMISC_UARTEN must be set to enable UART address decoding + * - IRMISC_UARTSEL configured + * - IRCFG_MASTER must be cleared + * - IRCFG_SIR must be set + * - IRENABLE_PHYANDCLOCK must be asserted 0->1 (and hence IRENABLE_SIR_ON) + */ + +enum vlsi_pci_irmisc { + + /* IR transceiver control */ + + IRMISC_IRRAIL = 0x40, /* (ro?) IR rail power indication (and control?) + * 0=3.3V / 1=5V. Probably set during power-on? + * unclear - not touched by driver */ + IRMISC_IRPD = 0x08, /* transceiver power down, if set */ + + /* legacy UART control */ + + IRMISC_UARTTST = 0x80, /* UART test mode - "always write 0" */ + IRMISC_UARTEN = 0x04, /* enable UART address decoding */ + + /* bits [1:0] IRMISC_UARTSEL to select legacy UART address */ + + IRMISC_UARTSEL_3f8 = 0x00, + IRMISC_UARTSEL_2f8 = 0x01, + IRMISC_UARTSEL_3e8 = 0x02, + IRMISC_UARTSEL_2e8 = 0x03 +}; + +/* ================================================================ */ + +/* registers mapped to 32 byte PCI IO space */ + +/* note: better access all registers at the indicated u8/u16 size + * although some of them contain only 1 byte of information. + * some of them (particaluarly PROMPT and IRCFG) ignore + * access when using the wrong addressing mode! + */ + +enum vlsi_pio_regs { + VLSI_PIO_IRINTR = 0x00, /* interrupt enable/request (u8, rw) */ + VLSI_PIO_RINGPTR = 0x02, /* rx/tx ring pointer (u16, ro) */ + VLSI_PIO_RINGBASE = 0x04, /* [23:10] of ring address (u16, rw) */ + VLSI_PIO_RINGSIZE = 0x06, /* rx/tx ring size (u16, rw) */ + VLSI_PIO_PROMPT = 0x08, /* triggers ring processing (u16, wo) */ + /* 0x0a-0x0f: reserved / duplicated UART regs */ + VLSI_PIO_IRCFG = 0x10, /* configuration select (u16, rw) */ + VLSI_PIO_SIRFLAG = 0x12, /* BOF/EOF for filtered SIR (u16, ro) */ + VLSI_PIO_IRENABLE = 0x14, /* enable and status register (u16, rw/ro) */ + VLSI_PIO_PHYCTL = 0x16, /* physical layer current status (u16, ro) */ + VLSI_PIO_NPHYCTL = 0x18, /* next physical layer select (u16, rw) */ + VLSI_PIO_MAXPKT = 0x1a, /* [11:0] max len for packet receive (u16, rw) */ + VLSI_PIO_RCVBCNT = 0x1c /* current receive-FIFO byte count (u16, ro) */ + /* 0x1e-0x1f: reserved / duplicated UART regs */ +}; + +/* ------------------------------------------ */ + +/* VLSI_PIO_IRINTR: Interrupt Register (u8, rw) */ + +/* enable-bits: + * 1 = enable / 0 = disable + * interrupt condition bits: + * set according to corresponding interrupt source + * (regardless of the state of the enable bits) + * enable bit status indicates whether interrupt gets raised + * write-to-clear + * note: RPKTINT and TPKTINT behave different in legacy UART mode (which we don't use :-) + */ + +enum vlsi_pio_irintr { + IRINTR_ACTEN = 0x80, /* activity interrupt enable */ + IRINTR_ACTIVITY = 0x40, /* activity monitor (traffic detected) */ + IRINTR_RPKTEN = 0x20, /* receive packet interrupt enable*/ + IRINTR_RPKTINT = 0x10, /* rx-packet transferred from fifo to memory finished */ + IRINTR_TPKTEN = 0x08, /* transmit packet interrupt enable */ + IRINTR_TPKTINT = 0x04, /* last bit of tx-packet+crc shifted to ir-pulser */ + IRINTR_OE_EN = 0x02, /* UART rx fifo overrun error interrupt enable */ + IRINTR_OE_INT = 0x01 /* UART rx fifo overrun error (read LSR to clear) */ +}; + +/* we use this mask to check whether the (shared PCI) interrupt is ours */ + +#define IRINTR_INT_MASK (IRINTR_ACTIVITY|IRINTR_RPKTINT|IRINTR_TPKTINT) + +/* ------------------------------------------ */ + +/* VLSI_PIO_RINGPTR: Ring Pointer Read-Back Register (u16, ro) */ + +/* _both_ ring pointers are indices relative to the _entire_ rx,tx-ring! + * i.e. the referenced descriptor is located + * at RINGBASE + PTR * sizeof(descr) for rx and tx + * therefore, the tx-pointer has offset MAX_RING_DESCR + */ + +#define MAX_RING_DESCR 64 /* tx, rx rings may contain up to 64 descr each */ + +#define RINGPTR_RX_MASK (MAX_RING_DESCR-1) +#define RINGPTR_TX_MASK ((MAX_RING_DESCR-1)<<8) + +#define RINGPTR_GET_RX(p) ((p)&RINGPTR_RX_MASK) +#define RINGPTR_GET_TX(p) (((p)&RINGPTR_TX_MASK)>>8) + +/* ------------------------------------------ */ + +/* VLSI_PIO_RINGBASE: Ring Pointer Base Address Register (u16, ro) */ + +/* Contains [23:10] part of the ring base (bus-) address + * which must be 1k-alinged. [31:24] is taken from + * VLSI_PCI_MSTRPAGE above. + * The controller initiates non-burst PCI BM cycles to + * fetch and update the descriptors in the ring. + * Once fetched, the descriptor remains cached onchip + * until it gets closed and updated due to the ring + * processing state machine. + * The entire ring area is split in rx and tx areas with each + * area consisting of 64 descriptors of 8 bytes each. + * The rx(tx) ring is located at ringbase+0 (ringbase+64*8). + */ + +#define BUS_TO_RINGBASE(p) (((p)>>10)&0x3fff) + +/* ------------------------------------------ */ + +/* VLSI_PIO_RINGSIZE: Ring Size Register (u16, rw) */ + +/* bit mask to indicate the ring size to be used for rx and tx. + * possible values encoded bits + * 4 0000 + * 8 0001 + * 16 0011 + * 32 0111 + * 64 1111 + * located at [15:12] for tx and [11:8] for rx ([7:0] unused) + * + * note: probably a good idea to have IRCFG_MSTR cleared when writing + * this so the state machines are stopped and the RINGPTR is reset! + */ + +#define SIZE_TO_BITS(num) ((((num)-1)>>2)&0x0f) +#define TX_RX_TO_RINGSIZE(tx,rx) ((SIZE_TO_BITS(tx)<<12)|(SIZE_TO_BITS(rx)<<8)) +#define RINGSIZE_TO_RXSIZE(rs) ((((rs)&0x0f00)>>6)+4) +#define RINGSIZE_TO_TXSIZE(rs) ((((rs)&0xf000)>>10)+4) + + +/* ------------------------------------------ */ + +/* VLSI_PIO_PROMPT: Ring Prompting Register (u16, write-to-start) */ + +/* writing any value kicks the ring processing state machines + * for both tx, rx rings as follows: + * - active rings (currently owning an active descriptor) + * ignore the prompt and continue + * - idle rings fetch the next descr from the ring and start + * their processing + */ + +/* ------------------------------------------ */ + +/* VLSI_PIO_IRCFG: IR Config Register (u16, rw) */ + +/* notes: + * - not more than one SIR/MIR/FIR bit must be set at any time + * - SIR, MIR, FIR and CRC16 select the configuration which will + * be applied on next 0->1 transition of IRENABLE_PHYANDCLOCK (see below). + * - besides allowing the PCI interface to execute busmaster cycles + * and therefore the ring SM to operate, the MSTR bit has side-effects: + * when MSTR is cleared, the RINGPTR's get reset and the legacy UART mode + * (in contrast to busmaster access mode) gets enabled. + * - clearing ENRX or setting ENTX while data is received may stall the + * receive fifo until ENRX reenabled _and_ another packet arrives + * - SIRFILT means the chip performs the required unwrapping of hardware + * headers (XBOF's, BOF/EOF) and un-escaping in the _receive_ direction. + * Only the resulting IrLAP payload is copied to the receive buffers - + * but with the 16bit FCS still encluded. Question remains, whether it + * was already checked or we should do it before passing the packet to IrLAP? + */ + +enum vlsi_pio_ircfg { + IRCFG_LOOP = 0x4000, /* enable loopback test mode */ + IRCFG_ENTX = 0x1000, /* transmit enable */ + IRCFG_ENRX = 0x0800, /* receive enable */ + IRCFG_MSTR = 0x0400, /* master enable */ + IRCFG_RXANY = 0x0200, /* receive any packet */ + IRCFG_CRC16 = 0x0080, /* 16bit (not 32bit) CRC select for MIR/FIR */ + IRCFG_FIR = 0x0040, /* FIR 4PPM encoding mode enable */ + IRCFG_MIR = 0x0020, /* MIR HDLC encoding mode enable */ + IRCFG_SIR = 0x0010, /* SIR encoding mode enable */ + IRCFG_SIRFILT = 0x0008, /* enable SIR decode filter (receiver unwrapping) */ + IRCFG_SIRTEST = 0x0004, /* allow SIR decode filter when not in SIR mode */ + IRCFG_TXPOL = 0x0002, /* invert tx polarity when set */ + IRCFG_RXPOL = 0x0001 /* invert rx polarity when set */ +}; + +/* ------------------------------------------ */ + +/* VLSI_PIO_SIRFLAG: SIR Flag Register (u16, ro) */ + +/* register contains hardcoded BOF=0xc0 at [7:0] and EOF=0xc1 at [15:8] + * which is used for unwrapping received frames in SIR decode-filter mode + */ + +/* ------------------------------------------ */ + +/* VLSI_PIO_IRENABLE: IR Enable Register (u16, rw/ro) */ + +/* notes: + * - IREN acts as gate for latching the configured IR mode information + * from IRCFG and IRPHYCTL when IREN=reset and applying them when + * IREN gets set afterwards. + * - ENTXST reflects IRCFG_ENTX + * - ENRXST = IRCFG_ENRX && (!IRCFG_ENTX || IRCFG_LOOP) + */ + +enum vlsi_pio_irenable { + IRENABLE_PHYANDCLOCK = 0x8000, /* enable IR phy and gate the mode config (rw) */ + IRENABLE_CFGER = 0x4000, /* mode configuration error (ro) */ + IRENABLE_FIR_ON = 0x2000, /* FIR on status (ro) */ + IRENABLE_MIR_ON = 0x1000, /* MIR on status (ro) */ + IRENABLE_SIR_ON = 0x0800, /* SIR on status (ro) */ + IRENABLE_ENTXST = 0x0400, /* transmit enable status (ro) */ + IRENABLE_ENRXST = 0x0200, /* Receive enable status (ro) */ + IRENABLE_CRC16_ON = 0x0100 /* 16bit (not 32bit) CRC enabled status (ro) */ +}; + +#define IRENABLE_MASK 0xff00 /* Read mask */ + +/* ------------------------------------------ */ + +/* VLSI_PIO_PHYCTL: IR Physical Layer Current Control Register (u16, ro) */ + +/* read-back of the currently applied physical layer status. + * applied from VLSI_PIO_NPHYCTL at rising edge of IRENABLE_PHYANDCLOCK + * contents identical to VLSI_PIO_NPHYCTL (see below) + */ + +/* ------------------------------------------ */ + +/* VLSI_PIO_NPHYCTL: IR Physical Layer Next Control Register (u16, rw) */ + +/* latched during IRENABLE_PHYANDCLOCK=0 and applied at 0-1 transition + * + * consists of BAUD[15:10], PLSWID[9:5] and PREAMB[4:0] bits defined as follows: + * + * SIR-mode: BAUD = (115.2kHz / baudrate) - 1 + * PLSWID = (pulsetime * freq / (BAUD+1)) - 1 + * where pulsetime is the requested IrPHY pulse width + * and freq is 8(16)MHz for 40(48)MHz primary input clock + * PREAMB: don't care for SIR + * + * The nominal SIR pulse width is 3/16 bit time so we have PLSWID=12 + * fixed for all SIR speeds at 40MHz input clock (PLSWID=24 at 48MHz). + * IrPHY also allows shorter pulses down to the nominal pulse duration + * at 115.2kbaud (minus some tolerance) which is 1.41 usec. + * Using the expression PLSWID = 12/(BAUD+1)-1 (multiplied by two for 48MHz) + * we get the minimum acceptable PLSWID values according to the VLSI + * specification, which provides 1.5 usec pulse width for all speeds (except + * for 2.4kbaud getting 6usec). This is fine with IrPHY v1.3 specs and + * reduces the transceiver power which drains the battery. At 9.6kbaud for + * example this amounts to more than 90% battery power saving! + * + * MIR-mode: BAUD = 0 + * PLSWID = 9(10) for 40(48) MHz input clock + * to get nominal MIR pulse width + * PREAMB = 1 + * + * FIR-mode: BAUD = 0 + * PLSWID: don't care + * PREAMB = 15 + */ + +#define PHYCTL_BAUD_SHIFT 10 +#define PHYCTL_BAUD_MASK 0xfc00 +#define PHYCTL_PLSWID_SHIFT 5 +#define PHYCTL_PLSWID_MASK 0x03e0 +#define PHYCTL_PREAMB_SHIFT 0 +#define PHYCTL_PREAMB_MASK 0x001f + +#define PHYCTL_TO_BAUD(bwp) (((bwp)&PHYCTL_BAUD_MASK)>>PHYCTL_BAUD_SHIFT) +#define PHYCTL_TO_PLSWID(bwp) (((bwp)&PHYCTL_PLSWID_MASK)>>PHYCTL_PLSWID_SHIFT) +#define PHYCTL_TO_PREAMB(bwp) (((bwp)&PHYCTL_PREAMB_MASK)>>PHYCTL_PREAMB_SHIFT) + +#define BWP_TO_PHYCTL(b,w,p) ((((b)<0) ? (tmp-1) : 0; +} + +#define PHYCTL_SIR(br,ws,cs) BWP_TO_PHYCTL(BAUD_BITS(br),calc_width_bits((br),(ws),(cs)),0) +#define PHYCTL_MIR(cs) BWP_TO_PHYCTL(0,((cs)?9:10),1) +#define PHYCTL_FIR BWP_TO_PHYCTL(0,0,15) + +/* quite ugly, I know. But implementing these calculations here avoids + * having magic numbers in the code and allows some playing with pulsewidths + * without risk to violate the standards. + * FWIW, here is the table for reference: + * + * baudrate BAUD min-PLSWID nom-PLSWID PREAMB + * 2400 47 0(0) 12(24) 0 + * 9600 11 0(0) 12(24) 0 + * 19200 5 1(2) 12(24) 0 + * 38400 2 3(6) 12(24) 0 + * 57600 1 5(10) 12(24) 0 + * 115200 0 11(22) 12(24) 0 + * MIR 0 - 9(10) 1 + * FIR 0 - 0 15 + * + * note: x(y) means x-value for 40MHz / y-value for 48MHz primary input clock + */ + +/* ------------------------------------------ */ + + +/* VLSI_PIO_MAXPKT: Maximum Packet Length register (u16, rw) */ + +/* maximum acceptable length for received packets */ + +/* hw imposed limitation - register uses only [11:0] */ +#define MAX_PACKET_LENGTH 0x0fff + +/* IrLAP I-field (apparently not defined elsewhere) */ +#define IRDA_MTU 2048 + +/* complete packet consists of A(1)+C(1)+I(<=IRDA_MTU) */ +#define IRLAP_SKB_ALLOCSIZE (1+1+IRDA_MTU) + +/* the buffers we use to exchange frames with the hardware need to be + * larger than IRLAP_SKB_ALLOCSIZE because we may have up to 4 bytes FCS + * appended and, in SIR mode, a lot of frame wrapping bytes. The worst + * case appears to be a SIR packet with I-size==IRDA_MTU and all bytes + * requiring to be escaped to provide transparency. Furthermore, the peer + * might ask for quite a number of additional XBOFs: + * up to 115+48 XBOFS 163 + * regular BOF 1 + * A-field 1 + * C-field 1 + * I-field, IRDA_MTU, all escaped 4096 + * FCS (16 bit at SIR, escaped) 4 + * EOF 1 + * AFAICS nothing in IrLAP guarantees A/C field not to need escaping + * (f.e. 0xc0/0xc1 - i.e. BOF/EOF - are legal values there) so in the + * worst case we have 4269 bytes total frame size. + * However, the VLSI uses 12 bits only for all buffer length values, + * which limits the maximum useable buffer size <= 4095. + * Note this is not a limitation in the receive case because we use + * the SIR filtering mode where the hw unwraps the frame and only the + * bare packet+fcs is stored into the buffer - in contrast to the SIR + * tx case where we have to pass frame-wrapped packets to the hw. + * If this would ever become an issue in real life, the only workaround + * I see would be using the legacy UART emulation in SIR mode. + */ + +#define XFER_BUF_SIZE MAX_PACKET_LENGTH + +/* ------------------------------------------ */ + +/* VLSI_PIO_RCVBCNT: Receive Byte Count Register (u16, ro) */ + +/* receive packet counter gets incremented on every non-filtered + * byte which was put in the receive fifo and reset for each + * new packet. Used to decide whether we are just in the middle + * of receiving + */ + +/* better apply the [11:0] mask when reading, as some docs say the + * reserved [15:12] would return 1 when reading - which is wrong AFAICS + */ +#define RCVBCNT_MASK 0x0fff + +/******************************************************************/ + +/* descriptors for rx/tx ring + * + * accessed by hardware - don't change! + * + * the descriptor is owned by hardware, when the ACTIVE status bit + * is set and nothing (besides reading status to test the bit) + * shall be done. The bit gets cleared by hw, when the descriptor + * gets closed. Premature reaping of descriptors owned be the chip + * can be achieved by disabling IRCFG_MSTR + * + * Attention: Writing addr overwrites status! + * + * ### FIXME: depends on endianess (but there ain't no non-i586 ob800 ;-) + */ + +struct ring_descr_hw { + volatile __le16 rd_count; /* tx/rx count [11:0] */ + __le16 reserved; + union { + __le32 addr; /* [23:0] of the buffer's busaddress */ + struct { + u8 addr_res[3]; + volatile u8 status; /* descriptor status */ + } __packed rd_s; + } __packed rd_u; +} __packed; + +#define rd_addr rd_u.addr +#define rd_status rd_u.rd_s.status + +/* ring descriptor status bits */ + +#define RD_ACTIVE 0x80 /* descriptor owned by hw (both TX,RX) */ + +/* TX ring descriptor status */ + +#define RD_TX_DISCRC 0x40 /* do not send CRC (for SIR) */ +#define RD_TX_BADCRC 0x20 /* force a bad CRC */ +#define RD_TX_PULSE 0x10 /* send indication pulse after this frame (MIR/FIR) */ +#define RD_TX_FRCEUND 0x08 /* force underrun */ +#define RD_TX_CLRENTX 0x04 /* clear ENTX after this frame */ +#define RD_TX_UNDRN 0x01 /* TX fifo underrun (probably PCI problem) */ + +/* RX ring descriptor status */ + +#define RD_RX_PHYERR 0x40 /* physical encoding error */ +#define RD_RX_CRCERR 0x20 /* CRC error (MIR/FIR) */ +#define RD_RX_LENGTH 0x10 /* frame exceeds buffer length */ +#define RD_RX_OVER 0x08 /* RX fifo overrun (probably PCI problem) */ +#define RD_RX_SIRBAD 0x04 /* EOF missing: BOF follows BOF (SIR, filtered) */ + +#define RD_RX_ERROR 0x7c /* any error in received frame */ + +/* the memory required to hold the 2 descriptor rings */ +#define HW_RING_AREA_SIZE (2 * MAX_RING_DESCR * sizeof(struct ring_descr_hw)) + +/******************************************************************/ + +/* sw-ring descriptors consists of a bus-mapped transfer buffer with + * associated skb and a pointer to the hw entry descriptor + */ + +struct ring_descr { + struct ring_descr_hw *hw; + struct sk_buff *skb; + void *buf; +}; + +/* wrappers for operations on hw-exposed ring descriptors + * access to the hw-part of the descriptors must use these. + */ + +static inline int rd_is_active(struct ring_descr *rd) +{ + return (rd->hw->rd_status & RD_ACTIVE) != 0; +} + +static inline void rd_activate(struct ring_descr *rd) +{ + rd->hw->rd_status |= RD_ACTIVE; +} + +static inline void rd_set_status(struct ring_descr *rd, u8 s) +{ + rd->hw->rd_status = s; /* may pass ownership to the hardware */ +} + +static inline void rd_set_addr_status(struct ring_descr *rd, dma_addr_t a, u8 s) +{ + /* order is important for two reasons: + * - overlayed: writing addr overwrites status + * - we want to write status last so we have valid address in + * case status has RD_ACTIVE set + */ + + if ((a & ~DMA_MASK_MSTRPAGE)>>24 != MSTRPAGE_VALUE) { + net_err_ratelimited("%s: pci busaddr inconsistency!\n", + __func__); + dump_stack(); + return; + } + + a &= DMA_MASK_MSTRPAGE; /* clear highbyte to make sure we won't write + * to status - just in case MSTRPAGE_VALUE!=0 + */ + rd->hw->rd_addr = cpu_to_le32(a); + wmb(); + rd_set_status(rd, s); /* may pass ownership to the hardware */ +} + +static inline void rd_set_count(struct ring_descr *rd, u16 c) +{ + rd->hw->rd_count = cpu_to_le16(c); +} + +static inline u8 rd_get_status(struct ring_descr *rd) +{ + return rd->hw->rd_status; +} + +static inline dma_addr_t rd_get_addr(struct ring_descr *rd) +{ + dma_addr_t a; + + a = le32_to_cpu(rd->hw->rd_addr); + return (a & DMA_MASK_MSTRPAGE) | (MSTRPAGE_VALUE << 24); +} + +static inline u16 rd_get_count(struct ring_descr *rd) +{ + return le16_to_cpu(rd->hw->rd_count); +} + +/******************************************************************/ + +/* sw descriptor rings for rx, tx: + * + * operations follow producer-consumer paradigm, with the hw + * in the middle doing the processing. + * ring size must be power of two. + * + * producer advances r->tail after inserting for processing + * consumer advances r->head after removing processed rd + * ring is empty if head==tail / full if (tail+1)==head + */ + +struct vlsi_ring { + struct pci_dev *pdev; + int dir; + unsigned len; + unsigned size; + unsigned mask; + atomic_t head, tail; + struct ring_descr *rd; +}; + +/* ring processing helpers */ + +static inline struct ring_descr *ring_last(struct vlsi_ring *r) +{ + int t; + + t = atomic_read(&r->tail) & r->mask; + return (((t+1) & r->mask) == (atomic_read(&r->head) & r->mask)) ? NULL : &r->rd[t]; +} + +static inline struct ring_descr *ring_put(struct vlsi_ring *r) +{ + atomic_inc(&r->tail); + return ring_last(r); +} + +static inline struct ring_descr *ring_first(struct vlsi_ring *r) +{ + int h; + + h = atomic_read(&r->head) & r->mask; + return (h == (atomic_read(&r->tail) & r->mask)) ? NULL : &r->rd[h]; +} + +static inline struct ring_descr *ring_get(struct vlsi_ring *r) +{ + atomic_inc(&r->head); + return ring_first(r); +} + +/******************************************************************/ + +/* our private compound VLSI-PCI-IRDA device information */ + +typedef struct vlsi_irda_dev { + struct pci_dev *pdev; + + struct irlap_cb *irlap; + + struct qos_info qos; + + unsigned mode; + int baud, new_baud; + + dma_addr_t busaddr; + void *virtaddr; + struct vlsi_ring *tx_ring, *rx_ring; + + ktime_t last_rx; + + spinlock_t lock; + struct mutex mtx; + + u8 resume_ok; + struct proc_dir_entry *proc_entry; + +} vlsi_irda_dev_t; + +/********************************************************/ + +/* the remapped error flags we use for returning from frame + * post-processing in vlsi_process_tx/rx() after it was completed + * by the hardware. These functions either return the >=0 number + * of transferred bytes in case of success or the negative (-) + * of the or'ed error flags. + */ + +#define VLSI_TX_DROP 0x0001 +#define VLSI_TX_FIFO 0x0002 + +#define VLSI_RX_DROP 0x0100 +#define VLSI_RX_OVER 0x0200 +#define VLSI_RX_LENGTH 0x0400 +#define VLSI_RX_FRAME 0x0800 +#define VLSI_RX_CRC 0x1000 + +/********************************************************/ + +#endif /* IRDA_VLSI_FIR_H */ + diff --git a/drivers/staging/irda/drivers/w83977af.h b/drivers/staging/irda/drivers/w83977af.h new file mode 100644 index 000000000000..04476c2e9121 --- /dev/null +++ b/drivers/staging/irda/drivers/w83977af.h @@ -0,0 +1,53 @@ +#ifndef W83977AF_H +#define W83977AF_H + +#define W977_EFIO_BASE 0x370 +#define W977_EFIO2_BASE 0x3f0 +#define W977_DEVICE_IR 0x06 + + +/* + * Enter extended function mode + */ +static inline void w977_efm_enter(unsigned int efio) +{ + outb(0x87, efio); + outb(0x87, efio); +} + +/* + * Select a device to configure + */ + +static inline void w977_select_device(__u8 devnum, unsigned int efio) +{ + outb(0x07, efio); + outb(devnum, efio+1); +} + +/* + * Write a byte to a register + */ +static inline void w977_write_reg(__u8 reg, __u8 value, unsigned int efio) +{ + outb(reg, efio); + outb(value, efio+1); +} + +/* + * read a byte from a register + */ +static inline __u8 w977_read_reg(__u8 reg, unsigned int efio) +{ + outb(reg, efio); + return inb(efio+1); +} + +/* + * Exit extended function mode + */ +static inline void w977_efm_exit(unsigned int efio) +{ + outb(0xAA, efio); +} +#endif diff --git a/drivers/staging/irda/drivers/w83977af_ir.c b/drivers/staging/irda/drivers/w83977af_ir.c new file mode 100644 index 000000000000..282b6c9ae05b --- /dev/null +++ b/drivers/staging/irda/drivers/w83977af_ir.c @@ -0,0 +1,1285 @@ +/********************************************************************* + * + * Filename: w83977af_ir.c + * Version: 1.0 + * Description: FIR driver for the Winbond W83977AF Super I/O chip + * Status: Experimental. + * Author: Paul VanderSpek + * Created at: Wed Nov 4 11:46:16 1998 + * Modified at: Fri Jan 28 12:10:59 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-2000 Dag Brattli + * Copyright (c) 1998-1999 Rebel.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Paul VanderSpek nor Rebel.com admit liability nor provide + * warranty for any of this software. This material is provided "AS-IS" + * and at no charge. + * + * If you find bugs in this file, its very likely that the same bug + * will also be in pc87108.c since the implementations are quite + * similar. + * + * Notice that all functions that needs to access the chip in _any_ + * way, must save BSR register on entry, and restore it on exit. + * It is _very_ important to follow this policy! + * + * __u8 bank; + * + * bank = inb( iobase+BSR); + * + * do_your_stuff_here(); + * + * outb( bank, iobase+BSR); + * + ********************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include "w83977af.h" +#include "w83977af_ir.h" + +#define CONFIG_USE_W977_PNP /* Currently needed */ +#define PIO_MAX_SPEED 115200 + +static char *driver_name = "w83977af_ir"; +static int qos_mtt_bits = 0x07; /* 1 ms or more */ + +#define CHIP_IO_EXTENT 8 + +static unsigned int io[] = { 0x180, ~0, ~0, ~0 }; +#ifdef CONFIG_ARCH_NETWINDER /* Adjust to NetWinder differences */ +static unsigned int irq[] = { 6, 0, 0, 0 }; +#else +static unsigned int irq[] = { 11, 0, 0, 0 }; +#endif +static unsigned int dma[] = { 1, 0, 0, 0 }; +static unsigned int efbase[] = { W977_EFIO_BASE, W977_EFIO2_BASE }; +static unsigned int efio = W977_EFIO_BASE; + +static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL}; + +/* Some prototypes */ +static int w83977af_open(int i, unsigned int iobase, unsigned int irq, + unsigned int dma); +static int w83977af_close(struct w83977af_ir *self); +static int w83977af_probe(int iobase, int irq, int dma); +static int w83977af_dma_receive(struct w83977af_ir *self); +static int w83977af_dma_receive_complete(struct w83977af_ir *self); +static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, + struct net_device *dev); +static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size); +static void w83977af_dma_write(struct w83977af_ir *self, int iobase); +static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed); +static int w83977af_is_receiving(struct w83977af_ir *self); + +static int w83977af_net_open(struct net_device *dev); +static int w83977af_net_close(struct net_device *dev); +static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); + +/* + * Function w83977af_init () + * + * Initialize chip. Just try to find out how many chips we are dealing with + * and where they are + */ +static int __init w83977af_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dev_self) && io[i] < 2000; i++) { + if (w83977af_open(i, io[i], irq[i], dma[i]) == 0) + return 0; + } + return -ENODEV; +} + +/* + * Function w83977af_cleanup () + * + * Close all configured chips + * + */ +static void __exit w83977af_cleanup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dev_self); i++) { + if (dev_self[i]) + w83977af_close(dev_self[i]); + } +} + +static const struct net_device_ops w83977_netdev_ops = { + .ndo_open = w83977af_net_open, + .ndo_stop = w83977af_net_close, + .ndo_start_xmit = w83977af_hard_xmit, + .ndo_do_ioctl = w83977af_net_ioctl, +}; + +/* + * Function w83977af_open (iobase, irq) + * + * Open driver instance + * + */ +static int w83977af_open(int i, unsigned int iobase, unsigned int irq, + unsigned int dma) +{ + struct net_device *dev; + struct w83977af_ir *self; + int err; + + /* Lock the port that we need */ + if (!request_region(iobase, CHIP_IO_EXTENT, driver_name)) { + pr_debug("%s: can't get iobase of 0x%03x\n", + __func__, iobase); + return -ENODEV; + } + + if (w83977af_probe(iobase, irq, dma) == -1) { + err = -1; + goto err_out; + } + /* + * Allocate new instance of the driver + */ + dev = alloc_irdadev(sizeof(struct w83977af_ir)); + if (!dev) { + pr_err("IrDA: Can't allocate memory for IrDA control block!\n"); + err = -ENOMEM; + goto err_out; + } + + self = netdev_priv(dev); + spin_lock_init(&self->lock); + + /* Initialize IO */ + self->io.fir_base = iobase; + self->io.irq = irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = dma; + self->io.fifo_size = 32; + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* The only value we must override it the baudrate */ + + /* FIXME: The HP HDLS-1100 does not support 1152000! */ + self->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 | IR_57600 | + IR_115200 | IR_576000 | IR_1152000 | (IR_4000000 << 8); + + /* The HP HDLS-1100 needs 1 ms according to the specs */ + self->qos.min_turn_time.bits = qos_mtt_bits; + irda_qos_bits_to_value(&self->qos); + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384; + self->tx_buff.truesize = 4000; + + /* Allocate memory if needed */ + self->rx_buff.head = + dma_zalloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (!self->rx_buff.head) { + err = -ENOMEM; + goto err_out1; + } + + self->tx_buff.head = + dma_zalloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (!self->tx_buff.head) { + err = -ENOMEM; + goto err_out2; + } + + self->rx_buff.in_frame = FALSE; + self->rx_buff.state = OUTSIDE_FRAME; + self->tx_buff.data = self->tx_buff.head; + self->rx_buff.data = self->rx_buff.head; + self->netdev = dev; + + dev->netdev_ops = &w83977_netdev_ops; + + err = register_netdev(dev); + if (err) { + net_err_ratelimited("%s:, register_netdevice() failed!\n", + __func__); + goto err_out3; + } + net_info_ratelimited("IrDA: Registered device %s\n", dev->name); + + /* Need to store self somewhere */ + dev_self[i] = self; + + return 0; +err_out3: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); +err_out2: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); +err_out1: + free_netdev(dev); +err_out: + release_region(iobase, CHIP_IO_EXTENT); + return err; +} + +/* + * Function w83977af_close (self) + * + * Close driver instance + * + */ +static int w83977af_close(struct w83977af_ir *self) +{ + int iobase; + + iobase = self->io.fir_base; + +#ifdef CONFIG_USE_W977_PNP + /* enter PnP configuration mode */ + w977_efm_enter(efio); + + w977_select_device(W977_DEVICE_IR, efio); + + /* Deactivate device */ + w977_write_reg(0x30, 0x00, efio); + + w977_efm_exit(efio); +#endif /* CONFIG_USE_W977_PNP */ + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the PORT that this driver is using */ + pr_debug("%s: Releasing Region %03x\n", __func__, self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + + if (self->tx_buff.head) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + free_netdev(self->netdev); + + return 0; +} + +static int w83977af_probe(int iobase, int irq, int dma) +{ + int version; + int i; + + for (i = 0; i < 2; i++) { +#ifdef CONFIG_USE_W977_PNP + /* Enter PnP configuration mode */ + w977_efm_enter(efbase[i]); + + w977_select_device(W977_DEVICE_IR, efbase[i]); + + /* Configure PnP port, IRQ, and DMA channel */ + w977_write_reg(0x60, (iobase >> 8) & 0xff, efbase[i]); + w977_write_reg(0x61, (iobase) & 0xff, efbase[i]); + + w977_write_reg(0x70, irq, efbase[i]); +#ifdef CONFIG_ARCH_NETWINDER + /* Netwinder uses 1 higher than Linux */ + w977_write_reg(0x74, dma + 1, efbase[i]); +#else + w977_write_reg(0x74, dma, efbase[i]); +#endif /* CONFIG_ARCH_NETWINDER */ + w977_write_reg(0x75, 0x04, efbase[i]);/* Disable Tx DMA */ + + /* Set append hardware CRC, enable IR bank selection */ + w977_write_reg(0xf0, APEDCRC | ENBNKSEL, efbase[i]); + + /* Activate device */ + w977_write_reg(0x30, 0x01, efbase[i]); + + w977_efm_exit(efbase[i]); +#endif /* CONFIG_USE_W977_PNP */ + /* Disable Advanced mode */ + switch_bank(iobase, SET2); + outb(iobase + 2, 0x00); + + /* Turn on UART (global) interrupts */ + switch_bank(iobase, SET0); + outb(HCR_EN_IRQ, iobase + HCR); + + /* Switch to advanced mode */ + switch_bank(iobase, SET2); + outb(inb(iobase + ADCR1) | ADCR1_ADV_SL, iobase + ADCR1); + + /* Set default IR-mode */ + switch_bank(iobase, SET0); + outb(HCR_SIR, iobase + HCR); + + /* Read the Advanced IR ID */ + switch_bank(iobase, SET3); + version = inb(iobase + AUID); + + /* Should be 0x1? */ + if (0x10 == (version & 0xf0)) { + efio = efbase[i]; + + /* Set FIFO size to 32 */ + switch_bank(iobase, SET2); + outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2); + + /* Set FIFO threshold to TX17, RX16 */ + switch_bank(iobase, SET0); + outb(UFR_RXTL | UFR_TXTL | UFR_TXF_RST | UFR_RXF_RST | + UFR_EN_FIFO, iobase + UFR); + + /* Receiver frame length */ + switch_bank(iobase, SET4); + outb(2048 & 0xff, iobase + 6); + outb((2048 >> 8) & 0x1f, iobase + 7); + + /* + * Init HP HSDL-1100 transceiver. + * + * Set IRX_MSL since we have 2 * receive paths IRRX, + * and IRRXH. Clear IRSL0D since we want IRSL0 * to + * be a input pin used for IRRXH + * + * IRRX pin 37 connected to receiver + * IRTX pin 38 connected to transmitter + * FIRRX pin 39 connected to receiver (IRSL0) + * CIRRX pin 40 connected to pin 37 + */ + switch_bank(iobase, SET7); + outb(0x40, iobase + 7); + + net_info_ratelimited("W83977AF (IR) driver loaded. Version: 0x%02x\n", + version); + + return 0; + } else { + /* Try next extented function register address */ + pr_debug("%s: Wrong chip version\n", __func__); + } + } + return -1; +} + +static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) +{ + int ir_mode = HCR_SIR; + int iobase; + __u8 set; + + iobase = self->io.fir_base; + + /* Update accounting for new speed */ + self->io.speed = speed; + + /* Save current bank */ + set = inb(iobase + SSR); + + /* Disable interrupts */ + switch_bank(iobase, SET0); + outb(0, iobase + ICR); + + /* Select Set 2 */ + switch_bank(iobase, SET2); + outb(0x00, iobase + ABHL); + + switch (speed) { + case 9600: outb(0x0c, iobase + ABLL); break; + case 19200: outb(0x06, iobase + ABLL); break; + case 38400: outb(0x03, iobase + ABLL); break; + case 57600: outb(0x02, iobase + ABLL); break; + case 115200: outb(0x01, iobase + ABLL); break; + case 576000: + ir_mode = HCR_MIR_576; + pr_debug("%s: handling baud of 576000\n", __func__); + break; + case 1152000: + ir_mode = HCR_MIR_1152; + pr_debug("%s: handling baud of 1152000\n", __func__); + break; + case 4000000: + ir_mode = HCR_FIR; + pr_debug("%s: handling baud of 4000000\n", __func__); + break; + default: + ir_mode = HCR_FIR; + pr_debug("%s: unknown baud rate of %d\n", __func__, speed); + break; + } + + /* Set speed mode */ + switch_bank(iobase, SET0); + outb(ir_mode, iobase + HCR); + + /* set FIFO size to 32 */ + switch_bank(iobase, SET2); + outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2); + + /* set FIFO threshold to TX17, RX16 */ + switch_bank(iobase, SET0); + outb(0x00, iobase + UFR); /* Reset */ + outb(UFR_EN_FIFO, iobase + UFR); /* First we must enable FIFO */ + outb(0xa7, iobase + UFR); + + netif_wake_queue(self->netdev); + + /* Enable some interrupts so we can receive frames */ + switch_bank(iobase, SET0); + if (speed > PIO_MAX_SPEED) { + outb(ICR_EFSFI, iobase + ICR); + w83977af_dma_receive(self); + } else { + outb(ICR_ERBRI, iobase + ICR); + } + + /* Restore SSR */ + outb(set, iobase + SSR); +} + +/* + * Function w83977af_hard_xmit (skb, dev) + * + * Sets up a DMA transfer to send the current frame. + * + */ +static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct w83977af_ir *self; + __s32 speed; + int iobase; + __u8 set; + int mtt; + + self = netdev_priv(dev); + + iobase = self->io.fir_base; + + pr_debug("%s: %ld, skb->len=%d\n", __func__, jiffies, (int)skb->len); + + /* Lock transmit buffer */ + netif_stop_queue(dev); + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame */ + if (!skb->len) { + w83977af_change_speed(self, speed); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + self->new_speed = speed; + } + + /* Save current set */ + set = inb(iobase + SSR); + + /* Decide if we should use PIO or DMA transfer */ + if (self->io.speed > PIO_MAX_SPEED) { + self->tx_buff.data = self->tx_buff.head; + skb_copy_from_linear_data(skb, self->tx_buff.data, skb->len); + self->tx_buff.len = skb->len; + + mtt = irda_get_mtt(skb); + pr_debug("%s: %ld, mtt=%d\n", __func__, jiffies, mtt); + if (mtt > 1000) + mdelay(mtt / 1000); + else if (mtt) + udelay(mtt); + + /* Enable DMA interrupt */ + switch_bank(iobase, SET0); + outb(ICR_EDMAI, iobase + ICR); + w83977af_dma_write(self, iobase); + } else { + self->tx_buff.data = self->tx_buff.head; + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + /* Add interrupt on tx low level (will fire immediately) */ + switch_bank(iobase, SET0); + outb(ICR_ETXTHI, iobase + ICR); + } + dev_kfree_skb(skb); + + /* Restore set register */ + outb(set, iobase + SSR); + + return NETDEV_TX_OK; +} + +/* + * Function w83977af_dma_write (self, iobase) + * + * Send frame using DMA + * + */ +static void w83977af_dma_write(struct w83977af_ir *self, int iobase) +{ + __u8 set; + + pr_debug("%s: len=%d\n", __func__, self->tx_buff.len); + + /* Save current set */ + set = inb(iobase + SSR); + + /* Disable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); + + /* Choose transmit DMA channel */ + switch_bank(iobase, SET2); + outb(ADCR1_D_CHSW | /*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase + ADCR1); + irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, + DMA_MODE_WRITE); + self->io.direction = IO_XMIT; + + /* Enable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase + HCR) | HCR_EN_DMA | HCR_TX_WT, iobase + HCR); + + /* Restore set register */ + outb(set, iobase + SSR); +} + +/* + * Function w83977af_pio_write (iobase, buf, len, fifo_size) + * + * + * + */ +static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) +{ + int actual = 0; + __u8 set; + + /* Save current bank */ + set = inb(iobase + SSR); + + switch_bank(iobase, SET0); + if (!(inb_p(iobase + USR) & USR_TSRE)) { + pr_debug("%s: warning, FIFO not empty yet!\n", __func__); + + fifo_size -= 17; + pr_debug("%s: %d bytes left in tx fifo\n", __func__, fifo_size); + } + + /* Fill FIFO with current frame */ + while ((fifo_size-- > 0) && (actual < len)) { + /* Transmit next byte */ + outb(buf[actual++], iobase + TBR); + } + + pr_debug("%s: fifo_size %d ; %d sent of %d\n", + __func__, fifo_size, actual, len); + + /* Restore bank */ + outb(set, iobase + SSR); + + return actual; +} + +/* + * Function w83977af_dma_xmit_complete (self) + * + * The transfer of a frame in finished. So do the necessary things + * + * + */ +static void w83977af_dma_xmit_complete(struct w83977af_ir *self) +{ + int iobase; + __u8 set; + + pr_debug("%s: %ld\n", __func__, jiffies); + + IRDA_ASSERT(self, return;); + + iobase = self->io.fir_base; + + /* Save current set */ + set = inb(iobase + SSR); + + /* Disable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); + + /* Check for underrun! */ + if (inb(iobase + AUDR) & AUDR_UNDR) { + pr_debug("%s: Transmit underrun!\n", __func__); + + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; + + /* Clear bit, by writing 1 to it */ + outb(AUDR_UNDR, iobase + AUDR); + } else { + self->netdev->stats.tx_packets++; + } + + if (self->new_speed) { + w83977af_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Unlock tx_buff and request another frame */ + /* Tell the network layer, that we want more frames */ + netif_wake_queue(self->netdev); + + /* Restore set */ + outb(set, iobase + SSR); +} + +/* + * Function w83977af_dma_receive (self) + * + * Get ready for receiving a frame. The device will initiate a DMA + * if it starts to receive a frame. + * + */ +static int w83977af_dma_receive(struct w83977af_ir *self) +{ + int iobase; + __u8 set; +#ifdef CONFIG_ARCH_NETWINDER + unsigned long flags; + __u8 hcr; +#endif + IRDA_ASSERT(self, return -1;); + + pr_debug("%s\n", __func__); + + iobase = self->io.fir_base; + + /* Save current set */ + set = inb(iobase + SSR); + + /* Disable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); + + /* Choose DMA Rx, DMA Fairness, and Advanced mode */ + switch_bank(iobase, SET2); + outb((inb(iobase + ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/ | ADCR1_ADV_SL, + iobase + ADCR1); + + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + +#ifdef CONFIG_ARCH_NETWINDER + spin_lock_irqsave(&self->lock, flags); + + disable_dma(self->io.dma); + clear_dma_ff(self->io.dma); + set_dma_mode(self->io.dma, DMA_MODE_READ); + set_dma_addr(self->io.dma, self->rx_buff_dma); + set_dma_count(self->io.dma, self->rx_buff.truesize); +#else + irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, + DMA_MODE_READ); +#endif + /* + * Reset Rx FIFO. This will also flush the ST_FIFO, it's very + * important that we don't reset the Tx FIFO since it might not + * be finished transmitting yet + */ + switch_bank(iobase, SET0); + outb(UFR_RXTL | UFR_TXTL | UFR_RXF_RST | UFR_EN_FIFO, iobase + UFR); + self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0; + + /* Enable DMA */ + switch_bank(iobase, SET0); +#ifdef CONFIG_ARCH_NETWINDER + hcr = inb(iobase + HCR); + outb(hcr | HCR_EN_DMA, iobase + HCR); + enable_dma(self->io.dma); + spin_unlock_irqrestore(&self->lock, flags); +#else + outb(inb(iobase + HCR) | HCR_EN_DMA, iobase + HCR); +#endif + /* Restore set */ + outb(set, iobase + SSR); + + return 0; +} + +/* + * Function w83977af_receive_complete (self) + * + * Finished with receiving a frame + * + */ +static int w83977af_dma_receive_complete(struct w83977af_ir *self) +{ + struct sk_buff *skb; + struct st_fifo *st_fifo; + int len; + int iobase; + __u8 set; + __u8 status; + + pr_debug("%s\n", __func__); + + st_fifo = &self->st_fifo; + + iobase = self->io.fir_base; + + /* Save current set */ + set = inb(iobase + SSR); + + iobase = self->io.fir_base; + + /* Read status FIFO */ + switch_bank(iobase, SET5); + while ((status = inb(iobase + FS_FO)) & FS_FO_FSFDR) { + st_fifo->entries[st_fifo->tail].status = status; + + st_fifo->entries[st_fifo->tail].len = inb(iobase + RFLFL); + st_fifo->entries[st_fifo->tail].len |= inb(iobase + RFLFH) << 8; + + st_fifo->tail++; + st_fifo->len++; + } + + while (st_fifo->len) { + /* Get first entry */ + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->head++; + st_fifo->len--; + + /* Check for errors */ + if (status & FS_FO_ERR_MSK) { + if (status & FS_FO_LST_FR) { + /* Add number of lost frames to stats */ + self->netdev->stats.rx_errors += len; + } else { + /* Skip frame */ + self->netdev->stats.rx_errors++; + + self->rx_buff.data += len; + + if (status & FS_FO_MX_LEX) + self->netdev->stats.rx_length_errors++; + + if (status & FS_FO_PHY_ERR) + self->netdev->stats.rx_frame_errors++; + + if (status & FS_FO_CRC_ERR) + self->netdev->stats.rx_crc_errors++; + } + /* The errors below can be reported in both cases */ + if (status & FS_FO_RX_OV) + self->netdev->stats.rx_fifo_errors++; + + if (status & FS_FO_FSF_OV) + self->netdev->stats.rx_fifo_errors++; + + } else { + /* Check if we have transferred all data to memory */ + switch_bank(iobase, SET0); + if (inb(iobase + USR) & USR_RDR) + udelay(80); /* Should be enough!? */ + + skb = dev_alloc_skb(len + 1); + if (!skb) { + pr_info("%s: memory squeeze, dropping frame\n", + __func__); + /* Restore set register */ + outb(set, iobase + SSR); + + return FALSE; + } + + /* Align to 20 bytes */ + skb_reserve(skb, 1); + + /* Copy frame without CRC */ + if (self->io.speed < 4000000) { + skb_put(skb, len - 2); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 2); + } else { + skb_put(skb, len - 4); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 4); + } + + /* Move to next frame */ + self->rx_buff.data += len; + self->netdev->stats.rx_packets++; + + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + } + } + /* Restore set register */ + outb(set, iobase + SSR); + + return TRUE; +} + +/* + * Function pc87108_pio_receive (self) + * + * Receive all data in receiver FIFO + * + */ +static void w83977af_pio_receive(struct w83977af_ir *self) +{ + __u8 byte = 0x00; + int iobase; + + IRDA_ASSERT(self, return;); + + iobase = self->io.fir_base; + + /* Receive all characters in Rx FIFO */ + do { + byte = inb(iobase + RBR); + async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, + byte); + } while (inb(iobase + USR) & USR_RDR); /* Data available */ +} + +/* + * Function w83977af_sir_interrupt (self, eir) + * + * Handle SIR interrupt + * + */ +static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) +{ + int actual; + __u8 new_icr = 0; + __u8 set; + int iobase; + + pr_debug("%s: isr=%#x\n", __func__, isr); + + iobase = self->io.fir_base; + /* Transmit FIFO low on data */ + if (isr & ISR_TXTH_I) { + /* Write data left in transmit buffer */ + actual = w83977af_pio_write(self->io.fir_base, + self->tx_buff.data, + self->tx_buff.len, + self->io.fifo_size); + + self->tx_buff.data += actual; + self->tx_buff.len -= actual; + + self->io.direction = IO_XMIT; + + /* Check if finished */ + if (self->tx_buff.len > 0) { + new_icr |= ICR_ETXTHI; + } else { + set = inb(iobase + SSR); + switch_bank(iobase, SET0); + outb(AUDR_SFEND, iobase + AUDR); + outb(set, iobase + SSR); + + self->netdev->stats.tx_packets++; + + /* Feed me more packets */ + netif_wake_queue(self->netdev); + new_icr |= ICR_ETBREI; + } + } + /* Check if transmission has completed */ + if (isr & ISR_TXEMP_I) { + /* Check if we need to change the speed? */ + if (self->new_speed) { + pr_debug("%s: Changing speed!\n", __func__); + w83977af_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Turn around and get ready to receive some data */ + self->io.direction = IO_RECV; + new_icr |= ICR_ERBRI; + } + + /* Rx FIFO threshold or timeout */ + if (isr & ISR_RXTH_I) { + w83977af_pio_receive(self); + + /* Keep receiving */ + new_icr |= ICR_ERBRI; + } + return new_icr; +} + +/* + * Function pc87108_fir_interrupt (self, eir) + * + * Handle MIR/FIR interrupt + * + */ +static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) +{ + __u8 new_icr = 0; + __u8 set; + int iobase; + + iobase = self->io.fir_base; + set = inb(iobase + SSR); + + /* End of frame detected in FIFO */ + if (isr & (ISR_FEND_I | ISR_FSF_I)) { + if (w83977af_dma_receive_complete(self)) { + /* Wait for next status FIFO interrupt */ + new_icr |= ICR_EFSFI; + } else { + /* DMA not finished yet */ + + /* Set timer value, resolution 1 ms */ + switch_bank(iobase, SET4); + outb(0x01, iobase + TMRL); /* 1 ms */ + outb(0x00, iobase + TMRH); + + /* Start timer */ + outb(IR_MSL_EN_TMR, iobase + IR_MSL); + + new_icr |= ICR_ETMRI; + } + } + /* Timer finished */ + if (isr & ISR_TMR_I) { + /* Disable timer */ + switch_bank(iobase, SET4); + outb(0, iobase + IR_MSL); + + /* Clear timer event */ + /* switch_bank(iobase, SET0); */ +/* outb(ASCR_CTE, iobase+ASCR); */ + + /* Check if this is a TX timer interrupt */ + if (self->io.direction == IO_XMIT) { + w83977af_dma_write(self, iobase); + + new_icr |= ICR_EDMAI; + } else { + /* Check if DMA has now finished */ + w83977af_dma_receive_complete(self); + + new_icr |= ICR_EFSFI; + } + } + /* Finished with DMA */ + if (isr & ISR_DMA_I) { + w83977af_dma_xmit_complete(self); + + /* Check if there are more frames to be transmitted */ + /* if (irda_device_txqueue_empty(self)) { */ + + /* Prepare for receive + * + * ** Netwinder Tx DMA likes that we do this anyway ** + */ + w83977af_dma_receive(self); + new_icr = ICR_EFSFI; + /* } */ + } + + /* Restore set */ + outb(set, iobase + SSR); + + return new_icr; +} + +/* + * Function w83977af_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t w83977af_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct w83977af_ir *self; + __u8 set, icr, isr; + int iobase; + + self = netdev_priv(dev); + + iobase = self->io.fir_base; + + /* Save current bank */ + set = inb(iobase + SSR); + switch_bank(iobase, SET0); + + icr = inb(iobase + ICR); + isr = inb(iobase + ISR) & icr; /* Mask out the interesting ones */ + + outb(0, iobase + ICR); /* Disable interrupts */ + + if (isr) { + /* Dispatch interrupt handler for the current speed */ + if (self->io.speed > PIO_MAX_SPEED) + icr = w83977af_fir_interrupt(self, isr); + else + icr = w83977af_sir_interrupt(self, isr); + } + + outb(icr, iobase + ICR); /* Restore (new) interrupts */ + outb(set, iobase + SSR); /* Restore bank register */ + return IRQ_RETVAL(isr); +} + +/* + * Function w83977af_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int w83977af_is_receiving(struct w83977af_ir *self) +{ + int status = FALSE; + int iobase; + __u8 set; + + IRDA_ASSERT(self, return FALSE;); + + if (self->io.speed > 115200) { + iobase = self->io.fir_base; + + /* Check if rx FIFO is not empty */ + set = inb(iobase + SSR); + switch_bank(iobase, SET2); + if ((inb(iobase + RXFDTH) & 0x3f) != 0) { + /* We are receiving something */ + status = TRUE; + } + outb(set, iobase + SSR); + } else { + status = (self->rx_buff.state != OUTSIDE_FRAME); + } + + return status; +} + +/* + * Function w83977af_net_open (dev) + * + * Start the device + * + */ +static int w83977af_net_open(struct net_device *dev) +{ + struct w83977af_ir *self; + int iobase; + char hwname[32]; + __u8 set; + + IRDA_ASSERT(dev, return -1;); + self = netdev_priv(dev); + + IRDA_ASSERT(self, return 0;); + + iobase = self->io.fir_base; + + if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, + (void *)dev)) { + return -EAGAIN; + } + /* + * Always allocate the DMA channel after the IRQ, + * and clean up on failure. + */ + if (request_dma(self->io.dma, dev->name)) { + free_irq(self->io.irq, dev); + return -EAGAIN; + } + + /* Save current set */ + set = inb(iobase + SSR); + + /* Enable some interrupts so we can receive frames again */ + switch_bank(iobase, SET0); + if (self->io.speed > 115200) { + outb(ICR_EFSFI, iobase + ICR); + w83977af_dma_receive(self); + } else { + outb(ICR_ERBRI, iobase + ICR); + } + + /* Restore bank register */ + outb(set, iobase + SSR); + + /* Ready to play! */ + netif_start_queue(dev); + + /* Give self a hardware name */ + sprintf(hwname, "w83977af @ 0x%03x", self->io.fir_base); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + self->irlap = irlap_open(dev, &self->qos, hwname); + + return 0; +} + +/* + * Function w83977af_net_close (dev) + * + * Stop the device + * + */ +static int w83977af_net_close(struct net_device *dev) +{ + struct w83977af_ir *self; + int iobase; + __u8 set; + + IRDA_ASSERT(dev, return -1;); + + self = netdev_priv(dev); + + IRDA_ASSERT(self, return 0;); + + iobase = self->io.fir_base; + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + disable_dma(self->io.dma); + + /* Save current set */ + set = inb(iobase + SSR); + + /* Disable interrupts */ + switch_bank(iobase, SET0); + outb(0, iobase + ICR); + + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + + /* Restore bank register */ + outb(set, iobase + SSR); + + return 0; +} + +/* + * Function w83977af_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *)rq; + struct w83977af_ir *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT(dev, return -1;); + + self = netdev_priv(dev); + + IRDA_ASSERT(self, return -1;); + + pr_debug("%s: %s, (cmd=0x%X)\n", __func__, dev->name, cmd); + + spin_lock_irqsave(&self->lock, flags); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + w83977af_change_speed(self, irq->ifr_baudrate); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = w83977af_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } +out: + spin_unlock_irqrestore(&self->lock, flags); + return ret; +} + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver"); +MODULE_LICENSE("GPL"); + +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Mimimum Turn Time"); +module_param_hw_array(io, int, ioport, NULL, 0); +MODULE_PARM_DESC(io, "Base I/O addresses"); +module_param_hw_array(irq, int, irq, NULL, 0); +MODULE_PARM_DESC(irq, "IRQ lines"); + +/* + * Function init_module (void) + * + * + * + */ +module_init(w83977af_init); + +/* + * Function cleanup_module (void) + * + * + * + */ +module_exit(w83977af_cleanup); diff --git a/drivers/staging/irda/drivers/w83977af_ir.h b/drivers/staging/irda/drivers/w83977af_ir.h new file mode 100644 index 000000000000..fefe9b11e200 --- /dev/null +++ b/drivers/staging/irda/drivers/w83977af_ir.h @@ -0,0 +1,198 @@ +/********************************************************************* + * + * Filename: w83977af_ir.h + * Version: + * Description: + * Status: Experimental. + * Author: Paul VanderSpek + * Created at: Thu Nov 19 13:55:34 1998 + * Modified at: Tue Jan 11 13:08:19 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#ifndef W83977AF_IR_H +#define W83977AF_IR_H + +#include +#include + +/* Flags for configuration register CRF0 */ +#define ENBNKSEL 0x01 +#define APEDCRC 0x02 +#define TXW4C 0x04 +#define RXW4C 0x08 + +/* Bank 0 */ +#define RBR 0x00 /* Receiver buffer register */ +#define TBR 0x00 /* Transmitter buffer register */ + +#define ICR 0x01 /* Interrupt configuration register */ +#define ICR_ERBRI 0x01 /* Receiver buffer register interrupt */ +#define ICR_ETBREI 0x02 /* Transeiver empty interrupt */ +#define ICR_EUSRI 0x04//* IR status interrupt */ +#define ICR_EHSRI 0x04 +#define ICR_ETXURI 0x04 /* Tx underrun */ +#define ICR_EDMAI 0x10 /* DMA interrupt */ +#define ICR_ETXTHI 0x20 /* Transmitter threshold interrupt */ +#define ICR_EFSFI 0x40 /* Frame status FIFO interrupt */ +#define ICR_ETMRI 0x80 /* Timer interrupt */ + +#define UFR 0x02 /* FIFO control register */ +#define UFR_EN_FIFO 0x01 /* Enable FIFO's */ +#define UFR_RXF_RST 0x02 /* Reset Rx FIFO */ +#define UFR_TXF_RST 0x04 /* Reset Tx FIFO */ +#define UFR_RXTL 0x80 /* Rx FIFO threshold (set to 16) */ +#define UFR_TXTL 0x20 /* Tx FIFO threshold (set to 17) */ + +#define ISR 0x02 /* Interrupt status register */ +#define ISR_RXTH_I 0x01 /* Receive threshold interrupt */ +#define ISR_TXEMP_I 0x02 /* Transmitter empty interrupt */ +#define ISR_FEND_I 0x04 +#define ISR_DMA_I 0x10 +#define ISR_TXTH_I 0x20 /* Transmitter threshold interrupt */ +#define ISR_FSF_I 0x40 +#define ISR_TMR_I 0x80 /* Timer interrupt */ + +#define UCR 0x03 /* Uart control register */ +#define UCR_DLS8 0x03 /* 8N1 */ + +#define SSR 0x03 /* Sets select register */ +#define SET0 UCR_DLS8 /* Make sure we keep 8N1 */ +#define SET1 (0x80|UCR_DLS8) /* Make sure we keep 8N1 */ +#define SET2 0xE0 +#define SET3 0xE4 +#define SET4 0xE8 +#define SET5 0xEC +#define SET6 0xF0 +#define SET7 0xF4 + +#define HCR 0x04 +#define HCR_MODE_MASK ~(0xD0) +#define HCR_SIR 0x60 +#define HCR_MIR_576 0x20 +#define HCR_MIR_1152 0x80 +#define HCR_FIR 0xA0 +#define HCR_EN_DMA 0x04 +#define HCR_EN_IRQ 0x08 +#define HCR_TX_WT 0x08 + +#define USR 0x05 /* IR status register */ +#define USR_RDR 0x01 /* Receive data ready */ +#define USR_TSRE 0x40 /* Transmitter empty? */ + +#define AUDR 0x07 +#define AUDR_SFEND 0x08 /* Set a frame end */ +#define AUDR_RXBSY 0x20 /* Rx busy */ +#define AUDR_UNDR 0x40 /* Transeiver underrun */ + +/* Set 2 */ +#define ABLL 0x00 /* Advanced baud rate divisor latch (low byte) */ +#define ABHL 0x01 /* Advanced baud rate divisor latch (high byte) */ + +#define ADCR1 0x02 +#define ADCR1_ADV_SL 0x01 +#define ADCR1_D_CHSW 0x08 /* the specs are wrong. its bit 3, not 4 */ +#define ADCR1_DMA_F 0x02 + +#define ADCR2 0x04 +#define ADCR2_TXFS32 0x01 +#define ADCR2_RXFS32 0x04 + +#define RXFDTH 0x07 + +/* Set 3 */ +#define AUID 0x00 + +/* Set 4 */ +#define TMRL 0x00 /* Timer value register (low byte) */ +#define TMRH 0x01 /* Timer value register (high byte) */ + +#define IR_MSL 0x02 /* Infrared mode select */ +#define IR_MSL_EN_TMR 0x01 /* Enable timer */ + +#define TFRLL 0x04 /* Transmitter frame length (low byte) */ +#define TFRLH 0x05 /* Transmitter frame length (high byte) */ +#define RFRLL 0x06 /* Receiver frame length (low byte) */ +#define RFRLH 0x07 /* Receiver frame length (high byte) */ + +/* Set 5 */ + +#define FS_FO 0x05 /* Frame status FIFO */ +#define FS_FO_FSFDR 0x80 /* Frame status FIFO data ready */ +#define FS_FO_LST_FR 0x40 /* Frame lost */ +#define FS_FO_MX_LEX 0x10 /* Max frame len exceeded */ +#define FS_FO_PHY_ERR 0x08 /* Physical layer error */ +#define FS_FO_CRC_ERR 0x04 +#define FS_FO_RX_OV 0x02 /* Receive overrun */ +#define FS_FO_FSF_OV 0x01 /* Frame status FIFO overrun */ +#define FS_FO_ERR_MSK 0x5f /* Error mask */ + +#define RFLFL 0x06 +#define RFLFH 0x07 + +/* Set 6 */ +#define IR_CFG2 0x00 +#define IR_CFG2_DIS_CRC 0x02 + +/* Set 7 */ +#define IRM_CR 0x07 /* Infrared module control register */ +#define IRM_CR_IRX_MSL 0x40 +#define IRM_CR_AF_MNT 0x80 /* Automatic format */ + +/* For storing entries in the status FIFO */ +struct st_fifo_entry { + int status; + int len; +}; + +struct st_fifo { + struct st_fifo_entry entries[10]; + int head; + int tail; + int len; +}; + +/* Private data for each instance */ +struct w83977af_ir { + struct st_fifo st_fifo; + + int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */ + int tx_len; /* Number of frames in tx_buff */ + + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct qos_info qos; /* QoS capabilities for this device */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + /* Note : currently locking is *very* incomplete, but this + * will get you started. Check in nsc-ircc.c for a proper + * locking strategy. - Jean II */ + spinlock_t lock; /* For serializing operations */ + + __u32 new_speed; +}; + +static inline void switch_bank( int iobase, int set) +{ + outb(set, iobase+SSR); +} + +#endif diff --git a/drivers/staging/irda/net/Kconfig b/drivers/staging/irda/net/Kconfig index cd775e1ea0e9..6abeae6c666a 100644 --- a/drivers/staging/irda/net/Kconfig +++ b/drivers/staging/irda/net/Kconfig @@ -92,5 +92,5 @@ config IRDA_DEBUG If unsure, say Y (since it makes it easier to find the bugs). -source "drivers/net/irda/Kconfig" +source "drivers/staging/irda/drivers/Kconfig" -- cgit v1.2.3 From 52f31422d462d1a0cb3ed3f2de7d9d34d1a6e46a Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 28 Aug 2017 01:20:26 +0800 Subject: net-next/hinic: Fix MTU limitation Fix the hw MTU limitation by setting max_mtu Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index ae7ad48c9f65..eb53bd93065e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -919,6 +919,7 @@ static int nic_dev_init(struct pci_dev *pdev) netdev->netdev_ops = &hinic_netdev_ops; netdev->ethtool_ops = &hinic_ethtool_ops; + netdev->max_mtu = ETH_MAX_MTU; nic_dev = netdev_priv(netdev); nic_dev->netdev = netdev; -- cgit v1.2.3 From cde66f24c3bf42123647c5233447c5790d92557f Mon Sep 17 00:00:00 2001 From: Aviad Krawczyk Date: Mon, 28 Aug 2017 01:35:30 +0800 Subject: net-next/hinic: fix comparison of a uint16_t type with -1 Remove the search for index of constant buffer size Signed-off-by: Aviad Krawczyk Signed-off-by: Zhao Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 37 +----------------------- drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h | 21 ++++++++++++++ 2 files changed, 22 insertions(+), 36 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index 09dec6de8dd5..79b567447084 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -69,31 +69,6 @@ struct hinic_dev_cap { u8 rsvd3[208]; }; -struct rx_buf_sz { - int idx; - size_t sz; -}; - -static struct rx_buf_sz rx_buf_sz_table[] = { - {0, 32}, - {1, 64}, - {2, 96}, - {3, 128}, - {4, 192}, - {5, 256}, - {6, 384}, - {7, 512}, - {8, 768}, - {9, 1024}, - {10, 1536}, - {11, 2048}, - {12, 3072}, - {13, 4096}, - {14, 8192}, - {15, 16384}, - {-1, -1}, -}; - /** * get_capability - convert device capabilities to NIC capabilities * @hwdev: the HW device to set and convert device capabilities for @@ -330,7 +305,6 @@ static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int rq_depth, struct hinic_cmd_hw_ioctxt hw_ioctxt; struct pci_dev *pdev = hwif->pdev; struct hinic_pfhwdev *pfhwdev; - int i; if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { dev_err(&pdev->dev, "Unsupported PCI Function type\n"); @@ -344,16 +318,7 @@ static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int rq_depth, hw_ioctxt.rq_depth = ilog2(rq_depth); - for (i = 0; ; i++) { - if ((rx_buf_sz_table[i].sz == HINIC_RX_BUF_SZ) || - (rx_buf_sz_table[i].sz == -1)) { - hw_ioctxt.rx_buf_sz_idx = rx_buf_sz_table[i].idx; - break; - } - } - - if (hw_ioctxt.rx_buf_sz_idx == -1) - return -EINVAL; + hw_ioctxt.rx_buf_sz_idx = HINIC_RX_BUF_SZ_IDX; hw_ioctxt.sq_depth = ilog2(sq_depth); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h index e642a8a8cb7f..df729a1587e9 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -53,7 +53,9 @@ #define HINIC_SQ_DEPTH SZ_4K #define HINIC_RQ_DEPTH SZ_4K +/* In any change to HINIC_RX_BUF_SZ, HINIC_RX_BUF_SZ_IDX must be changed */ #define HINIC_RX_BUF_SZ 2048 +#define HINIC_RX_BUF_SZ_IDX HINIC_RX_BUF_SZ_2048_IDX #define HINIC_MIN_TX_WQE_SIZE(wq) \ ALIGN(HINIC_SQ_WQE_SIZE(1), (wq)->wqebb_size) @@ -61,6 +63,25 @@ #define HINIC_MIN_TX_NUM_WQEBBS(sq) \ (HINIC_MIN_TX_WQE_SIZE((sq)->wq) / (sq)->wq->wqebb_size) +enum hinic_rx_buf_sz_idx { + HINIC_RX_BUF_SZ_32_IDX, + HINIC_RX_BUF_SZ_64_IDX, + HINIC_RX_BUF_SZ_96_IDX, + HINIC_RX_BUF_SZ_128_IDX, + HINIC_RX_BUF_SZ_192_IDX, + HINIC_RX_BUF_SZ_256_IDX, + HINIC_RX_BUF_SZ_384_IDX, + HINIC_RX_BUF_SZ_512_IDX, + HINIC_RX_BUF_SZ_768_IDX, + HINIC_RX_BUF_SZ_1024_IDX, + HINIC_RX_BUF_SZ_1536_IDX, + HINIC_RX_BUF_SZ_2048_IDX, + HINIC_RX_BUF_SZ_3072_IDX, + HINIC_RX_BUF_SZ_4096_IDX, + HINIC_RX_BUF_SZ_8192_IDX, + HINIC_RX_BUF_SZ_16384_IDX, +}; + struct hinic_sq { struct hinic_hwif *hwif; -- cgit v1.2.3 From 51564585d8c6de28931ceff51f73eb800147221d Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Mon, 28 Aug 2017 16:18:43 +1000 Subject: ftgmac100: Support NCSI VLAN filtering when available Register the ndo_vlan_rx_{add,kill}_vid callbacks and set the NETIF_F_HW_VLAN_CTAG_FILTER if NCSI is available. This allows the VLAN core to notify the NCSI driver when changes occur so that the remote NCSI channel can be properly configured to filter on the set VLAN tags. Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 34dae51effd4..05fe7123d5ae 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1623,6 +1623,8 @@ static const struct net_device_ops ftgmac100_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ftgmac100_poll_controller, #endif + .ndo_vlan_rx_add_vid = ncsi_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = ncsi_vlan_rx_kill_vid, }; static int ftgmac100_setup_mdio(struct net_device *netdev) @@ -1837,6 +1839,9 @@ static int ftgmac100_probe(struct platform_device *pdev) NETIF_F_GRO | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; + if (priv->use_ncsi) + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + /* AST2400 doesn't have working HW checksum generation */ if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac"))) netdev->hw_features &= ~NETIF_F_HW_CSUM; -- cgit v1.2.3 From 6a17eb27bf7ece364627fcf16ad50c24b793300b Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 28 Aug 2017 13:40:25 -0400 Subject: bnxt_en: Update firmware interface spec. to 1.8.1.4. Flow APIs are added in this firmware interface. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h | 186 +++++++++++++++++++++++++- 1 file changed, 181 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 3ba22e8ee914..cb04cc76e8ad 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -11,14 +11,14 @@ #ifndef BNXT_HSI_H #define BNXT_HSI_H -/* HSI and HWRM Specification 1.8.0 */ +/* HSI and HWRM Specification 1.8.1 */ #define HWRM_VERSION_MAJOR 1 #define HWRM_VERSION_MINOR 8 -#define HWRM_VERSION_UPDATE 0 +#define HWRM_VERSION_UPDATE 1 -#define HWRM_VERSION_RSVD 0 /* non-zero means beta version */ +#define HWRM_VERSION_RSVD 4 /* non-zero means beta version */ -#define HWRM_VERSION_STR "1.8.0.0" +#define HWRM_VERSION_STR "1.8.1.4" /* * Following is the signature for HWRM message field that indicates not * applicable (All F's). Need to cast it the size of the field if needed. @@ -946,6 +946,7 @@ struct hwrm_func_cfg_input { #define FUNC_CFG_REQ_FLAGS_STD_TX_RING_MODE_DISABLE 0x400UL #define FUNC_CFG_REQ_FLAGS_VIRT_MAC_PERSIST 0x800UL #define FUNC_CFG_REQ_FLAGS_NO_AUTOCLEAR_STATISTIC 0x1000UL + #define FUNC_CFG_REQ_FLAGS_TX_ASSETS_TEST 0x2000UL __le32 enables; #define FUNC_CFG_REQ_ENABLES_MTU 0x1UL #define FUNC_CFG_REQ_ENABLES_MRU 0x2UL @@ -1972,7 +1973,12 @@ struct hwrm_port_phy_qcaps_output { #define PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED 0x1UL #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_MASK 0xfeUL #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_SFT 1 - u8 unused_0; + u8 port_cnt; + #define PORT_PHY_QCAPS_RESP_PORT_CNT_UNKNOWN 0x0UL + #define PORT_PHY_QCAPS_RESP_PORT_CNT_1 0x1UL + #define PORT_PHY_QCAPS_RESP_PORT_CNT_2 0x2UL + #define PORT_PHY_QCAPS_RESP_PORT_CNT_3 0x3UL + #define PORT_PHY_QCAPS_RESP_PORT_CNT_4 0x4UL __le16 supported_speeds_force_mode; #define PORT_PHY_QCAPS_RESP_SUPPORTED_SPEEDS_FORCE_MODE_100MBHD 0x1UL #define PORT_PHY_QCAPS_RESP_SUPPORTED_SPEEDS_FORCE_MODE_100MB 0x2UL @@ -4407,6 +4413,164 @@ struct hwrm_cfa_ntuple_filter_cfg_output { u8 valid; }; +/* hwrm_cfa_flow_alloc */ +/* Input (128 bytes) */ +struct hwrm_cfa_flow_alloc_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 flags; + #define CFA_FLOW_ALLOC_REQ_FLAGS_TUNNEL 0x1UL + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_MASK 0x6UL + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_SFT 1 + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_NONE (0x0UL << 1) + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_ONE (0x1UL << 1) + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_TWO (0x2UL << 1) + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_LAST CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_TWO + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_MASK 0x38UL + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_SFT 3 + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_L2 (0x0UL << 3) + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV4 (0x1UL << 3) + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6 (0x2UL << 3) + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_LAST CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6 + __le16 src_fid; + __le32 tunnel_handle; + __le16 action_flags; + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD 0x1UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_RECYCLE 0x2UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP 0x4UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_METER 0x8UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL 0x10UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC 0x20UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST 0x40UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_IPV4_ADDRESS 0x80UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE 0x100UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TTL_DECREMENT 0x200UL + __le16 dst_fid; + __be16 l2_rewrite_vlan_tpid; + __be16 l2_rewrite_vlan_tci; + __le16 act_meter_id; + __le16 ref_flow_handle; + __be16 ethertype; + __be16 outer_vlan_tci; + __be16 dmac[3]; + __be16 inner_vlan_tci; + __be16 smac[3]; + u8 ip_dst_mask_len; + u8 ip_src_mask_len; + __be32 ip_dst[4]; + __be32 ip_src[4]; + __be16 l4_src_port; + __be16 l4_src_port_mask; + __be16 l4_dst_port; + __be16 l4_dst_port_mask; + __be32 nat_ip_address[4]; + __be16 l2_rewrite_dmac[3]; + __be16 nat_port; + __be16 l2_rewrite_smac[3]; + u8 ip_proto; + u8 unused_0; +}; + +/* Output (16 bytes) */ +struct hwrm_cfa_flow_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 flow_handle; + u8 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 unused_4; + u8 valid; +}; + +/* hwrm_cfa_flow_free */ +/* Input (24 bytes) */ +struct hwrm_cfa_flow_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 flow_handle; + __le16 unused_0[3]; +}; + +/* Output (32 bytes) */ +struct hwrm_cfa_flow_free_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le64 packet; + __le64 byte; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_cfa_flow_stats */ +/* Input (40 bytes) */ +struct hwrm_cfa_flow_stats_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 num_flows; + __le16 flow_handle_0; + __le16 flow_handle_1; + __le16 flow_handle_2; + __le16 flow_handle_3; + __le16 flow_handle_4; + __le16 flow_handle_5; + __le16 flow_handle_6; + __le16 flow_handle_7; + __le16 flow_handle_8; + __le16 flow_handle_9; + __le16 unused_0; +}; + +/* Output (176 bytes) */ +struct hwrm_cfa_flow_stats_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le64 packet_0; + __le64 packet_1; + __le64 packet_2; + __le64 packet_3; + __le64 packet_4; + __le64 packet_5; + __le64 packet_6; + __le64 packet_7; + __le64 packet_8; + __le64 packet_9; + __le64 byte_0; + __le64 byte_1; + __le64 byte_2; + __le64 byte_3; + __le64 byte_4; + __le64 byte_5; + __le64 byte_6; + __le64 byte_7; + __le64 byte_8; + __le64 byte_9; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + /* hwrm_cfa_vfr_alloc */ /* Input (32 bytes) */ struct hwrm_cfa_vfr_alloc_input { @@ -5534,11 +5698,15 @@ struct hwrm_selftest_qlist_output { #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_LINK_TEST 0x2UL #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_REGISTER_TEST 0x4UL #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_MEMORY_TEST 0x8UL + #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_ETHERNET_EYE_TEST 0x20UL u8 offline_tests; #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_NVM_TEST 0x1UL #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_LINK_TEST 0x2UL #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_REGISTER_TEST 0x4UL #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_MEMORY_TEST 0x8UL + #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_ETHERNET_EYE_TEST 0x20UL u8 unused_0; __le16 test_timeout; u8 unused_1; @@ -5566,6 +5734,8 @@ struct hwrm_selftest_exec_input { #define SELFTEST_EXEC_REQ_FLAGS_LINK_TEST 0x2UL #define SELFTEST_EXEC_REQ_FLAGS_REGISTER_TEST 0x4UL #define SELFTEST_EXEC_REQ_FLAGS_MEMORY_TEST 0x8UL + #define SELFTEST_EXEC_REQ_FLAGS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_EXEC_REQ_FLAGS_ETHERNET_EYE_TEST 0x20UL u8 unused_0[7]; }; @@ -5580,11 +5750,15 @@ struct hwrm_selftest_exec_output { #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_LINK_TEST 0x2UL #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_REGISTER_TEST 0x4UL #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_MEMORY_TEST 0x8UL + #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_ETHERNET_EYE_TEST 0x20UL u8 test_success; #define SELFTEST_EXEC_RESP_TEST_SUCCESS_NVM_TEST 0x1UL #define SELFTEST_EXEC_RESP_TEST_SUCCESS_LINK_TEST 0x2UL #define SELFTEST_EXEC_RESP_TEST_SUCCESS_REGISTER_TEST 0x4UL #define SELFTEST_EXEC_RESP_TEST_SUCCESS_MEMORY_TEST 0x8UL + #define SELFTEST_EXEC_RESP_TEST_SUCCESS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_EXEC_RESP_TEST_SUCCESS_ETHERNET_EYE_TEST 0x20UL __le16 unused_0[3]; }; @@ -5767,6 +5941,7 @@ struct cmd_nums { #define HWRM_SELFTEST_QLIST (0x200UL) #define HWRM_SELFTEST_EXEC (0x201UL) #define HWRM_SELFTEST_IRQ (0x202UL) + #define HWRM_SELFTEST_RETREIVE_EYE_DATA (0x203UL) #define HWRM_DBG_READ_DIRECT (0xff10UL) #define HWRM_DBG_READ_INDIRECT (0xff11UL) #define HWRM_DBG_WRITE_DIRECT (0xff12UL) @@ -5984,6 +6159,7 @@ struct hwrm_struct_hdr { #define STRUCT_HDR_STRUCT_ID_LLDP_DEVICE 0x426UL #define STRUCT_HDR_STRUCT_ID_AFM_OPAQUE 0x1UL #define STRUCT_HDR_STRUCT_ID_PORT_DESCRIPTION 0xaUL + #define STRUCT_HDR_STRUCT_ID_RSS_V2 0x64UL __le16 len; u8 version; u8 count; -- cgit v1.2.3 From 98fdbe73bfb809b1f8eec9f27a36e737caed3a44 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 28 Aug 2017 13:40:26 -0400 Subject: bnxt_en: Improve tx ring reservation logic. When the number of TX rings is changed (e.g. ethtool -L, enabling XDP TX rings, etc), the current code tries to reserve the new number of TX rings before closing and re-opening the NIC. If we are unable to reserve the new TX rings, we abort the operation and keep the current TX rings. The problem is that the firmware will disable the current TX rings even when it cannot reserve the new set of TX rings. We fix it as follows: 1. Instead of reserving the new set of TX rings, just ask the firmware to check if the new set of TX rings is available. There is a flag in the firmware message to do that. If not available, abort and the current TX rings will not be disabled. 2. Do the actual TX ring reservation in the path that opens the NIC. We keep the number of TX rings currently successfully reserved. If the number of TX rings is different than the reserved TX rings, we call firmware and reserve again. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 46 +++++++++++++++++++---- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 5 ++- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 3 +- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 4 +- 4 files changed, 44 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 6e14fc4fe2c8..4b0a80721c89 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4461,9 +4461,33 @@ static int bnxt_hwrm_reserve_tx_rings(struct bnxt *bp, int *tx_rings) mutex_lock(&bp->hwrm_cmd_lock); rc = __bnxt_hwrm_get_tx_rings(bp, 0xffff, tx_rings); mutex_unlock(&bp->hwrm_cmd_lock); + if (!rc) + bp->tx_reserved_rings = *tx_rings; return rc; } +static int bnxt_hwrm_check_tx_rings(struct bnxt *bp, int tx_rings) +{ + struct hwrm_func_cfg_input req = {0}; + int rc; + + if (bp->hwrm_spec_code < 0x10801) + return 0; + + if (BNXT_VF(bp)) + return 0; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1); + req.fid = cpu_to_le16(0xffff); + req.flags = cpu_to_le32(FUNC_CFG_REQ_FLAGS_TX_ASSETS_TEST); + req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_NUM_TX_RINGS); + req.num_tx_rings = cpu_to_le16(tx_rings); + rc = hwrm_send_message_silent(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + return -ENOMEM; + return 0; +} + static void bnxt_hwrm_set_coal_params(struct bnxt *bp, u32 max_bufs, u32 buf_tmrs, u16 flags, struct hwrm_ring_cmpl_ring_cfg_aggint_params_input *req) @@ -5115,6 +5139,15 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) rc); goto err_out; } + if (bp->tx_reserved_rings != bp->tx_nr_rings) { + int tx = bp->tx_nr_rings; + + if (bnxt_hwrm_reserve_tx_rings(bp, &tx) || + tx < bp->tx_nr_rings) { + rc = -ENOMEM; + goto err_out; + } + } } rc = bnxt_hwrm_ring_alloc(bp); @@ -6998,8 +7031,8 @@ static void bnxt_sp_task(struct work_struct *work) } /* Under rtnl_lock */ -int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, - int tx_xdp) +int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, + int tx_xdp) { int max_rx, max_tx, tx_sets = 1; int tx_rings_needed; @@ -7019,10 +7052,7 @@ int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, if (max_tx < tx_rings_needed) return -ENOMEM; - if (bnxt_hwrm_reserve_tx_rings(bp, &tx_rings_needed) || - tx_rings_needed < (tx * tx_sets + tx_xdp)) - return -ENOMEM; - return 0; + return bnxt_hwrm_check_tx_rings(bp, tx_rings_needed); } static void bnxt_unmap_bars(struct bnxt *bp, struct pci_dev *pdev) @@ -7211,8 +7241,8 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) if (bp->flags & BNXT_FLAG_SHARED_RINGS) sh = true; - rc = bnxt_reserve_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, - sh, tc, bp->tx_nr_rings_xdp); + rc = bnxt_check_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, + sh, tc, bp->tx_nr_rings_xdp); if (rc) return rc; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 2d84d5719b70..568516f9e36e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1118,6 +1118,7 @@ struct bnxt { int tx_nr_rings; int tx_nr_rings_per_tc; int tx_nr_rings_xdp; + int tx_reserved_rings; int tx_wake_thresh; int tx_push_thresh; @@ -1346,8 +1347,8 @@ int bnxt_open_nic(struct bnxt *, bool, bool); int bnxt_half_open_nic(struct bnxt *bp); void bnxt_half_close_nic(struct bnxt *bp); int bnxt_close_nic(struct bnxt *, bool, bool); -int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, - int tx_xdp); +int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, + int tx_xdp); int bnxt_setup_mq_tc(struct net_device *dev, u8 tc); int bnxt_get_max_rings(struct bnxt *, int *, int *, bool); void bnxt_restore_pf_fw_resources(struct bnxt *bp); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 08b870d7d466..8eff05a3e0e4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -435,8 +435,7 @@ static int bnxt_set_channels(struct net_device *dev, } tx_xdp = req_rx_rings; } - rc = bnxt_reserve_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, - tx_xdp); + rc = bnxt_check_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, tx_xdp); if (rc) { netdev_warn(dev, "Unable to allocate the requested rings\n"); return rc; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 3961a6807454..d8f0c837b72c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -169,8 +169,8 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) tc = netdev_get_num_tc(dev); if (!tc) tc = 1; - rc = bnxt_reserve_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, - true, tc, tx_xdp); + rc = bnxt_check_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, + true, tc, tx_xdp); if (rc) { netdev_warn(dev, "Unable to reserve enough TX rings to support XDP.\n"); return rc; -- cgit v1.2.3 From 56f0fd80d1886479a42ac07ed239538eb145a669 Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Mon, 28 Aug 2017 13:40:27 -0400 Subject: bnxt_en: assign CPU affinity hints to bnxt_en IRQs This patch provides hints to irqbalance to map bnxt_en device IRQs to specific CPU cores. cpumask_local_spread() is used, which first maps IRQs to near NUMA cores; when those cores are exhausted, IRQs are mapped to far NUMA cores. Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 25 ++++++++++++++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 4 +++- 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4b0a80721c89..9657bfbeb816 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -49,6 +49,7 @@ #include #include #include +#include #include "bnxt_hsi.h" #include "bnxt.h" @@ -5554,8 +5555,15 @@ static void bnxt_free_irq(struct bnxt *bp) for (i = 0; i < bp->cp_nr_rings; i++) { irq = &bp->irq_tbl[i]; - if (irq->requested) + if (irq->requested) { + if (irq->have_cpumask) { + irq_set_affinity_hint(irq->vector, NULL); + free_cpumask_var(irq->cpu_mask); + irq->have_cpumask = 0; + } free_irq(irq->vector, bp->bnapi[i]); + } + irq->requested = 0; } } @@ -5588,6 +5596,21 @@ static int bnxt_request_irq(struct bnxt *bp) break; irq->requested = 1; + + if (zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL)) { + int numa_node = dev_to_node(&bp->pdev->dev); + + irq->have_cpumask = 1; + cpumask_set_cpu(cpumask_local_spread(i, numa_node), + irq->cpu_mask); + rc = irq_set_affinity_hint(irq->vector, irq->cpu_mask); + if (rc) { + netdev_warn(bp->dev, + "Set affinity failed, IRQ = %d\n", + irq->vector); + break; + } + } } return rc; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 568516f9e36e..801c0f77d4a5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -701,8 +701,10 @@ struct bnxt_napi { struct bnxt_irq { irq_handler_t handler; unsigned int vector; - u8 requested; + u8 requested:1; + u8 have_cpumask:1; char name[IFNAMSIZ + 2]; + cpumask_var_t cpu_mask; }; #define HWRM_RING_ALLOC_TX 0x1 -- cgit v1.2.3 From 4a58139b8493624c6c6223b58a9e70ebbdf56338 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Mon, 28 Aug 2017 13:40:28 -0400 Subject: bnxt: Add PCIe device IDs for bcm58802/bcm58808 Add PCIe device ID for bcm58802 and bcm58808. Also add chip number update to declare bcm588xx as chip class phase 4 and later Signed-off-by: Ray Jui Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 +++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 9657bfbeb816..500681da3c16 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -104,6 +104,8 @@ enum board_idx { BCM57416_NPAR, BCM57452, BCM57454, + BCM58802, + BCM58808, NETXTREME_E_VF, NETXTREME_C_VF, }; @@ -140,11 +142,14 @@ static const struct { { "Broadcom BCM57416 NetXtreme-E Ethernet Partition" }, { "Broadcom BCM57452 NetXtreme-E 10Gb/25Gb/40Gb/50Gb Ethernet" }, { "Broadcom BCM57454 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" }, + { "Broadcom BCM58802 NetXtreme-S 10Gb/25Gb/40Gb/50Gb Ethernet" }, + { "Broadcom BCM58808 NetXtreme-S 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" }, { "Broadcom NetXtreme-E Ethernet Virtual Function" }, { "Broadcom NetXtreme-C Ethernet Virtual Function" }, }; static const struct pci_device_id bnxt_pci_tbl[] = { + { PCI_VDEVICE(BROADCOM, 0x1614), .driver_data = BCM57454 }, { PCI_VDEVICE(BROADCOM, 0x16c0), .driver_data = BCM57417_NPAR }, { PCI_VDEVICE(BROADCOM, 0x16c8), .driver_data = BCM57301 }, { PCI_VDEVICE(BROADCOM, 0x16c9), .driver_data = BCM57302 }, @@ -175,8 +180,9 @@ static const struct pci_device_id bnxt_pci_tbl[] = { { PCI_VDEVICE(BROADCOM, 0x16ed), .driver_data = BCM57414_NPAR }, { PCI_VDEVICE(BROADCOM, 0x16ee), .driver_data = BCM57416_NPAR }, { PCI_VDEVICE(BROADCOM, 0x16ef), .driver_data = BCM57416_NPAR }, + { PCI_VDEVICE(BROADCOM, 0x16f0), .driver_data = BCM58808 }, { PCI_VDEVICE(BROADCOM, 0x16f1), .driver_data = BCM57452 }, - { PCI_VDEVICE(BROADCOM, 0x1614), .driver_data = BCM57454 }, + { PCI_VDEVICE(BROADCOM, 0xd802), .driver_data = BCM58802 }, #ifdef CONFIG_BNXT_SRIOV { PCI_VDEVICE(BROADCOM, 0x1606), .driver_data = NETXTREME_E_VF }, { PCI_VDEVICE(BROADCOM, 0x1609), .driver_data = NETXTREME_E_VF }, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 801c0f77d4a5..3521e467a64c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -990,6 +990,9 @@ struct bnxt { #define CHIP_NUM_5745X 0xd730 +#define CHIP_NUM_58802 0xd802 +#define CHIP_NUM_58808 0xd808 + #define BNXT_CHIP_NUM_5730X(chip_num) \ ((chip_num) >= CHIP_NUM_57301 && \ (chip_num) <= CHIP_NUM_57304) @@ -1021,6 +1024,10 @@ struct bnxt { #define BNXT_CHIP_NUM_57X1X(chip_num) \ (BNXT_CHIP_NUM_5731X(chip_num) || BNXT_CHIP_NUM_5741X(chip_num)) +#define BNXT_CHIP_NUM_588XX(chip_num) \ + ((chip_num) == CHIP_NUM_58802 || \ + (chip_num) == CHIP_NUM_58808) + struct net_device *dev; struct pci_dev *pdev; @@ -1079,6 +1086,7 @@ struct bnxt { #define BNXT_CHIP_P4_PLUS(bp) \ (BNXT_CHIP_NUM_57X1X((bp)->chip_num) || \ BNXT_CHIP_NUM_5745X((bp)->chip_num) || \ + BNXT_CHIP_NUM_588XX((bp)->chip_num) || \ (BNXT_CHIP_NUM_58700((bp)->chip_num) && \ !BNXT_CHIP_TYPE_NITRO_A0(bp))) -- cgit v1.2.3 From 27573a7d905a49dc756fda9c0e148372136356e6 Mon Sep 17 00:00:00 2001 From: Scott Branden Date: Mon, 28 Aug 2017 13:40:29 -0400 Subject: bnxt: initialize board_info values with proper enums initialize board_info values with proper enums for defensive programming purposes. This will avoid any errors of the enums being declared not lining up with the board_info array. Signed-off-by: Scott Branden Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 64 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 500681da3c16..1afb408798d1 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -114,38 +114,38 @@ enum board_idx { static const struct { char *name; } board_info[] = { - { "Broadcom BCM57301 NetXtreme-C 10Gb Ethernet" }, - { "Broadcom BCM57302 NetXtreme-C 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57304 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" }, - { "Broadcom BCM57417 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM58700 Nitro 1Gb/2.5Gb/10Gb Ethernet" }, - { "Broadcom BCM57311 NetXtreme-C 10Gb Ethernet" }, - { "Broadcom BCM57312 NetXtreme-C 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57402 NetXtreme-E 10Gb Ethernet" }, - { "Broadcom BCM57404 NetXtreme-E 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57406 NetXtreme-E 10GBase-T Ethernet" }, - { "Broadcom BCM57402 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57407 NetXtreme-E 10GBase-T Ethernet" }, - { "Broadcom BCM57412 NetXtreme-E 10Gb Ethernet" }, - { "Broadcom BCM57414 NetXtreme-E 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57416 NetXtreme-E 10GBase-T Ethernet" }, - { "Broadcom BCM57417 NetXtreme-E 10GBase-T Ethernet" }, - { "Broadcom BCM57412 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57314 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" }, - { "Broadcom BCM57417 NetXtreme-E 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57416 NetXtreme-E 10Gb Ethernet" }, - { "Broadcom BCM57404 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57406 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57407 NetXtreme-E 25Gb Ethernet" }, - { "Broadcom BCM57407 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57414 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57416 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57452 NetXtreme-E 10Gb/25Gb/40Gb/50Gb Ethernet" }, - { "Broadcom BCM57454 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" }, - { "Broadcom BCM58802 NetXtreme-S 10Gb/25Gb/40Gb/50Gb Ethernet" }, - { "Broadcom BCM58808 NetXtreme-S 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" }, - { "Broadcom NetXtreme-E Ethernet Virtual Function" }, - { "Broadcom NetXtreme-C Ethernet Virtual Function" }, + [BCM57301] = { "Broadcom BCM57301 NetXtreme-C 10Gb Ethernet" }, + [BCM57302] = { "Broadcom BCM57302 NetXtreme-C 10Gb/25Gb Ethernet" }, + [BCM57304] = { "Broadcom BCM57304 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" }, + [BCM57417_NPAR] = { "Broadcom BCM57417 NetXtreme-E Ethernet Partition" }, + [BCM58700] = { "Broadcom BCM58700 Nitro 1Gb/2.5Gb/10Gb Ethernet" }, + [BCM57311] = { "Broadcom BCM57311 NetXtreme-C 10Gb Ethernet" }, + [BCM57312] = { "Broadcom BCM57312 NetXtreme-C 10Gb/25Gb Ethernet" }, + [BCM57402] = { "Broadcom BCM57402 NetXtreme-E 10Gb Ethernet" }, + [BCM57404] = { "Broadcom BCM57404 NetXtreme-E 10Gb/25Gb Ethernet" }, + [BCM57406] = { "Broadcom BCM57406 NetXtreme-E 10GBase-T Ethernet" }, + [BCM57402_NPAR] = { "Broadcom BCM57402 NetXtreme-E Ethernet Partition" }, + [BCM57407] = { "Broadcom BCM57407 NetXtreme-E 10GBase-T Ethernet" }, + [BCM57412] = { "Broadcom BCM57412 NetXtreme-E 10Gb Ethernet" }, + [BCM57414] = { "Broadcom BCM57414 NetXtreme-E 10Gb/25Gb Ethernet" }, + [BCM57416] = { "Broadcom BCM57416 NetXtreme-E 10GBase-T Ethernet" }, + [BCM57417] = { "Broadcom BCM57417 NetXtreme-E 10GBase-T Ethernet" }, + [BCM57412_NPAR] = { "Broadcom BCM57412 NetXtreme-E Ethernet Partition" }, + [BCM57314] = { "Broadcom BCM57314 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" }, + [BCM57417_SFP] = { "Broadcom BCM57417 NetXtreme-E 10Gb/25Gb Ethernet" }, + [BCM57416_SFP] = { "Broadcom BCM57416 NetXtreme-E 10Gb Ethernet" }, + [BCM57404_NPAR] = { "Broadcom BCM57404 NetXtreme-E Ethernet Partition" }, + [BCM57406_NPAR] = { "Broadcom BCM57406 NetXtreme-E Ethernet Partition" }, + [BCM57407_SFP] = { "Broadcom BCM57407 NetXtreme-E 25Gb Ethernet" }, + [BCM57407_NPAR] = { "Broadcom BCM57407 NetXtreme-E Ethernet Partition" }, + [BCM57414_NPAR] = { "Broadcom BCM57414 NetXtreme-E Ethernet Partition" }, + [BCM57416_NPAR] = { "Broadcom BCM57416 NetXtreme-E Ethernet Partition" }, + [BCM57452] = { "Broadcom BCM57452 NetXtreme-E 10Gb/25Gb/40Gb/50Gb Ethernet" }, + [BCM57454] = { "Broadcom BCM57454 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" }, + [BCM58802] = { "Broadcom BCM58802 NetXtreme-S 10Gb/25Gb/40Gb/50Gb Ethernet" }, + [BCM58808] = { "Broadcom BCM58808 NetXtreme-S 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" }, + [NETXTREME_E_VF] = { "Broadcom NetXtreme-E Ethernet Virtual Function" }, + [NETXTREME_C_VF] = { "Broadcom NetXtreme-C Ethernet Virtual Function" }, }; static const struct pci_device_id bnxt_pci_tbl[] = { -- cgit v1.2.3 From 903649e718f80da2ba4b65a0adf6930219b4b2e5 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 28 Aug 2017 13:40:30 -0400 Subject: bnxt_en: Improve -ENOMEM logic in NAPI poll loop. If we cannot allocate RX buffers in the NAPI poll loop when processing an RX event, the current code does not count that event towards the NAPI budget. This can cause us to potentially loop forever in NAPI if we consistently cannot allocate new buffers. Improve it by counting -ENOMEM event as 1 towards the NAPI budget. Cc: Martin KaFai Lau Signed-off-by: Michael Chan Reported-by: Martin KaFai Lau Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 1afb408798d1..a34fcddbd14c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1850,6 +1850,13 @@ static int bnxt_poll_work(struct bnxt *bp, struct bnxt_napi *bnapi, int budget) &event); if (likely(rc >= 0)) rx_pkts += rc; + /* Increment rx_pkts when rc is -ENOMEM to count towards + * the NAPI budget. Otherwise, we may potentially loop + * here forever if we consistently cannot allocate + * buffers. + */ + else if (rc == -ENOMEM) + rx_pkts++; else if (rc == -EBUSY) /* partial completion */ break; } else if (unlikely((TX_CMP_TYPE(txcmp) == -- cgit v1.2.3 From d5430d31ca72ec37fd539fd1c5230859509be4ef Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 28 Aug 2017 13:40:31 -0400 Subject: bnxt_en: Reduce default rings on multi-port cards. Reduce default rings from 8 to 4 on multi-port cards to reduce memory usage. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 13 +++++++++---- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index a34fcddbd14c..4406f9195851 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5795,6 +5795,8 @@ static int bnxt_hwrm_phy_qcaps(struct bnxt *bp) link_info->support_auto_speeds = le16_to_cpu(resp->supported_speeds_auto_mode); + bp->port_count = resp->port_cnt; + hwrm_phy_qcaps_exit: mutex_unlock(&bp->hwrm_cmd_lock); return rc; @@ -7877,6 +7879,9 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) if (sh) bp->flags |= BNXT_FLAG_SHARED_RINGS; dflt_rings = netif_get_num_default_rss_queues(); + /* Reduce default rings to reduce memory usage on multi-port cards */ + if (bp->port_count > 1) + dflt_rings = min_t(int, dflt_rings, 4); rc = bnxt_get_dflt_rings(bp, &max_rx_rings, &max_tx_rings, sh); if (rc) return rc; @@ -8049,6 +8054,10 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) bnxt_ethtool_init(bp); bnxt_dcb_init(bp); + rc = bnxt_probe_phy(bp); + if (rc) + goto init_err_pci_clean; + bnxt_set_rx_skb_mode(bp, false); bnxt_set_tpa_flags(bp); bnxt_set_ring_params(bp); @@ -8083,10 +8092,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (dev->hw_features & NETIF_F_HW_VLAN_CTAG_RX) bp->flags |= BNXT_FLAG_STRIP_VLAN; - rc = bnxt_probe_phy(bp); - if (rc) - goto init_err_pci_clean; - rc = bnxt_init_int_mode(bp); if (rc) goto init_err_pci_clean; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 3521e467a64c..86af8ead7e5b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1207,6 +1207,7 @@ struct bnxt { u8 nge_port_cnt; __le16 nge_fw_dst_port_id; u8 port_partition_type; + u8 port_count; u16 br_mode; u16 rx_coal_ticks; -- cgit v1.2.3 From 70855603e0a129d1c810947a4e4dd7ecd6f4560d Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Mon, 28 Aug 2017 13:40:32 -0400 Subject: bnxt_en: fix clearing devlink ptr from bnxt struct The routine bnxt_link_bp_to_dl() is used to set the devlink ptr in bnxt struct (bp) and also to set the bnxt back ptr in the devlink struct. If devlink_register() fails, bp->dl must be cleared which is not happening currently. This patch fixes bnxt_link_bp_to_dl() to clear bp->dl by passing a NULL dl ptr. Fixes: 4ab0c6a8ffd7 ("bnxt_en: add support to enable VF-representors") Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 4 ++-- drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index 86cce6f53f78..c365d3cf8e51 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -468,11 +468,11 @@ int bnxt_dl_register(struct bnxt *bp) return -ENOMEM; } - bnxt_link_bp_to_dl(dl, bp); + bnxt_link_bp_to_dl(bp, dl); bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; rc = devlink_register(dl, &bp->pdev->dev); if (rc) { - bnxt_link_bp_to_dl(dl, NULL); + bnxt_link_bp_to_dl(bp, NULL); devlink_free(dl); netdev_warn(bp->dev, "devlink_register failed. rc=%d", rc); return rc; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h index e55a3b693e20..3e997c9e2580 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h @@ -24,13 +24,17 @@ static inline struct bnxt *bnxt_get_bp_from_dl(struct devlink *dl) return ((struct bnxt_dl *)devlink_priv(dl))->bp; } -static inline void bnxt_link_bp_to_dl(struct devlink *dl, struct bnxt *bp) +/* To clear devlink pointer from bp, pass NULL dl */ +static inline void bnxt_link_bp_to_dl(struct bnxt *bp, struct devlink *dl) { - struct bnxt_dl *bp_dl = devlink_priv(dl); + bp->dl = dl; - bp_dl->bp = bp; - if (bp) - bp->dl = dl; + /* add a back pointer in dl to bp */ + if (dl) { + struct bnxt_dl *bp_dl = devlink_priv(dl); + + bp_dl->bp = bp; + } } int bnxt_dl_register(struct bnxt *bp); -- cgit v1.2.3 From 2ae7408fedfee979e01ed3801223c632bb124c46 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Mon, 28 Aug 2017 13:40:33 -0400 Subject: bnxt_en: bnxt: add TC flower filter offload support This patch adds support for offloading TC based flow rules and actions for the 'flower' classifier in the bnxt_en driver. It includes logic to parse flow rules and actions received from the TC subsystem, store them and issue the corresponding hwrm_cfa_flow_alloc/free FW cmds. L2/IPv4/IPv6 flows and drop, redir, vlan push/pop actions are supported in this patch. In this patch the hwrm_cfa_flow_xxx routines are just stubs. The code for these routines is introduced in the next patch for easier review. Also, the code to query the TC/flower action stats will be introduced in a subsequent patch. Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/Kconfig | 9 + drivers/net/ethernet/broadcom/bnxt/Makefile | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 39 +- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 23 + drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c | 602 ++++++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h | 158 +++++++ drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 18 + drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h | 8 + 8 files changed, 850 insertions(+), 9 deletions(-) create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 1456cb18f830..67134ece1107 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -212,6 +212,15 @@ config BNXT_SRIOV Virtualization support in the NetXtreme-C/E products. This allows for virtual function acceleration in virtual environments. +config BNXT_FLOWER_OFFLOAD + bool "TC Flower offload support for NetXtreme-C/E" + depends on BNXT + default y + ---help--- + This configuration parameter enables TC Flower packet classifier + offload for eswitch. This option enables SR-IOV switchdev eswitch + offload. + config BNXT_DCB bool "Data Center Bridging (DCB) Support" default n diff --git a/drivers/net/ethernet/broadcom/bnxt/Makefile b/drivers/net/ethernet/broadcom/bnxt/Makefile index d141a22ac50b..4f0cb8e1ffc0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/Makefile +++ b/drivers/net/ethernet/broadcom/bnxt/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_BNXT) += bnxt_en.o -bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o bnxt_vfr.o +bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o bnxt_vfr.o bnxt_tc.o diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4406f9195851..d6367c10afb5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -50,6 +50,7 @@ #include #include #include +#include #include "bnxt_hsi.h" #include "bnxt.h" @@ -59,6 +60,7 @@ #include "bnxt_dcb.h" #include "bnxt_xdp.h" #include "bnxt_vfr.h" +#include "bnxt_tc.h" #define BNXT_TX_TIMEOUT (5 * HZ) @@ -7305,17 +7307,33 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) return 0; } -static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, - void *type_data) +static int bnxt_setup_flower(struct net_device *dev, + struct tc_cls_flower_offload *cls_flower) { - struct tc_mqprio_qopt *mqprio = type_data; + struct bnxt *bp = netdev_priv(dev); - if (type != TC_SETUP_MQPRIO) + if (BNXT_VF(bp)) return -EOPNOTSUPP; - mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + return bnxt_tc_setup_flower(bp, bp->pf.fw_fid, cls_flower); +} + +static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + switch (type) { + case TC_SETUP_CLSFLOWER: + return bnxt_setup_flower(dev, type_data); + case TC_SETUP_MQPRIO: { + struct tc_mqprio_qopt *mqprio = type_data; + + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return bnxt_setup_mq_tc(dev, mqprio->num_tc); + return bnxt_setup_mq_tc(dev, mqprio->num_tc); + } + default: + return -EOPNOTSUPP; + } } #ifdef CONFIG_RFS_ACCEL @@ -7711,6 +7729,7 @@ static void bnxt_remove_one(struct pci_dev *pdev) pci_disable_pcie_error_reporting(pdev); unregister_netdev(dev); + bnxt_shutdown_tc(bp); cancel_work_sync(&bp->sp_task); bp->sp_event = 0; @@ -8102,9 +8121,12 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) else device_set_wakeup_capable(&pdev->dev, false); + if (BNXT_PF(bp)) + bnxt_init_tc(bp); + rc = register_netdev(dev); if (rc) - goto init_err_clr_int; + goto init_err_cleanup_tc; if (BNXT_PF(bp)) bnxt_dl_register(bp); @@ -8117,7 +8139,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; -init_err_clr_int: +init_err_cleanup_tc: + bnxt_shutdown_tc(bp); bnxt_clear_int_mode(bp); init_err_pci_clean: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 86af8ead7e5b..7b888d4b2b55 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -19,6 +19,7 @@ #define DRV_VER_UPD 0 #include +#include #include #include #include @@ -943,6 +944,27 @@ struct bnxt_test_info { #define BNXT_CAG_REG_LEGACY_INT_STATUS 0x4014 #define BNXT_CAG_REG_BASE 0x300000 +struct bnxt_tc_info { + bool enabled; + + /* hash table to store TC offloaded flows */ + struct rhashtable flow_table; + struct rhashtable_params flow_ht_params; + + /* hash table to store L2 keys of TC flows */ + struct rhashtable l2_table; + struct rhashtable_params l2_ht_params; + + /* lock to atomically add/del an l2 node when a flow is + * added or deleted. + */ + struct mutex lock; + + /* Stat counter mask (width) */ + u64 bytes_mask; + u64 packets_mask; +}; + struct bnxt_vf_rep_stats { u64 packets; u64 bytes; @@ -1289,6 +1311,7 @@ struct bnxt { enum devlink_eswitch_mode eswitch_mode; struct bnxt_vf_rep **vf_reps; /* array of vf-rep ptrs */ u16 *cfa_code_map; /* cfa_code -> vf_idx map */ + struct bnxt_tc_info tc_info; }; #define BNXT_RX_STATS_OFFSET(counter) \ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c new file mode 100644 index 000000000000..a10df27fa32c --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c @@ -0,0 +1,602 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2017 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bnxt_hsi.h" +#include "bnxt.h" +#include "bnxt_sriov.h" +#include "bnxt_tc.h" +#include "bnxt_vfr.h" + +#ifdef CONFIG_BNXT_FLOWER_OFFLOAD + +#define BNXT_FID_INVALID 0xffff +#define VLAN_TCI(vid, prio) ((vid) | ((prio) << VLAN_PRIO_SHIFT)) + +/* Return the dst fid of the func for flow forwarding + * For PFs: src_fid is the fid of the PF + * For VF-reps: src_fid the fid of the VF + */ +static u16 bnxt_flow_get_dst_fid(struct bnxt *pf_bp, struct net_device *dev) +{ + struct bnxt *bp; + + /* check if dev belongs to the same switch */ + if (!switchdev_port_same_parent_id(pf_bp->dev, dev)) { + netdev_info(pf_bp->dev, "dev(ifindex=%d) not on same switch", + dev->ifindex); + return BNXT_FID_INVALID; + } + + /* Is dev a VF-rep? */ + if (dev != pf_bp->dev) + return bnxt_vf_rep_get_fid(dev); + + bp = netdev_priv(dev); + return bp->pf.fw_fid; +} + +static int bnxt_tc_parse_redir(struct bnxt *bp, + struct bnxt_tc_actions *actions, + const struct tc_action *tc_act) +{ + int ifindex = tcf_mirred_ifindex(tc_act); + struct net_device *dev; + u16 dst_fid; + + dev = __dev_get_by_index(dev_net(bp->dev), ifindex); + if (!dev) { + netdev_info(bp->dev, "no dev for ifindex=%d", ifindex); + return -EINVAL; + } + + /* find the FID from dev */ + dst_fid = bnxt_flow_get_dst_fid(bp, dev); + if (dst_fid == BNXT_FID_INVALID) { + netdev_info(bp->dev, "can't get fid for ifindex=%d", ifindex); + return -EINVAL; + } + + actions->flags |= BNXT_TC_ACTION_FLAG_FWD; + actions->dst_fid = dst_fid; + actions->dst_dev = dev; + return 0; +} + +static void bnxt_tc_parse_vlan(struct bnxt *bp, + struct bnxt_tc_actions *actions, + const struct tc_action *tc_act) +{ + if (tcf_vlan_action(tc_act) == TCA_VLAN_ACT_POP) { + actions->flags |= BNXT_TC_ACTION_FLAG_POP_VLAN; + } else if (tcf_vlan_action(tc_act) == TCA_VLAN_ACT_PUSH) { + actions->flags |= BNXT_TC_ACTION_FLAG_PUSH_VLAN; + actions->push_vlan_tci = htons(tcf_vlan_push_vid(tc_act)); + actions->push_vlan_tpid = tcf_vlan_push_proto(tc_act); + } +} + +static int bnxt_tc_parse_actions(struct bnxt *bp, + struct bnxt_tc_actions *actions, + struct tcf_exts *tc_exts) +{ + const struct tc_action *tc_act; + LIST_HEAD(tc_actions); + int rc; + + if (!tcf_exts_has_actions(tc_exts)) { + netdev_info(bp->dev, "no actions"); + return -EINVAL; + } + + tcf_exts_to_list(tc_exts, &tc_actions); + list_for_each_entry(tc_act, &tc_actions, list) { + /* Drop action */ + if (is_tcf_gact_shot(tc_act)) { + actions->flags |= BNXT_TC_ACTION_FLAG_DROP; + return 0; /* don't bother with other actions */ + } + + /* Redirect action */ + if (is_tcf_mirred_egress_redirect(tc_act)) { + rc = bnxt_tc_parse_redir(bp, actions, tc_act); + if (rc) + return rc; + continue; + } + + /* Push/pop VLAN */ + if (is_tcf_vlan(tc_act)) { + bnxt_tc_parse_vlan(bp, actions, tc_act); + continue; + } + } + + return 0; +} + +#define GET_KEY(flow_cmd, key_type) \ + skb_flow_dissector_target((flow_cmd)->dissector, key_type,\ + (flow_cmd)->key) +#define GET_MASK(flow_cmd, key_type) \ + skb_flow_dissector_target((flow_cmd)->dissector, key_type,\ + (flow_cmd)->mask) + +static int bnxt_tc_parse_flow(struct bnxt *bp, + struct tc_cls_flower_offload *tc_flow_cmd, + struct bnxt_tc_flow *flow) +{ + struct flow_dissector *dissector = tc_flow_cmd->dissector; + u16 addr_type = 0; + + /* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */ + if ((dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) == 0 || + (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC)) == 0) { + netdev_info(bp->dev, "cannot form TC key: used_keys = 0x%x", + dissector->used_keys); + return -EOPNOTSUPP; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_CONTROL)) { + struct flow_dissector_key_control *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_CONTROL); + + addr_type = key->addr_type; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_dissector_key_basic *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_BASIC); + struct flow_dissector_key_basic *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_BASIC); + + flow->l2_key.ether_type = key->n_proto; + flow->l2_mask.ether_type = mask->n_proto; + + if (key->n_proto == htons(ETH_P_IP) || + key->n_proto == htons(ETH_P_IPV6)) { + flow->l4_key.ip_proto = key->ip_proto; + flow->l4_mask.ip_proto = mask->ip_proto; + } + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_dissector_key_eth_addrs *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ETH_ADDRS); + struct flow_dissector_key_eth_addrs *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ETH_ADDRS); + + flow->flags |= BNXT_TC_FLOW_FLAGS_ETH_ADDRS; + ether_addr_copy(flow->l2_key.dmac, key->dst); + ether_addr_copy(flow->l2_mask.dmac, mask->dst); + ether_addr_copy(flow->l2_key.smac, key->src); + ether_addr_copy(flow->l2_mask.smac, mask->src); + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_VLAN)) { + struct flow_dissector_key_vlan *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_VLAN); + struct flow_dissector_key_vlan *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_VLAN); + + flow->l2_key.inner_vlan_tci = + cpu_to_be16(VLAN_TCI(key->vlan_id, key->vlan_priority)); + flow->l2_mask.inner_vlan_tci = + cpu_to_be16((VLAN_TCI(mask->vlan_id, mask->vlan_priority))); + flow->l2_key.inner_vlan_tpid = htons(ETH_P_8021Q); + flow->l2_mask.inner_vlan_tpid = htons(0xffff); + flow->l2_key.num_vlans = 1; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { + struct flow_dissector_key_ipv4_addrs *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV4_ADDRS); + struct flow_dissector_key_ipv4_addrs *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV4_ADDRS); + + flow->flags |= BNXT_TC_FLOW_FLAGS_IPV4_ADDRS; + flow->l3_key.ipv4.daddr.s_addr = key->dst; + flow->l3_mask.ipv4.daddr.s_addr = mask->dst; + flow->l3_key.ipv4.saddr.s_addr = key->src; + flow->l3_mask.ipv4.saddr.s_addr = mask->src; + } else if (dissector_uses_key(dissector, + FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { + struct flow_dissector_key_ipv6_addrs *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV6_ADDRS); + struct flow_dissector_key_ipv6_addrs *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV6_ADDRS); + + flow->flags |= BNXT_TC_FLOW_FLAGS_IPV6_ADDRS; + flow->l3_key.ipv6.daddr = key->dst; + flow->l3_mask.ipv6.daddr = mask->dst; + flow->l3_key.ipv6.saddr = key->src; + flow->l3_mask.ipv6.saddr = mask->src; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_dissector_key_ports *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_PORTS); + struct flow_dissector_key_ports *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_PORTS); + + flow->flags |= BNXT_TC_FLOW_FLAGS_PORTS; + flow->l4_key.ports.dport = key->dst; + flow->l4_mask.ports.dport = mask->dst; + flow->l4_key.ports.sport = key->src; + flow->l4_mask.ports.sport = mask->src; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ICMP)) { + struct flow_dissector_key_icmp *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ICMP); + struct flow_dissector_key_icmp *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ICMP); + + flow->flags |= BNXT_TC_FLOW_FLAGS_ICMP; + flow->l4_key.icmp.type = key->type; + flow->l4_key.icmp.code = key->code; + flow->l4_mask.icmp.type = mask->type; + flow->l4_mask.icmp.code = mask->code; + } + + return bnxt_tc_parse_actions(bp, &flow->actions, tc_flow_cmd->exts); +} + +static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp, __le16 flow_handle) +{ + return 0; +} + +static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow, + __le16 ref_flow_handle, __le16 *flow_handle) +{ + return 0; +} + +static int bnxt_tc_put_l2_node(struct bnxt *bp, + struct bnxt_tc_flow_node *flow_node) +{ + struct bnxt_tc_l2_node *l2_node = flow_node->l2_node; + struct bnxt_tc_info *tc_info = &bp->tc_info; + int rc; + + /* remove flow_node from the L2 shared flow list */ + list_del(&flow_node->l2_list_node); + if (--l2_node->refcount == 0) { + rc = rhashtable_remove_fast(&tc_info->l2_table, &l2_node->node, + tc_info->l2_ht_params); + if (rc) + netdev_err(bp->dev, + "Error: %s: rhashtable_remove_fast: %d", + __func__, rc); + kfree_rcu(l2_node, rcu); + } + return 0; +} + +static struct bnxt_tc_l2_node * +bnxt_tc_get_l2_node(struct bnxt *bp, struct rhashtable *l2_table, + struct rhashtable_params ht_params, + struct bnxt_tc_l2_key *l2_key) +{ + struct bnxt_tc_l2_node *l2_node; + int rc; + + l2_node = rhashtable_lookup_fast(l2_table, l2_key, ht_params); + if (!l2_node) { + l2_node = kzalloc(sizeof(*l2_node), GFP_KERNEL); + if (!l2_node) { + rc = -ENOMEM; + return NULL; + } + + l2_node->key = *l2_key; + rc = rhashtable_insert_fast(l2_table, &l2_node->node, + ht_params); + if (rc) { + kfree(l2_node); + netdev_err(bp->dev, + "Error: %s: rhashtable_insert_fast: %d", + __func__, rc); + return NULL; + } + INIT_LIST_HEAD(&l2_node->common_l2_flows); + } + return l2_node; +} + +/* Get the ref_flow_handle for a flow by checking if there are any other + * flows that share the same L2 key as this flow. + */ +static int +bnxt_tc_get_ref_flow_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, + struct bnxt_tc_flow_node *flow_node, + __le16 *ref_flow_handle) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + struct bnxt_tc_flow_node *ref_flow_node; + struct bnxt_tc_l2_node *l2_node; + + l2_node = bnxt_tc_get_l2_node(bp, &tc_info->l2_table, + tc_info->l2_ht_params, + &flow->l2_key); + if (!l2_node) + return -1; + + /* If any other flow is using this l2_node, use it's flow_handle + * as the ref_flow_handle + */ + if (l2_node->refcount > 0) { + ref_flow_node = list_first_entry(&l2_node->common_l2_flows, + struct bnxt_tc_flow_node, + l2_list_node); + *ref_flow_handle = ref_flow_node->flow_handle; + } else { + *ref_flow_handle = cpu_to_le16(0xffff); + } + + /* Insert the l2_node into the flow_node so that subsequent flows + * with a matching l2 key can use the flow_handle of this flow + * as their ref_flow_handle + */ + flow_node->l2_node = l2_node; + list_add(&flow_node->l2_list_node, &l2_node->common_l2_flows); + l2_node->refcount++; + return 0; +} + +/* After the flow parsing is done, this routine is used for checking + * if there are any aspects of the flow that prevent it from being + * offloaded. + */ +static bool bnxt_tc_can_offload(struct bnxt *bp, struct bnxt_tc_flow *flow) +{ + /* If L4 ports are specified then ip_proto must be TCP or UDP */ + if ((flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) && + (flow->l4_key.ip_proto != IPPROTO_TCP && + flow->l4_key.ip_proto != IPPROTO_UDP)) { + netdev_info(bp->dev, "Cannot offload non-TCP/UDP (%d) ports", + flow->l4_key.ip_proto); + return false; + } + + return true; +} + +static int __bnxt_tc_del_flow(struct bnxt *bp, + struct bnxt_tc_flow_node *flow_node) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + int rc; + + /* send HWRM cmd to free the flow-id */ + bnxt_hwrm_cfa_flow_free(bp, flow_node->flow_handle); + + mutex_lock(&tc_info->lock); + + /* release reference to l2 node */ + bnxt_tc_put_l2_node(bp, flow_node); + + mutex_unlock(&tc_info->lock); + + rc = rhashtable_remove_fast(&tc_info->flow_table, &flow_node->node, + tc_info->flow_ht_params); + if (rc) + netdev_err(bp->dev, "Error: %s: rhashtable_remove_fast rc=%d", + __func__, rc); + + kfree_rcu(flow_node, rcu); + return 0; +} + +/* Add a new flow or replace an existing flow. + * Notes on locking: + * There are essentially two critical sections here. + * 1. while adding a new flow + * a) lookup l2-key + * b) issue HWRM cmd and get flow_handle + * c) link l2-key with flow + * 2. while deleting a flow + * a) unlinking l2-key from flow + * A lock is needed to protect these two critical sections. + * + * The hash-tables are already protected by the rhashtable API. + */ +static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid, + struct tc_cls_flower_offload *tc_flow_cmd) +{ + struct bnxt_tc_flow_node *new_node, *old_node; + struct bnxt_tc_info *tc_info = &bp->tc_info; + struct bnxt_tc_flow *flow; + __le16 ref_flow_handle; + int rc; + + /* allocate memory for the new flow and it's node */ + new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); + if (!new_node) { + rc = -ENOMEM; + goto done; + } + new_node->cookie = tc_flow_cmd->cookie; + flow = &new_node->flow; + + rc = bnxt_tc_parse_flow(bp, tc_flow_cmd, flow); + if (rc) + goto free_node; + flow->src_fid = src_fid; + + if (!bnxt_tc_can_offload(bp, flow)) { + rc = -ENOSPC; + goto free_node; + } + + /* If a flow exists with the same cookie, delete it */ + old_node = rhashtable_lookup_fast(&tc_info->flow_table, + &tc_flow_cmd->cookie, + tc_info->flow_ht_params); + if (old_node) + __bnxt_tc_del_flow(bp, old_node); + + /* Check if the L2 part of the flow has been offloaded already. + * If so, bump up it's refcnt and get it's reference handle. + */ + mutex_lock(&tc_info->lock); + rc = bnxt_tc_get_ref_flow_handle(bp, flow, new_node, &ref_flow_handle); + if (rc) + goto unlock; + + /* send HWRM cmd to alloc the flow */ + rc = bnxt_hwrm_cfa_flow_alloc(bp, flow, ref_flow_handle, + &new_node->flow_handle); + if (rc) + goto put_l2; + + /* add new flow to flow-table */ + rc = rhashtable_insert_fast(&tc_info->flow_table, &new_node->node, + tc_info->flow_ht_params); + if (rc) + goto hwrm_flow_free; + + mutex_unlock(&tc_info->lock); + return 0; + +hwrm_flow_free: + bnxt_hwrm_cfa_flow_free(bp, new_node->flow_handle); +put_l2: + bnxt_tc_put_l2_node(bp, new_node); +unlock: + mutex_unlock(&tc_info->lock); +free_node: + kfree(new_node); +done: + netdev_err(bp->dev, "Error: %s: cookie=0x%lx error=%d", + __func__, tc_flow_cmd->cookie, rc); + return rc; +} + +static int bnxt_tc_del_flow(struct bnxt *bp, + struct tc_cls_flower_offload *tc_flow_cmd) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + struct bnxt_tc_flow_node *flow_node; + + flow_node = rhashtable_lookup_fast(&tc_info->flow_table, + &tc_flow_cmd->cookie, + tc_info->flow_ht_params); + if (!flow_node) { + netdev_info(bp->dev, "ERROR: no flow_node for cookie %lx", + tc_flow_cmd->cookie); + return -EINVAL; + } + + return __bnxt_tc_del_flow(bp, flow_node); +} + +static int bnxt_tc_get_flow_stats(struct bnxt *bp, + struct tc_cls_flower_offload *tc_flow_cmd) +{ + return 0; +} + +int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid, + struct tc_cls_flower_offload *cls_flower) +{ + int rc = 0; + + switch (cls_flower->command) { + case TC_CLSFLOWER_REPLACE: + rc = bnxt_tc_add_flow(bp, src_fid, cls_flower); + break; + + case TC_CLSFLOWER_DESTROY: + rc = bnxt_tc_del_flow(bp, cls_flower); + break; + + case TC_CLSFLOWER_STATS: + rc = bnxt_tc_get_flow_stats(bp, cls_flower); + break; + } + return rc; +} + +static const struct rhashtable_params bnxt_tc_flow_ht_params = { + .head_offset = offsetof(struct bnxt_tc_flow_node, node), + .key_offset = offsetof(struct bnxt_tc_flow_node, cookie), + .key_len = sizeof(((struct bnxt_tc_flow_node *)0)->cookie), + .automatic_shrinking = true +}; + +static const struct rhashtable_params bnxt_tc_l2_ht_params = { + .head_offset = offsetof(struct bnxt_tc_l2_node, node), + .key_offset = offsetof(struct bnxt_tc_l2_node, key), + .key_len = BNXT_TC_L2_KEY_LEN, + .automatic_shrinking = true +}; + +/* convert counter width in bits to a mask */ +#define mask(width) ((u64)~0 >> (64 - (width))) + +int bnxt_init_tc(struct bnxt *bp) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + int rc; + + if (bp->hwrm_spec_code < 0x10800) { + netdev_warn(bp->dev, + "Firmware does not support TC flower offload.\n"); + return -ENOTSUPP; + } + mutex_init(&tc_info->lock); + + /* Counter widths are programmed by FW */ + tc_info->bytes_mask = mask(36); + tc_info->packets_mask = mask(28); + + tc_info->flow_ht_params = bnxt_tc_flow_ht_params; + rc = rhashtable_init(&tc_info->flow_table, &tc_info->flow_ht_params); + if (rc) + return rc; + + tc_info->l2_ht_params = bnxt_tc_l2_ht_params; + rc = rhashtable_init(&tc_info->l2_table, &tc_info->l2_ht_params); + if (rc) + goto destroy_flow_table; + + tc_info->enabled = true; + bp->dev->hw_features |= NETIF_F_HW_TC; + bp->dev->features |= NETIF_F_HW_TC; + return 0; + +destroy_flow_table: + rhashtable_destroy(&tc_info->flow_table); + return rc; +} + +void bnxt_shutdown_tc(struct bnxt *bp) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + + if (!tc_info->enabled) + return; + + rhashtable_destroy(&tc_info->flow_table); + rhashtable_destroy(&tc_info->l2_table); +} + +#else +#endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h new file mode 100644 index 000000000000..6c4c1ed279ef --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h @@ -0,0 +1,158 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2017 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#ifndef BNXT_TC_H +#define BNXT_TC_H + +#ifdef CONFIG_BNXT_FLOWER_OFFLOAD + +/* Structs used for storing the filter/actions of the TC cmd. + */ +struct bnxt_tc_l2_key { + u8 dmac[ETH_ALEN]; + u8 smac[ETH_ALEN]; + __be16 inner_vlan_tpid; + __be16 inner_vlan_tci; + __be16 ether_type; + u8 num_vlans; +}; + +struct bnxt_tc_l3_key { + union { + struct { + struct in_addr daddr; + struct in_addr saddr; + } ipv4; + struct { + struct in6_addr daddr; + struct in6_addr saddr; + } ipv6; + }; +}; + +struct bnxt_tc_l4_key { + u8 ip_proto; + union { + struct { + __be16 sport; + __be16 dport; + } ports; + struct { + u8 type; + u8 code; + } icmp; + }; +}; + +struct bnxt_tc_actions { + u32 flags; +#define BNXT_TC_ACTION_FLAG_FWD BIT(0) +#define BNXT_TC_ACTION_FLAG_FWD_VXLAN BIT(1) +#define BNXT_TC_ACTION_FLAG_PUSH_VLAN BIT(3) +#define BNXT_TC_ACTION_FLAG_POP_VLAN BIT(4) +#define BNXT_TC_ACTION_FLAG_DROP BIT(5) + + u16 dst_fid; + struct net_device *dst_dev; + __be16 push_vlan_tpid; + __be16 push_vlan_tci; +}; + +struct bnxt_tc_flow_stats { + u64 packets; + u64 bytes; +}; + +struct bnxt_tc_flow { + u32 flags; +#define BNXT_TC_FLOW_FLAGS_ETH_ADDRS BIT(1) +#define BNXT_TC_FLOW_FLAGS_IPV4_ADDRS BIT(2) +#define BNXT_TC_FLOW_FLAGS_IPV6_ADDRS BIT(3) +#define BNXT_TC_FLOW_FLAGS_PORTS BIT(4) +#define BNXT_TC_FLOW_FLAGS_ICMP BIT(5) + + /* flow applicable to pkts ingressing on this fid */ + u16 src_fid; + struct bnxt_tc_l2_key l2_key; + struct bnxt_tc_l2_key l2_mask; + struct bnxt_tc_l3_key l3_key; + struct bnxt_tc_l3_key l3_mask; + struct bnxt_tc_l4_key l4_key; + struct bnxt_tc_l4_key l4_mask; + + struct bnxt_tc_actions actions; + + /* updated stats accounting for hw-counter wrap-around */ + struct bnxt_tc_flow_stats stats; + /* previous snap-shot of stats */ + struct bnxt_tc_flow_stats prev_stats; + unsigned long lastused; /* jiffies */ +}; + +/* L2 hash table + * This data-struct is used for L2-flow table. + * The L2 part of a flow is stored in a hash table. + * A flow that shares the same L2 key/mask with an + * already existing flow must refer to it's flow handle. + */ +struct bnxt_tc_l2_node { + /* hash key: first 16b of key */ +#define BNXT_TC_L2_KEY_LEN 16 + struct bnxt_tc_l2_key key; + struct rhash_head node; + + /* a linked list of flows that share the same l2 key */ + struct list_head common_l2_flows; + + /* number of flows sharing the l2 key */ + u16 refcount; + + struct rcu_head rcu; +}; + +struct bnxt_tc_flow_node { + /* hash key: provided by TC */ + unsigned long cookie; + struct rhash_head node; + + struct bnxt_tc_flow flow; + + __le16 flow_handle; + + /* L2 node in l2 hashtable that shares flow's l2 key */ + struct bnxt_tc_l2_node *l2_node; + /* for the shared_flows list maintained in l2_node */ + struct list_head l2_list_node; + + struct rcu_head rcu; +}; + +int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid, + struct tc_cls_flower_offload *cls_flower); +int bnxt_init_tc(struct bnxt *bp); +void bnxt_shutdown_tc(struct bnxt *bp); + +#else /* CONFIG_BNXT_FLOWER_OFFLOAD */ + +static inline int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid, + struct tc_cls_flower_offload *cls_flower) +{ + return -EOPNOTSUPP; +} + +static inline int bnxt_init_tc(struct bnxt *bp) +{ + return 0; +} + +static inline void bnxt_shutdown_tc(struct bnxt *bp) +{ +} +#endif /* CONFIG_BNXT_FLOWER_OFFLOAD */ +#endif /* BNXT_TC_H */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index c365d3cf8e51..e75db04c6cdc 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -11,10 +11,12 @@ #include #include #include +#include #include "bnxt_hsi.h" #include "bnxt.h" #include "bnxt_vfr.h" +#include "bnxt_tc.h" #ifdef CONFIG_BNXT_SRIOV @@ -113,6 +115,21 @@ bnxt_vf_rep_get_stats64(struct net_device *dev, stats->tx_bytes = vf_rep->tx_stats.bytes; } +static int bnxt_vf_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + struct bnxt *bp = vf_rep->bp; + int vf_fid = bp->pf.vf[vf_rep->vf_idx].fw_fid; + + switch (type) { + case TC_SETUP_CLSFLOWER: + return bnxt_tc_setup_flower(bp, vf_fid, type_data); + default: + return -EOPNOTSUPP; + } +} + struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code) { u16 vf_idx; @@ -182,6 +199,7 @@ static const struct net_device_ops bnxt_vf_rep_netdev_ops = { .ndo_stop = bnxt_vf_rep_close, .ndo_start_xmit = bnxt_vf_rep_xmit, .ndo_get_stats64 = bnxt_vf_rep_get_stats64, + .ndo_setup_tc = bnxt_vf_rep_setup_tc, .ndo_get_phys_port_name = bnxt_vf_rep_get_phys_port_name }; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h index 3e997c9e2580..d8b5f89427fa 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h @@ -45,6 +45,14 @@ void bnxt_vf_reps_open(struct bnxt *bp); void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb); struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code); +static inline u16 bnxt_vf_rep_get_fid(struct net_device *dev) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + struct bnxt *bp = vf_rep->bp; + + return bp->pf.vf[vf_rep->vf_idx].fw_fid; +} + #else static inline int bnxt_dl_register(struct bnxt *bp) -- cgit v1.2.3 From db1d36a27324d51e62944f702a4d2d50548896ee Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Mon, 28 Aug 2017 13:40:34 -0400 Subject: bnxt_en: add TC flower offload flow_alloc/free FW cmds This patch adds the hwrm_cfa_flow_alloc/free() routines that are needed to issue the FW cmds needed for TC flower offload. Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c | 141 ++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c index a10df27fa32c..5fa0835afafe 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c @@ -259,13 +259,150 @@ static int bnxt_tc_parse_flow(struct bnxt *bp, static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp, __le16 flow_handle) { - return 0; + struct hwrm_cfa_flow_free_input req = { 0 }; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_FREE, -1, -1); + req.flow_handle = flow_handle; + + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + netdev_info(bp->dev, "Error: %s: flow_handle=0x%x rc=%d", + __func__, flow_handle, rc); + return rc; +} + +static int ipv6_mask_len(struct in6_addr *mask) +{ + int mask_len = 0, i; + + for (i = 0; i < 4; i++) + mask_len += inet_mask_len(mask->s6_addr32[i]); + + return mask_len; +} + +static bool is_wildcard(void *mask, int len) +{ + const u8 *p = mask; + int i; + + for (i = 0; i < len; i++) { + if (p[i] != 0) + return false; + } + return true; } static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow, __le16 ref_flow_handle, __le16 *flow_handle) { - return 0; + struct hwrm_cfa_flow_alloc_output *resp = bp->hwrm_cmd_resp_addr; + struct bnxt_tc_actions *actions = &flow->actions; + struct bnxt_tc_l3_key *l3_mask = &flow->l3_mask; + struct bnxt_tc_l3_key *l3_key = &flow->l3_key; + struct hwrm_cfa_flow_alloc_input req = { 0 }; + u16 flow_flags = 0, action_flags = 0; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_ALLOC, -1, -1); + + req.src_fid = cpu_to_le16(flow->src_fid); + req.ref_flow_handle = ref_flow_handle; + req.ethertype = flow->l2_key.ether_type; + req.ip_proto = flow->l4_key.ip_proto; + + if (flow->flags & BNXT_TC_FLOW_FLAGS_ETH_ADDRS) { + memcpy(req.dmac, flow->l2_key.dmac, ETH_ALEN); + memcpy(req.smac, flow->l2_key.smac, ETH_ALEN); + } + + if (flow->l2_key.num_vlans > 0) { + flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_ONE; + /* FW expects the inner_vlan_tci value to be set + * in outer_vlan_tci when num_vlans is 1 (which is + * always the case in TC.) + */ + req.outer_vlan_tci = flow->l2_key.inner_vlan_tci; + } + + /* If all IP and L4 fields are wildcarded then this is an L2 flow */ + if (is_wildcard(&l3_mask, sizeof(l3_mask)) && + is_wildcard(&flow->l4_mask, sizeof(flow->l4_mask))) { + flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_L2; + } else { + flow_flags |= flow->l2_key.ether_type == htons(ETH_P_IP) ? + CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV4 : + CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6; + + if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV4_ADDRS) { + req.ip_dst[0] = l3_key->ipv4.daddr.s_addr; + req.ip_dst_mask_len = + inet_mask_len(l3_mask->ipv4.daddr.s_addr); + req.ip_src[0] = l3_key->ipv4.saddr.s_addr; + req.ip_src_mask_len = + inet_mask_len(l3_mask->ipv4.saddr.s_addr); + } else if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV6_ADDRS) { + memcpy(req.ip_dst, l3_key->ipv6.daddr.s6_addr32, + sizeof(req.ip_dst)); + req.ip_dst_mask_len = + ipv6_mask_len(&l3_mask->ipv6.daddr); + memcpy(req.ip_src, l3_key->ipv6.saddr.s6_addr32, + sizeof(req.ip_src)); + req.ip_src_mask_len = + ipv6_mask_len(&l3_mask->ipv6.saddr); + } + } + + if (flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) { + req.l4_src_port = flow->l4_key.ports.sport; + req.l4_src_port_mask = flow->l4_mask.ports.sport; + req.l4_dst_port = flow->l4_key.ports.dport; + req.l4_dst_port_mask = flow->l4_mask.ports.dport; + } else if (flow->flags & BNXT_TC_FLOW_FLAGS_ICMP) { + /* l4 ports serve as type/code when ip_proto is ICMP */ + req.l4_src_port = htons(flow->l4_key.icmp.type); + req.l4_src_port_mask = htons(flow->l4_mask.icmp.type); + req.l4_dst_port = htons(flow->l4_key.icmp.code); + req.l4_dst_port_mask = htons(flow->l4_mask.icmp.code); + } + req.flags = cpu_to_le16(flow_flags); + + if (actions->flags & BNXT_TC_ACTION_FLAG_DROP) { + action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP; + } else { + if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { + action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD; + req.dst_fid = cpu_to_le16(actions->dst_fid); + } + if (actions->flags & BNXT_TC_ACTION_FLAG_PUSH_VLAN) { + action_flags |= + CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; + req.l2_rewrite_vlan_tpid = actions->push_vlan_tpid; + req.l2_rewrite_vlan_tci = actions->push_vlan_tci; + memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); + memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); + } + if (actions->flags & BNXT_TC_ACTION_FLAG_POP_VLAN) { + action_flags |= + CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; + /* Rewrite config with tpid = 0 implies vlan pop */ + req.l2_rewrite_vlan_tpid = 0; + memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); + memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); + } + } + req.action_flags = cpu_to_le16(action_flags); + + mutex_lock(&bp->hwrm_cmd_lock); + + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) + *flow_handle = resp->flow_handle; + + mutex_unlock(&bp->hwrm_cmd_lock); + + return rc; } static int bnxt_tc_put_l2_node(struct bnxt *bp, -- cgit v1.2.3 From d7bc73053024eecb239a5b8644a05c7745fd87a1 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Mon, 28 Aug 2017 13:40:35 -0400 Subject: bnxt_en: add code to query TC flower offload stats This patch adds code to implement TC_CLSFLOWER_STATS TC-cmd and the required FW code to query the stats from the HW. Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c | 95 ++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c index 5fa0835afafe..ccd699fb2d70 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c @@ -405,6 +405,81 @@ static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow, return rc; } +/* Add val to accum while handling a possible wraparound + * of val. Eventhough val is of type u64, its actual width + * is denoted by mask and will wrap-around beyond that width. + */ +static void accumulate_val(u64 *accum, u64 val, u64 mask) +{ +#define low_bits(x, mask) ((x) & (mask)) +#define high_bits(x, mask) ((x) & ~(mask)) + bool wrapped = val < low_bits(*accum, mask); + + *accum = high_bits(*accum, mask) + val; + if (wrapped) + *accum += (mask + 1); +} + +/* The HW counters' width is much less than 64bits. + * Handle possible wrap-around while updating the stat counters + */ +static void bnxt_flow_stats_fix_wraparound(struct bnxt_tc_info *tc_info, + struct bnxt_tc_flow_stats *stats, + struct bnxt_tc_flow_stats *hw_stats) +{ + accumulate_val(&stats->bytes, hw_stats->bytes, tc_info->bytes_mask); + accumulate_val(&stats->packets, hw_stats->packets, + tc_info->packets_mask); +} + +/* Fix possible wraparound of the stats queried from HW, calculate + * the delta from prev_stats, and also update the prev_stats. + * The HW flow stats are fetched under the hwrm_cmd_lock mutex. + * This routine is best called while under the mutex so that the + * stats processing happens atomically. + */ +static void bnxt_flow_stats_calc(struct bnxt_tc_info *tc_info, + struct bnxt_tc_flow *flow, + struct bnxt_tc_flow_stats *stats) +{ + struct bnxt_tc_flow_stats *acc_stats, *prev_stats; + + acc_stats = &flow->stats; + bnxt_flow_stats_fix_wraparound(tc_info, acc_stats, stats); + + prev_stats = &flow->prev_stats; + stats->bytes = acc_stats->bytes - prev_stats->bytes; + stats->packets = acc_stats->packets - prev_stats->packets; + *prev_stats = *acc_stats; +} + +static int bnxt_hwrm_cfa_flow_stats_get(struct bnxt *bp, + __le16 flow_handle, + struct bnxt_tc_flow *flow, + struct bnxt_tc_flow_stats *stats) +{ + struct hwrm_cfa_flow_stats_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_cfa_flow_stats_input req = { 0 }; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_STATS, -1, -1); + req.num_flows = cpu_to_le16(1); + req.flow_handle_0 = flow_handle; + + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) { + stats->packets = le64_to_cpu(resp->packet_0); + stats->bytes = le64_to_cpu(resp->byte_0); + bnxt_flow_stats_calc(&bp->tc_info, flow, stats); + } else { + netdev_info(bp->dev, "error rc=%d", rc); + } + + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + static int bnxt_tc_put_l2_node(struct bnxt *bp, struct bnxt_tc_flow_node *flow_node) { @@ -647,6 +722,26 @@ static int bnxt_tc_del_flow(struct bnxt *bp, static int bnxt_tc_get_flow_stats(struct bnxt *bp, struct tc_cls_flower_offload *tc_flow_cmd) { + struct bnxt_tc_info *tc_info = &bp->tc_info; + struct bnxt_tc_flow_node *flow_node; + struct bnxt_tc_flow_stats stats; + int rc; + + flow_node = rhashtable_lookup_fast(&tc_info->flow_table, + &tc_flow_cmd->cookie, + tc_info->flow_ht_params); + if (!flow_node) { + netdev_info(bp->dev, "Error: no flow_node for cookie %lx", + tc_flow_cmd->cookie); + return -1; + } + + rc = bnxt_hwrm_cfa_flow_stats_get(bp, flow_node->flow_handle, + &flow_node->flow, &stats); + if (rc) + return rc; + + tcf_exts_stats_update(tc_flow_cmd->exts, stats.bytes, stats.packets, 0); return 0; } -- cgit v1.2.3 From c8488a8ad7c284b1159973baf536ca58fba0ad36 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Mon, 28 Aug 2017 15:16:05 +0200 Subject: hinic: don't build the module by default We probably don't want to enable code supporting particular hardware by default e.g. when someone does 'make defconfig'. Other ethernet modules don't do it. Signed-off-by: Vitaly Kuznetsov Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/huawei/hinic/Kconfig b/drivers/net/ethernet/huawei/hinic/Kconfig index 69f2b1fba48d..08db24954f7e 100644 --- a/drivers/net/ethernet/huawei/hinic/Kconfig +++ b/drivers/net/ethernet/huawei/hinic/Kconfig @@ -5,7 +5,6 @@ config HINIC tristate "Huawei Intelligent PCIE Network Interface Card" depends on (PCI_MSI && X86) - default m ---help--- This driver supports HiNIC PCIE Ethernet cards. To compile this driver as part of the kernel, choose Y here. -- cgit v1.2.3 From f143647a02825038d8d6251422e1d0ebdcb6d9ea Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 29 Aug 2017 11:45:03 +0530 Subject: bnxt_en: add a dummy definition for bnxt_vf_rep_get_fid() When bnxt VF-reps are not compiled in (CONFIG_BNXT_SRIOV is off) bnxt_tc.c needs a dummy definition of the routine bnxt_vf_rep_get_fid(). Reported-by: kbuild test robot Fixes: 2ae7408fedfe ("bnxt_en: bnxt: add TC flower filter offload support") Signed-off-by: Sathya Perla Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h index d8b5f89427fa..7787cd24606a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h @@ -80,5 +80,10 @@ static inline struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code) { return NULL; } + +static inline u16 bnxt_vf_rep_get_fid(struct net_device *dev) +{ + return 0; +} #endif /* CONFIG_BNXT_SRIOV */ #endif /* BNXT_VFR_H */ -- cgit v1.2.3 From 22eac913fe27c5970a9e3815cf3fac8c36a7b35c Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 28 Aug 2017 11:22:20 +0530 Subject: net: stmmac: constify clk_div_table clk_div_table are not supposed to change at runtime. meson8b_dwmac structure is working with const clk_div_table. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index 9685555932ea..4404650b32c5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -89,7 +89,7 @@ static int meson8b_init_clk(struct meson8b_dwmac *dwmac) char clk_name[32]; const char *clk_div_parents[1]; const char *mux_parent_names[MUX_CLK_NUM_PARENTS]; - static struct clk_div_table clk_25m_div_table[] = { + static const struct clk_div_table clk_25m_div_table[] = { { .val = 0, .div = 5 }, { .val = 1, .div = 10 }, { /* sentinel */ }, -- cgit v1.2.3 From f1dd1993dbd1b9eb3ea92fc1e7af9857d4cefe57 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 29 Aug 2017 13:35:15 -0700 Subject: net: systemport: Use correct I/O accessors The SYSTEMPORT driver currently uses __raw_{read,write}l which means native I/O endian. This works correctly for an ARM LE kernel (default) but fails miserably on an ARM BE (BE8) kernel where registers are kept little endian, so replace uses with {read,write}l_relaxed here which is what we want because this is all performance sensitive code. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index b3a21418f511..a7e84292af50 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -32,13 +32,13 @@ #define BCM_SYSPORT_IO_MACRO(name, offset) \ static inline u32 name##_readl(struct bcm_sysport_priv *priv, u32 off) \ { \ - u32 reg = __raw_readl(priv->base + offset + off); \ + u32 reg = readl_relaxed(priv->base + offset + off); \ return reg; \ } \ static inline void name##_writel(struct bcm_sysport_priv *priv, \ u32 val, u32 off) \ { \ - __raw_writel(val, priv->base + offset + off); \ + writel_relaxed(val, priv->base + offset + off); \ } \ BCM_SYSPORT_IO_MACRO(intrl2_0, SYS_PORT_INTRL2_0_OFFSET); @@ -59,14 +59,14 @@ static inline u32 rdma_readl(struct bcm_sysport_priv *priv, u32 off) { if (priv->is_lite && off >= RDMA_STATUS) off += 4; - return __raw_readl(priv->base + SYS_PORT_RDMA_OFFSET + off); + return readl_relaxed(priv->base + SYS_PORT_RDMA_OFFSET + off); } static inline void rdma_writel(struct bcm_sysport_priv *priv, u32 val, u32 off) { if (priv->is_lite && off >= RDMA_STATUS) off += 4; - __raw_writel(val, priv->base + SYS_PORT_RDMA_OFFSET + off); + writel_relaxed(val, priv->base + SYS_PORT_RDMA_OFFSET + off); } static inline u32 tdma_control_bit(struct bcm_sysport_priv *priv, u32 bit) @@ -110,10 +110,10 @@ static inline void dma_desc_set_addr(struct bcm_sysport_priv *priv, dma_addr_t addr) { #ifdef CONFIG_PHYS_ADDR_T_64BIT - __raw_writel(upper_32_bits(addr) & DESC_ADDR_HI_MASK, + writel_relaxed(upper_32_bits(addr) & DESC_ADDR_HI_MASK, d + DESC_ADDR_HI_STATUS_LEN); #endif - __raw_writel(lower_32_bits(addr), d + DESC_ADDR_LO); + writel_relaxed(lower_32_bits(addr), d + DESC_ADDR_LO); } static inline void tdma_port_write_desc_addr(struct bcm_sysport_priv *priv, -- cgit v1.2.3 From fdb71a2d1adcaca0df2f73ced7851fe0c85312f8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 29 Aug 2017 13:35:16 -0700 Subject: net: dsa: bcm_sf2: Use correct I/O accessors The Starfigther 2 driver currently uses __raw_{read,write}l which means native I/O endian. This works correctly for an ARM LE kernel (default) but fails miserably on an ARM BE (BE8) kernel where registers are kept little endian, so replace uses with {read,write}l_relaxed here which is what we want because this is all performance sensitive code. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index 7d3030e04f11..d9c96b281fc0 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h @@ -130,12 +130,12 @@ static inline u32 bcm_sf2_mangle_addr(struct bcm_sf2_priv *priv, u32 off) #define SF2_IO_MACRO(name) \ static inline u32 name##_readl(struct bcm_sf2_priv *priv, u32 off) \ { \ - return __raw_readl(priv->name + off); \ + return readl_relaxed(priv->name + off); \ } \ static inline void name##_writel(struct bcm_sf2_priv *priv, \ u32 val, u32 off) \ { \ - __raw_writel(val, priv->name + off); \ + writel_relaxed(val, priv->name + off); \ } \ /* Accesses to 64-bits register requires us to latch the hi/lo pairs @@ -179,23 +179,23 @@ static inline void intrl2_##which##_mask_set(struct bcm_sf2_priv *priv, \ static inline u32 core_readl(struct bcm_sf2_priv *priv, u32 off) { u32 tmp = bcm_sf2_mangle_addr(priv, off); - return __raw_readl(priv->core + tmp); + return readl_relaxed(priv->core + tmp); } static inline void core_writel(struct bcm_sf2_priv *priv, u32 val, u32 off) { u32 tmp = bcm_sf2_mangle_addr(priv, off); - __raw_writel(val, priv->core + tmp); + writel_relaxed(val, priv->core + tmp); } static inline u32 reg_readl(struct bcm_sf2_priv *priv, u16 off) { - return __raw_readl(priv->reg + priv->reg_offsets[off]); + return readl_relaxed(priv->reg + priv->reg_offsets[off]); } static inline void reg_writel(struct bcm_sf2_priv *priv, u32 val, u16 off) { - __raw_writel(val, priv->reg + priv->reg_offsets[off]); + writel_relaxed(val, priv->reg + priv->reg_offsets[off]); } SF2_IO64_MACRO(core); -- cgit v1.2.3 From 389a06bc534ec19778598242e40c59bc73d2a87d Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 29 Aug 2017 13:35:17 -0700 Subject: net: systemport: Set correct RSB endian bits based on host RSB_SWAP0 needs to match the host CPU endian, and it needs to be set for LE and clear for BE. RSB_SWAP1 must always be cleared for SYSTEMPORT Lite. With these settings, we have the Receive Status Block always match the host endian and we do not need to perform any conversion. Since there is not necessarily a CONFIG_CPU_LITTLE_ENDIAN option defined, we test for !CONFIG_CPU_BIG_ENDIAN which is guaranteed to be set. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index a7e84292af50..931751e4f369 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -1762,10 +1762,14 @@ static void rbuf_init(struct bcm_sysport_priv *priv) reg = rbuf_readl(priv, RBUF_CONTROL); reg |= RBUF_4B_ALGN | RBUF_RSB_EN; /* Set a correct RSB format on SYSTEMPORT Lite */ - if (priv->is_lite) { + if (priv->is_lite) reg &= ~RBUF_RSB_SWAP1; + + /* Set a correct RSB format based on host endian */ + if (!IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) reg |= RBUF_RSB_SWAP0; - } + else + reg &= ~RBUF_RSB_SWAP0; rbuf_writel(priv, reg, RBUF_CONTROL); } -- cgit v1.2.3 From cb51a0916ae768ecbb246535fa68bb1ae05e8c97 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 29 Aug 2017 13:35:18 -0700 Subject: net: phy: mdio-bcm-unimac: Use correct I/O accessors The driver currently uses __raw_{read,write}l which works for all platforms supported: Broadcom MIPS LE/BE (native endian), ARM LE (native endian) but not ARM BE (registers are still LE). Switch to using the proper accessors for all platforms and explain why Broadcom MIPS BE is special here, in doing so, we introduce a couple of helper functions to abstract these differences. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio-bcm-unimac.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 73c5267a11fd..08e0647b85e2 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -47,18 +47,38 @@ struct unimac_mdio_priv { void *wait_func_data; }; +static inline u32 unimac_mdio_readl(struct unimac_mdio_priv *priv, u32 offset) +{ + /* MIPS chips strapped for BE will automagically configure the + * peripheral registers for CPU-native byte order. + */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + return __raw_readl(priv->base + offset); + else + return readl_relaxed(priv->base + offset); +} + +static inline void unimac_mdio_writel(struct unimac_mdio_priv *priv, u32 val, + u32 offset) +{ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + __raw_writel(val, priv->base + offset); + else + writel_relaxed(val, priv->base + offset); +} + static inline void unimac_mdio_start(struct unimac_mdio_priv *priv) { u32 reg; - reg = __raw_readl(priv->base + MDIO_CMD); + reg = unimac_mdio_readl(priv, MDIO_CMD); reg |= MDIO_START_BUSY; - __raw_writel(reg, priv->base + MDIO_CMD); + unimac_mdio_writel(priv, reg, MDIO_CMD); } static inline unsigned int unimac_mdio_busy(struct unimac_mdio_priv *priv) { - return __raw_readl(priv->base + MDIO_CMD) & MDIO_START_BUSY; + return unimac_mdio_readl(priv, MDIO_CMD) & MDIO_START_BUSY; } static int unimac_mdio_poll(void *wait_func_data) @@ -87,7 +107,7 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) /* Prepare the read operation */ cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT); - __raw_writel(cmd, priv->base + MDIO_CMD); + unimac_mdio_writel(priv, cmd, MDIO_CMD); /* Start MDIO transaction */ unimac_mdio_start(priv); @@ -96,7 +116,7 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) if (ret) return ret; - cmd = __raw_readl(priv->base + MDIO_CMD); + cmd = unimac_mdio_readl(priv, MDIO_CMD); /* Some broken devices are known not to release the line during * turn-around, e.g: Broadcom BCM53125 external switches, so check for @@ -118,7 +138,7 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id, /* Prepare the write operation */ cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT) | (0xffff & val); - __raw_writel(cmd, priv->base + MDIO_CMD); + unimac_mdio_writel(priv, cmd, MDIO_CMD); unimac_mdio_start(priv); -- cgit v1.2.3 From c73c8b1e47ca275388f774fd36560ab7e994e99e Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Mon, 28 Aug 2017 16:38:20 +0300 Subject: net/mlx4_core: Dynamically allocate structs at mlx4_slave_cap In order to avoid temporary large structs on the stack, allocate them dynamically. Signed-off-by: Eran Ben Elisha Signed-off-by: Tal Alon Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx4/qp.c | 26 ++-- drivers/net/ethernet/mellanox/mlx4/fw.c | 12 +- drivers/net/ethernet/mellanox/mlx4/fw.h | 6 +- drivers/net/ethernet/mellanox/mlx4/main.c | 235 ++++++++++++++++-------------- drivers/net/ethernet/mellanox/mlx4/qp.c | 24 +-- include/linux/mlx4/device.h | 14 +- 6 files changed, 159 insertions(+), 158 deletions(-) (limited to 'drivers/net') diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 75c0e6c5dd56..83e5c72f19b8 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -145,8 +145,8 @@ static int is_sqp(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp) /* VF or PF -- proxy SQP */ if (mlx4_is_mfunc(dev->dev)) { for (i = 0; i < dev->dev->caps.num_ports; i++) { - if (qp->mqp.qpn == dev->dev->caps.qp0_proxy[i] || - qp->mqp.qpn == dev->dev->caps.qp1_proxy[i]) { + if (qp->mqp.qpn == dev->dev->caps.spec_qps[i].qp0_proxy || + qp->mqp.qpn == dev->dev->caps.spec_qps[i].qp1_proxy) { proxy_sqp = 1; break; } @@ -173,7 +173,7 @@ static int is_qp0(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp) /* VF or PF -- proxy QP0 */ if (mlx4_is_mfunc(dev->dev)) { for (i = 0; i < dev->dev->caps.num_ports; i++) { - if (qp->mqp.qpn == dev->dev->caps.qp0_proxy[i]) { + if (qp->mqp.qpn == dev->dev->caps.spec_qps[i].qp0_proxy) { proxy_qp0 = 1; break; } @@ -614,8 +614,8 @@ static int qp0_enabled_vf(struct mlx4_dev *dev, int qpn) { int i; for (i = 0; i < dev->caps.num_ports; i++) { - if (qpn == dev->caps.qp0_proxy[i]) - return !!dev->caps.qp0_qkey[i]; + if (qpn == dev->caps.spec_qps[i].qp0_proxy) + return !!dev->caps.spec_qps[i].qp0_qkey; } return 0; } @@ -1114,9 +1114,9 @@ static u32 get_sqp_num(struct mlx4_ib_dev *dev, struct ib_qp_init_attr *attr) } /* PF or VF -- creating proxies */ if (attr->qp_type == IB_QPT_SMI) - return dev->dev->caps.qp0_proxy[attr->port_num - 1]; + return dev->dev->caps.spec_qps[attr->port_num - 1].qp0_proxy; else - return dev->dev->caps.qp1_proxy[attr->port_num - 1]; + return dev->dev->caps.spec_qps[attr->port_num - 1].qp1_proxy; } static struct ib_qp *_mlx4_ib_create_qp(struct ib_pd *pd, @@ -2277,9 +2277,9 @@ static int vf_get_qp0_qkey(struct mlx4_dev *dev, int qpn, u32 *qkey) { int i; for (i = 0; i < dev->caps.num_ports; i++) { - if (qpn == dev->caps.qp0_proxy[i] || - qpn == dev->caps.qp0_tunnel[i]) { - *qkey = dev->caps.qp0_qkey[i]; + if (qpn == dev->caps.spec_qps[i].qp0_proxy || + qpn == dev->caps.spec_qps[i].qp0_tunnel) { + *qkey = dev->caps.spec_qps[i].qp0_qkey; return 0; } } @@ -2340,7 +2340,7 @@ static int build_sriov_qp0_header(struct mlx4_ib_sqp *sqp, sqp->ud_header.bth.destination_qpn = cpu_to_be32(wr->remote_qpn); else sqp->ud_header.bth.destination_qpn = - cpu_to_be32(mdev->dev->caps.qp0_tunnel[sqp->qp.port - 1]); + cpu_to_be32(mdev->dev->caps.spec_qps[sqp->qp.port - 1].qp0_tunnel); sqp->ud_header.bth.psn = cpu_to_be32((sqp->send_psn++) & ((1 << 24) - 1)); if (mlx4_is_master(mdev->dev)) { @@ -2800,9 +2800,9 @@ static void set_tunnel_datagram_seg(struct mlx4_ib_dev *dev, memcpy(dseg->av, &sqp_av, sizeof (struct mlx4_av)); if (qpt == MLX4_IB_QPT_PROXY_GSI) - dseg->dqpn = cpu_to_be32(dev->dev->caps.qp1_tunnel[port - 1]); + dseg->dqpn = cpu_to_be32(dev->dev->caps.spec_qps[port - 1].qp1_tunnel); else - dseg->dqpn = cpu_to_be32(dev->dev->caps.qp0_tunnel[port - 1]); + dseg->dqpn = cpu_to_be32(dev->dev->caps.spec_qps[port - 1].qp0_tunnel); /* Use QKEY from the QP context, which is set by master */ dseg->qkey = cpu_to_be32(IB_QP_SET_QKEY); } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 042707623922..f76cac01d564 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -679,22 +679,22 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u8 gen_or_port, if (func_cap->flags1 & QUERY_FUNC_CAP_VF_ENABLE_QP0) { MLX4_GET(qkey, outbox, QUERY_FUNC_CAP_PRIV_VF_QKEY_OFFSET); - func_cap->qp0_qkey = qkey; + func_cap->spec_qps.qp0_qkey = qkey; } else { - func_cap->qp0_qkey = 0; + func_cap->spec_qps.qp0_qkey = 0; } MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP0_TUNNEL); - func_cap->qp0_tunnel_qpn = size & 0xFFFFFF; + func_cap->spec_qps.qp0_tunnel = size & 0xFFFFFF; MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP0_PROXY); - func_cap->qp0_proxy_qpn = size & 0xFFFFFF; + func_cap->spec_qps.qp0_proxy = size & 0xFFFFFF; MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP1_TUNNEL); - func_cap->qp1_tunnel_qpn = size & 0xFFFFFF; + func_cap->spec_qps.qp1_tunnel = size & 0xFFFFFF; MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP1_PROXY); - func_cap->qp1_proxy_qpn = size & 0xFFFFFF; + func_cap->spec_qps.qp1_proxy = size & 0xFFFFFF; if (func_cap->flags1 & QUERY_FUNC_CAP_FLAGS1_NIC_INFO) MLX4_GET(func_cap->phys_port_id, outbox, diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index b52ba01aa486..cd6399c76bfd 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -144,11 +144,7 @@ struct mlx4_func_cap { int max_eq; int reserved_eq; int mcg_quota; - u32 qp0_qkey; - u32 qp0_tunnel_qpn; - u32 qp0_proxy_qpn; - u32 qp1_tunnel_qpn; - u32 qp1_proxy_qpn; + struct mlx4_spec_qps spec_qps; u32 reserved_lkey; u8 physical_port; u8 flags0; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 8404e165eca4..e6413a8a5f07 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -819,38 +819,93 @@ static void slave_adjust_steering_mode(struct mlx4_dev *dev, mlx4_steering_mode_str(dev->caps.steering_mode)); } +static void mlx4_slave_destroy_special_qp_cap(struct mlx4_dev *dev) +{ + kfree(dev->caps.spec_qps); + dev->caps.spec_qps = NULL; +} + +static int mlx4_slave_special_qp_cap(struct mlx4_dev *dev) +{ + struct mlx4_func_cap *func_cap = NULL; + struct mlx4_caps *caps = &dev->caps; + int i, err = 0; + + func_cap = kzalloc(sizeof(*func_cap), GFP_KERNEL); + caps->spec_qps = kcalloc(caps->num_ports, sizeof(*caps->spec_qps), GFP_KERNEL); + + if (!func_cap || !caps->spec_qps) { + mlx4_err(dev, "Failed to allocate memory for special qps cap\n"); + err = -ENOMEM; + goto err_mem; + } + + for (i = 1; i <= caps->num_ports; ++i) { + err = mlx4_QUERY_FUNC_CAP(dev, i, func_cap); + if (err) { + mlx4_err(dev, "QUERY_FUNC_CAP port command failed for port %d, aborting (%d)\n", + i, err); + goto err_mem; + } + caps->spec_qps[i - 1] = func_cap->spec_qps; + caps->port_mask[i] = caps->port_type[i]; + caps->phys_port_id[i] = func_cap->phys_port_id; + err = mlx4_get_slave_pkey_gid_tbl_len(dev, i, + &caps->gid_table_len[i], + &caps->pkey_table_len[i]); + if (err) { + mlx4_err(dev, "QUERY_PORT command failed for port %d, aborting (%d)\n", + i, err); + goto err_mem; + } + } + +err_mem: + if (err) + mlx4_slave_destroy_special_qp_cap(dev); + kfree(func_cap); + return err; +} + static int mlx4_slave_cap(struct mlx4_dev *dev) { int err; u32 page_size; - struct mlx4_dev_cap dev_cap; - struct mlx4_func_cap func_cap; - struct mlx4_init_hca_param hca_param; - u8 i; + struct mlx4_dev_cap *dev_cap = NULL; + struct mlx4_func_cap *func_cap = NULL; + struct mlx4_init_hca_param *hca_param = NULL; + + hca_param = kzalloc(sizeof(*hca_param), GFP_KERNEL); + func_cap = kzalloc(sizeof(*func_cap), GFP_KERNEL); + dev_cap = kzalloc(sizeof(*dev_cap), GFP_KERNEL); + if (!hca_param || !func_cap || !dev_cap) { + mlx4_err(dev, "Failed to allocate memory for slave_cap\n"); + err = -ENOMEM; + goto free_mem; + } - memset(&hca_param, 0, sizeof(hca_param)); - err = mlx4_QUERY_HCA(dev, &hca_param); + err = mlx4_QUERY_HCA(dev, hca_param); if (err) { mlx4_err(dev, "QUERY_HCA command failed, aborting\n"); - return err; + goto free_mem; } /* fail if the hca has an unknown global capability * at this time global_caps should be always zeroed */ - if (hca_param.global_caps) { + if (hca_param->global_caps) { mlx4_err(dev, "Unknown hca global capabilities\n"); - return -EINVAL; + err = -EINVAL; + goto free_mem; } - dev->caps.hca_core_clock = hca_param.hca_core_clock; + dev->caps.hca_core_clock = hca_param->hca_core_clock; - memset(&dev_cap, 0, sizeof(dev_cap)); - dev->caps.max_qp_dest_rdma = 1 << hca_param.log_rd_per_qp; - err = mlx4_dev_cap(dev, &dev_cap); + dev->caps.max_qp_dest_rdma = 1 << hca_param->log_rd_per_qp; + err = mlx4_dev_cap(dev, dev_cap); if (err) { mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting\n"); - return err; + goto free_mem; } err = mlx4_QUERY_FW(dev); @@ -862,21 +917,23 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) if (page_size > PAGE_SIZE) { mlx4_err(dev, "HCA minimum page size of %d bigger than kernel PAGE_SIZE of %ld, aborting\n", page_size, PAGE_SIZE); - return -ENODEV; + err = -ENODEV; + goto free_mem; } /* Set uar_page_shift for VF */ - dev->uar_page_shift = hca_param.uar_page_sz + 12; + dev->uar_page_shift = hca_param->uar_page_sz + 12; /* Make sure the master uar page size is valid */ if (dev->uar_page_shift > PAGE_SHIFT) { mlx4_err(dev, "Invalid configuration: uar page size is larger than system page size\n"); - return -ENODEV; + err = -ENODEV; + goto free_mem; } /* Set reserved_uars based on the uar_page_shift */ - mlx4_set_num_reserved_uars(dev, &dev_cap); + mlx4_set_num_reserved_uars(dev, dev_cap); /* Although uar page size in FW differs from system page size, * upper software layers (mlx4_ib, mlx4_en and part of mlx4_core) @@ -884,34 +941,35 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) */ dev->caps.uar_page_size = PAGE_SIZE; - memset(&func_cap, 0, sizeof(func_cap)); - err = mlx4_QUERY_FUNC_CAP(dev, 0, &func_cap); + err = mlx4_QUERY_FUNC_CAP(dev, 0, func_cap); if (err) { mlx4_err(dev, "QUERY_FUNC_CAP general command failed, aborting (%d)\n", err); - return err; + goto free_mem; } - if ((func_cap.pf_context_behaviour | PF_CONTEXT_BEHAVIOUR_MASK) != + if ((func_cap->pf_context_behaviour | PF_CONTEXT_BEHAVIOUR_MASK) != PF_CONTEXT_BEHAVIOUR_MASK) { mlx4_err(dev, "Unknown pf context behaviour %x known flags %x\n", - func_cap.pf_context_behaviour, PF_CONTEXT_BEHAVIOUR_MASK); - return -EINVAL; - } - - dev->caps.num_ports = func_cap.num_ports; - dev->quotas.qp = func_cap.qp_quota; - dev->quotas.srq = func_cap.srq_quota; - dev->quotas.cq = func_cap.cq_quota; - dev->quotas.mpt = func_cap.mpt_quota; - dev->quotas.mtt = func_cap.mtt_quota; - dev->caps.num_qps = 1 << hca_param.log_num_qps; - dev->caps.num_srqs = 1 << hca_param.log_num_srqs; - dev->caps.num_cqs = 1 << hca_param.log_num_cqs; - dev->caps.num_mpts = 1 << hca_param.log_mpt_sz; - dev->caps.num_eqs = func_cap.max_eq; - dev->caps.reserved_eqs = func_cap.reserved_eq; - dev->caps.reserved_lkey = func_cap.reserved_lkey; + func_cap->pf_context_behaviour, + PF_CONTEXT_BEHAVIOUR_MASK); + err = -EINVAL; + goto free_mem; + } + + dev->caps.num_ports = func_cap->num_ports; + dev->quotas.qp = func_cap->qp_quota; + dev->quotas.srq = func_cap->srq_quota; + dev->quotas.cq = func_cap->cq_quota; + dev->quotas.mpt = func_cap->mpt_quota; + dev->quotas.mtt = func_cap->mtt_quota; + dev->caps.num_qps = 1 << hca_param->log_num_qps; + dev->caps.num_srqs = 1 << hca_param->log_num_srqs; + dev->caps.num_cqs = 1 << hca_param->log_num_cqs; + dev->caps.num_mpts = 1 << hca_param->log_mpt_sz; + dev->caps.num_eqs = func_cap->max_eq; + dev->caps.reserved_eqs = func_cap->reserved_eq; + dev->caps.reserved_lkey = func_cap->reserved_lkey; dev->caps.num_pds = MLX4_NUM_PDS; dev->caps.num_mgms = 0; dev->caps.num_amgms = 0; @@ -924,38 +982,10 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) mlx4_replace_zero_macs(dev); - dev->caps.qp0_qkey = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - dev->caps.qp1_tunnel = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - dev->caps.qp1_proxy = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - - if (!dev->caps.qp0_tunnel || !dev->caps.qp0_proxy || - !dev->caps.qp1_tunnel || !dev->caps.qp1_proxy || - !dev->caps.qp0_qkey) { - err = -ENOMEM; - goto err_mem; - } - - for (i = 1; i <= dev->caps.num_ports; ++i) { - err = mlx4_QUERY_FUNC_CAP(dev, i, &func_cap); - if (err) { - mlx4_err(dev, "QUERY_FUNC_CAP port command failed for port %d, aborting (%d)\n", - i, err); - goto err_mem; - } - dev->caps.qp0_qkey[i - 1] = func_cap.qp0_qkey; - dev->caps.qp0_tunnel[i - 1] = func_cap.qp0_tunnel_qpn; - dev->caps.qp0_proxy[i - 1] = func_cap.qp0_proxy_qpn; - dev->caps.qp1_tunnel[i - 1] = func_cap.qp1_tunnel_qpn; - dev->caps.qp1_proxy[i - 1] = func_cap.qp1_proxy_qpn; - dev->caps.port_mask[i] = dev->caps.port_type[i]; - dev->caps.phys_port_id[i] = func_cap.phys_port_id; - err = mlx4_get_slave_pkey_gid_tbl_len(dev, i, - &dev->caps.gid_table_len[i], - &dev->caps.pkey_table_len[i]); - if (err) - goto err_mem; + err = mlx4_slave_special_qp_cap(dev); + if (err) { + mlx4_err(dev, "Set special QP caps failed. aborting\n"); + goto free_mem; } if (dev->caps.uar_page_size * (dev->caps.num_uars - @@ -970,7 +1000,7 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) goto err_mem; } - if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_64B_EQE_ENABLED) { + if (hca_param->dev_cap_enabled & MLX4_DEV_CAP_64B_EQE_ENABLED) { dev->caps.eqe_size = 64; dev->caps.eqe_factor = 1; } else { @@ -978,20 +1008,20 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) dev->caps.eqe_factor = 0; } - if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_64B_CQE_ENABLED) { + if (hca_param->dev_cap_enabled & MLX4_DEV_CAP_64B_CQE_ENABLED) { dev->caps.cqe_size = 64; dev->caps.userspace_caps |= MLX4_USER_DEV_CAP_LARGE_CQE; } else { dev->caps.cqe_size = 32; } - if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_EQE_STRIDE_ENABLED) { - dev->caps.eqe_size = hca_param.eqe_size; + if (hca_param->dev_cap_enabled & MLX4_DEV_CAP_EQE_STRIDE_ENABLED) { + dev->caps.eqe_size = hca_param->eqe_size; dev->caps.eqe_factor = 0; } - if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_CQE_STRIDE_ENABLED) { - dev->caps.cqe_size = hca_param.cqe_size; + if (hca_param->dev_cap_enabled & MLX4_DEV_CAP_CQE_STRIDE_ENABLED) { + dev->caps.cqe_size = hca_param->cqe_size; /* User still need to know when CQE > 32B */ dev->caps.userspace_caps |= MLX4_USER_DEV_CAP_LARGE_CQE; } @@ -999,31 +1029,24 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS; mlx4_warn(dev, "Timestamping is not supported in slave mode\n"); - slave_adjust_steering_mode(dev, &dev_cap, &hca_param); + slave_adjust_steering_mode(dev, dev_cap, hca_param); mlx4_dbg(dev, "RSS support for IP fragments is %s\n", - hca_param.rss_ip_frags ? "on" : "off"); + hca_param->rss_ip_frags ? "on" : "off"); - if (func_cap.extra_flags & MLX4_QUERY_FUNC_FLAGS_BF_RES_QP && + if (func_cap->extra_flags & MLX4_QUERY_FUNC_FLAGS_BF_RES_QP && dev->caps.bf_reg_size) dev->caps.alloc_res_qp_mask |= MLX4_RESERVE_ETH_BF_QP; - if (func_cap.extra_flags & MLX4_QUERY_FUNC_FLAGS_A0_RES_QP) + if (func_cap->extra_flags & MLX4_QUERY_FUNC_FLAGS_A0_RES_QP) dev->caps.alloc_res_qp_mask |= MLX4_RESERVE_A0_QP; - return 0; - err_mem: - kfree(dev->caps.qp0_qkey); - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); - dev->caps.qp0_qkey = NULL; - dev->caps.qp0_tunnel = NULL; - dev->caps.qp0_proxy = NULL; - dev->caps.qp1_tunnel = NULL; - dev->caps.qp1_proxy = NULL; - + if (err) + mlx4_slave_destroy_special_qp_cap(dev); +free_mem: + kfree(hca_param); + kfree(func_cap); + kfree(dev_cap); return err; } @@ -2407,13 +2430,8 @@ unmap_bf: unmap_internal_clock(dev); unmap_bf_area(dev); - if (mlx4_is_slave(dev)) { - kfree(dev->caps.qp0_qkey); - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); - } + if (mlx4_is_slave(dev)) + mlx4_slave_destroy_special_qp_cap(dev); err_close: if (mlx4_is_slave(dev)) @@ -3596,13 +3614,8 @@ err_master_mfunc: mlx4_multi_func_cleanup(dev); } - if (mlx4_is_slave(dev)) { - kfree(dev->caps.qp0_qkey); - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); - } + if (mlx4_is_slave(dev)) + mlx4_slave_destroy_special_qp_cap(dev); err_close: mlx4_close_hca(dev); @@ -3942,11 +3955,7 @@ static void mlx4_unload_one(struct pci_dev *pdev) if (!mlx4_is_slave(dev)) mlx4_free_ownership(dev); - kfree(dev->caps.qp0_qkey); - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); + mlx4_slave_destroy_special_qp_cap(dev); kfree(dev->dev_vfs); mlx4_clean_dev(dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 2b067763a6bc..28c441c0d31f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -844,24 +844,20 @@ int mlx4_init_qp_table(struct mlx4_dev *dev) /* In mfunc, calculate proxy and tunnel qp offsets for the PF here, * since the PF does not call mlx4_slave_caps */ - dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - dev->caps.qp1_tunnel = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - dev->caps.qp1_proxy = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); + dev->caps.spec_qps = kcalloc(dev->caps.num_ports, sizeof(dev->caps.spec_qps), GFP_KERNEL); - if (!dev->caps.qp0_tunnel || !dev->caps.qp0_proxy || - !dev->caps.qp1_tunnel || !dev->caps.qp1_proxy) { + if (!dev->caps.spec_qps) { err = -ENOMEM; goto err_mem; } for (k = 0; k < dev->caps.num_ports; k++) { - dev->caps.qp0_proxy[k] = dev->phys_caps.base_proxy_sqpn + + dev->caps.spec_qps[k].qp0_proxy = dev->phys_caps.base_proxy_sqpn + 8 * mlx4_master_func_num(dev) + k; - dev->caps.qp0_tunnel[k] = dev->caps.qp0_proxy[k] + 8 * MLX4_MFUNC_MAX; - dev->caps.qp1_proxy[k] = dev->phys_caps.base_proxy_sqpn + + dev->caps.spec_qps[k].qp0_tunnel = dev->caps.spec_qps[k].qp0_proxy + 8 * MLX4_MFUNC_MAX; + dev->caps.spec_qps[k].qp1_proxy = dev->phys_caps.base_proxy_sqpn + 8 * mlx4_master_func_num(dev) + MLX4_MAX_PORTS + k; - dev->caps.qp1_tunnel[k] = dev->caps.qp1_proxy[k] + 8 * MLX4_MFUNC_MAX; + dev->caps.spec_qps[k].qp1_tunnel = dev->caps.spec_qps[k].qp1_proxy + 8 * MLX4_MFUNC_MAX; } } @@ -873,12 +869,8 @@ int mlx4_init_qp_table(struct mlx4_dev *dev) return err; err_mem: - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); - dev->caps.qp0_tunnel = dev->caps.qp0_proxy = - dev->caps.qp1_tunnel = dev->caps.qp1_proxy = NULL; + kfree(dev->caps.spec_qps); + dev->caps.spec_qps = NULL; mlx4_cleanup_qp_zones(dev); return err; } diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index b54517c05e9a..9844606f9491 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -518,6 +518,14 @@ struct mlx4_phys_caps { u32 base_tunnel_sqpn; }; +struct mlx4_spec_qps { + u32 qp0_qkey; + u32 qp0_proxy; + u32 qp0_tunnel; + u32 qp1_proxy; + u32 qp1_tunnel; +}; + struct mlx4_caps { u64 fw_ver; u32 function; @@ -547,11 +555,7 @@ struct mlx4_caps { int max_qp_init_rdma; int max_qp_dest_rdma; int max_tc_eth; - u32 *qp0_qkey; - u32 *qp0_proxy; - u32 *qp1_proxy; - u32 *qp0_tunnel; - u32 *qp1_tunnel; + struct mlx4_spec_qps *spec_qps; int num_srqs; int max_srq_wqes; int max_srq_sge; -- cgit v1.2.3 From 187782eb58a89ea030731114c6ae37842a4472fe Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 28 Aug 2017 16:38:21 +0300 Subject: net/mlx4_core: Make explicit conversion to 64bit value The "lg" variable is declared as int so in all places where this variable is used as a shift operand, the output will be int too. This produces the following smatch warning: drivers/net/ethernet/mellanox/mlx4/fw.c:1532 mlx4_map_cmd() warn: should '1 << lg' be a 64 bit type? Simple declaration of "1" to be "1ULL" will fix the issue. Fixes: 225c7b1feef1 ("IB/mlx4: Add a driver Mellanox ConnectX InfiniBand adapters") Signed-off-by: Leon Romanovsky Signed-off-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index f76cac01d564..10cfb058a9de 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -1534,7 +1534,7 @@ int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt) for (i = 0; i < mlx4_icm_size(&iter) >> lg; ++i) { if (virt != -1) { pages[nent * 2] = cpu_to_be64(virt); - virt += 1 << lg; + virt += 1ULL << lg; } pages[nent * 2 + 1] = -- cgit v1.2.3 From a434f1fd2c96f44d7483d5bd789eec2d3cbb2d20 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 28 Aug 2017 16:38:22 +0300 Subject: net/mlx4_core: Fix misplaced brackets of sizeof When changing the sizeof style usage in the patch cited below, one brackets misplacement was introduced. Here we fix it. Fixes: 31975e27a4b5 ("mlx4: sizeof style usage") Signed-off-by: Tariq Toukan Cc: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/eq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index b98698bf75dd..6f57c052053e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -726,7 +726,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) } memcpy(&priv->mfunc.master.comm_arm_bit_vector, eqe->event.comm_channel_arm.bit_vec, - sizeof(eqe)->event.comm_channel_arm.bit_vec); + sizeof(eqe->event.comm_channel_arm.bit_vec)); queue_work(priv->mfunc.master.comm_wq, &priv->mfunc.master.comm_work); break; -- cgit v1.2.3 From be59960395f86991c6599c41d8c421fe4bf7a210 Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Mon, 28 Aug 2017 16:38:23 +0300 Subject: net/mlx4: Add user mac FW update support Adding support for updating the FW on new port mac, when port mac change is requested by the user. This info is required by the FW as OEM management tools require this info directly from the NIC FW. Check device capability bit to verify the FW supports user mac. If the FW does support it, use set_port command to notify the FW on the new mac. The feature is relevant only to PF port mac. Signed-off-by: Moshe Shemesh Signed-off-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 23 +++++++++++++++++++++-- drivers/net/ethernet/mellanox/mlx4/fw.c | 5 +++++ drivers/net/ethernet/mellanox/mlx4/main.c | 3 +++ drivers/net/ethernet/mellanox/mlx4/mlx4.h | 2 ++ drivers/net/ethernet/mellanox/mlx4/port.c | 25 +++++++++++++++++++++++++ include/linux/mlx4/device.h | 2 ++ 6 files changed, 58 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 6e67ca7aa7f5..3753943aec0f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -732,6 +732,21 @@ static int mlx4_en_replace_mac(struct mlx4_en_priv *priv, int qpn, return __mlx4_replace_mac(dev, priv->port, qpn, new_mac_u64); } +static void mlx4_en_update_user_mac(struct mlx4_en_priv *priv, + unsigned char new_mac[ETH_ALEN + 2]) +{ + struct mlx4_en_dev *mdev = priv->mdev; + int err; + + if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_USER_MAC_EN)) + return; + + err = mlx4_SET_PORT_user_mac(mdev->dev, priv->port, new_mac); + if (err) + en_err(priv, "Failed to pass user MAC(%pM) to Firmware for port %d, with error %d\n", + new_mac, priv->port, err); +} + static int mlx4_en_do_set_mac(struct mlx4_en_priv *priv, unsigned char new_mac[ETH_ALEN + 2]) { @@ -766,8 +781,12 @@ static int mlx4_en_set_mac(struct net_device *dev, void *addr) mutex_lock(&mdev->state_lock); memcpy(new_mac, saddr->sa_data, ETH_ALEN); err = mlx4_en_do_set_mac(priv, new_mac); - if (!err) - memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN); + if (err) + goto out; + + memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN); + mlx4_en_update_user_mac(priv, new_mac); +out: mutex_unlock(&mdev->state_lock); return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 10cfb058a9de..d4c7c15cb4ad 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -162,6 +162,7 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [35] = "Diag counters per port", [36] = "QinQ VST mode support", [37] = "sl to vl mapping table change event support", + [38] = "user MAC support", }; int i; @@ -778,6 +779,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_MAX_DESC_SZ_SQ_OFFSET 0x52 #define QUERY_DEV_CAP_MAX_SG_RQ_OFFSET 0x55 #define QUERY_DEV_CAP_MAX_DESC_SZ_RQ_OFFSET 0x56 +#define QUERY_DEV_CAP_USER_MAC_EN_OFFSET 0x5C #define QUERY_DEV_CAP_SVLAN_BY_QP_OFFSET 0x5D #define QUERY_DEV_CAP_MAX_QP_MCG_OFFSET 0x61 #define QUERY_DEV_CAP_RSVD_MCG_OFFSET 0x62 @@ -949,6 +951,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(size, outbox, QUERY_DEV_CAP_MAX_DESC_SZ_SQ_OFFSET); dev_cap->max_sq_desc_sz = size; + MLX4_GET(field, outbox, QUERY_DEV_CAP_USER_MAC_EN_OFFSET); + if (field & (1 << 2)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_USER_MAC_EN; MLX4_GET(field, outbox, QUERY_DEV_CAP_SVLAN_BY_QP_OFFSET); if (field & 0x1) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_SVLAN_BY_QP; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index e6413a8a5f07..c631d157b97d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -1029,6 +1029,9 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS; mlx4_warn(dev, "Timestamping is not supported in slave mode\n"); + dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_USER_MAC_EN; + mlx4_dbg(dev, "User MAC FW update is not supported in slave mode\n"); + slave_adjust_steering_mode(dev, dev_cap, hca_param); mlx4_dbg(dev, "RSS support for IP fragments is %s\n", hca_param->rss_ip_frags ? "on" : "off"); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 706d7f21ac5c..16f1e097d33a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -807,6 +807,8 @@ struct mlx4_set_port_general_context { u8 phv_en; u8 reserved6[5]; __be16 user_mtu; + u16 reserved7; + u8 user_mac[6]; }; struct mlx4_set_port_rqp_calc_context { diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 4e36e287d605..3ef3406ff4cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -52,6 +52,7 @@ #define MLX4_FLAG2_V_IGNORE_FCS_MASK BIT(1) #define MLX4_FLAG2_V_USER_MTU_MASK BIT(5) +#define MLX4_FLAG2_V_USER_MAC_MASK BIT(6) #define MLX4_FLAG_V_MTU_MASK BIT(0) #define MLX4_FLAG_V_PPRX_MASK BIT(1) #define MLX4_FLAG_V_PPTX_MASK BIT(2) @@ -1700,6 +1701,30 @@ int mlx4_SET_PORT_user_mtu(struct mlx4_dev *dev, u8 port, u16 user_mtu) } EXPORT_SYMBOL(mlx4_SET_PORT_user_mtu); +int mlx4_SET_PORT_user_mac(struct mlx4_dev *dev, u8 port, u8 *user_mac) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_port_general_context *context; + u32 in_mod; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + context = mailbox->buf; + context->flags2 |= MLX4_FLAG2_V_USER_MAC_MASK; + memcpy(context->user_mac, user_mac, sizeof(context->user_mac)); + + in_mod = MLX4_SET_PORT_GENERAL << 8 | port; + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_user_mac); + int mlx4_SET_PORT_fcs_check(struct mlx4_dev *dev, u8 port, u8 ignore_fcs_value) { struct mlx4_cmd_mailbox *mailbox; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 9844606f9491..89c0e7f7cd9b 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -224,6 +224,7 @@ enum { MLX4_DEV_CAP_FLAG2_DIAG_PER_PORT = 1ULL << 35, MLX4_DEV_CAP_FLAG2_SVLAN_BY_QP = 1ULL << 36, MLX4_DEV_CAP_FLAG2_SL_TO_VL_CHANGE_EVENT = 1ULL << 37, + MLX4_DEV_CAP_FLAG2_USER_MAC_EN = 1ULL << 38, }; enum { @@ -1377,6 +1378,7 @@ int mlx4_get_base_qpn(struct mlx4_dev *dev, u8 port); int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac); int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx); +int mlx4_SET_PORT_user_mac(struct mlx4_dev *dev, u8 port, u8 *user_mac); int mlx4_SET_PORT_user_mtu(struct mlx4_dev *dev, u8 port, u16 user_mtu); int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, u8 promisc); -- cgit v1.2.3 From fa20e0e32cb3dfc1760b6254b64977f2fb5bd851 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Mon, 28 Aug 2017 21:43:22 +0200 Subject: vxlan: factor out VXLAN-GPE next protocol The values are shared between VXLAN-GPE and NSH. Originally probably by coincidence but I notified both working groups about this last year and they seem to keep the values in sync since then. Hopefully they'll get a single IANA registry for the values, too. (I asked them for that.) Factor out the code to be shared by the NSH implementation. NSH and MPLS values are added in this patch, too. For MPLS, the drafts incorrectly assign only a single value, while we have two MPLS ethertypes. I raised the problem with both groups. For now, I assume the value is for unicast. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 32 +++++++------------------------- include/net/tun_proto.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ include/net/vxlan.h | 6 ------ 3 files changed, 56 insertions(+), 31 deletions(-) create mode 100644 include/net/tun_proto.h (limited to 'drivers/net') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index ae3a1da703c2..d7c49cf1d5e9 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #if IS_ENABLED(CONFIG_IPV6) @@ -1261,19 +1262,9 @@ static bool vxlan_parse_gpe_hdr(struct vxlanhdr *unparsed, if (gpe->oam_flag) return false; - switch (gpe->next_protocol) { - case VXLAN_GPE_NP_IPV4: - *protocol = htons(ETH_P_IP); - break; - case VXLAN_GPE_NP_IPV6: - *protocol = htons(ETH_P_IPV6); - break; - case VXLAN_GPE_NP_ETHERNET: - *protocol = htons(ETH_P_TEB); - break; - default: + *protocol = tun_p_to_eth_p(gpe->next_protocol); + if (!*protocol) return false; - } unparsed->vx_flags &= ~VXLAN_GPE_USED_BITS; return true; @@ -1799,19 +1790,10 @@ static int vxlan_build_gpe_hdr(struct vxlanhdr *vxh, u32 vxflags, struct vxlanhdr_gpe *gpe = (struct vxlanhdr_gpe *)vxh; gpe->np_applied = 1; - - switch (protocol) { - case htons(ETH_P_IP): - gpe->next_protocol = VXLAN_GPE_NP_IPV4; - return 0; - case htons(ETH_P_IPV6): - gpe->next_protocol = VXLAN_GPE_NP_IPV6; - return 0; - case htons(ETH_P_TEB): - gpe->next_protocol = VXLAN_GPE_NP_ETHERNET; - return 0; - } - return -EPFNOSUPPORT; + gpe->next_protocol = tun_p_from_eth_p(protocol); + if (!gpe->next_protocol) + return -EPFNOSUPPORT; + return 0; } static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst, diff --git a/include/net/tun_proto.h b/include/net/tun_proto.h new file mode 100644 index 000000000000..2ea3deba4c99 --- /dev/null +++ b/include/net/tun_proto.h @@ -0,0 +1,49 @@ +#ifndef __NET_TUN_PROTO_H +#define __NET_TUN_PROTO_H + +#include + +/* One byte protocol values as defined by VXLAN-GPE and NSH. These will + * hopefully get a shared IANA registry. + */ +#define TUN_P_IPV4 0x01 +#define TUN_P_IPV6 0x02 +#define TUN_P_ETHERNET 0x03 +#define TUN_P_NSH 0x04 +#define TUN_P_MPLS_UC 0x05 + +static inline __be16 tun_p_to_eth_p(u8 proto) +{ + switch (proto) { + case TUN_P_IPV4: + return htons(ETH_P_IP); + case TUN_P_IPV6: + return htons(ETH_P_IPV6); + case TUN_P_ETHERNET: + return htons(ETH_P_TEB); + case TUN_P_NSH: + return htons(ETH_P_NSH); + case TUN_P_MPLS_UC: + return htons(ETH_P_MPLS_UC); + } + return 0; +} + +static inline u8 tun_p_from_eth_p(__be16 proto) +{ + switch (proto) { + case htons(ETH_P_IP): + return TUN_P_IPV4; + case htons(ETH_P_IPV6): + return TUN_P_IPV6; + case htons(ETH_P_TEB): + return TUN_P_ETHERNET; + case htons(ETH_P_NSH): + return TUN_P_NSH; + case htons(ETH_P_MPLS_UC): + return TUN_P_MPLS_UC; + } + return 0; +} + +#endif diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 3f430e38ab82..4e3876dde295 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -168,12 +168,6 @@ reserved_flags2:2; #define VXLAN_GPE_USED_BITS (VXLAN_HF_VER | VXLAN_HF_NP | VXLAN_HF_OAM | \ cpu_to_be32(0xff)) -/* VXLAN-GPE header Next Protocol. */ -#define VXLAN_GPE_NP_IPV4 0x01 -#define VXLAN_GPE_NP_IPV6 0x02 -#define VXLAN_GPE_NP_ETHERNET 0x03 -#define VXLAN_GPE_NP_NSH 0x04 - struct vxlan_metadata { u32 gbp; }; -- cgit v1.2.3 From f602b976b6b0eab8de59e438f9313da72b47a8e4 Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Mon, 28 Aug 2017 15:29:34 -0500 Subject: amd-xgbe: Interrupt summary bits are h/w version dependent There is a difference in the bit position of the normal interrupt summary enable (NIE) and abnormal interrupt summary enable (AIE) between revisions of the hardware. For older revisions the NIE and AIE bits are positions 16 and 15 respectively. For newer revisions the NIE and AIE bits are positions 15 and 14. The effect in changing the bit position is that newer hardware won't receive AIE interrupts in the current version of the driver. Specifically, the driver uses this interrupt to collect statistics on when a receive buffer unavailable event occurs and to restart the driver/device when a fatal bus error occurs. Update the driver to set the interrupt enable bit based on the reported version of the hardware. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 8 ++++++-- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 943133034879..7ea72ef11a55 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -210,11 +210,15 @@ #define DMA_CH_CR_PBLX8_WIDTH 1 #define DMA_CH_CR_SPH_INDEX 24 #define DMA_CH_CR_SPH_WIDTH 1 -#define DMA_CH_IER_AIE_INDEX 15 +#define DMA_CH_IER_AIE20_INDEX 15 +#define DMA_CH_IER_AIE20_WIDTH 1 +#define DMA_CH_IER_AIE_INDEX 14 #define DMA_CH_IER_AIE_WIDTH 1 #define DMA_CH_IER_FBEE_INDEX 12 #define DMA_CH_IER_FBEE_WIDTH 1 -#define DMA_CH_IER_NIE_INDEX 16 +#define DMA_CH_IER_NIE20_INDEX 16 +#define DMA_CH_IER_NIE20_WIDTH 1 +#define DMA_CH_IER_NIE_INDEX 15 #define DMA_CH_IER_NIE_WIDTH 1 #define DMA_CH_IER_RBUE_INDEX 7 #define DMA_CH_IER_RBUE_WIDTH 1 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 671203dbea3d..e107e180e2c8 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -649,13 +649,15 @@ static void xgbe_config_flow_control(struct xgbe_prv_data *pdata) static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) { struct xgbe_channel *channel; - unsigned int i; + unsigned int i, ver; /* Set the interrupt mode if supported */ if (pdata->channel_irq_mode) XGMAC_IOWRITE_BITS(pdata, DMA_MR, INTM, pdata->channel_irq_mode); + ver = XGMAC_GET_BITS(pdata->hw_feat.version, MAC_VR, SNPSVER); + for (i = 0; i < pdata->channel_count; i++) { channel = pdata->channel[i]; @@ -671,8 +673,13 @@ static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) * AIE - Abnormal Interrupt Summary Enable * FBEE - Fatal Bus Error Enable */ - XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, NIE, 1); - XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, AIE, 1); + if (ver < 0x21) { + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, NIE20, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, AIE20, 1); + } else { + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, NIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, AIE, 1); + } XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 1); if (channel->tx_ring) { -- cgit v1.2.3 From 7e3108fab50a292e95ac8821da8b7d34b2fbb809 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Tue, 29 Aug 2017 22:17:52 +0530 Subject: net: dsa: make some structures const Make these const as they are not modified anywhere. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 2 +- drivers/net/dsa/qca8k.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index bbcb4053e04e..8492c9d64004 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -983,7 +983,7 @@ static int bcm_sf2_core_write64(struct b53_device *dev, u8 page, u8 reg, return 0; } -static struct b53_io_ops bcm_sf2_io_ops = { +static const struct b53_io_ops bcm_sf2_io_ops = { .read8 = bcm_sf2_core_read8, .read16 = bcm_sf2_core_read16, .read32 = bcm_sf2_core_read32, diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 17977f06cb98..5ada7a41449c 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -250,7 +250,7 @@ static const struct regmap_range qca8k_readable_ranges[] = { }; -static struct regmap_access_table qca8k_readable_table = { +static const struct regmap_access_table qca8k_readable_table = { .yes_ranges = qca8k_readable_ranges, .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), }; -- cgit v1.2.3 From b6eb9d500087a79feab2be8adb84826ffc955690 Mon Sep 17 00:00:00 2001 From: Weilin Chang Date: Tue, 29 Aug 2017 12:19:57 -0700 Subject: liquidio: show NIC's U-Boot version in a dev_info() message Signed-off-by: Weilin Chang Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../net/ethernet/cavium/liquidio/octeon_console.c | 78 ++++++++++++++++++++++ 1 file changed, 78 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index 19e5212f66d1..ec3dd69cd6b2 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -574,6 +574,82 @@ int octeon_init_consoles(struct octeon_device *oct) return ret; } +static void octeon_get_uboot_version(struct octeon_device *oct) +{ + s32 bytes_read, tries, total_read; + struct octeon_console *console; + u32 console_num = 0; + char *uboot_ver; + char *buf; + char *p; + +#define OCTEON_UBOOT_VER_BUF_SIZE 512 + buf = kmalloc(OCTEON_UBOOT_VER_BUF_SIZE, GFP_KERNEL); + if (!buf) + return; + + if (octeon_console_send_cmd(oct, "setenv stdout pci\n", 50)) { + kfree(buf); + return; + } + + if (octeon_console_send_cmd(oct, "version\n", 1)) { + kfree(buf); + return; + } + + console = &oct->console[console_num]; + tries = 0; + total_read = 0; + + do { + /* Take console output regardless of whether it will + * be logged + */ + bytes_read = + octeon_console_read(oct, + console_num, buf + total_read, + OCTEON_UBOOT_VER_BUF_SIZE - 1 - + total_read); + if (bytes_read > 0) { + buf[bytes_read] = '\0'; + + total_read += bytes_read; + if (console->waiting) + octeon_console_handle_result(oct, console_num); + } else if (bytes_read < 0) { + dev_err(&oct->pci_dev->dev, "Error reading console %u, ret=%d\n", + console_num, bytes_read); + } + + tries++; + } while ((bytes_read > 0) && (tries < 16)); + + /* If nothing is read after polling the console, + * output any leftovers if any + */ + if ((total_read == 0) && (console->leftover[0])) { + dev_dbg(&oct->pci_dev->dev, "%u: %s\n", + console_num, console->leftover); + console->leftover[0] = '\0'; + } + + buf[OCTEON_UBOOT_VER_BUF_SIZE - 1] = '\0'; + + uboot_ver = strstr(buf, "U-Boot"); + if (uboot_ver) { + p = strstr(uboot_ver, "mips"); + if (p) { + p--; + *p = '\0'; + dev_info(&oct->pci_dev->dev, "%s\n", uboot_ver); + } + } + + kfree(buf); + octeon_console_send_cmd(oct, "setenv stdout serial\n", 50); +} + int octeon_add_console(struct octeon_device *oct, u32 console_num, char *dbg_enb) { @@ -611,6 +687,8 @@ int octeon_add_console(struct octeon_device *oct, u32 console_num, work = &oct->console_poll_work[console_num].work; + octeon_get_uboot_version(oct); + INIT_DELAYED_WORK(work, check_console); oct->console_poll_work[console_num].ctxptr = (void *)oct; oct->console_poll_work[console_num].ctxul = console_num; -- cgit v1.2.3 From 69d2ea9c798983c4a7157278ec84ff969d1cd8e8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 29 Aug 2017 12:25:31 -0700 Subject: net: bcmgenet: Use correct I/O accessors The GENET driver currently uses __raw_{read,write}l which means native I/O endian. This works correctly for an ARM LE kernel (default) but fails miserably on an ARM BE (BE8) kernel where registers are kept little endian, so replace uses with {read,write}l_relaxed here which is what we want because this is all performance sensitive code. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 75 ++++++++++++++++---------- drivers/net/ethernet/broadcom/genet/bcmgenet.h | 13 ++++- 2 files changed, 58 insertions(+), 30 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index a981c4ee9d72..612d1ef3b5f5 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -72,23 +72,42 @@ #define GENET_RDMA_REG_OFF (priv->hw_params->rdma_offset + \ TOTAL_DESC * DMA_DESC_SIZE) +static inline void bcmgenet_writel(u32 value, void __iomem *offset) +{ + /* MIPS chips strapped for BE will automagically configure the + * peripheral registers for CPU-native byte order. + */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + __raw_writel(value, offset); + else + writel_relaxed(value, offset); +} + +static inline u32 bcmgenet_readl(void __iomem *offset) +{ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + return __raw_readl(offset); + else + return readl_relaxed(offset); +} + static inline void dmadesc_set_length_status(struct bcmgenet_priv *priv, void __iomem *d, u32 value) { - __raw_writel(value, d + DMA_DESC_LENGTH_STATUS); + bcmgenet_writel(value, d + DMA_DESC_LENGTH_STATUS); } static inline u32 dmadesc_get_length_status(struct bcmgenet_priv *priv, void __iomem *d) { - return __raw_readl(d + DMA_DESC_LENGTH_STATUS); + return bcmgenet_readl(d + DMA_DESC_LENGTH_STATUS); } static inline void dmadesc_set_addr(struct bcmgenet_priv *priv, void __iomem *d, dma_addr_t addr) { - __raw_writel(lower_32_bits(addr), d + DMA_DESC_ADDRESS_LO); + bcmgenet_writel(lower_32_bits(addr), d + DMA_DESC_ADDRESS_LO); /* Register writes to GISB bus can take couple hundred nanoseconds * and are done for each packet, save these expensive writes unless @@ -96,7 +115,7 @@ static inline void dmadesc_set_addr(struct bcmgenet_priv *priv, */ #ifdef CONFIG_PHYS_ADDR_T_64BIT if (priv->hw_params->flags & GENET_HAS_40BITS) - __raw_writel(upper_32_bits(addr), d + DMA_DESC_ADDRESS_HI); + bcmgenet_writel(upper_32_bits(addr), d + DMA_DESC_ADDRESS_HI); #endif } @@ -113,7 +132,7 @@ static inline dma_addr_t dmadesc_get_addr(struct bcmgenet_priv *priv, { dma_addr_t addr; - addr = __raw_readl(d + DMA_DESC_ADDRESS_LO); + addr = bcmgenet_readl(d + DMA_DESC_ADDRESS_LO); /* Register writes to GISB bus can take couple hundred nanoseconds * and are done for each packet, save these expensive writes unless @@ -121,7 +140,7 @@ static inline dma_addr_t dmadesc_get_addr(struct bcmgenet_priv *priv, */ #ifdef CONFIG_PHYS_ADDR_T_64BIT if (priv->hw_params->flags & GENET_HAS_40BITS) - addr |= (u64)__raw_readl(d + DMA_DESC_ADDRESS_HI) << 32; + addr |= (u64)bcmgenet_readl(d + DMA_DESC_ADDRESS_HI) << 32; #endif return addr; } @@ -156,8 +175,8 @@ static inline u32 bcmgenet_tbuf_ctrl_get(struct bcmgenet_priv *priv) if (GENET_IS_V1(priv)) return bcmgenet_rbuf_readl(priv, TBUF_CTRL_V1); else - return __raw_readl(priv->base + - priv->hw_params->tbuf_offset + TBUF_CTRL); + return bcmgenet_readl(priv->base + + priv->hw_params->tbuf_offset + TBUF_CTRL); } static inline void bcmgenet_tbuf_ctrl_set(struct bcmgenet_priv *priv, u32 val) @@ -165,7 +184,7 @@ static inline void bcmgenet_tbuf_ctrl_set(struct bcmgenet_priv *priv, u32 val) if (GENET_IS_V1(priv)) bcmgenet_rbuf_writel(priv, val, TBUF_CTRL_V1); else - __raw_writel(val, priv->base + + bcmgenet_writel(val, priv->base + priv->hw_params->tbuf_offset + TBUF_CTRL); } @@ -174,8 +193,8 @@ static inline u32 bcmgenet_bp_mc_get(struct bcmgenet_priv *priv) if (GENET_IS_V1(priv)) return bcmgenet_rbuf_readl(priv, TBUF_BP_MC_V1); else - return __raw_readl(priv->base + - priv->hw_params->tbuf_offset + TBUF_BP_MC); + return bcmgenet_readl(priv->base + + priv->hw_params->tbuf_offset + TBUF_BP_MC); } static inline void bcmgenet_bp_mc_set(struct bcmgenet_priv *priv, u32 val) @@ -183,7 +202,7 @@ static inline void bcmgenet_bp_mc_set(struct bcmgenet_priv *priv, u32 val) if (GENET_IS_V1(priv)) bcmgenet_rbuf_writel(priv, val, TBUF_BP_MC_V1); else - __raw_writel(val, priv->base + + bcmgenet_writel(val, priv->base + priv->hw_params->tbuf_offset + TBUF_BP_MC); } @@ -326,28 +345,28 @@ static inline struct bcmgenet_priv *dev_to_priv(struct device *dev) static inline u32 bcmgenet_tdma_readl(struct bcmgenet_priv *priv, enum dma_reg r) { - return __raw_readl(priv->base + GENET_TDMA_REG_OFF + - DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); + return bcmgenet_readl(priv->base + GENET_TDMA_REG_OFF + + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); } static inline void bcmgenet_tdma_writel(struct bcmgenet_priv *priv, u32 val, enum dma_reg r) { - __raw_writel(val, priv->base + GENET_TDMA_REG_OFF + + bcmgenet_writel(val, priv->base + GENET_TDMA_REG_OFF + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); } static inline u32 bcmgenet_rdma_readl(struct bcmgenet_priv *priv, enum dma_reg r) { - return __raw_readl(priv->base + GENET_RDMA_REG_OFF + - DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); + return bcmgenet_readl(priv->base + GENET_RDMA_REG_OFF + + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); } static inline void bcmgenet_rdma_writel(struct bcmgenet_priv *priv, u32 val, enum dma_reg r) { - __raw_writel(val, priv->base + GENET_RDMA_REG_OFF + + bcmgenet_writel(val, priv->base + GENET_RDMA_REG_OFF + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); } @@ -418,16 +437,16 @@ static inline u32 bcmgenet_tdma_ring_readl(struct bcmgenet_priv *priv, unsigned int ring, enum dma_ring_reg r) { - return __raw_readl(priv->base + GENET_TDMA_REG_OFF + - (DMA_RING_SIZE * ring) + - genet_dma_ring_regs[r]); + return bcmgenet_readl(priv->base + GENET_TDMA_REG_OFF + + (DMA_RING_SIZE * ring) + + genet_dma_ring_regs[r]); } static inline void bcmgenet_tdma_ring_writel(struct bcmgenet_priv *priv, unsigned int ring, u32 val, enum dma_ring_reg r) { - __raw_writel(val, priv->base + GENET_TDMA_REG_OFF + + bcmgenet_writel(val, priv->base + GENET_TDMA_REG_OFF + (DMA_RING_SIZE * ring) + genet_dma_ring_regs[r]); } @@ -436,16 +455,16 @@ static inline u32 bcmgenet_rdma_ring_readl(struct bcmgenet_priv *priv, unsigned int ring, enum dma_ring_reg r) { - return __raw_readl(priv->base + GENET_RDMA_REG_OFF + - (DMA_RING_SIZE * ring) + - genet_dma_ring_regs[r]); + return bcmgenet_readl(priv->base + GENET_RDMA_REG_OFF + + (DMA_RING_SIZE * ring) + + genet_dma_ring_regs[r]); } static inline void bcmgenet_rdma_ring_writel(struct bcmgenet_priv *priv, unsigned int ring, u32 val, enum dma_ring_reg r) { - __raw_writel(val, priv->base + GENET_RDMA_REG_OFF + + bcmgenet_writel(val, priv->base + GENET_RDMA_REG_OFF + (DMA_RING_SIZE * ring) + genet_dma_ring_regs[r]); } @@ -991,12 +1010,12 @@ static void bcmgenet_eee_enable_set(struct net_device *dev, bool enable) bcmgenet_umac_writel(priv, reg, UMAC_EEE_CTRL); /* Enable EEE and switch to a 27Mhz clock automatically */ - reg = __raw_readl(priv->base + off); + reg = bcmgenet_readl(priv->base + off); if (enable) reg |= TBUF_EEE_EN | TBUF_PM_EN; else reg &= ~(TBUF_EEE_EN | TBUF_PM_EN); - __raw_writel(reg, priv->base + off); + bcmgenet_writel(reg, priv->base + off); /* Do the same for thing for RBUF */ reg = bcmgenet_rbuf_readl(priv, RBUF_ENERGY_CTRL); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index b1fdd3cc10d1..2bfeaefcca0f 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -672,12 +672,21 @@ struct bcmgenet_priv { static inline u32 bcmgenet_##name##_readl(struct bcmgenet_priv *priv, \ u32 off) \ { \ - return __raw_readl(priv->base + offset + off); \ + /* MIPS chips strapped for BE will automagically configure the \ + * peripheral registers for CPU-native byte order. \ + */ \ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \ + return __raw_readl(priv->base + offset + off); \ + else \ + return readl_relaxed(priv->base + offset + off); \ } \ static inline void bcmgenet_##name##_writel(struct bcmgenet_priv *priv, \ u32 val, u32 off) \ { \ - __raw_writel(val, priv->base + offset + off); \ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \ + return __raw_writel(val, priv->base + offset + off); \ + else \ + writel_relaxed(val, priv->base + offset + off); \ } GENET_IO_MACRO(ext, GENET_EXT_OFF); -- cgit v1.2.3 From d081a16db80ef7a260fb178aa1199e01f7432625 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 29 Aug 2017 21:48:51 -0700 Subject: net: bcmgenet: Do not return from void function A stray return was added in the macro bcmgenet_##name##_writel where it should not, drop it. Reported-by: kbuild test robot Fixes: 69d2ea9c7989 ("net: bcmgenet: Use correct I/O accessors") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 2bfeaefcca0f..4c49d0b97748 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -684,7 +684,7 @@ static inline void bcmgenet_##name##_writel(struct bcmgenet_priv *priv, \ u32 val, u32 off) \ { \ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \ - return __raw_writel(val, priv->base + offset + off); \ + __raw_writel(val, priv->base + offset + off); \ else \ writel_relaxed(val, priv->base + offset + off); \ } -- cgit v1.2.3 From 075d8a75daedec14f2337c2881186aabd8fa4fda Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 20 Aug 2017 18:10:12 +0300 Subject: iwlwifi: fix long debug print There is a debug print that sometimes reaches over 110 chars, thus generating a warning in those cases. Split the print into two to prevent these cases. Fixes: 92b0f7b26b31 ("iwlwifi: split the regulatory rules when the bandwidth flags require it") Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index b46796944cf2..3014beef4873 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -915,7 +915,7 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, prev_reg_rule_flags = reg_rule_flags; IWL_DEBUG_DEV(dev, IWL_DL_LAR, - "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s%s%s%s(0x%02x) reg_flags 0x%x: %s\n", + "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s%s%s%s(0x%02x)\n", center_freq, band == NL80211_BAND_5GHZ ? "5.2" : "2.4", CHECK_AND_PRINT_I(VALID), @@ -930,7 +930,12 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, CHECK_AND_PRINT_I(80MHZ), CHECK_AND_PRINT_I(160MHZ), CHECK_AND_PRINT_I(DC_HIGH), - ch_flags, reg_rule_flags, + ch_flags); + IWL_DEBUG_DEV(dev, IWL_DL_LAR, + "Ch. %d [%sGHz] reg_flags 0x%x: %s\n", + center_freq, + band == NL80211_BAND_5GHZ ? "5.2" : "2.4", + reg_rule_flags, ((ch_flags & NVM_CHANNEL_ACTIVE) && !(ch_flags & NVM_CHANNEL_RADAR)) ? "Ad-Hoc" : ""); -- cgit v1.2.3 From eb045e6e0389eabfd704dd7a76d8b2a892970e85 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Thu, 17 Aug 2017 17:28:22 +0300 Subject: iwlwifi: mvm: Avoid deferring non bufferable frames Use bcast station for all non bufferable frames on AP and AD-HOC. The host is no longer aware of STAs PS status because of buffer station offload, so we can't rely on mac80211 to toggle on IEEE80211_TX_CTL_NO_PS_BUFFER bit. A possible issue with buffering such frames, beside the obvious spec violation, is when a station disconnects while in PS but the AP isn't aware of that. In such scenarios the AP won't be able to send probe responses or auth frames so the STA won't be able to reconnect and the AP will have a queue hang. Fixes: 3e56eadfb6a1 ("iwlwifi: mvm: implement AP/GO uAPSD support") Signed-off-by: David Spinadel Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index d7feb1ab4dc9..15f2d826bb4b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -810,10 +810,11 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) goto drop; - /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */ - if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER && - ieee80211_is_mgmt(hdr->frame_control) && - !ieee80211_is_bufferable_mmpdu(hdr->frame_control))) + /* treat non-bufferable MMPDUs on AP interfaces as broadcast */ + if ((info->control.vif->type == NL80211_IFTYPE_AP || + info->control.vif->type == NL80211_IFTYPE_ADHOC) && + ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) sta = NULL; if (sta) { -- cgit v1.2.3 From d7a5b3e9e42ee95190742fef0d617bbde4f74d80 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 26 Jun 2017 18:33:59 +0300 Subject: iwlwifi: mvm: bump API to 34 for 8000 and up These devices support -34.ucode, so load it. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/cfg/8000.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/cfg/9000.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/a000.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c index 5081720608af..2e6c52664cee 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c @@ -70,8 +70,8 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 33 -#define IWL8265_UCODE_API_MAX 33 +#define IWL8000_UCODE_API_MAX 34 +#define IWL8265_UCODE_API_MAX 34 /* Lowest firmware API version supported */ #define IWL8000_UCODE_API_MIN 22 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c index 97208ce19f92..2babe0a1f18b 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c @@ -55,7 +55,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL9000_UCODE_API_MAX 33 +#define IWL9000_UCODE_API_MAX 34 /* Lowest firmware API version supported */ #define IWL9000_UCODE_API_MIN 30 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/a000.c b/drivers/net/wireless/intel/iwlwifi/cfg/a000.c index dcd35b5c9d24..76ba1f8bc72f 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/a000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/a000.c @@ -55,7 +55,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL_A000_UCODE_API_MAX 33 +#define IWL_A000_UCODE_API_MAX 34 /* Lowest firmware API version supported */ #define IWL_A000_UCODE_API_MIN 24 -- cgit v1.2.3 From fc4386729491f8f1cb9516f2e3c2ff1d7af6cb4e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 25 Aug 2017 11:40:37 +0300 Subject: rsi: update some comments These functions don't return -1 on failure. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index e24c8b36b157..5c4c43dfdf33 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -754,7 +754,7 @@ static int rsi_mac80211_conf_tx(struct ieee80211_hw *hw, * @vif: Pointer to the ieee80211_vif structure. * @key: Pointer to the ieee80211_key_conf structure. * - * Return: status: 0 on success, -1 on failure. + * Return: status: 0 on success, negative error codes on failure. */ static int rsi_hal_key_config(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1192,7 +1192,7 @@ static void rsi_set_min_rate(struct ieee80211_hw *hw, * @vif: Pointer to the ieee80211_vif structure. * @sta: Pointer to the ieee80211_sta structure. * - * Return: 0 on success, -1 on failure. + * Return: 0 on success, negative error codes on failure. */ static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1301,7 +1301,7 @@ static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, * @vif: Pointer to the ieee80211_vif structure. * @sta: Pointer to the ieee80211_sta structure. * - * Return: 0 on success, -1 on failure. + * Return: 0 on success, negative error codes on failure. */ static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1421,7 +1421,7 @@ fail_set_antenna: * @tx_ant: Bitmap for tx antenna * @rx_ant: Bitmap for rx antenna * - * Return: 0 on success, -1 on failure. + * Return: 0 on success, negative error codes on failure. */ static int rsi_mac80211_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) @@ -1528,7 +1528,7 @@ static const struct ieee80211_ops mac80211_ops = { * rsi_mac80211_attach() - This function is used to initialize Mac80211 stack. * @common: Pointer to the driver private structure. * - * Return: 0 on success, -1 on failure. + * Return: 0 on success, negative error codes on failure. */ int rsi_mac80211_attach(struct rsi_common *common) { -- cgit v1.2.3 From 0270639e899e21ad0531621f23a238d4bff63653 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 25 Aug 2017 11:43:51 +0300 Subject: rsi: missing unlocks on error paths There is a missing unlock if rsi_find_sta() fails in rsi_mac80211_ampdu_action() or if we hit the -EINVAL path in rsi_mac80211_sta_add(). Fixes: 3528608f3a79 ("rsi: handle station connection in AP mode") Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 5c4c43dfdf33..fa12c05d9e23 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -906,7 +906,8 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, rsta = rsi_find_sta(common, sta->addr); if (!rsta) { rsi_dbg(ERR_ZONE, "No station mapped\n"); - return 0; + status = 0; + goto unlock; } sta_id = rsta->sta_id; } @@ -974,6 +975,7 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, break; } +unlock: mutex_unlock(&common->mutex); return status; } @@ -1202,6 +1204,7 @@ static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, struct rsi_common *common = adapter->priv; bool sta_exist = false; struct rsi_sta *rsta; + int status = 0; rsi_dbg(INFO_ZONE, "Station Add: %pM\n", sta->addr); @@ -1215,8 +1218,8 @@ static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, /* Check if max stations reached */ if (common->num_stations >= common->max_stations) { rsi_dbg(ERR_ZONE, "Reject: Max Stations exists\n"); - mutex_unlock(&common->mutex); - return -EOPNOTSUPP; + status = -EOPNOTSUPP; + goto unlock; } for (cnt = 0; cnt < common->max_stations; cnt++) { rsta = &common->stations[cnt]; @@ -1241,7 +1244,8 @@ static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, rsi_dbg(ERR_ZONE, "%s: Some problem reaching here...\n", __func__); - return -EINVAL; + status = -EINVAL; + goto unlock; } rsta = &common->stations[sta_idx]; rsta->sta = sta; @@ -1289,9 +1293,10 @@ static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, } } +unlock: mutex_unlock(&common->mutex); - return 0; + return status; } /** -- cgit v1.2.3 From 83ec489193894e52bd395eec470f4f7c4286d4a5 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 25 Aug 2017 17:04:15 +0200 Subject: rt2800: fix TX_PIN_CFG setting for non MT7620 chips Since commit 41977e86c984 ("rt2x00: add support for MT7620") we do not initialize TX_PIN_CFG setting. This cause breakage at least on some RT3573 devices. To fix the problem patch restores previous behaviour for non MT7620 chips. Fixes: 41977e86c984 ("rt2x00: add support for MT7620") Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1480829 Reported-and-tested-by: Jussi Eloranta Cc: Daniel Golle Signed-off-by: Stanislaw Gruszka Acked-by: Daniel Golle Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 0b75def39c6c..d2c289446c00 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -3702,7 +3702,10 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, if (rt2x00_rt(rt2x00dev, RT3572)) rt2800_rfcsr_write(rt2x00dev, 8, 0); - tx_pin = rt2800_register_read(rt2x00dev, TX_PIN_CFG); + if (rt2x00_rt(rt2x00dev, RT6352)) + tx_pin = rt2800_register_read(rt2x00dev, TX_PIN_CFG); + else + tx_pin = 0; switch (rt2x00dev->default_ant.tx_chain_num) { case 3: -- cgit v1.2.3 From 66a3479e12176e98a847971558ff8b795f732c8c Mon Sep 17 00:00:00 2001 From: Himanshu Jha Date: Wed, 30 Aug 2017 00:24:50 +0530 Subject: rsi: remove memset before memcpy calling memcpy immediately after memset with the same region of memory makes memset redundant. Signed-off-by: Himanshu Jha Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_sdio.c | 1 - drivers/net/wireless/rsi/rsi_91x_usb.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index 742f6cd44f6c..8d3a4839b6ef 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -584,7 +584,6 @@ static int rsi_sdio_load_data_master_write(struct rsi_hw *adapter, } for (offset = 0, i = 0; i < num_blocks; i++, offset += block_size) { - memset(temp_buf, 0, block_size); memcpy(temp_buf, ta_firmware + offset, block_size); lsb_address = (u16)base_address; status = rsi_sdio_write_register_multiple diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 9097f7e6229e..81df09dd2636 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -439,7 +439,6 @@ static int rsi_usb_load_data_master_write(struct rsi_hw *adapter, rsi_dbg(INFO_ZONE, "num_blocks: %d\n", num_blocks); for (cur_indx = 0, i = 0; i < num_blocks; i++, cur_indx += block_size) { - memset(temp_buf, 0, block_size); memcpy(temp_buf, ta_firmware + cur_indx, block_size); status = rsi_usb_write_register_multiple(adapter, base_address, (u8 *)(temp_buf), -- cgit v1.2.3 From ceed73a2cf4aff2921802aa3d21d45280677547d Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Tue, 29 Aug 2017 22:44:18 -0600 Subject: drivers: net: ethernet: qualcomm: rmnet: Initial implementation RmNet driver provides a transport agnostic MAP (multiplexing and aggregation protocol) support in embedded module. Module provides virtual network devices which can be attached to any IP-mode physical device. This will be used to provide all MAP functionality on future hardware in a single consistent location. Signed-off-by: Subash Abhinov Kasiviswanathan Signed-off-by: David S. Miller --- Documentation/networking/rmnet.txt | 82 ++++ drivers/net/ethernet/qualcomm/Kconfig | 2 + drivers/net/ethernet/qualcomm/Makefile | 2 + drivers/net/ethernet/qualcomm/rmnet/Kconfig | 12 + drivers/net/ethernet/qualcomm/rmnet/Makefile | 10 + drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 419 +++++++++++++++++++++ drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 56 +++ .../net/ethernet/qualcomm/rmnet/rmnet_handlers.c | 271 +++++++++++++ .../net/ethernet/qualcomm/rmnet/rmnet_handlers.h | 26 ++ drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h | 88 +++++ .../ethernet/qualcomm/rmnet/rmnet_map_command.c | 107 ++++++ .../net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 105 ++++++ .../net/ethernet/qualcomm/rmnet/rmnet_private.h | 45 +++ drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 170 +++++++++ drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h | 29 ++ 15 files changed, 1424 insertions(+) create mode 100644 Documentation/networking/rmnet.txt create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Kconfig create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Makefile create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h (limited to 'drivers/net') diff --git a/Documentation/networking/rmnet.txt b/Documentation/networking/rmnet.txt new file mode 100644 index 000000000000..6b341eaf2062 --- /dev/null +++ b/Documentation/networking/rmnet.txt @@ -0,0 +1,82 @@ +1. Introduction + +rmnet driver is used for supporting the Multiplexing and aggregation +Protocol (MAP). This protocol is used by all recent chipsets using Qualcomm +Technologies, Inc. modems. + +This driver can be used to register onto any physical network device in +IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator. + +Multiplexing allows for creation of logical netdevices (rmnet devices) to +handle multiple private data networks (PDN) like a default internet, tethering, +multimedia messaging service (MMS) or IP media subsystem (IMS). Hardware sends +packets with MAP headers to rmnet. Based on the multiplexer id, rmnet +routes to the appropriate PDN after removing the MAP header. + +Aggregation is required to achieve high data rates. This involves hardware +sending aggregated bunch of MAP frames. rmnet driver will de-aggregate +these MAP frames and send them to appropriate PDN's. + +2. Packet format + +a. MAP packet (data / control) + +MAP header has the same endianness of the IP packet. + +Packet format - + +Bit 0 1 2-7 8 - 15 16 - 31 +Function Command / Data Reserved Pad Multiplexer ID Payload length +Bit 32 - x +Function Raw Bytes + +Command (1)/ Data (0) bit value is to indicate if the packet is a MAP command +or data packet. Control packet is used for transport level flow control. Data +packets are standard IP packets. + +Reserved bits are usually zeroed out and to be ignored by receiver. + +Padding is number of bytes to be added for 4 byte alignment if required by +hardware. + +Multiplexer ID is to indicate the PDN on which data has to be sent. + +Payload length includes the padding length but does not include MAP header +length. + +b. MAP packet (command specific) + +Bit 0 1 2-7 8 - 15 16 - 31 +Function Command Reserved Pad Multiplexer ID Payload length +Bit 32 - 39 40 - 45 46 - 47 48 - 63 +Function Command name Reserved Command Type Reserved +Bit 64 - 95 +Function Transaction ID +Bit 96 - 127 +Function Command data + +Command 1 indicates disabling flow while 2 is enabling flow + +Command types - +0 for MAP command request +1 is to acknowledge the receipt of a command +2 is for unsupported commands +3 is for error during processing of commands + +c. Aggregation + +Aggregation is multiple MAP packets (can be data or command) delivered to +rmnet in a single linear skb. rmnet will process the individual +packets and either ACK the MAP command or deliver the IP packet to the +network stack as needed + +MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding.... +MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad... + +3. Userspace configuration + +rmnet userspace configuration is done through netlink library librmnetctl +and command line utility rmnetcli. Utility is hosted in codeaurora forum git. +The driver uses rtnl_link_ops for communication. + +https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig index 877675a27b9f..f5200712718d 100644 --- a/drivers/net/ethernet/qualcomm/Kconfig +++ b/drivers/net/ethernet/qualcomm/Kconfig @@ -59,4 +59,6 @@ config QCOM_EMAC low power, Receive-Side Scaling (RSS), and IEEE 1588-2008 Precision Clock Synchronization Protocol. +source "drivers/net/ethernet/qualcomm/rmnet/Kconfig" + endif # NET_VENDOR_QUALCOMM diff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile index 92fa7c4da90a..1847350f48a7 100644 --- a/drivers/net/ethernet/qualcomm/Makefile +++ b/drivers/net/ethernet/qualcomm/Makefile @@ -9,3 +9,5 @@ obj-$(CONFIG_QCA7000_UART) += qcauart.o qcauart-objs := qca_uart.o obj-y += emac/ + +obj-$(CONFIG_RMNET) += rmnet/ diff --git a/drivers/net/ethernet/qualcomm/rmnet/Kconfig b/drivers/net/ethernet/qualcomm/rmnet/Kconfig new file mode 100644 index 000000000000..6e2587af47a4 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/Kconfig @@ -0,0 +1,12 @@ +# +# RMNET MAP driver +# + +menuconfig RMNET + tristate "RmNet MAP driver" + default n + ---help--- + If you select this, you will enable the RMNET module which is used + for handling data in the multiplexing and aggregation protocol (MAP) + format in the embedded data path. RMNET devices can be attached to + any IP mode physical device. diff --git a/drivers/net/ethernet/qualcomm/rmnet/Makefile b/drivers/net/ethernet/qualcomm/rmnet/Makefile new file mode 100644 index 000000000000..01bddf207cac --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the RMNET module +# + +rmnet-y := rmnet_config.o +rmnet-y += rmnet_vnd.o +rmnet-y += rmnet_handlers.o +rmnet-y += rmnet_map_data.o +rmnet-y += rmnet_map_command.o +obj-$(CONFIG_RMNET) += rmnet.o diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c new file mode 100644 index 000000000000..e836d267d5cc --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -0,0 +1,419 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET configuration engine + * + */ + +#include +#include +#include +#include +#include "rmnet_config.h" +#include "rmnet_handlers.h" +#include "rmnet_vnd.h" +#include "rmnet_private.h" + +/* Locking scheme - + * The shared resource which needs to be protected is realdev->rx_handler_data. + * For the writer path, this is using rtnl_lock(). The writer paths are + * rmnet_newlink(), rmnet_dellink() and rmnet_force_unassociate_device(). These + * paths are already called with rtnl_lock() acquired in. There is also an + * ASSERT_RTNL() to ensure that we are calling with rtnl acquired. For + * dereference here, we will need to use rtnl_dereference(). Dev list writing + * needs to happen with rtnl_lock() acquired for netdev_master_upper_dev_link(). + * For the reader path, the real_dev->rx_handler_data is called in the TX / RX + * path. We only need rcu_read_lock() for these scenarios. In these cases, + * the rcu_read_lock() is held in __dev_queue_xmit() and + * netif_receive_skb_internal(), so readers need to use rcu_dereference_rtnl() + * to get the relevant information. For dev list reading, we again acquire + * rcu_read_lock() in rmnet_dellink() for netdev_master_upper_dev_get_rcu(). + * We also use unregister_netdevice_many() to free all rmnet devices in + * rmnet_force_unassociate_device() so we dont lose the rtnl_lock() and free in + * same context. + */ + +/* Local Definitions and Declarations */ +#define RMNET_LOCAL_LOGICAL_ENDPOINT -1 + +struct rmnet_walk_data { + struct net_device *real_dev; + struct list_head *head; + struct rmnet_real_dev_info *real_dev_info; +}; + +static int rmnet_is_real_dev_registered(const struct net_device *real_dev) +{ + rx_handler_func_t *rx_handler; + + rx_handler = rcu_dereference(real_dev->rx_handler); + return (rx_handler == rmnet_rx_handler); +} + +/* Needs either rcu_read_lock() or rtnl lock */ +static struct rmnet_real_dev_info* +__rmnet_get_real_dev_info(const struct net_device *real_dev) +{ + if (rmnet_is_real_dev_registered(real_dev)) + return rcu_dereference_rtnl(real_dev->rx_handler_data); + else + return NULL; +} + +/* Needs rtnl lock */ +static struct rmnet_real_dev_info* +rmnet_get_real_dev_info_rtnl(const struct net_device *real_dev) +{ + return rtnl_dereference(real_dev->rx_handler_data); +} + +static struct rmnet_endpoint* +rmnet_get_endpoint(struct net_device *dev, int config_id) +{ + struct rmnet_real_dev_info *r; + struct rmnet_endpoint *ep; + + if (!rmnet_is_real_dev_registered(dev)) { + ep = rmnet_vnd_get_endpoint(dev); + } else { + r = __rmnet_get_real_dev_info(dev); + + if (!r) + return NULL; + + if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT) + ep = &r->local_ep; + else + ep = &r->muxed_ep[config_id]; + } + + return ep; +} + +static int rmnet_unregister_real_device(struct net_device *real_dev, + struct rmnet_real_dev_info *r) +{ + if (r->nr_rmnet_devs) + return -EINVAL; + + kfree(r); + + netdev_rx_handler_unregister(real_dev); + + /* release reference on real_dev */ + dev_put(real_dev); + + netdev_dbg(real_dev, "Removed from rmnet\n"); + return 0; +} + +static int rmnet_register_real_device(struct net_device *real_dev) +{ + struct rmnet_real_dev_info *r; + int rc; + + ASSERT_RTNL(); + + if (rmnet_is_real_dev_registered(real_dev)) + return 0; + + r = kzalloc(sizeof(*r), GFP_ATOMIC); + if (!r) + return -ENOMEM; + + r->dev = real_dev; + rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, r); + if (rc) { + kfree(r); + return -EBUSY; + } + + /* hold on to real dev for MAP data */ + dev_hold(real_dev); + + netdev_dbg(real_dev, "registered with rmnet\n"); + return 0; +} + +static int rmnet_set_ingress_data_format(struct net_device *dev, u32 idf) +{ + struct rmnet_real_dev_info *r; + + netdev_dbg(dev, "Ingress format 0x%08X\n", idf); + + r = __rmnet_get_real_dev_info(dev); + + r->ingress_data_format = idf; + + return 0; +} + +static int rmnet_set_egress_data_format(struct net_device *dev, u32 edf, + u16 agg_size, u16 agg_count) +{ + struct rmnet_real_dev_info *r; + + netdev_dbg(dev, "Egress format 0x%08X agg size %d cnt %d\n", + edf, agg_size, agg_count); + + r = __rmnet_get_real_dev_info(dev); + + r->egress_data_format = edf; + + return 0; +} + +static int __rmnet_set_endpoint_config(struct net_device *dev, int config_id, + struct rmnet_endpoint *ep) +{ + struct rmnet_endpoint *dev_ep; + + dev_ep = rmnet_get_endpoint(dev, config_id); + + if (!dev_ep) + return -EINVAL; + + memcpy(dev_ep, ep, sizeof(struct rmnet_endpoint)); + if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT) + dev_ep->mux_id = 0; + else + dev_ep->mux_id = config_id; + + return 0; +} + +static int rmnet_set_endpoint_config(struct net_device *dev, + int config_id, u8 rmnet_mode, + struct net_device *egress_dev) +{ + struct rmnet_endpoint ep; + + netdev_dbg(dev, "id %d mode %d dev %s\n", + config_id, rmnet_mode, egress_dev->name); + + if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT || + config_id >= RMNET_MAX_LOGICAL_EP) + return -EINVAL; + + /* This config is cleared on every set, so its ok to not + * clear it on a device delete. + */ + memset(&ep, 0, sizeof(struct rmnet_endpoint)); + ep.rmnet_mode = rmnet_mode; + ep.egress_dev = egress_dev; + + return __rmnet_set_endpoint_config(dev, config_id, &ep); +} + +static int rmnet_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + int ingress_format = RMNET_INGRESS_FORMAT_DEMUXING | + RMNET_INGRESS_FORMAT_DEAGGREGATION | + RMNET_INGRESS_FORMAT_MAP; + int egress_format = RMNET_EGRESS_FORMAT_MUXING | + RMNET_EGRESS_FORMAT_MAP; + struct rmnet_real_dev_info *r; + struct net_device *real_dev; + int mode = RMNET_EPMODE_VND; + int err = 0; + u16 mux_id; + + real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); + if (!real_dev || !dev) + return -ENODEV; + + if (!data[IFLA_VLAN_ID]) + return -EINVAL; + + mux_id = nla_get_u16(data[IFLA_VLAN_ID]); + + err = rmnet_register_real_device(real_dev); + if (err) + goto err0; + + r = rmnet_get_real_dev_info_rtnl(real_dev); + err = rmnet_vnd_newlink(mux_id, dev, r); + if (err) + goto err1; + + err = netdev_master_upper_dev_link(dev, real_dev, NULL, NULL); + if (err) + goto err2; + + rmnet_vnd_set_mux(dev, mux_id); + rmnet_set_egress_data_format(real_dev, egress_format, 0, 0); + rmnet_set_ingress_data_format(real_dev, ingress_format); + rmnet_set_endpoint_config(real_dev, mux_id, mode, dev); + rmnet_set_endpoint_config(dev, mux_id, mode, real_dev); + return 0; + +err2: + rmnet_vnd_dellink(mux_id, r); +err1: + rmnet_unregister_real_device(real_dev, r); +err0: + return err; +} + +static void rmnet_dellink(struct net_device *dev, struct list_head *head) +{ + struct rmnet_real_dev_info *r; + struct net_device *real_dev; + u8 mux_id; + + rcu_read_lock(); + real_dev = netdev_master_upper_dev_get_rcu(dev); + rcu_read_unlock(); + + if (!real_dev || !rmnet_is_real_dev_registered(real_dev)) + return; + + r = rmnet_get_real_dev_info_rtnl(real_dev); + + mux_id = rmnet_vnd_get_mux(dev); + rmnet_vnd_dellink(mux_id, r); + netdev_upper_dev_unlink(dev, real_dev); + rmnet_unregister_real_device(real_dev, r); + + unregister_netdevice_queue(dev, head); +} + +static int rmnet_dev_walk_unreg(struct net_device *rmnet_dev, void *data) +{ + struct rmnet_walk_data *d = data; + u8 mux_id; + + mux_id = rmnet_vnd_get_mux(rmnet_dev); + + rmnet_vnd_dellink(mux_id, d->real_dev_info); + netdev_upper_dev_unlink(rmnet_dev, d->real_dev); + unregister_netdevice_queue(rmnet_dev, d->head); + + return 0; +} + +static void rmnet_force_unassociate_device(struct net_device *dev) +{ + struct net_device *real_dev = dev; + struct rmnet_real_dev_info *r; + struct rmnet_walk_data d; + LIST_HEAD(list); + + if (!rmnet_is_real_dev_registered(real_dev)) + return; + + ASSERT_RTNL(); + + d.real_dev = real_dev; + d.head = &list; + + r = rmnet_get_real_dev_info_rtnl(dev); + d.real_dev_info = r; + + rcu_read_lock(); + netdev_walk_all_lower_dev_rcu(real_dev, rmnet_dev_walk_unreg, &d); + rcu_read_unlock(); + unregister_netdevice_many(&list); + + rmnet_unregister_real_device(real_dev, r); +} + +static int rmnet_config_notify_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct net_device *dev = netdev_notifier_info_to_dev(data); + + if (!dev) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_UNREGISTER: + netdev_dbg(dev, "Kernel unregister\n"); + rmnet_force_unassociate_device(dev); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block rmnet_dev_notifier __read_mostly = { + .notifier_call = rmnet_config_notify_cb, +}; + +static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + u16 mux_id; + + if (!data || !data[IFLA_VLAN_ID]) + return -EINVAL; + + mux_id = nla_get_u16(data[IFLA_VLAN_ID]); + if (mux_id > (RMNET_MAX_LOGICAL_EP - 1)) + return -ERANGE; + + return 0; +} + +static size_t rmnet_get_size(const struct net_device *dev) +{ + return nla_total_size(2); /* IFLA_VLAN_ID */ +} + +struct rtnl_link_ops rmnet_link_ops __read_mostly = { + .kind = "rmnet", + .maxtype = __IFLA_VLAN_MAX, + .priv_size = sizeof(struct rmnet_priv), + .setup = rmnet_vnd_setup, + .validate = rmnet_rtnl_validate, + .newlink = rmnet_newlink, + .dellink = rmnet_dellink, + .get_size = rmnet_get_size, +}; + +struct rmnet_real_dev_info* +rmnet_get_real_dev_info(struct net_device *real_dev) +{ + return __rmnet_get_real_dev_info(real_dev); +} + +/* Startup/Shutdown */ + +static int __init rmnet_init(void) +{ + int rc; + + rc = register_netdevice_notifier(&rmnet_dev_notifier); + if (rc != 0) + return rc; + + rc = rtnl_link_register(&rmnet_link_ops); + if (rc != 0) { + unregister_netdevice_notifier(&rmnet_dev_notifier); + return rc; + } + return rc; +} + +static void __exit rmnet_exit(void) +{ + unregister_netdevice_notifier(&rmnet_dev_notifier); + rtnl_link_unregister(&rmnet_link_ops); +} + +module_init(rmnet_init) +module_exit(rmnet_exit) +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h new file mode 100644 index 000000000000..985d372e0d8d --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data configuration engine + * + */ + +#include + +#ifndef _RMNET_CONFIG_H_ +#define _RMNET_CONFIG_H_ + +#define RMNET_MAX_LOGICAL_EP 255 +#define RMNET_MAX_VND 32 + +/* Information about the next device to deliver the packet to. + * Exact usage of this parameter depends on the rmnet_mode. + */ +struct rmnet_endpoint { + u8 rmnet_mode; + u8 mux_id; + struct net_device *egress_dev; +}; + +/* One instance of this structure is instantiated for each real_dev associated + * with rmnet. + */ +struct rmnet_real_dev_info { + struct net_device *dev; + struct rmnet_endpoint local_ep; + struct rmnet_endpoint muxed_ep[RMNET_MAX_LOGICAL_EP]; + u32 ingress_data_format; + u32 egress_data_format; + struct net_device *rmnet_devices[RMNET_MAX_VND]; + u8 nr_rmnet_devs; +}; + +extern struct rtnl_link_ops rmnet_link_ops; + +struct rmnet_priv { + struct rmnet_endpoint local_ep; + u8 mux_id; +}; + +struct rmnet_real_dev_info* +rmnet_get_real_dev_info(struct net_device *real_dev); + +#endif /* _RMNET_CONFIG_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c new file mode 100644 index 000000000000..7dab3bbfeda5 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -0,0 +1,271 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data ingress/egress handler + * + */ + +#include +#include +#include "rmnet_private.h" +#include "rmnet_config.h" +#include "rmnet_vnd.h" +#include "rmnet_map.h" +#include "rmnet_handlers.h" + +#define RMNET_IP_VERSION_4 0x40 +#define RMNET_IP_VERSION_6 0x60 + +/* Helper Functions */ + +static void rmnet_set_skb_proto(struct sk_buff *skb) +{ + switch (skb->data[0] & 0xF0) { + case RMNET_IP_VERSION_4: + skb->protocol = htons(ETH_P_IP); + break; + case RMNET_IP_VERSION_6: + skb->protocol = htons(ETH_P_IPV6); + break; + default: + skb->protocol = htons(ETH_P_MAP); + break; + } +} + +/* Generic handler */ + +static rx_handler_result_t +rmnet_bridge_handler(struct sk_buff *skb, struct rmnet_endpoint *ep) +{ + if (!ep->egress_dev) + kfree_skb(skb); + else + rmnet_egress_handler(skb, ep); + + return RX_HANDLER_CONSUMED; +} + +static rx_handler_result_t +rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_endpoint *ep) +{ + switch (ep->rmnet_mode) { + case RMNET_EPMODE_NONE: + return RX_HANDLER_PASS; + + case RMNET_EPMODE_BRIDGE: + return rmnet_bridge_handler(skb, ep); + + case RMNET_EPMODE_VND: + skb_reset_transport_header(skb); + skb_reset_network_header(skb); + rmnet_vnd_rx_fixup(skb, skb->dev); + + skb->pkt_type = PACKET_HOST; + skb_set_mac_header(skb, 0); + netif_receive_skb(skb); + return RX_HANDLER_CONSUMED; + + default: + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } +} + +static rx_handler_result_t +rmnet_ingress_deliver_packet(struct sk_buff *skb, + struct rmnet_real_dev_info *r) +{ + if (!r) { + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + skb->dev = r->local_ep.egress_dev; + + return rmnet_deliver_skb(skb, &r->local_ep); +} + +/* MAP handler */ + +static rx_handler_result_t +__rmnet_map_ingress_handler(struct sk_buff *skb, + struct rmnet_real_dev_info *r) +{ + struct rmnet_endpoint *ep; + u8 mux_id; + u16 len; + + if (RMNET_MAP_GET_CD_BIT(skb)) { + if (r->ingress_data_format + & RMNET_INGRESS_FORMAT_MAP_COMMANDS) + return rmnet_map_command(skb, r); + + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + mux_id = RMNET_MAP_GET_MUX_ID(skb); + len = RMNET_MAP_GET_LENGTH(skb) - RMNET_MAP_GET_PAD(skb); + + if (mux_id >= RMNET_MAX_LOGICAL_EP) { + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + ep = &r->muxed_ep[mux_id]; + + if (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING) + skb->dev = ep->egress_dev; + + /* Subtract MAP header */ + skb_pull(skb, sizeof(struct rmnet_map_header)); + skb_trim(skb, len); + rmnet_set_skb_proto(skb); + return rmnet_deliver_skb(skb, ep); +} + +static rx_handler_result_t +rmnet_map_ingress_handler(struct sk_buff *skb, + struct rmnet_real_dev_info *r) +{ + struct sk_buff *skbn; + int rc; + + if (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { + while ((skbn = rmnet_map_deaggregate(skb, r)) != NULL) + __rmnet_map_ingress_handler(skbn, r); + + consume_skb(skb); + rc = RX_HANDLER_CONSUMED; + } else { + rc = __rmnet_map_ingress_handler(skb, r); + } + + return rc; +} + +static int rmnet_map_egress_handler(struct sk_buff *skb, + struct rmnet_real_dev_info *r, + struct rmnet_endpoint *ep, + struct net_device *orig_dev) +{ + int required_headroom, additional_header_len; + struct rmnet_map_header *map_header; + + additional_header_len = 0; + required_headroom = sizeof(struct rmnet_map_header); + + if (skb_headroom(skb) < required_headroom) { + if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) + return RMNET_MAP_CONSUMED; + } + + map_header = rmnet_map_add_map_header(skb, additional_header_len, 0); + if (!map_header) + return RMNET_MAP_CONSUMED; + + if (r->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) { + if (ep->mux_id == 0xff) + map_header->mux_id = 0; + else + map_header->mux_id = ep->mux_id; + } + + skb->protocol = htons(ETH_P_MAP); + + return RMNET_MAP_SUCCESS; +} + +/* Ingress / Egress Entry Points */ + +/* Processes packet as per ingress data format for receiving device. Logical + * endpoint is determined from packet inspection. Packet is then sent to the + * egress device listed in the logical endpoint configuration. + */ +rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) +{ + struct rmnet_real_dev_info *r; + struct sk_buff *skb = *pskb; + struct net_device *dev; + int rc; + + if (!skb) + return RX_HANDLER_CONSUMED; + + dev = skb->dev; + r = rmnet_get_real_dev_info(dev); + + if (r->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) { + rc = rmnet_map_ingress_handler(skb, r); + } else { + switch (ntohs(skb->protocol)) { + case ETH_P_MAP: + if (r->local_ep.rmnet_mode == + RMNET_EPMODE_BRIDGE) { + rc = rmnet_ingress_deliver_packet(skb, r); + } else { + kfree_skb(skb); + rc = RX_HANDLER_CONSUMED; + } + break; + + case ETH_P_IP: + case ETH_P_IPV6: + rc = rmnet_ingress_deliver_packet(skb, r); + break; + + default: + rc = RX_HANDLER_PASS; + } + } + + return rc; +} + +/* Modifies packet as per logical endpoint configuration and egress data format + * for egress device configured in logical endpoint. Packet is then transmitted + * on the egress device. + */ +void rmnet_egress_handler(struct sk_buff *skb, + struct rmnet_endpoint *ep) +{ + struct rmnet_real_dev_info *r; + struct net_device *orig_dev; + + orig_dev = skb->dev; + skb->dev = ep->egress_dev; + + r = rmnet_get_real_dev_info(skb->dev); + if (!r) { + kfree_skb(skb); + return; + } + + if (r->egress_data_format & RMNET_EGRESS_FORMAT_MAP) { + switch (rmnet_map_egress_handler(skb, r, ep, orig_dev)) { + case RMNET_MAP_CONSUMED: + return; + + case RMNET_MAP_SUCCESS: + break; + + default: + kfree_skb(skb); + return; + } + } + + if (ep->rmnet_mode == RMNET_EPMODE_VND) + rmnet_vnd_tx_fixup(skb, orig_dev); + + dev_queue_xmit(skb); +} diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h new file mode 100644 index 000000000000..f2638cf5693c --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data ingress/egress handler + * + */ + +#ifndef _RMNET_HANDLERS_H_ +#define _RMNET_HANDLERS_H_ + +#include "rmnet_config.h" + +void rmnet_egress_handler(struct sk_buff *skb, + struct rmnet_endpoint *ep); + +rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb); + +#endif /* _RMNET_HANDLERS_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h new file mode 100644 index 000000000000..2aabad289b10 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _RMNET_MAP_H_ +#define _RMNET_MAP_H_ + +struct rmnet_map_control_command { + u8 command_name; + u8 cmd_type:2; + u8 reserved:6; + u16 reserved2; + u32 transaction_id; + union { + struct { + u16 ip_family:2; + u16 reserved:14; + u16 flow_control_seq_num; + u32 qos_id; + } flow_control; + u8 data[0]; + }; +} __aligned(1); + +enum rmnet_map_results { + RMNET_MAP_SUCCESS, + RMNET_MAP_CONSUMED, + RMNET_MAP_GENERAL_FAILURE, + RMNET_MAP_NOT_ENABLED, + RMNET_MAP_FAILED_AGGREGATION, + RMNET_MAP_FAILED_MUX +}; + +enum rmnet_map_commands { + RMNET_MAP_COMMAND_NONE, + RMNET_MAP_COMMAND_FLOW_DISABLE, + RMNET_MAP_COMMAND_FLOW_ENABLE, + /* These should always be the last 2 elements */ + RMNET_MAP_COMMAND_UNKNOWN, + RMNET_MAP_COMMAND_ENUM_LENGTH +}; + +struct rmnet_map_header { + u8 pad_len:6; + u8 reserved_bit:1; + u8 cd_bit:1; + u8 mux_id; + u16 pkt_len; +} __aligned(1); + +#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \ + (Y)->data)->mux_id) +#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \ + (Y)->data)->cd_bit) +#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header *) \ + (Y)->data)->pad_len) +#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command *) \ + ((Y)->data + \ + sizeof(struct rmnet_map_header))) +#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \ + (Y)->data)->pkt_len)) + +#define RMNET_MAP_COMMAND_REQUEST 0 +#define RMNET_MAP_COMMAND_ACK 1 +#define RMNET_MAP_COMMAND_UNSUPPORTED 2 +#define RMNET_MAP_COMMAND_INVALID 3 + +#define RMNET_MAP_NO_PAD_BYTES 0 +#define RMNET_MAP_ADD_PAD_BYTES 1 + +u8 rmnet_map_demultiplex(struct sk_buff *skb); +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, + struct rmnet_real_dev_info *rdinfo); + +struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, + int hdrlen, int pad); +rx_handler_result_t rmnet_map_command(struct sk_buff *skb, + struct rmnet_real_dev_info *rdinfo); + +#endif /* _RMNET_MAP_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c new file mode 100644 index 000000000000..ccded40ee551 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "rmnet_config.h" +#include "rmnet_map.h" +#include "rmnet_private.h" +#include "rmnet_vnd.h" + +static u8 rmnet_map_do_flow_control(struct sk_buff *skb, + struct rmnet_real_dev_info *rdinfo, + int enable) +{ + struct rmnet_map_control_command *cmd; + struct rmnet_endpoint *ep; + struct net_device *vnd; + u16 ip_family; + u16 fc_seq; + u32 qos_id; + u8 mux_id; + int r; + + mux_id = RMNET_MAP_GET_MUX_ID(skb); + cmd = RMNET_MAP_GET_CMD_START(skb); + + if (mux_id >= RMNET_MAX_LOGICAL_EP) { + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + ep = &rdinfo->muxed_ep[mux_id]; + vnd = ep->egress_dev; + + ip_family = cmd->flow_control.ip_family; + fc_seq = ntohs(cmd->flow_control.flow_control_seq_num); + qos_id = ntohl(cmd->flow_control.qos_id); + + /* Ignore the ip family and pass the sequence number for both v4 and v6 + * sequence. User space does not support creating dedicated flows for + * the 2 protocols + */ + r = rmnet_vnd_do_flow_control(vnd, enable); + if (r) { + kfree_skb(skb); + return RMNET_MAP_COMMAND_UNSUPPORTED; + } else { + return RMNET_MAP_COMMAND_ACK; + } +} + +static void rmnet_map_send_ack(struct sk_buff *skb, + unsigned char type, + struct rmnet_real_dev_info *rdinfo) +{ + struct rmnet_map_control_command *cmd; + int xmit_status; + + skb->protocol = htons(ETH_P_MAP); + + cmd = RMNET_MAP_GET_CMD_START(skb); + cmd->cmd_type = type & 0x03; + + netif_tx_lock(skb->dev); + xmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); + netif_tx_unlock(skb->dev); +} + +/* Process MAP command frame and send N/ACK message as appropriate. Message cmd + * name is decoded here and appropriate handler is called. + */ +rx_handler_result_t rmnet_map_command(struct sk_buff *skb, + struct rmnet_real_dev_info *rdinfo) +{ + struct rmnet_map_control_command *cmd; + unsigned char command_name; + unsigned char rc = 0; + + cmd = RMNET_MAP_GET_CMD_START(skb); + command_name = cmd->command_name; + + switch (command_name) { + case RMNET_MAP_COMMAND_FLOW_ENABLE: + rc = rmnet_map_do_flow_control(skb, rdinfo, 1); + break; + + case RMNET_MAP_COMMAND_FLOW_DISABLE: + rc = rmnet_map_do_flow_control(skb, rdinfo, 0); + break; + + default: + rc = RMNET_MAP_COMMAND_UNSUPPORTED; + kfree_skb(skb); + break; + } + if (rc == RMNET_MAP_COMMAND_ACK) + rmnet_map_send_ack(skb, rc, rdinfo); + return RX_HANDLER_CONSUMED; +} diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c new file mode 100644 index 000000000000..a29c476a4755 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -0,0 +1,105 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data MAP protocol + * + */ + +#include +#include "rmnet_config.h" +#include "rmnet_map.h" +#include "rmnet_private.h" + +#define RMNET_MAP_DEAGGR_SPACING 64 +#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) + +/* Adds MAP header to front of skb->data + * Padding is calculated and set appropriately in MAP header. Mux ID is + * initialized to 0. + */ +struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, + int hdrlen, int pad) +{ + struct rmnet_map_header *map_header; + u32 padding, map_datalen; + u8 *padbytes; + + if (skb_headroom(skb) < sizeof(struct rmnet_map_header)) + return NULL; + + map_datalen = skb->len - hdrlen; + map_header = (struct rmnet_map_header *) + skb_push(skb, sizeof(struct rmnet_map_header)); + memset(map_header, 0, sizeof(struct rmnet_map_header)); + + if (pad == RMNET_MAP_NO_PAD_BYTES) { + map_header->pkt_len = htons(map_datalen); + return map_header; + } + + padding = ALIGN(map_datalen, 4) - map_datalen; + + if (padding == 0) + goto done; + + if (skb_tailroom(skb) < padding) + return NULL; + + padbytes = (u8 *)skb_put(skb, padding); + memset(padbytes, 0, padding); + +done: + map_header->pkt_len = htons(map_datalen + padding); + map_header->pad_len = padding & 0x3F; + + return map_header; +} + +/* Deaggregates a single packet + * A whole new buffer is allocated for each portion of an aggregated frame. + * Caller should keep calling deaggregate() on the source skb until 0 is + * returned, indicating that there are no more packets to deaggregate. Caller + * is responsible for freeing the original skb. + */ +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, + struct rmnet_real_dev_info *rdinfo) +{ + struct rmnet_map_header *maph; + struct sk_buff *skbn; + u32 packet_len; + + if (skb->len == 0) + return NULL; + + maph = (struct rmnet_map_header *)skb->data; + packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header); + + if (((int)skb->len - (int)packet_len) < 0) + return NULL; + + skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC); + if (!skbn) + return NULL; + + skbn->dev = skb->dev; + skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM); + skb_put(skbn, packet_len); + memcpy(skbn->data, skb->data, packet_len); + skb_pull(skb, packet_len); + + /* Some hardware can send us empty frames. Catch them */ + if (ntohs(maph->pkt_len) == 0) { + kfree_skb(skb); + return NULL; + } + + return skbn; +} diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h new file mode 100644 index 000000000000..ed820b5522f5 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _RMNET_PRIVATE_H_ +#define _RMNET_PRIVATE_H_ + +#define RMNET_MAX_VND 32 +#define RMNET_MAX_PACKET_SIZE 16384 +#define RMNET_DFLT_PACKET_SIZE 1500 +#define RMNET_NEEDED_HEADROOM 16 +#define RMNET_TX_QUEUE_LEN 1000 + +/* Constants */ +#define RMNET_EGRESS_FORMAT__RESERVED__ BIT(0) +#define RMNET_EGRESS_FORMAT_MAP BIT(1) +#define RMNET_EGRESS_FORMAT_AGGREGATION BIT(2) +#define RMNET_EGRESS_FORMAT_MUXING BIT(3) +#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 BIT(4) +#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 BIT(5) + +#define RMNET_INGRESS_FIX_ETHERNET BIT(0) +#define RMNET_INGRESS_FORMAT_MAP BIT(1) +#define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(2) +#define RMNET_INGRESS_FORMAT_DEMUXING BIT(3) +#define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(4) +#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 BIT(5) +#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 BIT(6) + +/* Pass the frame up the stack with no modifications to skb->dev */ +#define RMNET_EPMODE_NONE (0) +/* Replace skb->dev to a virtual rmnet device and pass up the stack */ +#define RMNET_EPMODE_VND (1) +/* Pass the frame directly to another device with dev_queue_xmit() */ +#define RMNET_EPMODE_BRIDGE (2) + +#endif /* _RMNET_PRIVATE_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c new file mode 100644 index 000000000000..c8b573d28dcf --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -0,0 +1,170 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * RMNET Data virtual network driver + * + */ + +#include +#include +#include +#include "rmnet_config.h" +#include "rmnet_handlers.h" +#include "rmnet_private.h" +#include "rmnet_map.h" +#include "rmnet_vnd.h" + +/* RX/TX Fixup */ + +void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev) +{ + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; +} + +void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev) +{ + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; +} + +/* Network Device Operations */ + +static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct rmnet_priv *priv; + + priv = netdev_priv(dev); + if (priv->local_ep.egress_dev) { + rmnet_egress_handler(skb, &priv->local_ep); + } else { + dev->stats.tx_dropped++; + kfree_skb(skb); + } + return NETDEV_TX_OK; +} + +static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) +{ + if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE) + return -EINVAL; + + rmnet_dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops rmnet_vnd_ops = { + .ndo_start_xmit = rmnet_vnd_start_xmit, + .ndo_change_mtu = rmnet_vnd_change_mtu, +}; + +/* Called by kernel whenever a new rmnet device is created. Sets MTU, + * flags, ARP type, needed headroom, etc... + */ +void rmnet_vnd_setup(struct net_device *rmnet_dev) +{ + struct rmnet_priv *priv; + + priv = netdev_priv(rmnet_dev); + netdev_dbg(rmnet_dev, "Setting up device %s\n", rmnet_dev->name); + + rmnet_dev->netdev_ops = &rmnet_vnd_ops; + rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE; + rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM; + random_ether_addr(rmnet_dev->dev_addr); + rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN; + + /* Raw IP mode */ + rmnet_dev->header_ops = NULL; /* No header */ + rmnet_dev->type = ARPHRD_RAWIP; + rmnet_dev->hard_header_len = 0; + rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); + + rmnet_dev->needs_free_netdev = true; +} + +/* Exposed API */ + +int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, + struct rmnet_real_dev_info *r) +{ + int rc; + + if (r->rmnet_devices[id]) + return -EINVAL; + + rc = register_netdevice(rmnet_dev); + if (!rc) { + r->rmnet_devices[id] = rmnet_dev; + r->nr_rmnet_devs++; + rmnet_dev->rtnl_link_ops = &rmnet_link_ops; + } + + return rc; +} + +int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r) +{ + if (id >= RMNET_MAX_VND || !r->rmnet_devices[id]) + return -EINVAL; + + r->rmnet_devices[id] = NULL; + r->nr_rmnet_devs--; + return 0; +} + +u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev) +{ + struct rmnet_priv *priv; + + priv = netdev_priv(rmnet_dev); + return priv->mux_id; +} + +void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id) +{ + struct rmnet_priv *priv; + + priv = netdev_priv(rmnet_dev); + priv->mux_id = mux_id; +} + +/* Gets the logical endpoint configuration for a RmNet virtual network device + * node. Caller should confirm that devices is a RmNet VND before calling. + */ +struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *rmnet_dev) +{ + struct rmnet_priv *priv; + + if (!rmnet_dev) + return NULL; + + priv = netdev_priv(rmnet_dev); + + return &priv->local_ep; +} + +int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable) +{ + netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable); + /* Although we expect similar number of enable/disable + * commands, optimize for the disable. That is more + * latency sensitive than enable + */ + if (unlikely(enable)) + netif_wake_queue(rmnet_dev); + else + netif_stop_queue(rmnet_dev); + + return 0; +} diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h new file mode 100644 index 000000000000..b102b4269be1 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data Virtual Network Device APIs + * + */ + +#ifndef _RMNET_VND_H_ +#define _RMNET_VND_H_ + +int rmnet_vnd_do_flow_control(struct net_device *dev, int enable); +struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *dev); +int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, + struct rmnet_real_dev_info *r); +int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r); +void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev); +void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev); +u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev); +void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id); +void rmnet_vnd_setup(struct net_device *dev); +#endif /* _RMNET_VND_H_ */ -- cgit v1.2.3 From d36b82bcdd1908450f8c3ba3f9b7a189cda8c69c Mon Sep 17 00:00:00 2001 From: Stefan Sørensen Date: Wed, 30 Aug 2017 08:58:47 +0200 Subject: dp83640: don't hold spinlock while calling netif_rx_ni MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should not hold a spinlock while pushing the skb into the networking stack, so move the call to netif_rx_ni out of the critical region to where we have dropped the spinlock. Signed-off-by: Stefan Sørensen Signed-off-by: David S. Miller --- drivers/net/phy/dp83640.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index c3065236ffcc..cbd629822f04 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -874,7 +874,6 @@ static void decode_rxts(struct dp83640_private *dp83640, shhwtstamps = skb_hwtstamps(skb); memset(shhwtstamps, 0, sizeof(*shhwtstamps)); shhwtstamps->hwtstamp = ns_to_ktime(rxts->ns); - netif_rx_ni(skb); list_add(&rxts->list, &dp83640->rxpool); break; } @@ -885,6 +884,9 @@ static void decode_rxts(struct dp83640_private *dp83640, list_add_tail(&rxts->list, &dp83640->rxts); out: spin_unlock_irqrestore(&dp83640->rx_lock, flags); + + if (shhwtstamps) + netif_rx_ni(skb); } static void decode_txts(struct dp83640_private *dp83640, @@ -1425,7 +1427,6 @@ static bool dp83640_rxtstamp(struct phy_device *phydev, shhwtstamps = skb_hwtstamps(skb); memset(shhwtstamps, 0, sizeof(*shhwtstamps)); shhwtstamps->hwtstamp = ns_to_ktime(rxts->ns); - netif_rx_ni(skb); list_del_init(&rxts->list); list_add(&rxts->list, &dp83640->rxpool); break; @@ -1438,6 +1439,8 @@ static bool dp83640_rxtstamp(struct phy_device *phydev, skb_info->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT; skb_queue_tail(&dp83640->rx_queue, skb); schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT); + } else { + netif_rx_ni(skb); } return true; -- cgit v1.2.3 From 542897d987b0d16c4346952f116acb80a8e82a38 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Wed, 30 Aug 2017 10:29:15 +0200 Subject: net: mvpp2: initialize the comphy On some platforms, the comphy is between the MAC GoP and the PHYs. The mvpp2 driver currently relies on the firmware/bootloader to configure the comphy. As a comphy driver was added to the generic PHY framework, this patch uses it in the mvpp2 driver to configure the comphy at boot time to avoid relying on the bootloader. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 44 +++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index fea9ae5b70ba..3dc5f644a8e1 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -861,6 +862,7 @@ struct mvpp2_port { phy_interface_t phy_interface; struct device_node *phy_node; + struct phy *comphy; unsigned int link; unsigned int duplex; unsigned int speed; @@ -4420,6 +4422,32 @@ invalid_conf: return -EINVAL; } +static int mvpp22_comphy_init(struct mvpp2_port *port) +{ + enum phy_mode mode; + int ret; + + if (!port->comphy) + return 0; + + switch (port->phy_interface) { + case PHY_INTERFACE_MODE_SGMII: + mode = PHY_MODE_SGMII; + break; + case PHY_INTERFACE_MODE_10GKR: + mode = PHY_MODE_10GKR; + break; + default: + return -EINVAL; + } + + ret = phy_set_mode(port->comphy, mode); + if (ret) + return ret; + + return phy_power_on(port->comphy); +} + static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port) { u32 val; @@ -6404,8 +6432,10 @@ static void mvpp2_start_dev(struct mvpp2_port *port) /* Enable interrupts on all CPUs */ mvpp2_interrupts_enable(port); - if (port->priv->hw_version == MVPP22) + if (port->priv->hw_version == MVPP22) { + mvpp22_comphy_init(port); mvpp22_gop_init(port); + } mvpp2_port_mii_set(port); mvpp2_port_enable(port); @@ -6436,6 +6466,7 @@ static void mvpp2_stop_dev(struct mvpp2_port *port) mvpp2_egress_disable(port); mvpp2_port_disable(port); phy_stop(ndev->phydev); + phy_power_off(port->comphy); } static int mvpp2_check_ringparam_valid(struct net_device *dev, @@ -7242,6 +7273,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, struct mvpp2 *priv) { struct device_node *phy_node; + struct phy *comphy; struct mvpp2_port *port; struct mvpp2_port_pcpu *port_pcpu; struct net_device *dev; @@ -7285,6 +7317,15 @@ static int mvpp2_port_probe(struct platform_device *pdev, goto err_free_netdev; } + comphy = devm_of_phy_get(&pdev->dev, port_node, NULL); + if (IS_ERR(comphy)) { + if (PTR_ERR(comphy) == -EPROBE_DEFER) { + err = -EPROBE_DEFER; + goto err_free_netdev; + } + comphy = NULL; + } + if (of_property_read_u32(port_node, "port-id", &id)) { err = -EINVAL; dev_err(&pdev->dev, "missing port-id value\n"); @@ -7318,6 +7359,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->phy_node = phy_node; port->phy_interface = phy_mode; + port->comphy = comphy; if (priv->hw_version == MVPP21) { res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + id); -- cgit v1.2.3 From 968b211c620b567a4d2183beb0516cbd2c73560a Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Wed, 30 Aug 2017 10:29:16 +0200 Subject: net: mvpp2: simplify the link_event function The link_event function is somewhat complicated. This cosmetic patch simplifies it. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 3dc5f644a8e1..cacbd881cebd 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5740,7 +5740,6 @@ static void mvpp2_link_event(struct net_device *dev) { struct mvpp2_port *port = netdev_priv(dev); struct phy_device *phydev = dev->phydev; - int status_change = 0; u32 val; if (phydev->link) { @@ -5771,16 +5770,8 @@ static void mvpp2_link_event(struct net_device *dev) } if (phydev->link != port->link) { - if (!phydev->link) { - port->duplex = -1; - port->speed = 0; - } - port->link = phydev->link; - status_change = 1; - } - if (status_change) { if (phydev->link) { val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); val |= (MVPP2_GMAC_FORCE_LINK_PASS | @@ -5789,9 +5780,13 @@ static void mvpp2_link_event(struct net_device *dev) mvpp2_egress_enable(port); mvpp2_ingress_enable(port); } else { + port->duplex = -1; + port->speed = 0; + mvpp2_ingress_disable(port); mvpp2_egress_disable(port); } + phy_print_status(phydev); } } -- cgit v1.2.3 From f55744adeee8598a05f0714b8fe9d131865ef060 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Wed, 30 Aug 2017 10:29:17 +0200 Subject: net: mvpp2: improve the link management function When the link status changes, the phylib calls the link_event function in the mvpp2 driver. Before this patch only the egress/ingress transmit was enabled/disabled. This patch adds more functionality to the link status management code by enabling/disabling the port per-cpu interrupts, and the port itself. The queues are now stopped as well, and the netif carrier helpers are called. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index cacbd881cebd..0fe146f2bc8c 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5777,14 +5777,25 @@ static void mvpp2_link_event(struct net_device *dev) val |= (MVPP2_GMAC_FORCE_LINK_PASS | MVPP2_GMAC_FORCE_LINK_DOWN); writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + + mvpp2_interrupts_enable(port); + mvpp2_port_enable(port); + mvpp2_egress_enable(port); mvpp2_ingress_enable(port); + netif_carrier_on(dev); + netif_tx_wake_all_queues(dev); } else { port->duplex = -1; port->speed = 0; + netif_tx_stop_all_queues(dev); + netif_carrier_off(dev); mvpp2_ingress_disable(port); mvpp2_egress_disable(port); + + mvpp2_port_disable(port); + mvpp2_interrupts_disable(port); } phy_print_status(phydev); -- cgit v1.2.3 From 65a2c09aea8b9b620bbb3e59f68090a2b7151865 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Wed, 30 Aug 2017 10:29:18 +0200 Subject: net: mvpp2: do not set GMAC autoneg when using XLG MAC When using the XLG MAC, it does not make sense to force the GMAC autoneg parameters. This patch adds checks to only set the GMAC autoneg parameters when needed (i.e. when not using the XLG MAC). Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 64 +++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 22 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 0fe146f2bc8c..670395235932 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5735,6 +5735,37 @@ static irqreturn_t mvpp2_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port, + struct phy_device *phydev) +{ + u32 val; + + if (port->phy_interface != PHY_INTERFACE_MODE_RGMII && + port->phy_interface != PHY_INTERFACE_MODE_RGMII_ID && + port->phy_interface != PHY_INTERFACE_MODE_RGMII_RXID && + port->phy_interface != PHY_INTERFACE_MODE_RGMII_TXID && + port->phy_interface != PHY_INTERFACE_MODE_SGMII) + return; + + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX | + MVPP2_GMAC_AN_SPEED_EN | + MVPP2_GMAC_AN_DUPLEX_EN); + + if (phydev->duplex) + val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; + + if (phydev->speed == SPEED_1000) + val |= MVPP2_GMAC_CONFIG_GMII_SPEED; + else if (phydev->speed == SPEED_100) + val |= MVPP2_GMAC_CONFIG_MII_SPEED; + + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + +} + /* Adjust link */ static void mvpp2_link_event(struct net_device *dev) { @@ -5745,24 +5776,7 @@ static void mvpp2_link_event(struct net_device *dev) if (phydev->link) { if ((port->speed != phydev->speed) || (port->duplex != phydev->duplex)) { - u32 val; - - val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); - val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | - MVPP2_GMAC_CONFIG_GMII_SPEED | - MVPP2_GMAC_CONFIG_FULL_DUPLEX | - MVPP2_GMAC_AN_SPEED_EN | - MVPP2_GMAC_AN_DUPLEX_EN); - - if (phydev->duplex) - val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; - - if (phydev->speed == SPEED_1000) - val |= MVPP2_GMAC_CONFIG_GMII_SPEED; - else if (phydev->speed == SPEED_100) - val |= MVPP2_GMAC_CONFIG_MII_SPEED; - - writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + mvpp2_gmac_set_autoneg(port, phydev); port->duplex = phydev->duplex; port->speed = phydev->speed; @@ -5773,10 +5787,16 @@ static void mvpp2_link_event(struct net_device *dev) port->link = phydev->link; if (phydev->link) { - val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); - val |= (MVPP2_GMAC_FORCE_LINK_PASS | - MVPP2_GMAC_FORCE_LINK_DOWN); - writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + if (port->phy_interface == PHY_INTERFACE_MODE_RGMII || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val |= (MVPP2_GMAC_FORCE_LINK_PASS | + MVPP2_GMAC_FORCE_LINK_DOWN); + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + } mvpp2_interrupts_enable(port); mvpp2_port_enable(port); -- cgit v1.2.3 From 89273bc060d8c7a70999736fe15b2fb3326d0da8 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Wed, 30 Aug 2017 10:29:19 +0200 Subject: net: mvpp2: dynamic reconfiguration of the comphy/GoP/MAC This patch adds logic to reconfigure the comphy/GoP/MAC when the link state is updated at runtime. This is very useful on boards where many link speed are supported: depending on what is negotiated the PPv2 driver will automatically reconfigures the link between the PHY and the MAC. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 670395235932..358a9de9daa7 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5771,9 +5771,28 @@ static void mvpp2_link_event(struct net_device *dev) { struct mvpp2_port *port = netdev_priv(dev); struct phy_device *phydev = dev->phydev; + bool link_reconfigured = false; u32 val; if (phydev->link) { + if (port->phy_interface != phydev->interface && port->comphy) { + /* disable current port for reconfiguration */ + mvpp2_interrupts_disable(port); + netif_carrier_off(port->dev); + mvpp2_port_disable(port); + phy_power_off(port->comphy); + + /* comphy reconfiguration */ + port->phy_interface = phydev->interface; + mvpp22_comphy_init(port); + + /* gop/mac reconfiguration */ + mvpp22_gop_init(port); + mvpp2_port_mii_set(port); + + link_reconfigured = true; + } + if ((port->speed != phydev->speed) || (port->duplex != phydev->duplex)) { mvpp2_gmac_set_autoneg(port, phydev); @@ -5783,7 +5802,7 @@ static void mvpp2_link_event(struct net_device *dev) } } - if (phydev->link != port->link) { + if (phydev->link != port->link || link_reconfigured) { port->link = phydev->link; if (phydev->link) { -- cgit v1.2.3 From dc8007e8fa88be6d92ee6e2371d16b5860496a2d Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 30 Aug 2017 14:55:08 +0530 Subject: net: bcm63xx_enet: make bcm_enetsw_ethtool_ops const Make this const as it is never modified. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 61a88b64bd39..4f3845a58126 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -2674,7 +2674,7 @@ static int bcm_enetsw_set_ringparam(struct net_device *dev, return 0; } -static struct ethtool_ops bcm_enetsw_ethtool_ops = { +static const struct ethtool_ops bcm_enetsw_ethtool_ops = { .get_strings = bcm_enetsw_get_strings, .get_sset_count = bcm_enetsw_get_sset_count, .get_ethtool_stats = bcm_enetsw_get_ethtool_stats, -- cgit v1.2.3 From 752b06946cc99534a7a4a11b7196e9a1e3d7e098 Mon Sep 17 00:00:00 2001 From: Salil Mehta Date: Wed, 30 Aug 2017 12:06:03 +0100 Subject: net: hns3: Fixes the wrong IS_ERR check on the returned phydev value This patch removes the wrong check being done for the phy device being returned by the mdiobus_get_phy() function. This function never returns the error pointers. Fixes: 256727da7395 ("net: hns3: Add MDIO support to HNS3 Ethernet Driver for hip08 SoC") Reported-by: Dan Carpenter Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c index a2add8bb1945..f32d719c4f77 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c @@ -150,7 +150,7 @@ int hclge_mac_mdio_config(struct hclge_dev *hdev) } phydev = mdiobus_get_phy(mdio_bus, mac->phy_addr); - if (!phydev || IS_ERR(phydev)) { + if (!phydev) { dev_err(mdio_bus->parent, "Failed to get phy device\n"); mdiobus_unregister(mdio_bus); return -EIO; -- cgit v1.2.3 From 9e4a56139d091713b8dff93632f2149f28494ca8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 30 Aug 2017 12:40:12 +0100 Subject: qed: fix spelling mistake: "calescing" -> "coalescing" Trivial fix to spelling mistake in DP_NOTICE message Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 9a1645852015..085338990f49 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -2805,7 +2805,7 @@ static int qed_get_coalesce(struct qed_dev *cdev, u16 *coal, void *handle) p_hwfn = p_cid->p_owner; rc = qed_get_queue_coalesce(p_hwfn, coal, handle); if (rc) - DP_NOTICE(p_hwfn, "Unable to read queue calescing\n"); + DP_NOTICE(p_hwfn, "Unable to read queue coalescing\n"); return rc; } -- cgit v1.2.3 From 888fcd9cd250ca5d956776e8bd48b2632a18bf32 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Tue, 15 Aug 2017 14:18:08 +0300 Subject: net/mlx5e: Use IP version matching to classify IP traffic This change adds the ability for flow steering to classify IPv4/6 packets with MPLS tag (Ethertype 0x8847 and 0x8848) as standard IP packets and hit IPv4/6 classification steering rules. Since IP packets with MPLS tag header have MPLS ethertype, they missed the IPv4/6 ethertype rule and ended up hitting the default filter forwarding all the packets to the same single RQ (No RSS). Since our device is able to look past the MPLS tag and identify the next protocol we introduce this solution which replaces ethertype matching by the device's capability to perform IP version identification and matching in order to distinguish between IPv4 and IPv6. Therefore, when driver is performing flow steering configuration on the device it will use IP version matching in IP classified rules instead of ethertype matching which will cause relevant MPLS tagged packets to hit this rule as well. If the device doesn't support IP version matching the driver will fall back to use legacy ethertype matching in the steering as before. Signed-off-by: Gal Pressman Signed-off-by: Ariel Levkovich Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 33 ++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index eecbc6d4f51f..85e6226dacfb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -660,6 +660,17 @@ static struct { }, }; +static u8 mlx5e_etype_to_ipv(u16 ethertype) +{ + if (ethertype == ETH_P_IP) + return 4; + + if (ethertype == ETH_P_IPV6) + return 6; + + return 0; +} + static struct mlx5_flow_handle * mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, struct mlx5_flow_table *ft, @@ -667,10 +678,12 @@ mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, u16 etype, u8 proto) { + int match_ipv_outer = MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, ft_field_support.outer_ip_version); MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; int err = 0; + u8 ipv; spec = kvzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) @@ -681,7 +694,13 @@ mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_protocol); MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_protocol, proto); } - if (etype) { + + ipv = mlx5e_etype_to_ipv(etype); + if (match_ipv_outer && ipv) { + spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_version); + MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_version, ipv); + } else if (etype) { spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ethertype); MLX5_SET(fte_match_param, spec->match_value, outer_headers.ethertype, etype); @@ -739,7 +758,9 @@ del_rules: #define MLX5E_TTC_TABLE_SIZE (MLX5E_TTC_GROUP1_SIZE +\ MLX5E_TTC_GROUP2_SIZE +\ MLX5E_TTC_GROUP3_SIZE) -static int mlx5e_create_ttc_table_groups(struct mlx5e_ttc_table *ttc) + +static int mlx5e_create_ttc_table_groups(struct mlx5e_ttc_table *ttc, + bool use_ipv) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); struct mlx5e_flow_table *ft = &ttc->ft; @@ -761,7 +782,10 @@ static int mlx5e_create_ttc_table_groups(struct mlx5e_ttc_table *ttc) /* L4 Group */ mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + if (use_ipv) + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_version); + else + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); MLX5_SET_CFG(in, start_flow_index, ix); ix += MLX5E_TTC_GROUP1_SIZE; @@ -812,6 +836,7 @@ void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv) int mlx5e_create_ttc_table(struct mlx5e_priv *priv) { + bool match_ipv_outer = MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, ft_field_support.outer_ip_version); struct mlx5e_ttc_table *ttc = &priv->fs.ttc; struct mlx5_flow_table_attr ft_attr = {}; struct mlx5e_flow_table *ft = &ttc->ft; @@ -828,7 +853,7 @@ int mlx5e_create_ttc_table(struct mlx5e_priv *priv) return err; } - err = mlx5e_create_ttc_table_groups(ttc); + err = mlx5e_create_ttc_table_groups(ttc, match_ipv_outer); if (err) goto err; -- cgit v1.2.3 From 2729984149e6a23e849a40e16fc3efdc07dd3668 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Sun, 13 Aug 2017 13:34:42 +0300 Subject: net/mlx5e: Support TSO and TX checksum offloads for GRE tunnels Add TX offloads support for GRE tunneled packets by reporting the needed netdev features. Signed-off-by: Gal Pressman Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 51 +++++++++++++++-------- include/linux/mlx5/mlx5_ifc.h | 2 +- 2 files changed, 34 insertions(+), 19 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index fdc2b92f020b..9475fb89a744 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3499,13 +3499,13 @@ static void mlx5e_del_vxlan_port(struct net_device *netdev, mlx5e_vxlan_queue_work(priv, ti->sa_family, be16_to_cpu(ti->port), 0); } -static netdev_features_t mlx5e_vxlan_features_check(struct mlx5e_priv *priv, - struct sk_buff *skb, - netdev_features_t features) +static netdev_features_t mlx5e_tunnel_features_check(struct mlx5e_priv *priv, + struct sk_buff *skb, + netdev_features_t features) { struct udphdr *udph; - u16 proto; - u16 port = 0; + u8 proto; + u16 port; switch (vlan_get_protocol(skb)) { case htons(ETH_P_IP): @@ -3518,14 +3518,17 @@ static netdev_features_t mlx5e_vxlan_features_check(struct mlx5e_priv *priv, goto out; } - if (proto == IPPROTO_UDP) { + switch (proto) { + case IPPROTO_GRE: + return features; + case IPPROTO_UDP: udph = udp_hdr(skb); port = be16_to_cpu(udph->dest); - } - /* Verify if UDP port is being offloaded by HW */ - if (port && mlx5e_vxlan_lookup_port(priv, port)) - return features; + /* Verify if UDP port is being offloaded by HW */ + if (mlx5e_vxlan_lookup_port(priv, port)) + return features; + } out: /* Disable CSUM and GSO if the udp dport is not offloaded by HW */ @@ -3549,7 +3552,7 @@ static netdev_features_t mlx5e_features_check(struct sk_buff *skb, /* Validate if the tunneled packet is being offloaded by HW */ if (skb->encapsulation && (features & NETIF_F_CSUM_MASK || features & NETIF_F_GSO_MASK)) - return mlx5e_vxlan_features_check(priv, skb, features); + return mlx5e_tunnel_features_check(priv, skb, features); return features; } @@ -4014,20 +4017,32 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; - if (mlx5e_vxlan_allowed(mdev)) { - netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | - NETIF_F_GSO_UDP_TUNNEL_CSUM | - NETIF_F_GSO_PARTIAL; + if (mlx5e_vxlan_allowed(mdev) || MLX5_CAP_ETH(mdev, tunnel_stateless_gre)) { + netdev->hw_features |= NETIF_F_GSO_PARTIAL; netdev->hw_enc_features |= NETIF_F_IP_CSUM; netdev->hw_enc_features |= NETIF_F_IPV6_CSUM; netdev->hw_enc_features |= NETIF_F_TSO; netdev->hw_enc_features |= NETIF_F_TSO6; - netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL; - netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM | - NETIF_F_GSO_PARTIAL; + netdev->hw_enc_features |= NETIF_F_GSO_PARTIAL; + } + + if (mlx5e_vxlan_allowed(mdev)) { + netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; netdev->gso_partial_features = NETIF_F_GSO_UDP_TUNNEL_CSUM; } + if (MLX5_CAP_ETH(mdev, tunnel_stateless_gre)) { + netdev->hw_features |= NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM; + netdev->hw_enc_features |= NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM; + netdev->gso_partial_features |= NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM; + } + mlx5_query_port_fcs(mdev, &fcs_supported, &fcs_enabled); if (fcs_supported) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index ae7d09b9c52f..3d5d32e5446c 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -602,7 +602,7 @@ struct mlx5_ifc_per_protocol_networking_offload_caps_bits { u8 reserved_at_1a[0x1]; u8 tunnel_lso_const_out_ip_id[0x1]; u8 reserved_at_1c[0x2]; - u8 tunnel_statless_gre[0x1]; + u8 tunnel_stateless_gre[0x1]; u8 tunnel_stateless_vxlan[0x1]; u8 swp[0x1]; -- cgit v1.2.3 From 7b3722fa9ef647eb1ae6a60a5d46f7c67ab09a33 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Sun, 13 Aug 2017 16:22:38 +0300 Subject: net/mlx5e: Support RSS for GRE tunneled packets Introduce a new flow table and indirect TIRs which are used to hash the inner packet headers of GRE tunneled packets. When a GRE tunneled packet is received, the TTC flow table will match the new IPv4/6->GRE rules which will forward it to the inner TTC table. The inner TTC is similar to its counterpart outer TTC table, but matching the inner packet headers instead of the outer ones (and does not include the new IPv4/6->GRE rules). The new rules will not add steering hops since they are added to an already existing flow group which will be matched regardless of this patch. Non GRE traffic will not be affected. The inner flow table will forward the packet to inner indirect TIRs which hash the inner packet and thus result in RSS for the tunneled packets. Testing 8 TCP streams bandwidth over GRE: System: Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz NIC: Mellanox Technologies MT28800 Family [ConnectX-5 Ex] Before: 21.3 Gbps (Single RQ) Now : 90.5 Gbps (RSS spread on 8 RQs) Signed-off-by: Gal Pressman Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 18 +- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 11 +- drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 248 ++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 57 ++++- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 4 +- 5 files changed, 321 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 0039b4725405..a31912415264 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -620,6 +620,12 @@ enum mlx5e_traffic_types { MLX5E_NUM_INDIR_TIRS = MLX5E_TT_ANY, }; +enum mlx5e_tunnel_types { + MLX5E_TT_IPV4_GRE, + MLX5E_TT_IPV6_GRE, + MLX5E_NUM_TUNNEL_TT, +}; + enum { MLX5E_STATE_ASYNC_EVENTS_ENABLED, MLX5E_STATE_OPENED, @@ -679,6 +685,7 @@ struct mlx5e_l2_table { struct mlx5e_ttc_table { struct mlx5e_flow_table ft; struct mlx5_flow_handle *rules[MLX5E_NUM_TT]; + struct mlx5_flow_handle *tunnel_rules[MLX5E_NUM_TUNNEL_TT]; }; #define ARFS_HASH_SHIFT BITS_PER_BYTE @@ -711,6 +718,7 @@ enum { MLX5E_VLAN_FT_LEVEL = 0, MLX5E_L2_FT_LEVEL, MLX5E_TTC_FT_LEVEL, + MLX5E_INNER_TTC_FT_LEVEL, MLX5E_ARFS_FT_LEVEL }; @@ -736,6 +744,7 @@ struct mlx5e_flow_steering { struct mlx5e_vlan_table vlan; struct mlx5e_l2_table l2; struct mlx5e_ttc_table ttc; + struct mlx5e_ttc_table inner_ttc; struct mlx5e_arfs_tables arfs; }; @@ -769,6 +778,7 @@ struct mlx5e_priv { u32 tisn[MLX5E_MAX_NUM_TC]; struct mlx5e_rqt indir_rqt; struct mlx5e_tir indir_tir[MLX5E_NUM_INDIR_TIRS]; + struct mlx5e_tir inner_indir_tir[MLX5E_NUM_INDIR_TIRS]; struct mlx5e_tir direct_tir[MLX5E_MAX_NUM_CHANNELS]; u32 tx_rates[MLX5E_MAX_NUM_SQS]; int hard_mtu; @@ -903,7 +913,7 @@ int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz, struct mlx5e_redirect_rqt_param rrp); void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params, enum mlx5e_traffic_types tt, - void *tirc); + void *tirc, bool inner); int mlx5e_open_locked(struct net_device *netdev); int mlx5e_close_locked(struct net_device *netdev); @@ -932,6 +942,12 @@ void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, void mlx5e_set_rq_type_params(struct mlx5_core_dev *mdev, struct mlx5e_params *params, u8 rq_type); +static inline bool mlx5e_tunnel_inner_ft_supported(struct mlx5_core_dev *mdev) +{ + return (MLX5_CAP_ETH(mdev, tunnel_stateless_gre) && + MLX5_CAP_FLOWTABLE_NIC_RX(mdev, ft_field_support.inner_ip_version)); +} + static inline struct mlx5e_tx_wqe *mlx5e_post_nop(struct mlx5_wq_cyc *wq, u32 sqn, u16 *pc) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 0dd7e9caf150..c6ec90e9c95b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -1212,9 +1212,18 @@ static void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen) for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) { memset(tirc, 0, ctxlen); - mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc); + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false); mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in, inlen); } + + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return; + + for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) { + memset(tirc, 0, ctxlen); + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, true); + mlx5_core_modify_tir(mdev, priv->inner_indir_tir[tt].tirn, in, inlen); + } } static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index 85e6226dacfb..f11fd07ac4dd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -608,12 +608,21 @@ static void mlx5e_cleanup_ttc_rules(struct mlx5e_ttc_table *ttc) ttc->rules[i] = NULL; } } + + for (i = 0; i < MLX5E_NUM_TUNNEL_TT; i++) { + if (!IS_ERR_OR_NULL(ttc->tunnel_rules[i])) { + mlx5_del_flow_rules(ttc->tunnel_rules[i]); + ttc->tunnel_rules[i] = NULL; + } + } } -static struct { +struct mlx5e_etype_proto { u16 etype; u8 proto; -} ttc_rules[] = { +}; + +static struct mlx5e_etype_proto ttc_rules[] = { [MLX5E_TT_IPV4_TCP] = { .etype = ETH_P_IP, .proto = IPPROTO_TCP, @@ -660,6 +669,17 @@ static struct { }, }; +static struct mlx5e_etype_proto ttc_tunnel_rules[] = { + [MLX5E_TT_IPV4_GRE] = { + .etype = ETH_P_IP, + .proto = IPPROTO_GRE, + }, + [MLX5E_TT_IPV6_GRE] = { + .etype = ETH_P_IPV6, + .proto = IPPROTO_GRE, + }, +}; + static u8 mlx5e_etype_to_ipv(u16 ethertype) { if (ethertype == ETH_P_IP) @@ -742,6 +762,20 @@ static int mlx5e_generate_ttc_table_rules(struct mlx5e_priv *priv) goto del_rules; } + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return 0; + + rules = ttc->tunnel_rules; + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = priv->fs.inner_ttc.ft.t; + for (tt = 0; tt < MLX5E_NUM_TUNNEL_TT; tt++) { + rules[tt] = mlx5e_generate_ttc_rule(priv, ft, &dest, + ttc_tunnel_rules[tt].etype, + ttc_tunnel_rules[tt].proto); + if (IS_ERR(rules[tt])) + goto del_rules; + } + return 0; del_rules: @@ -752,13 +786,21 @@ del_rules: } #define MLX5E_TTC_NUM_GROUPS 3 -#define MLX5E_TTC_GROUP1_SIZE BIT(3) -#define MLX5E_TTC_GROUP2_SIZE BIT(1) -#define MLX5E_TTC_GROUP3_SIZE BIT(0) +#define MLX5E_TTC_GROUP1_SIZE (BIT(3) + MLX5E_NUM_TUNNEL_TT) +#define MLX5E_TTC_GROUP2_SIZE BIT(1) +#define MLX5E_TTC_GROUP3_SIZE BIT(0) #define MLX5E_TTC_TABLE_SIZE (MLX5E_TTC_GROUP1_SIZE +\ MLX5E_TTC_GROUP2_SIZE +\ MLX5E_TTC_GROUP3_SIZE) +#define MLX5E_INNER_TTC_NUM_GROUPS 3 +#define MLX5E_INNER_TTC_GROUP1_SIZE BIT(3) +#define MLX5E_INNER_TTC_GROUP2_SIZE BIT(1) +#define MLX5E_INNER_TTC_GROUP3_SIZE BIT(0) +#define MLX5E_INNER_TTC_TABLE_SIZE (MLX5E_INNER_TTC_GROUP1_SIZE +\ + MLX5E_INNER_TTC_GROUP2_SIZE +\ + MLX5E_INNER_TTC_GROUP3_SIZE) + static int mlx5e_create_ttc_table_groups(struct mlx5e_ttc_table *ttc, bool use_ipv) { @@ -826,6 +868,190 @@ err: return err; } +static struct mlx5_flow_handle * +mlx5e_generate_inner_ttc_rule(struct mlx5e_priv *priv, + struct mlx5_flow_table *ft, + struct mlx5_flow_destination *dest, + u16 etype, u8 proto) +{ + MLX5_DECLARE_FLOW_ACT(flow_act); + struct mlx5_flow_handle *rule; + struct mlx5_flow_spec *spec; + int err = 0; + u8 ipv; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return ERR_PTR(-ENOMEM); + + ipv = mlx5e_etype_to_ipv(etype); + if (etype && ipv) { + spec->match_criteria_enable = MLX5_MATCH_INNER_HEADERS; + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, inner_headers.ip_version); + MLX5_SET(fte_match_param, spec->match_value, inner_headers.ip_version, ipv); + } + + if (proto) { + spec->match_criteria_enable = MLX5_MATCH_INNER_HEADERS; + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, inner_headers.ip_protocol); + MLX5_SET(fte_match_param, spec->match_value, inner_headers.ip_protocol, proto); + } + + rule = mlx5_add_flow_rules(ft, spec, &flow_act, dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(priv->netdev, "%s: add rule failed\n", __func__); + } + + kvfree(spec); + return err ? ERR_PTR(err) : rule; +} + +static int mlx5e_generate_inner_ttc_table_rules(struct mlx5e_priv *priv) +{ + struct mlx5_flow_destination dest; + struct mlx5_flow_handle **rules; + struct mlx5e_ttc_table *ttc; + struct mlx5_flow_table *ft; + int err; + int tt; + + ttc = &priv->fs.inner_ttc; + ft = ttc->ft.t; + rules = ttc->rules; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; + for (tt = 0; tt < MLX5E_NUM_TT; tt++) { + if (tt == MLX5E_TT_ANY) + dest.tir_num = priv->direct_tir[0].tirn; + else + dest.tir_num = priv->inner_indir_tir[tt].tirn; + + rules[tt] = mlx5e_generate_inner_ttc_rule(priv, ft, &dest, + ttc_rules[tt].etype, + ttc_rules[tt].proto); + if (IS_ERR(rules[tt])) + goto del_rules; + } + + return 0; + +del_rules: + err = PTR_ERR(rules[tt]); + rules[tt] = NULL; + mlx5e_cleanup_ttc_rules(ttc); + return err; +} + +static int mlx5e_create_inner_ttc_table_groups(struct mlx5e_ttc_table *ttc) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5e_flow_table *ft = &ttc->ft; + int ix = 0; + u32 *in; + int err; + u8 *mc; + + ft->g = kcalloc(MLX5E_INNER_TTC_NUM_GROUPS, sizeof(*ft->g), GFP_KERNEL); + if (!ft->g) + return -ENOMEM; + in = kvzalloc(inlen, GFP_KERNEL); + if (!in) { + kfree(ft->g); + return -ENOMEM; + } + + /* L4 Group */ + mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + MLX5_SET_TO_ONES(fte_match_param, mc, inner_headers.ip_protocol); + MLX5_SET_TO_ONES(fte_match_param, mc, inner_headers.ip_version); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_INNER_HEADERS); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_INNER_TTC_GROUP1_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + /* L3 Group */ + MLX5_SET(fte_match_param, mc, inner_headers.ip_protocol, 0); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_INNER_TTC_GROUP2_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + /* Any Group */ + memset(in, 0, inlen); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_INNER_TTC_GROUP3_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + kvfree(in); + return 0; + +err: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + kvfree(in); + + return err; +} + +static int mlx5e_create_inner_ttc_table(struct mlx5e_priv *priv) +{ + struct mlx5e_ttc_table *ttc = &priv->fs.inner_ttc; + struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5e_flow_table *ft = &ttc->ft; + int err; + + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return 0; + + ft_attr.max_fte = MLX5E_INNER_TTC_TABLE_SIZE; + ft_attr.level = MLX5E_INNER_TTC_FT_LEVEL; + ft_attr.prio = MLX5E_NIC_PRIO; + + ft->t = mlx5_create_flow_table(priv->fs.ns, &ft_attr); + if (IS_ERR(ft->t)) { + err = PTR_ERR(ft->t); + ft->t = NULL; + return err; + } + + err = mlx5e_create_inner_ttc_table_groups(ttc); + if (err) + goto err; + + err = mlx5e_generate_inner_ttc_table_rules(priv); + if (err) + goto err; + + return 0; + +err: + mlx5e_destroy_flow_table(ft); + return err; +} + +static void mlx5e_destroy_inner_ttc_table(struct mlx5e_priv *priv) +{ + struct mlx5e_ttc_table *ttc = &priv->fs.inner_ttc; + + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return; + + mlx5e_cleanup_ttc_rules(ttc); + mlx5e_destroy_flow_table(&ttc->ft); +} + void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv) { struct mlx5e_ttc_table *ttc = &priv->fs.ttc; @@ -1179,11 +1405,18 @@ int mlx5e_create_flow_steering(struct mlx5e_priv *priv) priv->netdev->hw_features &= ~NETIF_F_NTUPLE; } + err = mlx5e_create_inner_ttc_table(priv); + if (err) { + netdev_err(priv->netdev, "Failed to create inner ttc table, err=%d\n", + err); + goto err_destroy_arfs_tables; + } + err = mlx5e_create_ttc_table(priv); if (err) { netdev_err(priv->netdev, "Failed to create ttc table, err=%d\n", err); - goto err_destroy_arfs_tables; + goto err_destroy_inner_ttc_table; } err = mlx5e_create_l2_table(priv); @@ -1208,6 +1441,8 @@ err_destroy_l2_table: mlx5e_destroy_l2_table(priv); err_destroy_ttc_table: mlx5e_destroy_ttc_table(priv); +err_destroy_inner_ttc_table: + mlx5e_destroy_inner_ttc_table(priv); err_destroy_arfs_tables: mlx5e_arfs_destroy_tables(priv); @@ -1219,6 +1454,7 @@ void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv) mlx5e_destroy_vlan_table(priv); mlx5e_destroy_l2_table(priv); mlx5e_destroy_ttc_table(priv); + mlx5e_destroy_inner_ttc_table(priv); mlx5e_arfs_destroy_tables(priv); mlx5e_ethtool_cleanup_steering(priv); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 9475fb89a744..111c7523d448 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2349,9 +2349,10 @@ static void mlx5e_build_tir_ctx_lro(struct mlx5e_params *params, void *tirc) void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params, enum mlx5e_traffic_types tt, - void *tirc) + void *tirc, bool inner) { - void *hfso = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer); + void *hfso = inner ? MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_inner) : + MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer); #define MLX5_HASH_IP (MLX5_HASH_FIELD_SEL_SRC_IP |\ MLX5_HASH_FIELD_SEL_DST_IP) @@ -2500,6 +2501,21 @@ free_in: return err; } +static void mlx5e_build_inner_indir_tir_ctx(struct mlx5e_priv *priv, + enum mlx5e_traffic_types tt, + u32 *tirc) +{ + MLX5_SET(tirc, tirc, transport_domain, priv->mdev->mlx5e_res.td.tdn); + + mlx5e_build_tir_ctx_lro(&priv->channels.params, tirc); + + MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT); + MLX5_SET(tirc, tirc, indirect_table, priv->indir_rqt.rqtn); + MLX5_SET(tirc, tirc, tunneled_offload_en, 0x1); + + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, true); +} + static int mlx5e_set_mtu(struct mlx5e_priv *priv, u16 mtu) { struct mlx5_core_dev *mdev = priv->mdev; @@ -2865,7 +2881,7 @@ static void mlx5e_build_indir_tir_ctx(struct mlx5e_priv *priv, MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT); MLX5_SET(tirc, tirc, indirect_table, priv->indir_rqt.rqtn); - mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc); + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false); } static void mlx5e_build_direct_tir_ctx(struct mlx5e_priv *priv, u32 rqtn, u32 *tirc) @@ -2884,6 +2900,7 @@ int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv) struct mlx5e_tir *tir; void *tirc; int inlen; + int i = 0; int err; u32 *in; int tt; @@ -2899,16 +2916,36 @@ int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv) tirc = MLX5_ADDR_OF(create_tir_in, in, ctx); mlx5e_build_indir_tir_ctx(priv, tt, tirc); err = mlx5e_create_tir(priv->mdev, tir, in, inlen); - if (err) - goto err_destroy_tirs; + if (err) { + mlx5_core_warn(priv->mdev, "create indirect tirs failed, %d\n", err); + goto err_destroy_inner_tirs; + } } + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + goto out; + + for (i = 0; i < MLX5E_NUM_INDIR_TIRS; i++) { + memset(in, 0, inlen); + tir = &priv->inner_indir_tir[i]; + tirc = MLX5_ADDR_OF(create_tir_in, in, ctx); + mlx5e_build_inner_indir_tir_ctx(priv, i, tirc); + err = mlx5e_create_tir(priv->mdev, tir, in, inlen); + if (err) { + mlx5_core_warn(priv->mdev, "create inner indirect tirs failed, %d\n", err); + goto err_destroy_inner_tirs; + } + } + +out: kvfree(in); return 0; -err_destroy_tirs: - mlx5_core_warn(priv->mdev, "create indirect tirs failed, %d\n", err); +err_destroy_inner_tirs: + for (i--; i >= 0; i--) + mlx5e_destroy_tir(priv->mdev, &priv->inner_indir_tir[i]); + for (tt--; tt >= 0; tt--) mlx5e_destroy_tir(priv->mdev, &priv->indir_tir[tt]); @@ -2962,6 +2999,12 @@ void mlx5e_destroy_indirect_tirs(struct mlx5e_priv *priv) for (i = 0; i < MLX5E_NUM_INDIR_TIRS; i++) mlx5e_destroy_tir(priv->mdev, &priv->indir_tir[i]); + + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return; + + for (i = 0; i < MLX5E_NUM_INDIR_TIRS; i++) + mlx5e_destroy_tir(priv->mdev, &priv->inner_indir_tir[i]); } void mlx5e_destroy_direct_tirs(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index d731d57a996a..5a7bea688ec8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -83,8 +83,8 @@ #define ETHTOOL_PRIO_NUM_LEVELS 1 #define ETHTOOL_NUM_PRIOS 11 #define ETHTOOL_MIN_LEVEL (KERNEL_MIN_LEVEL + ETHTOOL_NUM_PRIOS) -/* Vlan, mac, ttc, aRFS */ -#define KERNEL_NIC_PRIO_NUM_LEVELS 4 +/* Vlan, mac, ttc, inner ttc, aRFS */ +#define KERNEL_NIC_PRIO_NUM_LEVELS 5 #define KERNEL_NIC_NUM_PRIOS 1 /* One more level for tc */ #define KERNEL_MIN_LEVEL (KERNEL_NIC_PRIO_NUM_LEVELS + 1) -- cgit v1.2.3 From 62f3250f3d18ea7d4b4e51ac98cdca29515683a5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 30 Aug 2017 10:32:58 -0700 Subject: xen-netfront: be more drop monitor friendly xennet_start_xmit() might copy skb with inappropriate layout into a fresh one. Old skb is freed, and at this point it is not a drop, but a consume. New skb will then be either consumed or dropped. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/xen-netfront.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 7b61adb6270c..523387e71a80 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -611,7 +611,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) nskb = skb_copy(skb, GFP_ATOMIC); if (!nskb) goto drop; - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); skb = nskb; page = virt_to_page(skb->data); offset = offset_in_page(skb->data); -- cgit v1.2.3 From acfb98b99647aa7dc7c111db52d5f4199d2b641f Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Wed, 30 Aug 2017 16:19:53 -0700 Subject: liquidio: fix crash in presence of zeroed-out base address regs Fix crash in linux PF driver when BARs have been cleared/de-programmed; fail early init (prior to mapping BARs) if the BAR0 or BAR1 registers are zero. This situation can arise when the PF is added to a VM (PCI pass-through), then a PF FLR is issued (in the VM). After this occurs, the BAR registers will be zero. If we attempt to load the PF driver in the host (after VM has been shutdown), the host can reset. Signed-off-by: Rick Farrington Signed-off-by: Raghu Vatsavayi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../net/ethernet/cavium/liquidio/cn23xx_pf_device.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index f6c0bad78cd4..e8b290473ee2 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -1305,6 +1305,26 @@ static int cn23xx_sriov_config(struct octeon_device *oct) int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) { + u32 data32; + u64 BAR0, BAR1; + + pci_read_config_dword(oct->pci_dev, PCI_BASE_ADDRESS_0, &data32); + BAR0 = (u64)(data32 & ~0xf); + pci_read_config_dword(oct->pci_dev, PCI_BASE_ADDRESS_1, &data32); + BAR0 |= ((u64)data32 << 32); + pci_read_config_dword(oct->pci_dev, PCI_BASE_ADDRESS_2, &data32); + BAR1 = (u64)(data32 & ~0xf); + pci_read_config_dword(oct->pci_dev, PCI_BASE_ADDRESS_3, &data32); + BAR1 |= ((u64)data32 << 32); + + if (!BAR0 || !BAR1) { + if (!BAR0) + dev_err(&oct->pci_dev->dev, "device BAR0 unassigned\n"); + if (!BAR1) + dev_err(&oct->pci_dev->dev, "device BAR1 unassigned\n"); + return 1; + } + if (octeon_map_pci_barx(oct, 0, 0)) return 1; -- cgit v1.2.3 From c9353bf483d3724c116a9d502c0ead9cec54a61a Mon Sep 17 00:00:00 2001 From: Ryan Hsu Date: Tue, 22 Aug 2017 14:44:02 -0700 Subject: ath10k: fix napi_poll budget overflow In napi_poll, the budget number is used to control the amount of packets we should handle per poll to balance the resource in the system. In the list of the amsdu packets reception, we check if there is budget count left and handle the complete list of the packets, that it will have chances the very last list will over the budget leftover. So adding one more parameter - budget_left, this would help while traversing the list to avoid handling more than the budget given. Reported-by: Andrey Ryabinin Fix-suggested-by: Igor Mitsyanko Link: https://lkml.kernel.org/r/26670dce-4dd2-f8e4-0e14-90d74257e739@virtuozzo.com Signed-off-by: Ryan Hsu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 9323be663179..a3f5dc78353f 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1745,7 +1745,8 @@ static void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp) } static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list, - struct sk_buff_head *amsdu) + struct sk_buff_head *amsdu, + int budget_left) { struct sk_buff *msdu; struct htt_rx_desc *rxd; @@ -1756,8 +1757,9 @@ static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list, if (WARN_ON(!skb_queue_empty(amsdu))) return -EINVAL; - while ((msdu = __skb_dequeue(list))) { + while ((msdu = __skb_dequeue(list)) && budget_left) { __skb_queue_tail(amsdu, msdu); + budget_left--; rxd = (void *)msdu->data - sizeof(*rxd); if (rxd->msdu_end.common.info0 & @@ -1848,7 +1850,8 @@ static int ath10k_htt_rx_h_rx_offload(struct ath10k *ar, return num_msdu; } -static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb, + int budget_left) { struct ath10k_htt *htt = &ar->htt; struct htt_resp *resp = (void *)skb->data; @@ -1905,9 +1908,9 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) if (offload) num_msdus = ath10k_htt_rx_h_rx_offload(ar, &list); - while (!skb_queue_empty(&list)) { + while (!skb_queue_empty(&list) && budget_left) { __skb_queue_head_init(&amsdu); - ret = ath10k_htt_rx_extract_amsdu(&list, &amsdu); + ret = ath10k_htt_rx_extract_amsdu(&list, &amsdu, budget_left); switch (ret) { case 0: /* Note: The in-order indication may report interleaved @@ -1917,6 +1920,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) * should still give an idea about rx rate to the user. */ num_msdus += skb_queue_len(&amsdu); + budget_left -= skb_queue_len(&amsdu); ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id); ath10k_htt_rx_h_filter(ar, &amsdu, status); ath10k_htt_rx_h_mpdu(ar, &amsdu, status); @@ -2559,7 +2563,8 @@ int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget) } spin_lock_bh(&htt->rx_ring.lock); - num_rx_msdus = ath10k_htt_rx_in_ord_ind(ar, skb); + num_rx_msdus = ath10k_htt_rx_in_ord_ind(ar, skb, + (budget - quota)); spin_unlock_bh(&htt->rx_ring.lock); if (num_rx_msdus < 0) { resched_napi = true; -- cgit v1.2.3 From 30b83fff6d977da43b103e1ae9b5c67956b0ebda Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Wed, 23 Aug 2017 21:32:58 +0200 Subject: ath10k: sdio: remove unused struct member irq_wq in struct ath10k_sdio is a remnant from an earlier version of the sdio patchset. Its use was removed as a result of Kalle's review, but somehow the struct member survived. It is not used and can therefore safely be removed. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/sdio.c | 4 ---- drivers/net/wireless/ath/ath10k/sdio.h | 2 -- 2 files changed, 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 48268f02bc07..03a69e5b1116 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -1344,8 +1344,6 @@ static void ath10k_sdio_irq_handler(struct sdio_func *func) sdio_claim_host(ar_sdio->func); - wake_up(&ar_sdio->irq_wq); - if (ret && ret != -ECANCELED) ath10k_warn(ar, "failed to process pending SDIO interrupts: %d\n", ret); @@ -2000,8 +1998,6 @@ static int ath10k_sdio_probe(struct sdio_func *func, goto err_free_bmi_buf; } - init_waitqueue_head(&ar_sdio->irq_wq); - for (i = 0; i < ATH10K_SDIO_BUS_REQUEST_MAX_NUM; i++) ath10k_sdio_free_bus_req(ar, &ar_sdio->bus_req[i]); diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h index 3f61c67c601d..4ff7b545293b 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.h +++ b/drivers/net/wireless/ath/ath10k/sdio.h @@ -210,8 +210,6 @@ struct ath10k_sdio { /* temporary buffer for BMI requests */ u8 *bmi_buf; - wait_queue_head_t irq_wq; - bool is_disabled; struct workqueue_struct *workqueue; -- cgit v1.2.3 From c0cc00f250e19c717fc9cdbdb7f55aaa569c7498 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 24 Aug 2017 23:06:41 +0200 Subject: ath10k: activate user space firmware loading again In commit 9f5bcfe93315 ("ath10k: silence firmware file probing warnings") the firmware loading was changed from request_firmware() to request_firmware_direct() to silence some warnings in case it fails. request_firmware_direct() directly searches in the file system only and does not send a hotplug event to user space in case it could not find the firmware directly. In LEDE we use a user space script to extract the calibration data from the flash memory which gets triggered by the hotplug event. This way the firmware gets extracted from some vendor specific partition when the driver requests this firmware. This mechanism does not work any more after this change. Fixes: 9f5bcfe93315 ("ath10k: silence firmware file probing warnings") Signed-off-by: Hauke Mehrtens Cc: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 9b4c4facf725..a4f635820f35 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -519,7 +519,7 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, dir = "."; snprintf(filename, sizeof(filename), "%s/%s", dir, file); - ret = request_firmware_direct(&fw, filename, ar->dev); + ret = request_firmware(&fw, filename, ar->dev); ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot fw request '%s': %d\n", filename, ret); -- cgit v1.2.3 From 40cbd88be702a2455b6e047dc13f5d51ed542063 Mon Sep 17 00:00:00 2001 From: Lior David Date: Mon, 28 Aug 2017 22:18:41 +0300 Subject: wil6210: ratelimit errors in TX/RX interrupts Sometimes there is a firmware crash but the hardware is not fully stopped and continue to send TX/RX interrupts. This can cause an overload of messages which can bring the host down. Add ratelimit to these error messages to fix this. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/interrupt.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index cad8a95c4e4e..59def4f3fcf3 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -244,7 +244,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); if (unlikely(!isr)) { - wil_err(wil, "spurious IRQ: RX\n"); + wil_err_ratelimited(wil, "spurious IRQ: RX\n"); return IRQ_NONE; } @@ -269,11 +269,12 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) need_unmask = false; napi_schedule(&wil->napi_rx); } else { - wil_err(wil, + wil_err_ratelimited( + wil, "Got Rx interrupt while stopping interface\n"); } } else { - wil_err(wil, "Got Rx interrupt while in reset\n"); + wil_err_ratelimited(wil, "Got Rx interrupt while in reset\n"); } } @@ -302,7 +303,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); if (unlikely(!isr)) { - wil_err(wil, "spurious IRQ: TX\n"); + wil_err_ratelimited(wil, "spurious IRQ: TX\n"); return IRQ_NONE; } @@ -318,12 +319,13 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) need_unmask = false; napi_schedule(&wil->napi_tx); } else { - wil_err(wil, "Got Tx interrupt while in reset\n"); + wil_err_ratelimited(wil, "Got Tx interrupt while in reset\n"); } } if (unlikely(isr)) - wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); + wil_err_ratelimited(wil, "un-handled TX ISR bits 0x%08x\n", + isr); /* Tx IRQ will be enabled when NAPI processing finished */ -- cgit v1.2.3 From 5f591dac9f20f76439df2e18570765d384a94070 Mon Sep 17 00:00:00 2001 From: Gidon Studinski Date: Mon, 28 Aug 2017 22:18:42 +0300 Subject: wil6210: make debugfs compilation optional Since debugfs is a kernel configuration option, enable the driver to compile without debugfs. Signed-off-by: Gidon Studinski Signed-off-by: Hamad Kadmany Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/Kconfig | 12 ++++++++++++ drivers/net/wireless/ath/wil6210/Makefile | 2 +- drivers/net/wireless/ath/wil6210/wil6210.h | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig index 6dfedc8bd6a3..b448926b0c0f 100644 --- a/drivers/net/wireless/ath/wil6210/Kconfig +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -40,3 +40,15 @@ config WIL6210_TRACING option if you are interested in debugging the driver. If unsure, say Y to make it easier to debug problems. + +config WIL6210_DEBUGFS + bool "wil6210 debugfs support" + depends on WIL6210 + depends on DEBUG_FS + default y + ---help--- + Say Y here to enable wil6210 debugfs support, using the + kernel debugfs infrastructure. Select this + option if you are interested in debugging the driver. + + If unsure, say Y to make it easier to debug problems. diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index 4ae21da78e9e..d27efe83748b 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -4,7 +4,7 @@ wil6210-y := main.o wil6210-y += netdev.o wil6210-y += cfg80211.o wil6210-y += pcie_bus.o -wil6210-y += debugfs.o +wil6210-$(CONFIG_WIL6210_DEBUGFS) += debugfs.o wil6210-y += wmi.o wil6210-y += interrupt.o wil6210-y += txrx.o diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 73dbd62dfed7..a8c2680e70b4 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -940,8 +940,14 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, u64 *cookie); +#if defined(CONFIG_WIL6210_DEBUGFS) int wil6210_debugfs_init(struct wil6210_priv *wil); void wil6210_debugfs_remove(struct wil6210_priv *wil); +#else +static inline int wil6210_debugfs_init(struct wil6210_priv *wil) { return 0; } +static inline void wil6210_debugfs_remove(struct wil6210_priv *wil) {} +#endif + int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, struct station_info *sinfo); -- cgit v1.2.3 From 761f8e4d463e1cbf6da0d228bc74cecc800d4de5 Mon Sep 17 00:00:00 2001 From: Lazar Alexei Date: Mon, 28 Aug 2017 22:18:43 +0300 Subject: wil6210: align to latest auto generated wmi.h Align to latest version of the auto generated wmi file describing the interface with FW. Signed-off-by: Lazar Alexei Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.h | 714 ++++++++++++++++++++++----------- 1 file changed, 475 insertions(+), 239 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 1b426d7ef81f..5263ee717a4f 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -36,6 +36,11 @@ #define WMI_PROX_RANGE_NUM (3) #define WMI_MAX_LOSS_DMG_BEACONS (20) #define MAX_NUM_OF_SECTORS (128) +#define WMI_SCHED_MAX_ALLOCS_PER_CMD (4) +#define WMI_RF_DTYPE_LENGTH (3) +#define WMI_RF_ETYPE_LENGTH (3) +#define WMI_RF_RX2TX_LENGTH (3) +#define WMI_RF_ETYPE_VAL_PER_RANGE (5) /* Mailbox interface * used for commands and events @@ -52,15 +57,20 @@ enum wmi_mid { * the host */ enum wmi_fw_capability { - WMI_FW_CAPABILITY_FTM = 0, - WMI_FW_CAPABILITY_PS_CONFIG = 1, - WMI_FW_CAPABILITY_RF_SECTORS = 2, - WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3, - WMI_FW_CAPABILITY_DISABLE_AP_SME = 4, - WMI_FW_CAPABILITY_WMI_ONLY = 5, - WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7, - WMI_FW_CAPABILITY_D3_SUSPEND = 8, - WMI_FW_CAPABILITY_RSSI_REPORTING = 12, + WMI_FW_CAPABILITY_FTM = 0, + WMI_FW_CAPABILITY_PS_CONFIG = 1, + WMI_FW_CAPABILITY_RF_SECTORS = 2, + WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3, + WMI_FW_CAPABILITY_DISABLE_AP_SME = 4, + WMI_FW_CAPABILITY_WMI_ONLY = 5, + WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7, + WMI_FW_CAPABILITY_D3_SUSPEND = 8, + WMI_FW_CAPABILITY_LONG_RANGE = 9, + WMI_FW_CAPABILITY_FIXED_SCHEDULING = 10, + WMI_FW_CAPABILITY_MULTI_DIRECTED_OMNIS = 11, + WMI_FW_CAPABILITY_RSSI_REPORTING = 12, + WMI_FW_CAPABILITY_SET_SILENT_RSSI_TABLE = 13, + WMI_FW_CAPABILITY_LO_POWER_CALIB_FROM_OTP = 14, WMI_FW_CAPABILITY_MAX, }; @@ -80,6 +90,7 @@ enum wmi_command_id { WMI_START_SCAN_CMDID = 0x07, WMI_SET_BSS_FILTER_CMDID = 0x09, WMI_SET_PROBED_SSID_CMDID = 0x0A, + /* deprecated */ WMI_SET_LISTEN_INT_CMDID = 0x0B, WMI_BCON_CTRL_CMDID = 0x0F, WMI_ADD_CIPHER_KEY_CMDID = 0x16, @@ -94,26 +105,28 @@ enum wmi_command_id { WMI_ECHO_CMDID = 0x803, WMI_DEEP_ECHO_CMDID = 0x804, WMI_CONFIG_MAC_CMDID = 0x805, + /* deprecated */ WMI_CONFIG_PHY_DEBUG_CMDID = 0x806, WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x808, WMI_PHY_GET_STATISTICS_CMDID = 0x809, + /* deprecated */ WMI_FS_TUNE_CMDID = 0x80A, + /* deprecated */ WMI_CORR_MEASURE_CMDID = 0x80B, WMI_READ_RSSI_CMDID = 0x80C, WMI_TEMP_SENSE_CMDID = 0x80E, WMI_DC_CALIB_CMDID = 0x80F, + /* deprecated */ WMI_SEND_TONE_CMDID = 0x810, + /* deprecated */ WMI_IQ_TX_CALIB_CMDID = 0x811, + /* deprecated */ WMI_IQ_RX_CALIB_CMDID = 0x812, - WMI_SET_UCODE_IDLE_CMDID = 0x813, WMI_SET_WORK_MODE_CMDID = 0x815, WMI_LO_LEAKAGE_CALIB_CMDID = 0x816, - WMI_MARLON_R_READ_CMDID = 0x818, - WMI_MARLON_R_WRITE_CMDID = 0x819, - WMI_MARLON_R_TXRX_SEL_CMDID = 0x81A, - MAC_IO_STATIC_PARAMS_CMDID = 0x81B, - MAC_IO_DYNAMIC_PARAMS_CMDID = 0x81C, + WMI_LO_POWER_CALIB_FROM_OTP_CMDID = 0x817, WMI_SILENT_RSSI_CALIB_CMDID = 0x81D, + /* deprecated */ WMI_RF_RX_TEST_CMDID = 0x81E, WMI_CFG_RX_CHAIN_CMDID = 0x820, WMI_VRING_CFG_CMDID = 0x821, @@ -127,11 +140,6 @@ enum wmi_command_id { WMI_SET_PCP_CHANNEL_CMDID = 0x829, WMI_GET_PCP_CHANNEL_CMDID = 0x82A, WMI_SW_TX_REQ_CMDID = 0x82B, - WMI_READ_MAC_RXQ_CMDID = 0x830, - WMI_READ_MAC_TXQ_CMDID = 0x831, - WMI_WRITE_MAC_RXQ_CMDID = 0x832, - WMI_WRITE_MAC_TXQ_CMDID = 0x833, - WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x834, WMI_MLME_PUSH_CMDID = 0x835, WMI_BEAMFORMING_MGMT_CMDID = 0x836, WMI_BF_TXSS_MGMT_CMDID = 0x837, @@ -145,9 +153,13 @@ enum wmi_command_id { WMI_MAINTAIN_RESUME_CMDID = 0x851, WMI_RS_MGMT_CMDID = 0x852, WMI_RF_MGMT_CMDID = 0x853, - WMI_OTP_READ_CMDID = 0x856, - WMI_OTP_WRITE_CMDID = 0x857, + WMI_RF_XPM_READ_CMDID = 0x856, + WMI_RF_XPM_WRITE_CMDID = 0x857, WMI_LED_CFG_CMDID = 0x858, + WMI_SET_CONNECT_SNR_THR_CMDID = 0x85B, + WMI_SET_ACTIVE_SILENT_RSSI_TABLE_CMDID = 0x85C, + WMI_RF_PWR_ON_DELAY_CMDID = 0x85D, + WMI_SET_HIGH_POWER_TABLE_PARAMS_CMDID = 0x85E, /* Performance monitoring commands */ WMI_BF_CTRL_CMDID = 0x862, WMI_NOTIFY_REQ_CMDID = 0x863, @@ -155,7 +167,6 @@ enum wmi_command_id { WMI_GET_RF_STATUS_CMDID = 0x866, WMI_GET_BASEBAND_TYPE_CMDID = 0x867, WMI_UNIT_TEST_CMDID = 0x900, - WMI_HICCUP_CMDID = 0x901, WMI_FLASH_READ_CMDID = 0x902, WMI_FLASH_WRITE_CMDID = 0x903, /* Power management */ @@ -175,16 +186,6 @@ enum wmi_command_id { WMI_GET_PCP_FACTOR_CMDID = 0x91B, /* Power Save Configuration Commands */ WMI_PS_DEV_PROFILE_CFG_CMDID = 0x91C, - /* Not supported yet */ - WMI_PS_DEV_CFG_CMDID = 0x91D, - /* Not supported yet */ - WMI_PS_DEV_CFG_READ_CMDID = 0x91E, - /* Per MAC Power Save Configuration commands - * Not supported yet - */ - WMI_PS_MID_CFG_CMDID = 0x91F, - /* Not supported yet */ - WMI_PS_MID_CFG_READ_CMDID = 0x920, WMI_RS_CFG_CMDID = 0x921, WMI_GET_DETAILED_RS_RES_CMDID = 0x922, WMI_AOA_MEAS_CMDID = 0x923, @@ -195,13 +196,16 @@ enum wmi_command_id { WMI_DEL_STA_CMDID = 0x936, WMI_SET_THERMAL_THROTTLING_CFG_CMDID = 0x940, WMI_GET_THERMAL_THROTTLING_CFG_CMDID = 0x941, + /* Read Power Save profile type */ + WMI_PS_DEV_PROFILE_CFG_READ_CMDID = 0x942, WMI_TOF_SESSION_START_CMDID = 0x991, WMI_TOF_GET_CAPABILITIES_CMDID = 0x992, WMI_TOF_SET_LCR_CMDID = 0x993, WMI_TOF_SET_LCI_CMDID = 0x994, - WMI_TOF_CHANNEL_INFO_CMDID = 0x995, + WMI_TOF_CFG_RESPONDER_CMDID = 0x996, WMI_TOF_SET_TX_RX_OFFSET_CMDID = 0x997, WMI_TOF_GET_TX_RX_OFFSET_CMDID = 0x998, + WMI_TOF_CHANNEL_INFO_CMDID = 0x999, WMI_GET_RF_SECTOR_PARAMS_CMDID = 0x9A0, WMI_SET_RF_SECTOR_PARAMS_CMDID = 0x9A1, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID = 0x9A2, @@ -210,12 +214,20 @@ enum wmi_command_id { WMI_PRIO_TX_SECTORS_ORDER_CMDID = 0x9A5, WMI_PRIO_TX_SECTORS_NUMBER_CMDID = 0x9A6, WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_CMDID = 0x9A7, + WMI_SCHEDULING_SCHEME_CMDID = 0xA01, + WMI_FIXED_SCHEDULING_CONFIG_CMDID = 0xA02, + WMI_ENABLE_FIXED_SCHEDULING_CMDID = 0xA03, + WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_CMDID = 0xA04, + WMI_SET_LONG_RANGE_CONFIG_CMDID = 0xA05, WMI_SET_MAC_ADDRESS_CMDID = 0xF003, WMI_ABORT_SCAN_CMDID = 0xF007, WMI_SET_PROMISCUOUS_MODE_CMDID = 0xF041, + /* deprecated */ WMI_GET_PMK_CMDID = 0xF048, WMI_SET_PASSPHRASE_CMDID = 0xF049, + /* deprecated */ WMI_SEND_ASSOC_RES_CMDID = 0xF04A, + /* deprecated */ WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xF04B, WMI_MAC_ADDR_REQ_CMDID = 0xF04D, WMI_FW_VER_CMDID = 0xF04E, @@ -441,11 +453,6 @@ struct wmi_rf_mgmt_cmd { __le32 rf_mgmt_type; } __packed; -/* WMI_RF_RX_TEST_CMDID */ -struct wmi_rf_rx_test_cmd { - __le32 sector; -} __packed; - /* WMI_CORR_MEASURE_CMDID */ struct wmi_corr_measure_cmd { __le32 freq_mhz; @@ -658,6 +665,20 @@ struct wmi_bcast_vring_cfg_cmd { struct wmi_bcast_vring_cfg vring_cfg; } __packed; +/* WMI_LO_POWER_CALIB_FROM_OTP_CMDID */ +struct wmi_lo_power_calib_from_otp_cmd { + /* index to read from OTP. zero based */ + u8 index; + u8 reserved[3]; +} __packed; + +/* WMI_LO_POWER_CALIB_FROM_OTP_EVENTID */ +struct wmi_lo_power_calib_from_otp_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + /* WMI_VRING_BA_EN_CMDID */ struct wmi_vring_ba_en_cmd { u8 ringid; @@ -693,6 +714,24 @@ enum wmi_sniffer_cfg_mode { WMI_SNIFFER_ON = 0x01, }; +/* WMI_SILENT_RSSI_TABLE */ +enum wmi_silent_rssi_table { + RF_TEMPERATURE_CALIB_DEFAULT_DB = 0x00, + RF_TEMPERATURE_CALIB_HIGH_POWER_DB = 0x01, +}; + +/* WMI_SILENT_RSSI_STATUS */ +enum wmi_silent_rssi_status { + SILENT_RSSI_SUCCESS = 0x00, + SILENT_RSSI_FAILURE = 0x01, +}; + +/* WMI_SET_ACTIVE_SILENT_RSSI_TABLE_CMDID */ +struct wmi_set_active_silent_rssi_table_cmd { + /* enum wmi_silent_rssi_table */ + __le32 table; +} __packed; + enum wmi_sniffer_cfg_phy_info_mode { WMI_SNIFFER_PHY_INFO_DISABLED = 0x00, WMI_SNIFFER_PHY_INFO_ENABLED = 0x01, @@ -836,18 +875,85 @@ struct wmi_echo_cmd { __le32 value; } __packed; -/* WMI_OTP_READ_CMDID */ -struct wmi_otp_read_cmd { - __le32 addr; - __le32 size; - __le32 values; +/* WMI_RF_PWR_ON_DELAY_CMDID + * set FW time parameters used through RF resetting + * RF reset consists of bringing its power down for a period of time, then + * bringing the power up + * Returned event: WMI_RF_PWR_ON_DELAY_RSP_EVENTID + */ +struct wmi_rf_pwr_on_delay_cmd { + /* time in usec the FW waits after bringing the RF PWR down, + * set 0 for default + */ + __le16 down_delay_usec; + /* time in usec the FW waits after bringing the RF PWR up, + * set 0 for default + */ + __le16 up_delay_usec; +} __packed; + +/* \WMI_SET_HIGH_POWER_TABLE_PARAMS_CMDID + * This API controls the Tx and Rx gain over temperature. + * It controls the Tx D-type, Rx D-type and Rx E-type amplifiers. + * It also controls the Tx gain index, by controlling the Rx to Tx gain index + * offset. + * The control is divided by 3 temperature values to 4 temperature ranges. + * Each parameter uses its own temperature values. + * Returned event: WMI_SET_HIGH_POWER_TABLE_PARAMS_EVENTID + */ +struct wmi_set_high_power_table_params_cmd { + /* Temperature range for Tx D-type parameters */ + u8 tx_dtype_temp[WMI_RF_DTYPE_LENGTH]; + u8 reserved0; + /* Tx D-type values to be used for each temperature range */ + __le32 tx_dtype_conf[WMI_RF_DTYPE_LENGTH + 1]; + /* Temperature range for Rx D-type parameters */ + u8 rx_dtype_temp[WMI_RF_DTYPE_LENGTH]; + u8 reserved1; + /* Rx D-type values to be used for each temperature range */ + __le32 rx_dtype_conf[WMI_RF_DTYPE_LENGTH + 1]; + /* Temperature range for Rx E-type parameters */ + u8 rx_etype_temp[WMI_RF_ETYPE_LENGTH]; + u8 reserved2; + /* Rx E-type values to be used for each temperature range. + * The last 4 values of any range are the first 4 values of the next + * range and so on + */ + __le32 rx_etype_conf[WMI_RF_ETYPE_VAL_PER_RANGE + WMI_RF_ETYPE_LENGTH]; + /* Temperature range for rx_2_tx_offs parameters */ + u8 rx_2_tx_temp[WMI_RF_RX2TX_LENGTH]; + u8 reserved3; + /* Rx to Tx gain index offset */ + s8 rx_2_tx_offs[WMI_RF_RX2TX_LENGTH + 1]; } __packed; -/* WMI_OTP_WRITE_CMDID */ -struct wmi_otp_write_cmd { - __le32 addr; - __le32 size; - __le32 values; +/* CMD: WMI_RF_XPM_READ_CMDID */ +struct wmi_rf_xpm_read_cmd { + u8 rf_id; + u8 reserved[3]; + /* XPM bit start address in range [0,8191]bits - rounded by FW to + * multiple of 8bits + */ + __le32 xpm_bit_address; + __le32 num_bytes; +} __packed; + +/* CMD: WMI_RF_XPM_WRITE_CMDID */ +struct wmi_rf_xpm_write_cmd { + u8 rf_id; + u8 reserved0[3]; + /* XPM bit start address in range [0,8191]bits - rounded by FW to + * multiple of 8bits + */ + __le32 xpm_bit_address; + __le32 num_bytes; + /* boolean flag indicating whether FW should verify the write + * operation + */ + u8 verify; + u8 reserved1[3]; + /* actual size=num_bytes */ + u8 data_bytes[0]; } __packed; /* WMI_TEMP_SENSE_CMDID @@ -990,19 +1096,26 @@ struct wmi_ftm_dest_info { */ __le16 burst_period; u8 dst_mac[WMI_MAC_LEN]; - __le16 reserved; + u8 reserved; + u8 num_burst_per_aoa_meas; } __packed; /* WMI_TOF_SESSION_START_CMDID */ struct wmi_tof_session_start_cmd { __le32 session_id; - u8 num_of_aoa_measures; + u8 reserved1; u8 aoa_type; __le16 num_of_dest; u8 reserved[4]; struct wmi_ftm_dest_info ftm_dest_info[0]; } __packed; +/* WMI_TOF_CFG_RESPONDER_CMDID */ +struct wmi_tof_cfg_responder_cmd { + u8 enable; + u8 reserved[3]; +} __packed; + enum wmi_tof_channel_info_report_type { WMI_TOF_CHANNEL_INFO_TYPE_CIR = 0x1, WMI_TOF_CHANNEL_INFO_TYPE_RSSI = 0x2, @@ -1023,7 +1136,99 @@ struct wmi_tof_set_tx_rx_offset_cmd { __le32 tx_offset; /* RX delay offset */ __le32 rx_offset; - __le32 reserved[2]; + /* Mask to define which RFs to configure. 0 means all RFs */ + __le32 rf_mask; + /* Offset to strongest tap of CIR */ + __le32 precursor; +} __packed; + +/* WMI_TOF_GET_TX_RX_OFFSET_CMDID */ +struct wmi_tof_get_tx_rx_offset_cmd { + /* rf index to read offsets from */ + u8 rf_index; + u8 reserved[3]; +} __packed; + +/* WMI_FIXED_SCHEDULING_CONFIG_CMDID */ +struct wmi_map_mcs_to_schd_params { + u8 mcs; + /* time in usec from start slot to start tx flow - default 15 */ + u8 time_in_usec_before_initiate_tx; + /* RD enable - if yes consider RD according to STA mcs */ + u8 rd_enabled; + u8 reserved; + /* time in usec from start slot to stop vring */ + __le16 time_in_usec_to_stop_vring; + /* timeout to force flush from start of slot */ + __le16 flush_to_in_usec; + /* per mcs the mac buffer limit size in bytes */ + __le32 mac_buff_size_in_bytes; +} __packed; + +/* WMI_FIXED_SCHEDULING_CONFIG_COMPLETE_EVENTID */ +struct wmi_fixed_scheduling_config_complete_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +#define WMI_NUM_MCS (13) + +/* WMI_FIXED_SCHEDULING_CONFIG_CMDID */ +struct wmi_fixed_scheduling_config_cmd { + /* defaults in the SAS table */ + struct wmi_map_mcs_to_schd_params mcs_to_schd_params_map[WMI_NUM_MCS]; + /* default 150 uSec */ + __le16 max_sta_rd_ppdu_duration_in_usec; + /* default 300 uSec */ + __le16 max_sta_grant_ppdu_duration_in_usec; + /* default 1000 uSec */ + __le16 assoc_slot_duration_in_usec; + /* default 360 uSec */ + __le16 virtual_slot_duration_in_usec; + /* each this field value slots start with grant frame to the station + * - default 2 + */ + u8 number_of_ap_slots_for_initiate_grant; + u8 reserved[3]; +} __packed; + +/* WMI_ENABLE_FIXED_SCHEDULING_CMDID */ +struct wmi_enable_fixed_scheduling_cmd { + __le32 reserved; +} __packed; + +/* WMI_ENABLE_FIXED_SCHEDULING_COMPLETE_EVENTID */ +struct wmi_enable_fixed_scheduling_complete_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_CMDID */ +struct wmi_set_multi_directed_omnis_config_cmd { + /* number of directed omnis at destination AP */ + u8 dest_ap_num_directed_omnis; + u8 reserved[3]; +} __packed; + +/* WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_EVENTID */ +struct wmi_set_multi_directed_omnis_config_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_SET_LONG_RANGE_CONFIG_CMDID */ +struct wmi_set_long_range_config_cmd { + __le32 reserved; +} __packed; + +/* WMI_SET_LONG_RANGE_CONFIG_COMPLETE_EVENTID */ +struct wmi_set_long_range_config_complete_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; } __packed; /* WMI Events @@ -1039,19 +1244,22 @@ enum wmi_event_id { WMI_FW_READY_EVENTID = 0x1801, WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x200, WMI_ECHO_RSP_EVENTID = 0x1803, + /* deprecated */ WMI_FS_TUNE_DONE_EVENTID = 0x180A, + /* deprecated */ WMI_CORR_MEASURE_EVENTID = 0x180B, WMI_READ_RSSI_EVENTID = 0x180C, WMI_TEMP_SENSE_DONE_EVENTID = 0x180E, WMI_DC_CALIB_DONE_EVENTID = 0x180F, + /* deprecated */ WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811, + /* deprecated */ WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812, WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815, WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816, - WMI_MARLON_R_READ_DONE_EVENTID = 0x1818, - WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819, - WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181A, + WMI_LO_POWER_CALIB_FROM_OTP_EVENTID = 0x1817, WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181D, + /* deprecated */ WMI_RF_RX_TEST_DONE_EVENTID = 0x181E, WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820, WMI_VRING_CFG_DONE_EVENTID = 0x1821, @@ -1062,11 +1270,6 @@ enum wmi_event_id { WMI_GET_SSID_EVENTID = 0x1828, WMI_GET_PCP_CHANNEL_EVENTID = 0x182A, WMI_SW_TX_COMPLETE_EVENTID = 0x182B, - WMI_READ_MAC_RXQ_EVENTID = 0x1830, - WMI_READ_MAC_TXQ_EVENTID = 0x1831, - WMI_WRITE_MAC_RXQ_EVENTID = 0x1832, - WMI_WRITE_MAC_TXQ_EVENTID = 0x1833, - WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834, WMI_BEAMFORMING_MGMT_DONE_EVENTID = 0x1836, WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837, WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, @@ -1077,8 +1280,12 @@ enum wmi_event_id { WMI_TX_MGMT_PACKET_EVENTID = 0x1841, WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID = 0x1842, WMI_LINK_MAINTAIN_CFG_READ_DONE_EVENTID = 0x1843, - WMI_OTP_READ_RESULT_EVENTID = 0x1856, + WMI_RF_XPM_READ_RESULT_EVENTID = 0x1856, + WMI_RF_XPM_WRITE_RESULT_EVENTID = 0x1857, WMI_LED_CFG_DONE_EVENTID = 0x1858, + WMI_SET_SILENT_RSSI_TABLE_DONE_EVENTID = 0x185C, + WMI_RF_PWR_ON_DELAY_RSP_EVENTID = 0x185D, + WMI_SET_HIGH_POWER_TABLE_PARAMS_EVENTID = 0x185E, /* Performance monitoring events */ WMI_DATA_PORT_OPEN_EVENTID = 0x1860, WMI_WBE_LINK_DOWN_EVENTID = 0x1861, @@ -1107,14 +1314,6 @@ enum wmi_event_id { WMI_PCP_FACTOR_EVENTID = 0x191A, /* Power Save Configuration Events */ WMI_PS_DEV_PROFILE_CFG_EVENTID = 0x191C, - /* Not supported yet */ - WMI_PS_DEV_CFG_EVENTID = 0x191D, - /* Not supported yet */ - WMI_PS_DEV_CFG_READ_EVENTID = 0x191E, - /* Not supported yet */ - WMI_PS_MID_CFG_EVENTID = 0x191F, - /* Not supported yet */ - WMI_PS_MID_CFG_READ_EVENTID = 0x1920, WMI_RS_CFG_DONE_EVENTID = 0x1921, WMI_GET_DETAILED_RS_RES_EVENTID = 0x1922, WMI_AOA_MEAS_EVENTID = 0x1923, @@ -1123,14 +1322,17 @@ enum wmi_event_id { WMI_GET_MGMT_RETRY_LIMIT_EVENTID = 0x1931, WMI_SET_THERMAL_THROTTLING_CFG_EVENTID = 0x1940, WMI_GET_THERMAL_THROTTLING_CFG_EVENTID = 0x1941, + /* return the Power Save profile */ + WMI_PS_DEV_PROFILE_CFG_READ_EVENTID = 0x1942, WMI_TOF_SESSION_END_EVENTID = 0x1991, WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992, WMI_TOF_SET_LCR_EVENTID = 0x1993, WMI_TOF_SET_LCI_EVENTID = 0x1994, WMI_TOF_FTM_PER_DEST_RES_EVENTID = 0x1995, - WMI_TOF_CHANNEL_INFO_EVENTID = 0x1996, + WMI_TOF_CFG_RESPONDER_EVENTID = 0x1996, WMI_TOF_SET_TX_RX_OFFSET_EVENTID = 0x1997, WMI_TOF_GET_TX_RX_OFFSET_EVENTID = 0x1998, + WMI_TOF_CHANNEL_INFO_EVENTID = 0x1999, WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID = 0x19A0, WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID = 0x19A1, WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID = 0x19A2, @@ -1139,12 +1341,18 @@ enum wmi_event_id { WMI_PRIO_TX_SECTORS_ORDER_EVENTID = 0x19A5, WMI_PRIO_TX_SECTORS_NUMBER_EVENTID = 0x19A6, WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_EVENTID = 0x19A7, + WMI_SCHEDULING_SCHEME_EVENTID = 0x1A01, + WMI_FIXED_SCHEDULING_CONFIG_COMPLETE_EVENTID = 0x1A02, + WMI_ENABLE_FIXED_SCHEDULING_COMPLETE_EVENTID = 0x1A03, + WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_EVENTID = 0x1A04, + WMI_SET_LONG_RANGE_CONFIG_COMPLETE_EVENTID = 0x1A05, WMI_SET_CHANNEL_EVENTID = 0x9000, WMI_ASSOC_REQ_EVENTID = 0x9001, WMI_EAPOL_RX_EVENTID = 0x9002, WMI_MAC_ADDR_RESP_EVENTID = 0x9003, WMI_FW_VER_EVENTID = 0x9004, WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENTID = 0x9005, + WMI_COMMAND_NOT_SUPPORTED_EVENTID = 0xFFFF, }; /* Events data structures */ @@ -1201,7 +1409,7 @@ struct wmi_fw_ver_event { __le32 bl_minor; __le32 bl_subminor; __le32 bl_build; - /* The number of entries in the FW capabilies array */ + /* The number of entries in the FW capabilities array */ u8 fw_capabilities_len; u8 reserved[3]; /* FW capabilities info @@ -1246,7 +1454,9 @@ struct wmi_get_rf_status_event { __le32 board_file_platform_type; /* board file version */ __le32 board_file_version; - __le32 reserved[2]; + /* enabled XIFs bit vector */ + __le32 enabled_xif_vector; + __le32 reserved; } __packed; /* WMI_GET_BASEBAND_TYPE_EVENTID */ @@ -1300,6 +1510,7 @@ struct wmi_ready_event { /* enum wmi_phy_capability */ u8 phy_capability; u8 numof_additional_mids; + /* rfc read calibration result. 5..15 */ u8 rfc_read_calib_result; u8 reserved[3]; } __packed; @@ -1580,7 +1791,7 @@ struct wmi_sw_tx_complete_event { u8 reserved[3]; } __packed; -/* WMI_CORR_MEASURE_EVENTID */ +/* WMI_CORR_MEASURE_EVENTID - deprecated */ struct wmi_corr_measure_event { /* signed */ __le32 i; @@ -1612,25 +1823,29 @@ struct wmi_rx_mgmt_info { __le16 stype; __le16 status; __le32 len; - /* Not resolved when == 0xFFFFFFFF ==> Broadcast to all MIDS */ + /* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */ u8 qid; - /* Not resolved when == 0xFFFFFFFF ==> Broadcast to all MIDS */ + /* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */ u8 mid; u8 cid; /* From Radio MNGR */ u8 channel; } __packed; -/* wmi_otp_read_write_cmd */ -struct wmi_otp_read_write_cmd { - __le32 addr; - __le32 size; - u8 values[0]; +/* EVENT: WMI_RF_XPM_READ_RESULT_EVENTID */ +struct wmi_rf_xpm_read_result_event { + /* enum wmi_fw_status_e - success=0 or fail=1 */ + u8 status; + u8 reserved[3]; + /* requested num_bytes of data */ + u8 data_bytes[0]; } __packed; -/* WMI_OTP_READ_RESULT_EVENTID */ -struct wmi_otp_read_result_event { - u8 payload[0]; +/* EVENT: WMI_RF_XPM_WRITE_RESULT_EVENTID */ +struct wmi_rf_xpm_write_result_event { + /* enum wmi_fw_status_e - success=0 or fail=1 */ + u8 status; + u8 reserved[3]; } __packed; /* WMI_TX_MGMT_PACKET_EVENTID */ @@ -1649,6 +1864,20 @@ struct wmi_echo_rsp_event { __le32 echoed_value; } __packed; +/* WMI_RF_PWR_ON_DELAY_RSP_EVENTID */ +struct wmi_rf_pwr_on_delay_rsp_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_SET_HIGH_POWER_TABLE_PARAMS_EVENTID */ +struct wmi_set_high_power_table_params_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + /* WMI_TEMP_SENSE_DONE_EVENTID * * Measure MAC and radio temperatures @@ -1726,14 +1955,22 @@ struct wmi_led_cfg_cmd { u8 reserved; } __packed; +/* \WMI_SET_CONNECT_SNR_THR_CMDID */ +struct wmi_set_connect_snr_thr_cmd { + u8 enable; + u8 reserved; + /* 1/4 Db units */ + __le16 omni_snr_thr; + /* 1/4 Db units */ + __le16 direct_snr_thr; +} __packed; + /* WMI_LED_CFG_DONE_EVENTID */ struct wmi_led_cfg_done_event { /* led config status */ __le32 status; } __packed; -#define WMI_NUM_MCS (13) - /* Rate search parameters configuration per connection */ struct wmi_rs_cfg { /* The maximal allowed PER for each MCS @@ -1758,6 +1995,98 @@ struct wmi_rs_cfg { __le32 mcs_en_vec; } __packed; +/* Slot types */ +enum wmi_sched_scheme_slot_type { + WMI_SCHED_SLOT_SP = 0x0, + WMI_SCHED_SLOT_CBAP = 0x1, + WMI_SCHED_SLOT_IDLE = 0x2, + WMI_SCHED_SLOT_ANNOUNCE_NO_ACK = 0x3, + WMI_SCHED_SLOT_DISCOVERY = 0x4, +}; + +enum wmi_sched_scheme_slot_flags { + WMI_SCHED_SCHEME_SLOT_PERIODIC = 0x1, +}; + +struct wmi_sched_scheme_slot { + /* in microsecond */ + __le32 tbtt_offset; + /* wmi_sched_scheme_slot_flags */ + u8 flags; + /* wmi_sched_scheme_slot_type */ + u8 type; + /* in microsecond */ + __le16 duration; + /* frame_exchange_sequence_duration */ + __le16 tx_op; + /* time in microseconds between two consecutive slots + * relevant only if flag WMI_SCHED_SCHEME_SLOT_PERIODIC set + */ + __le16 period; + /* relevant only if flag WMI_SCHED_SCHEME_SLOT_PERIODIC set + * number of times to repeat allocation + */ + u8 num_of_blocks; + /* relevant only if flag WMI_SCHED_SCHEME_SLOT_PERIODIC set + * every idle_period allocation will be idle + */ + u8 idle_period; + u8 src_aid; + u8 dest_aid; + __le32 reserved; +} __packed; + +enum wmi_sched_scheme_flags { + /* should not be set when clearing scheduling scheme */ + WMI_SCHED_SCHEME_ENABLE = 0x01, + WMI_SCHED_PROTECTED_SP = 0x02, + /* should be set only on first WMI fragment of scheme */ + WMI_SCHED_FIRST = 0x04, + /* should be set only on last WMI fragment of scheme */ + WMI_SCHED_LAST = 0x08, + WMI_SCHED_IMMEDIATE_START = 0x10, +}; + +enum wmi_sched_scheme_advertisment { + /* ESE is not advertised at all, STA has to be configured with WMI + * also + */ + WMI_ADVERTISE_ESE_DISABLED = 0x0, + WMI_ADVERTISE_ESE_IN_BEACON = 0x1, + WMI_ADVERTISE_ESE_IN_ANNOUNCE_FRAME = 0x2, +}; + +/* WMI_SCHEDULING_SCHEME_CMD */ +struct wmi_scheduling_scheme_cmd { + u8 serial_num; + /* wmi_sched_scheme_advertisment */ + u8 ese_advertisment; + /* wmi_sched_scheme_flags */ + __le16 flags; + u8 num_allocs; + u8 reserved[3]; + __le64 start_tbtt; + /* allocations list */ + struct wmi_sched_scheme_slot allocs[WMI_SCHED_MAX_ALLOCS_PER_CMD]; +} __packed; + +enum wmi_sched_scheme_failure_type { + WMI_SCHED_SCHEME_FAILURE_NO_ERROR = 0x00, + WMI_SCHED_SCHEME_FAILURE_OLD_START_TSF_ERR = 0x01, +}; + +/* WMI_SCHEDULING_SCHEME_EVENTID */ +struct wmi_scheduling_scheme_event { + /* wmi_fw_status_e */ + u8 status; + /* serial number given in command */ + u8 serial_num; + /* wmi_sched_scheme_failure_type */ + u8 failure_type; + /* alignment to 32b */ + u8 reserved[1]; +} __packed; + /* WMI_RS_CFG_CMDID */ struct wmi_rs_cfg_cmd { /* connection id */ @@ -1975,6 +2304,19 @@ enum wmi_ps_profile_type { WMI_PS_PROFILE_TYPE_LOW_LATENCY_PS = 0x03, }; +/* WMI_PS_DEV_PROFILE_CFG_READ_CMDID */ +struct wmi_ps_dev_profile_cfg_read_cmd { + /* reserved */ + __le32 reserved; +} __packed; + +/* WMI_PS_DEV_PROFILE_CFG_READ_EVENTID */ +struct wmi_ps_dev_profile_cfg_read_event { + /* wmi_ps_profile_type_e */ + u8 ps_profile; + u8 reserved[3]; +} __packed; + /* WMI_PS_DEV_PROFILE_CFG_CMDID * * Power save profile to be used by the device @@ -2023,157 +2365,6 @@ enum wmi_ps_d3_resp_policy { WMI_PS_D3_RESP_POLICY_APPROVED = 0x02, }; -/* Device common power save configurations */ -struct wmi_ps_dev_cfg { - /* lowest level of PS allowed while unassociated, enum wmi_ps_level_e - */ - u8 ps_unassoc_min_level; - /* lowest deep sleep clock level while nonassoc, enum - * wmi_ps_deep_sleep_clk_level_e - */ - u8 ps_unassoc_deep_sleep_min_level; - /* lowest level of PS allowed while associated, enum wmi_ps_level_e */ - u8 ps_assoc_min_level; - /* lowest deep sleep clock level while assoc, enum - * wmi_ps_deep_sleep_clk_level_e - */ - u8 ps_assoc_deep_sleep_min_level; - /* enum wmi_ps_deep_sleep_clk_level_e */ - u8 ps_assoc_low_latency_ds_min_level; - /* enum wmi_ps_d3_resp_policy_e */ - u8 ps_D3_response_policy; - /* BOOL */ - u8 ps_D3_pm_pme_enabled; - /* BOOL */ - u8 ps_halp_enable; - u8 ps_deep_sleep_enter_thresh_msec; - /* BOOL */ - u8 ps_voltage_scaling_en; -} __packed; - -/* WMI_PS_DEV_CFG_CMDID - * - * Configure common Power Save parameters of the device and all MIDs. - * - * Returned event: - * - WMI_PS_DEV_CFG_EVENTID - */ -struct wmi_ps_dev_cfg_cmd { - /* Device Power Save configuration to be applied */ - struct wmi_ps_dev_cfg ps_dev_cfg; - /* alignment to 32b */ - u8 reserved[2]; -} __packed; - -/* WMI_PS_DEV_CFG_EVENTID */ -struct wmi_ps_dev_cfg_event { - /* wmi_ps_cfg_cmd_status_e */ - __le32 status; -} __packed; - -/* WMI_PS_DEV_CFG_READ_CMDID - * - * request to retrieve device Power Save configuration - * (WMI_PS_DEV_CFG_CMD params) - * - * Returned event: - * - WMI_PS_DEV_CFG_READ_EVENTID - */ -struct wmi_ps_dev_cfg_read_cmd { - __le32 reserved; -} __packed; - -/* WMI_PS_DEV_CFG_READ_EVENTID */ -struct wmi_ps_dev_cfg_read_event { - /* wmi_ps_cfg_cmd_status_e */ - __le32 status; - /* Retrieved device Power Save configuration (WMI_PS_DEV_CFG_CMD - * params) - */ - struct wmi_ps_dev_cfg dev_ps_cfg; - /* alignment to 32b */ - u8 reserved[2]; -} __packed; - -/* Per Mac Power Save configurations */ -struct wmi_ps_mid_cfg { - /* Low power RX in BTI is enabled, BOOL */ - u8 beacon_lprx_enable; - /* Sync to sector ID enabled, BOOL */ - u8 beacon_sync_to_sectorId_enable; - /* Low power RX in DTI is enabled, BOOL */ - u8 frame_exchange_lprx_enable; - /* Sleep Cycle while in scheduled PS, 1-31 */ - u8 scheduled_sleep_cycle_pow2; - /* Stay Awake for k BIs every (sleep_cycle - k) BIs, 1-31 */ - u8 scheduled_num_of_awake_bis; - u8 am_to_traffic_load_thresh_mbp; - u8 traffic_to_am_load_thresh_mbps; - u8 traffic_to_am_num_of_no_traffic_bis; - /* BOOL */ - u8 continuous_traffic_psm; - __le16 no_traffic_to_min_usec; - __le16 no_traffic_to_max_usec; - __le16 snoozing_sleep_interval_milisec; - u8 max_no_data_awake_events; - /* Trigger WEB after k failed beacons */ - u8 num_of_failed_beacons_rx_to_trigger_web; - /* Trigger BF after k failed beacons */ - u8 num_of_failed_beacons_rx_to_trigger_bf; - /* Trigger SOB after k successful beacons */ - u8 num_of_successful_beacons_rx_to_trigger_sob; -} __packed; - -/* WMI_PS_MID_CFG_CMDID - * - * Configure Power Save parameters of a specific MID. - * These parameters are relevant for the specific BSS this MID belongs to. - * - * Returned event: - * - WMI_PS_MID_CFG_EVENTID - */ -struct wmi_ps_mid_cfg_cmd { - /* MAC ID */ - u8 mid; - /* mid PS configuration to be applied */ - struct wmi_ps_mid_cfg ps_mid_cfg; -} __packed; - -/* WMI_PS_MID_CFG_EVENTID */ -struct wmi_ps_mid_cfg_event { - /* MAC ID */ - u8 mid; - /* alignment to 32b */ - u8 reserved[3]; - /* wmi_ps_cfg_cmd_status_e */ - __le32 status; -} __packed; - -/* WMI_PS_MID_CFG_READ_CMDID - * - * request to retrieve Power Save configuration of mid - * (WMI_PS_MID_CFG_CMD params) - * - * Returned event: - * - WMI_PS_MID_CFG_READ_EVENTID - */ -struct wmi_ps_mid_cfg_read_cmd { - /* MAC ID */ - u8 mid; - /* alignment to 32b */ - u8 reserved[3]; -} __packed; - -/* WMI_PS_MID_CFG_READ_EVENTID */ -struct wmi_ps_mid_cfg_read_event { - /* MAC ID */ - u8 mid; - /* Retrieved MID Power Save configuration(WMI_PS_MID_CFG_CMD params) */ - struct wmi_ps_mid_cfg mid_ps_cfg; - /* wmi_ps_cfg_cmd_status_e */ - __le32 status; -} __packed; - #define WMI_AOA_MAX_DATA_SIZE (128) enum wmi_aoa_meas_status { @@ -2264,6 +2455,20 @@ struct wmi_tof_session_end_event { u8 reserved[3]; } __packed; +/* WMI_TOF_SET_LCI_EVENTID */ +struct wmi_tof_set_lci_event { + /* enum wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_TOF_SET_LCR_EVENTID */ +struct wmi_tof_set_lcr_event { + /* enum wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + /* Responder FTM Results */ struct wmi_responder_ftm_res { u8 t1[6]; @@ -2317,10 +2522,19 @@ struct wmi_tof_ftm_per_dest_res_event { __le32 tsf_sync; /* actual received ftm per burst */ u8 actual_ftm_per_burst; - u8 reserved0[7]; + /* Measurments are from RFs, defined by the mask */ + __le32 meas_rf_mask; + u8 reserved0[3]; struct wmi_responder_ftm_res responder_ftm_res[0]; } __packed; +/* WMI_TOF_CFG_RESPONDER_EVENTID */ +struct wmi_tof_cfg_responder_event { + /* enum wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + enum wmi_tof_channel_info_type { WMI_TOF_CHANNEL_INFO_AOA = 0x00, WMI_TOF_CHANNEL_INFO_LCI = 0x01, @@ -2357,12 +2571,15 @@ struct wmi_tof_set_tx_rx_offset_event { struct wmi_tof_get_tx_rx_offset_event { /* enum wmi_fw_status */ u8 status; - u8 reserved1[3]; + /* RF index used to read the offsets */ + u8 rf_index; + u8 reserved1[2]; /* TX delay offset */ __le32 tx_offset; /* RX delay offset */ __le32 rx_offset; - __le32 reserved2[2]; + /* Offset to strongest tap of CIR */ + __le32 precursor; } __packed; /* Result status codes for WMI commands */ @@ -2625,4 +2842,23 @@ struct wmi_prio_tx_sectors_set_default_cfg_event { u8 reserved[3]; } __packed; +/* WMI_SET_SILENT_RSSI_TABLE_DONE_EVENTID */ +struct wmi_set_silent_rssi_table_done_event { + /* enum wmi_silent_rssi_status */ + __le32 status; + /* enum wmi_silent_rssi_table */ + __le32 table; +} __packed; + +/* \WMI_COMMAND_NOT_SUPPORTED_EVENTID */ +struct wmi_command_not_supported_event { + /* device id */ + u8 mid; + u8 reserved0; + __le16 command_id; + /* for UT command only, otherwise reserved */ + __le16 command_subtype; + __le16 reserved1; +} __packed; + #endif /* __WILOCITY_WMI_H__ */ -- cgit v1.2.3 From 1490846d256af947a4743f04ba8c97dc3bc37073 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Mon, 28 Aug 2017 22:18:44 +0300 Subject: wil6210: move pre-FW configuration to separate function Re-arrange the code to have dedicated function for device configuration which takes place before FW starts running. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 40 ++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index e2ea49077b78..927e332893f1 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -930,6 +930,27 @@ int wil_ps_update(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile) return rc; } +static void wil_pre_fw_config(struct wil6210_priv *wil) +{ + /* Mark FW as loaded from host */ + wil_s(wil, RGF_USER_USAGE_6, 1); + + /* clear any interrupts which on-card-firmware + * may have set + */ + wil6210_clear_irq(wil); + /* CAF_ICR - clear and mask */ + /* it is W1C, clear by writing back same value */ + wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0); + wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0); + + if (wil->fw_calib_result > 0) { + __le32 val = cpu_to_le32(wil->fw_calib_result | + (CALIB_RESULT_SIGNATURE << 8)); + wil_w(wil, RGF_USER_FW_CALIB_RESULT, (u32 __force)val); + } +} + /* * We reset all the structures, and we reset the UMAC. * After calling this routine, you're expected to reload @@ -1023,24 +1044,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) if (rc) return rc; - /* Mark FW as loaded from host */ - wil_s(wil, RGF_USER_USAGE_6, 1); - - /* clear any interrupts which on-card-firmware - * may have set - */ - wil6210_clear_irq(wil); - /* CAF_ICR - clear and mask */ - /* it is W1C, clear by writing back same value */ - wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0); - wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0); - - if (wil->fw_calib_result > 0) { - __le32 val = cpu_to_le32(wil->fw_calib_result | - (CALIB_RESULT_SIGNATURE << 8)); - wil_w(wil, RGF_USER_FW_CALIB_RESULT, (u32 __force)val); - } - + wil_pre_fw_config(wil); wil_release_cpu(wil); } -- cgit v1.2.3 From 7086d861753bc0aaa8b6445f657d8e39953e4fcd Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Mon, 28 Aug 2017 22:18:45 +0300 Subject: wil6210: clear PAL_UNIT_ICR part of device reset When FW starts running it can get D0 to D3 interrupt that is a leftover from previous system suspend while FW was not running. As this interrupt is not relevant anymore, clear it part of device reset procedure. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 2 ++ drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 2 files changed, 3 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 927e332893f1..bac829aa950d 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -943,6 +943,8 @@ static void wil_pre_fw_config(struct wil6210_priv *wil) /* it is W1C, clear by writing back same value */ wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0); wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0); + /* clear PAL_UNIT_ICR (potential D0->D3 leftover) */ + wil_s(wil, RGF_PAL_UNIT_ICR + offsetof(struct RGF_ICR, ICR), 0); if (wil->fw_calib_result > 0) { __le32 val = cpu_to_le32(wil->fw_calib_result | diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index a8c2680e70b4..315ec8b59662 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -268,6 +268,7 @@ struct RGF_ICR { #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) #define RGF_HP_CTRL (0x88265c) +#define RGF_PAL_UNIT_ICR (0x88266c) /* struct RGF_ICR */ #define RGF_PCIE_LOS_COUNTER_CTL (0x882dc4) /* MAC timer, usec, for packet lifetime */ -- cgit v1.2.3 From d83ad4c4fd78b22935711f865048bab78b43fc86 Mon Sep 17 00:00:00 2001 From: Lior David Date: Mon, 28 Aug 2017 22:18:46 +0300 Subject: wil6210: increase connect timeout Connect can take longer than current timeout in some scenarios, for example with long-range antenna array. Increase the timeout to support these scenarios. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 9b529ea62bff..0712a898205d 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -811,7 +811,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, wil->bss = bss; /* Connect can take lots of time */ mod_timer(&wil->connect_timer, - jiffies + msecs_to_jiffies(2000)); + jiffies + msecs_to_jiffies(5000)); } else { clear_bit(wil_status_fwconnecting, wil->status); } -- cgit v1.2.3 From 69fecf599f48e258984b2ffdf0a4c65a35a31287 Mon Sep 17 00:00:00 2001 From: Lior David Date: Mon, 28 Aug 2017 22:18:47 +0300 Subject: wil6210: ensure P2P device is stopped before removing interface User space can remove the P2P management interface while it is active (for example, while listen/search is active) and this can cause a crash. Ensure the P2P device is fully stopped before removing. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 57 +++++++++++++++-------------- 1 file changed, 29 insertions(+), 28 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 0712a898205d..85d5c04618eb 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -382,6 +382,34 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy, return rc; } +static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + wil_dbg_misc(wil, "start_p2p_device: entered\n"); + wil->p2p.p2p_dev_started = 1; + return 0; +} + +static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil_p2p_info *p2p = &wil->p2p; + + if (!p2p->p2p_dev_started) + return; + + wil_dbg_misc(wil, "stop_p2p_device: entered\n"); + mutex_lock(&wil->mutex); + mutex_lock(&wil->p2p_wdev_mutex); + wil_p2p_stop_radio_operations(wil); + p2p->p2p_dev_started = 0; + mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->mutex); +} + static struct wireless_dev * wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, @@ -430,6 +458,7 @@ static int wil_cfg80211_del_iface(struct wiphy *wiphy, return -EINVAL; } + wil_cfg80211_stop_p2p_device(wiphy, wdev); wil_p2p_wdev_free(wil); return 0; @@ -1661,34 +1690,6 @@ static int wil_cfg80211_change_bss(struct wiphy *wiphy, return 0; } -static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy, - struct wireless_dev *wdev) -{ - struct wil6210_priv *wil = wiphy_to_wil(wiphy); - - wil_dbg_misc(wil, "start_p2p_device: entered\n"); - wil->p2p.p2p_dev_started = 1; - return 0; -} - -static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy, - struct wireless_dev *wdev) -{ - struct wil6210_priv *wil = wiphy_to_wil(wiphy); - struct wil_p2p_info *p2p = &wil->p2p; - - if (!p2p->p2p_dev_started) - return; - - wil_dbg_misc(wil, "stop_p2p_device: entered\n"); - mutex_lock(&wil->mutex); - mutex_lock(&wil->p2p_wdev_mutex); - wil_p2p_stop_radio_operations(wil); - p2p->p2p_dev_started = 0; - mutex_unlock(&wil->p2p_wdev_mutex); - mutex_unlock(&wil->mutex); -} - static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout) -- cgit v1.2.3 From 57b18a75d90e7728bc3dec11d7294f3aef44c9a6 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 29 Aug 2017 15:16:19 +0300 Subject: qtnfmac: drop -D__CHECK_ENDIAN from cflags Flag -D__CHECK_ENDIAN was wrong: it should be -D__CHECK_ENDIAN__ instead. However now this flag is enabled by default, so it can be removed. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/Makefile | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/Makefile b/drivers/net/wireless/quantenna/qtnfmac/Makefile index 0d618e5e5f5b..f236b7dc2be3 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/Makefile +++ b/drivers/net/wireless/quantenna/qtnfmac/Makefile @@ -25,7 +25,3 @@ qtnfmac_pearl_pcie-objs += \ pearl/pcie.o qtnfmac_pearl_pcie-$(CONFIG_DEBUG_FS) += debug.o - -# - -ccflags-y += -D__CHECK_ENDIAN -- cgit v1.2.3 From 97f38011451f53fe9a495ff5e0ca035d91dd550d Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 29 Aug 2017 15:16:20 +0300 Subject: qtnfmac: module param sanity check Linux built-in circ_buf implementation assumes that that the circular buffer length is a power of 2. Make sure that rx and tx descriptor queue lengths are power-of-2. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index cd2f2b667643..fd552d64f943 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "qtn_hw_ids.h" #include "pcie_bus_priv.h" @@ -39,11 +40,11 @@ MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt"); static unsigned int tx_bd_size_param = 32; module_param(tx_bd_size_param, uint, 0644); -MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size"); +MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size, power of two"); static unsigned int rx_bd_size_param = 256; module_param(rx_bd_size_param, uint, 0644); -MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size"); +MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size, power of two"); static u8 flashboot = 1; module_param(flashboot, byte, 0644); @@ -509,6 +510,18 @@ static int qtnf_pcie_init_xfer(struct qtnf_pcie_bus_priv *priv) priv->rx_bd_w_index = 0; priv->rx_bd_r_index = 0; + if (!priv->tx_bd_num || !is_power_of_2(priv->tx_bd_num)) { + pr_err("tx_bd_size_param %u is not power of two\n", + priv->tx_bd_num); + return -EINVAL; + } + + if (!priv->rx_bd_num || !is_power_of_2(priv->rx_bd_num)) { + pr_err("rx_bd_size_param %u is not power of two\n", + priv->rx_bd_num); + return -EINVAL; + } + ret = alloc_skb_array(priv); if (ret) { pr_err("failed to allocate skb array\n"); -- cgit v1.2.3 From bab5dac73c08e0a18717756c4aff6417279bf2f9 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 29 Aug 2017 15:16:21 +0300 Subject: qtnfmac: modify qtnf_map_bar not to return NULL NULL is not a special type of success here but a error pointer. So it makes sense to check against NULL in qtnf_map_bar and return error code. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index fd552d64f943..bfbcd0bf75bf 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -184,8 +184,10 @@ static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index) return IOMEM_ERR_PTR(ret); busaddr = pci_resource_start(priv->pdev, index); - vaddr = pcim_iomap_table(priv->pdev)[index]; len = pci_resource_len(priv->pdev, index); + vaddr = pcim_iomap_table(priv->pdev)[index]; + if (!vaddr) + return IOMEM_ERR_PTR(-ENOMEM); pr_debug("BAR%u vaddr=0x%p busaddr=%pad len=%u\n", index, vaddr, &busaddr, (int)len); @@ -248,19 +250,19 @@ static int qtnf_pcie_init_memory(struct qtnf_pcie_bus_priv *priv) int ret = -ENOMEM; priv->sysctl_bar = qtnf_map_bar(priv, QTN_SYSCTL_BAR); - if (IS_ERR_OR_NULL(priv->sysctl_bar)) { + if (IS_ERR(priv->sysctl_bar)) { pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR); return ret; } priv->dmareg_bar = qtnf_map_bar(priv, QTN_DMA_BAR); - if (IS_ERR_OR_NULL(priv->dmareg_bar)) { + if (IS_ERR(priv->dmareg_bar)) { pr_err("failed to map BAR%u\n", QTN_DMA_BAR); return ret; } priv->epmem_bar = qtnf_map_bar(priv, QTN_SHMEM_BAR); - if (IS_ERR_OR_NULL(priv->epmem_bar)) { + if (IS_ERR(priv->epmem_bar)) { pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR); return ret; } -- cgit v1.2.3 From b00edea3ed5fd4241d211671602d7a39d9482295 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 29 Aug 2017 15:16:22 +0300 Subject: qtnfmac: fix free_xfer_buffer cleanup Check if skb tracking arrays has been already allocated. This additional check handles the case when init partially failed. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index bfbcd0bf75bf..2921d8069bf2 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -483,7 +483,7 @@ static void free_xfer_buffers(void *data) /* free rx buffers */ for (i = 0; i < priv->rx_bd_num; i++) { - if (priv->rx_skb[i]) { + if (priv->rx_skb && priv->rx_skb[i]) { rxbd = &priv->rx_bd_vbase[i]; paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h), le32_to_cpu(rxbd->addr)); @@ -496,7 +496,7 @@ static void free_xfer_buffers(void *data) /* free tx buffers */ for (i = 0; i < priv->tx_bd_num; i++) { - if (priv->tx_skb[i]) { + if (priv->tx_skb && priv->tx_skb[i]) { dev_kfree_skb_any(priv->tx_skb[i]); priv->tx_skb[i] = NULL; } -- cgit v1.2.3 From f31039d4aea97d3827d0518f267f32a74dbdc498 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 29 Aug 2017 15:16:23 +0300 Subject: qtnfmac: implement 64-bit dma support Use 64-bit dma for hosts with CONFIG_ARCH_DMA_ADDR_T_64BIT enabled. Signed-off-by: Sergey Matyukevich Signed-off-by: Kalle Valo --- .../net/wireless/quantenna/qtnfmac/pearl/pcie.c | 70 ++++++++++++++++++---- .../wireless/quantenna/qtnfmac/pearl/pcie_ipc.h | 10 ++-- .../quantenna/qtnfmac/pearl/pcie_regs_pearl.h | 1 + 3 files changed, 65 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index 2921d8069bf2..502e72b7cdcc 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -403,10 +403,12 @@ static int alloc_bd_table(struct qtnf_pcie_bus_priv *priv) priv->rx_bd_vbase = vaddr; priv->rx_bd_pbase = paddr; - writel(QTN_HOST_LO32(paddr), - PCIE_HDP_TX_HOST_Q_BASE_L(priv->pcie_reg_base)); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT writel(QTN_HOST_HI32(paddr), PCIE_HDP_TX_HOST_Q_BASE_H(priv->pcie_reg_base)); +#endif + writel(QTN_HOST_LO32(paddr), + PCIE_HDP_TX_HOST_Q_BASE_L(priv->pcie_reg_base)); writel(priv->rx_bd_num | (sizeof(struct qtnf_rx_bd)) << 16, PCIE_HDP_TX_HOST_Q_SZ_CTRL(priv->pcie_reg_base)); @@ -447,8 +449,10 @@ static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 index) /* sync up all descriptor updates */ wmb(); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT writel(QTN_HOST_HI32(paddr), PCIE_HDP_HHBM_BUF_PTR_H(priv->pcie_reg_base)); +#endif writel(QTN_HOST_LO32(paddr), PCIE_HDP_HHBM_BUF_PTR(priv->pcie_reg_base)); @@ -503,9 +507,28 @@ static void free_xfer_buffers(void *data) } } +static int qtnf_hhbm_init(struct qtnf_pcie_bus_priv *priv) +{ + u32 val; + + val = readl(PCIE_HHBM_CONFIG(priv->pcie_reg_base)); + val |= HHBM_CONFIG_SOFT_RESET; + writel(val, PCIE_HHBM_CONFIG(priv->pcie_reg_base)); + usleep_range(50, 100); + val &= ~HHBM_CONFIG_SOFT_RESET; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + val |= HHBM_64BIT; +#endif + writel(val, PCIE_HHBM_CONFIG(priv->pcie_reg_base)); + writel(priv->rx_bd_num, PCIE_HHBM_Q_LIMIT_REG(priv->pcie_reg_base)); + + return 0; +} + static int qtnf_pcie_init_xfer(struct qtnf_pcie_bus_priv *priv) { int ret; + u32 val; priv->tx_bd_num = tx_bd_size_param; priv->rx_bd_num = rx_bd_size_param; @@ -518,12 +541,32 @@ static int qtnf_pcie_init_xfer(struct qtnf_pcie_bus_priv *priv) return -EINVAL; } + val = priv->tx_bd_num * sizeof(struct qtnf_tx_bd); + if (val > PCIE_HHBM_MAX_SIZE) { + pr_err("tx_bd_size_param %u is too large\n", + priv->tx_bd_num); + return -EINVAL; + } + if (!priv->rx_bd_num || !is_power_of_2(priv->rx_bd_num)) { pr_err("rx_bd_size_param %u is not power of two\n", priv->rx_bd_num); return -EINVAL; } + val = priv->rx_bd_num * sizeof(dma_addr_t); + if (val > PCIE_HHBM_MAX_SIZE) { + pr_err("rx_bd_size_param %u is too large\n", + priv->rx_bd_num); + return -EINVAL; + } + + ret = qtnf_hhbm_init(priv); + if (ret) { + pr_err("failed to init h/w queues\n"); + return ret; + } + ret = alloc_skb_array(priv); if (ret) { pr_err("failed to allocate skb array\n"); @@ -653,10 +696,13 @@ static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) /* write new TX descriptor to PCIE_RX_FIFO on EP */ txbd_paddr = priv->tx_bd_pbase + i * sizeof(struct qtnf_tx_bd); - writel(QTN_HOST_LO32(txbd_paddr), - PCIE_HDP_HOST_WR_DESC0(priv->pcie_reg_base)); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT writel(QTN_HOST_HI32(txbd_paddr), PCIE_HDP_HOST_WR_DESC0_H(priv->pcie_reg_base)); +#endif + writel(QTN_HOST_LO32(txbd_paddr), + PCIE_HDP_HOST_WR_DESC0(priv->pcie_reg_base)); if (++i >= priv->tx_bd_num) i = 0; @@ -1237,6 +1283,16 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) pr_debug("successful init of PCI device %x\n", pdev->device); } +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); +#else + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); +#endif + if (ret) { + pr_err("PCIE DMA coherent mask init failed\n"); + goto err_base; + } + pcim_pin_device(pdev); pci_set_master(pdev); @@ -1258,12 +1314,6 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_base; } - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) { - pr_err("PCIE DMA mask init failed\n"); - goto err_base; - } - ret = devm_add_action(&pdev->dev, free_xfer_buffers, (void *)pcie_priv); if (ret) { pr_err("custom release callback init failed\n"); diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h index 667f5ec457e3..c5a4e46d26ef 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h @@ -57,16 +57,14 @@ | PCIE_HDP_INT_EP_RXDMA \ ) -#if BITS_PER_LONG == 64 +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT #define QTN_HOST_HI32(a) ((u32)(((u64)a) >> 32)) #define QTN_HOST_LO32(a) ((u32)(((u64)a) & 0xffffffffUL)) #define QTN_HOST_ADDR(h, l) ((((u64)h) << 32) | ((u64)l)) -#elif BITS_PER_LONG == 32 +#else #define QTN_HOST_HI32(a) 0 #define QTN_HOST_LO32(a) ((u32)(((u32)a) & 0xffffffffUL)) #define QTN_HOST_ADDR(h, l) ((u32)l) -#else -#error Unexpected BITS_PER_LONG value #endif #define QTN_SYSCTL_BAR 0 @@ -76,7 +74,7 @@ #define QTN_PCIE_BDA_VERSION 0x1002 #define PCIE_BDA_NAMELEN 32 -#define PCIE_HHBM_MAX_SIZE 512 +#define PCIE_HHBM_MAX_SIZE 2048 #define SKB_BUF_SIZE 2048 @@ -113,7 +111,7 @@ struct qtnf_pcie_bda { __le32 bda_flashsz; u8 bda_boardname[PCIE_BDA_NAMELEN]; __le32 bda_rc_msi_enabled; - __le32 bda_hhbm_list[PCIE_HHBM_MAX_SIZE]; + u8 bda_hhbm_list[PCIE_HHBM_MAX_SIZE]; __le32 bda_dsbw_start_index; __le32 bda_dsbw_end_index; __le32 bda_dsbw_total_bytes; diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h index 69696f118769..5b48b425fa7f 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h @@ -109,6 +109,7 @@ #define HHBM_WR_REQ (BIT(0)) #define HHBM_RD_REQ (BIT(1)) #define HHBM_DONE (BIT(31)) +#define HHBM_64BIT (BIT(10)) /* offsets for dual PCIE */ #define PCIE_PORT_LINK_CTL(base) ((base) + 0x0710) -- cgit v1.2.3 From f38966a7ace842afd3a9bf5d0fb56640f49df60c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 30 Aug 2017 15:54:49 +0200 Subject: brcmfmac: Log chip id and revision For debugging some problems, it is useful to know the chip revision add a brcmf_info message logging this. Signed-off-by: Hans de Goede Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index d231042f19d6..091b52979e03 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -601,6 +601,9 @@ int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev, if ((nvram_name) && (mapping_table[i].nvram)) strlcat(nvram_name, mapping_table[i].nvram, BRCMF_FW_NAME_LEN); + brcmf_info("using %s for chip %#08x(%d) rev %#08x\n", + fw_name, chip, chip, chiprev); + return 0; } -- cgit v1.2.3 From a9a65a6b2b69e51f453fe26eeba1e45a720bbb05 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 30 Aug 2017 12:04:24 -0500 Subject: rtlwifi: rtl8723be: fix duplicated code for different branches Refactor code in order to avoid identical code for different branches. Addresses-Coverity-ID: 1248728 Signed-off-by: Gustavo A. R. Silva Acked-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8723be/dm.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/dm.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/dm.c index 131c0d1d633e..15c117e95a99 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/dm.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/dm.c @@ -883,12 +883,8 @@ static void rtl8723be_dm_txpower_tracking_callback_thermalmeter( if ((rtldm->power_index_offset[RF90_PATH_A] != 0) && (rtldm->txpower_track_control)) { rtldm->done_txpower = true; - if (thermalvalue > rtlefuse->eeprom_thermalmeter) - rtl8723be_dm_tx_power_track_set_power(hw, BBSWING, 0, - index_for_channel); - else - rtl8723be_dm_tx_power_track_set_power(hw, BBSWING, 0, - index_for_channel); + rtl8723be_dm_tx_power_track_set_power(hw, BBSWING, 0, + index_for_channel); rtldm->swing_idx_cck_base = rtldm->swing_idx_cck; rtldm->swing_idx_ofdm_base[RF90_PATH_A] = -- cgit v1.2.3 From 32faa3f0ee509176aeac0009e3ee93f9b15f07a5 Mon Sep 17 00:00:00 2001 From: Ryan Hsu Date: Thu, 31 Aug 2017 15:36:14 +0300 Subject: ath10k: add the PCI PM core suspend/resume ops The actual PCI suspend/resume in ath10k has been handled in wow.c, but in the case of the device doesn't support remote wakeup, the .hif_suspend() and .hif_resume() will never be handled. ath10k_wow_op_suspend() { if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, ar->running_fw->fw_file.fw_features))) { ret = 1; goto exit; } .... ret = ath10k_hif_suspend(ar); } So register the PCI PM core to support the suspend/resume if the device doesn't support remote wakeup. Signed-off-by: Ryan Hsu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 0a5b88e80214..bc1633945a56 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3396,11 +3396,53 @@ static void ath10k_pci_remove(struct pci_dev *pdev) MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table); +#ifdef CONFIG_PM + +static int ath10k_pci_pm_suspend(struct device *dev) +{ + struct ath10k *ar = dev_get_drvdata(dev); + int ret; + + if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, + ar->running_fw->fw_file.fw_features)) + return 0; + + ret = ath10k_hif_suspend(ar); + if (ret) + ath10k_warn(ar, "failed to suspend hif: %d\n", ret); + + return ret; +} + +static int ath10k_pci_pm_resume(struct device *dev) +{ + struct ath10k *ar = dev_get_drvdata(dev); + int ret; + + if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, + ar->running_fw->fw_file.fw_features)) + return 0; + + ret = ath10k_hif_resume(ar); + if (ret) + ath10k_warn(ar, "failed to resume hif: %d\n", ret); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(ath10k_pci_pm_ops, + ath10k_pci_pm_suspend, + ath10k_pci_pm_resume); +#endif + static struct pci_driver ath10k_pci_driver = { .name = "ath10k_pci", .id_table = ath10k_pci_id_table, .probe = ath10k_pci_probe, .remove = ath10k_pci_remove, +#ifdef CONFIG_PM + .driver.pm = &ath10k_pci_pm_ops, +#endif }; static int __init ath10k_pci_init(void) -- cgit v1.2.3 From 393b706cf20c2b612ca88fb2e48b43e3e376796f Mon Sep 17 00:00:00 2001 From: Ryan Hsu Date: Thu, 31 Aug 2017 15:36:16 +0300 Subject: ath10k: configure and enable the wakeup capability ACPI will rely on device driver to tell it if the device could support wakeup function when system in D3 state. This has caused some platform can't support remote wakeup correctly, because the ACPI wakeup GPE is not enabled, hence registers the .set_wakeup callback to handle it if device supports wakeup. Tested with QCA6174 hw3.0, firmware ('WLAN.RM.4.4.1-00008-QCARMSWP-1') Signed-off-by: Ryan Hsu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 1 + drivers/net/wireless/ath/ath10k/wow.c | 14 ++++++++++++++ drivers/net/wireless/ath/ath10k/wow.h | 1 + 3 files changed, 16 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 523a5490dece..5683f1a5330e 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -7644,6 +7644,7 @@ static const struct ieee80211_ops ath10k_ops = { #ifdef CONFIG_PM .suspend = ath10k_wow_op_suspend, .resume = ath10k_wow_op_resume, + .set_wakeup = ath10k_wow_op_set_wakeup, #endif #ifdef CONFIG_MAC80211_DEBUGFS .sta_add_debugfs = ath10k_sta_add_debugfs, diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index 77100d42f401..0d46d6dc7578 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -277,6 +277,18 @@ exit: return ret ? 1 : 0; } +void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct ath10k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, + ar->running_fw->fw_file.fw_features)) { + device_set_wakeup_enable(ar->dev, enabled); + } + mutex_unlock(&ar->conf_mutex); +} + int ath10k_wow_op_resume(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; @@ -336,5 +348,7 @@ int ath10k_wow_init(struct ath10k *ar) ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; + device_set_wakeup_capable(ar->dev, true); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/wow.h b/drivers/net/wireless/ath/ath10k/wow.h index abbb04b6d1bd..9745b9ddc7f5 100644 --- a/drivers/net/wireless/ath/ath10k/wow.h +++ b/drivers/net/wireless/ath/ath10k/wow.h @@ -28,6 +28,7 @@ int ath10k_wow_init(struct ath10k *ar); int ath10k_wow_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int ath10k_wow_op_resume(struct ieee80211_hw *hw); +void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled); #else -- cgit v1.2.3 From 1d1056d80b8e44efcc05298bfa85fa5ec0998bc9 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 31 Aug 2017 17:59:13 +0200 Subject: mlxsw: spectrum_router: Export IPv6 link local address check helper Neighbors with link local addresses are not offloaded to the host table, yet, the are maintained in the driver for adjacency table usage. When dumping the IPv6 host neighbors this link local neighbors should be ignored. This patch exports this helper for dpipe usage. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 6 ++++-- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 377d85cff7fa..1f41bcdf04ca 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1396,8 +1396,10 @@ mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp, mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); } -static bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n) +bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry) { + struct neighbour *n = neigh_entry->key.n; + /* Packets with a link-local destination address are trapped * after LPM lookup and never reach the neighbour table, so * there is no need to program such neighbours to the device. @@ -1420,7 +1422,7 @@ mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry, mlxsw_sp_rauht_op(adding)); } else if (neigh_entry->key.n->tbl->family == AF_INET6) { - if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n)) + if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry)) return; mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry, mlxsw_sp_rauht_op(adding)); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index fb0f971c670c..5b68616a4234 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -76,5 +76,6 @@ void mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_neigh_entry *neigh_entry, bool adding); +bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry); #endif /* _MLXSW_ROUTER_H_*/ -- cgit v1.2.3 From 506f7dd56de7631ce7ba80ca150164c905ecf262 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 31 Aug 2017 17:59:14 +0200 Subject: mlxsw: spectrum_dpipe: Add IPv6 host table initial support Add IPv6 host table initial support. The action behavior for both IPv4/6 tables is the same, thus the same action dump op is used. Neighbors with link local address are ignored. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 76 ++++++++++++++++++++-- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.h | 1 + 2 files changed, 71 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 3ea13148ed05..93ba6a6ccfb8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -76,6 +76,7 @@ static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = { &mlxsw_sp_dpipe_header_metadata, &devlink_dpipe_header_ethernet, &devlink_dpipe_header_ipv4, + &devlink_dpipe_header_ipv6, }; static struct devlink_dpipe_headers mlxsw_sp_dpipe_headers = { @@ -328,9 +329,21 @@ static int mlxsw_sp_dpipe_table_host_matches_dump(struct sk_buff *skb, int type) if (err) return err; - match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; - match.header = &devlink_dpipe_header_ipv4; - match.field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP; + switch (type) { + case AF_INET: + match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match.header = &devlink_dpipe_header_ipv4; + match.field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP; + break; + case AF_INET6: + match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match.header = &devlink_dpipe_header_ipv6; + match.field_id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP; + break; + default: + WARN_ON(1); + return -EINVAL; + } return devlink_dpipe_match_put(skb, &match); } @@ -342,7 +355,7 @@ mlxsw_sp_dpipe_table_host4_matches_dump(void *priv, struct sk_buff *skb) } static int -mlxsw_sp_dpipe_table_host4_actions_dump(void *priv, struct sk_buff *skb) +mlxsw_sp_dpipe_table_host_actions_dump(void *priv, struct sk_buff *skb) { struct devlink_dpipe_action action = {0}; @@ -648,8 +661,15 @@ mlxsw_sp_dpipe_table_host_size_get(struct mlxsw_sp *mlxsw_sp, int type) if (!rif) continue; mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { - if (mlxsw_sp_neigh_entry_type(neigh_entry) != type) + int neigh_type = mlxsw_sp_neigh_entry_type(neigh_entry); + + if (neigh_type != type) continue; + + if (neigh_type == AF_INET6 && + mlxsw_sp_neigh_ipv6_ignore(neigh_entry)) + continue; + size++; } } @@ -667,7 +687,7 @@ static u64 mlxsw_sp_dpipe_table_host4_size_get(void *priv) static struct devlink_dpipe_table_ops mlxsw_sp_host4_ops = { .matches_dump = mlxsw_sp_dpipe_table_host4_matches_dump, - .actions_dump = mlxsw_sp_dpipe_table_host4_actions_dump, + .actions_dump = mlxsw_sp_dpipe_table_host_actions_dump, .entries_dump = mlxsw_sp_dpipe_table_host4_entries_dump, .counters_set_update = mlxsw_sp_dpipe_table_host4_counters_update, .size_get = mlxsw_sp_dpipe_table_host4_size_get, @@ -691,6 +711,43 @@ static void mlxsw_sp_dpipe_host4_table_fini(struct mlxsw_sp *mlxsw_sp) MLXSW_SP_DPIPE_TABLE_NAME_HOST4); } +static int +mlxsw_sp_dpipe_table_host6_matches_dump(void *priv, struct sk_buff *skb) +{ + return mlxsw_sp_dpipe_table_host_matches_dump(skb, AF_INET6); +} + +static u64 mlxsw_sp_dpipe_table_host6_size_get(void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return mlxsw_sp_dpipe_table_host_size_get(mlxsw_sp, AF_INET6); +} + +static struct devlink_dpipe_table_ops mlxsw_sp_host6_ops = { + .matches_dump = mlxsw_sp_dpipe_table_host6_matches_dump, + .actions_dump = mlxsw_sp_dpipe_table_host_actions_dump, + .size_get = mlxsw_sp_dpipe_table_host6_size_get, +}; + +static int mlxsw_sp_dpipe_host6_table_init(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + return devlink_dpipe_table_register(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_HOST6, + &mlxsw_sp_host6_ops, + mlxsw_sp, false); +} + +static void mlxsw_sp_dpipe_host6_table_fini(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + devlink_dpipe_table_unregister(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_HOST6); +} + int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) { struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); @@ -707,8 +764,14 @@ int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) err = mlxsw_sp_dpipe_host4_table_init(mlxsw_sp); if (err) goto err_host4_table_init; + + err = mlxsw_sp_dpipe_host6_table_init(mlxsw_sp); + if (err) + goto err_host6_table_init; return 0; +err_host6_table_init: + mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp); err_host4_table_init: mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp); err_erif_table_init: @@ -720,6 +783,7 @@ void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp) { struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + mlxsw_sp_dpipe_host6_table_fini(mlxsw_sp); mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp); mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp); devlink_dpipe_headers_unregister(devlink); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h index c56a3d92a2bb..283fde4e6783 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h @@ -55,5 +55,6 @@ static inline void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp) #define MLXSW_SP_DPIPE_TABLE_NAME_ERIF "mlxsw_erif" #define MLXSW_SP_DPIPE_TABLE_NAME_HOST4 "mlxsw_host4" +#define MLXSW_SP_DPIPE_TABLE_NAME_HOST6 "mlxsw_host6" #endif /* _MLXSW_PIPELINE_H_*/ -- cgit v1.2.3 From 0250768c6c6e131bded979fd939bc4642a506a07 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 31 Aug 2017 17:59:15 +0200 Subject: mlxsw: spectrum_router: Add IPv6 neighbor access helper Add helper for accessing destination IP in case of IPv6 neighbor. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 9 +++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 1f41bcdf04ca..db57c0c4c9d8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -947,6 +947,15 @@ u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry) return ntohl(*((__be32 *) n->primary_key)); } +struct in6_addr * +mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry) +{ + struct neighbour *n; + + n = neigh_entry->key.n; + return (struct in6_addr *) &n->primary_key; +} + int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_neigh_entry *neigh_entry, u64 *p_counter) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index 5b68616a4234..87a04afee138 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -65,6 +65,8 @@ int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry); unsigned char * mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry); u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry); +struct in6_addr * +mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry); #define mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) \ for (neigh_entry = mlxsw_sp_rif_neigh_next(rif, NULL); neigh_entry; \ -- cgit v1.2.3 From 6049e5390cfdcc5df9114d2721a5f0e32baf9824 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 31 Aug 2017 17:59:16 +0200 Subject: mlxsw: spectrum_dpipe: Make host entry fill handler more generic Change the host entry filler helper to be applicable for both IPv4/6 addresses. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 93ba6a6ccfb8..5924e97d5958 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -439,13 +439,12 @@ mlxsw_sp_dpipe_table_host_entry_prepare(struct devlink_dpipe_entry *entry, } static void -__mlxsw_sp_dpipe_table_host4_entry_fill(struct devlink_dpipe_entry *entry, - struct mlxsw_sp_rif *rif, - unsigned char *ha, u32 dip) +__mlxsw_sp_dpipe_table_host_entry_fill(struct devlink_dpipe_entry *entry, + struct mlxsw_sp_rif *rif, + unsigned char *ha, void *dip) { struct devlink_dpipe_value *value; u32 *rif_value; - u32 *dip_value; u8 *ha_value; /* Set Match RIF index */ @@ -458,9 +457,7 @@ __mlxsw_sp_dpipe_table_host4_entry_fill(struct devlink_dpipe_entry *entry, /* Set Match DIP */ value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; - - dip_value = value->value; - *dip_value = dip; + memcpy(value->value, dip, value->value_size); /* Set Action DMAC */ value = entry->action_values; @@ -478,7 +475,7 @@ mlxsw_sp_dpipe_table_host4_entry_fill(struct devlink_dpipe_entry *entry, ha = mlxsw_sp_neigh_entry_ha(neigh_entry); dip = mlxsw_sp_neigh4_entry_dip(neigh_entry); - __mlxsw_sp_dpipe_table_host4_entry_fill(entry, rif, ha, dip); + __mlxsw_sp_dpipe_table_host_entry_fill(entry, rif, ha, &dip); } static void -- cgit v1.2.3 From 410774bde1155c85076d6d7105f6af4e97815045 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 31 Aug 2017 17:59:17 +0200 Subject: mlxsw: spectrum_dpipe: Add support for IPv6 host table dump Add support for IPv6 host table dump. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 75 ++++++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 5924e97d5958..75da2eff4db9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -386,8 +386,19 @@ mlxsw_sp_dpipe_table_host_match_action_prepare(struct devlink_dpipe_match *match match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; - match->header = &devlink_dpipe_header_ipv4; - match->field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP; + switch (type) { + case AF_INET: + match->header = &devlink_dpipe_header_ipv4; + match->field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP; + break; + case AF_INET6: + match->header = &devlink_dpipe_header_ipv6; + match->field_id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP; + break; + default: + WARN_ON(1); + return; + } action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY; action->header = &devlink_dpipe_header_ethernet; @@ -424,7 +435,18 @@ mlxsw_sp_dpipe_table_host_entry_prepare(struct devlink_dpipe_entry *entry, match_value = &match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; match_value->match = match; - match_value->value_size = sizeof(u32); + switch (type) { + case AF_INET: + match_value->value_size = sizeof(u32); + break; + case AF_INET6: + match_value->value_size = sizeof(struct in6_addr); + break; + default: + WARN_ON(1); + return -EINVAL; + } + match_value->value = kmalloc(match_value->value_size, GFP_KERNEL); if (!match_value->value) return -ENOMEM; @@ -478,6 +500,20 @@ mlxsw_sp_dpipe_table_host4_entry_fill(struct devlink_dpipe_entry *entry, __mlxsw_sp_dpipe_table_host_entry_fill(entry, rif, ha, &dip); } +static void +mlxsw_sp_dpipe_table_host6_entry_fill(struct devlink_dpipe_entry *entry, + struct mlxsw_sp_neigh_entry *neigh_entry, + struct mlxsw_sp_rif *rif) +{ + struct in6_addr *dip; + unsigned char *ha; + + ha = mlxsw_sp_neigh_entry_ha(neigh_entry); + dip = mlxsw_sp_neigh6_entry_dip(neigh_entry); + + __mlxsw_sp_dpipe_table_host_entry_fill(entry, rif, ha, dip); +} + static void mlxsw_sp_dpipe_table_host_entry_fill(struct mlxsw_sp *mlxsw_sp, struct devlink_dpipe_entry *entry, @@ -487,7 +523,18 @@ mlxsw_sp_dpipe_table_host_entry_fill(struct mlxsw_sp *mlxsw_sp, { int err; - mlxsw_sp_dpipe_table_host4_entry_fill(entry, neigh_entry, rif); + switch (type) { + case AF_INET: + mlxsw_sp_dpipe_table_host4_entry_fill(entry, neigh_entry, rif); + break; + case AF_INET6: + mlxsw_sp_dpipe_table_host6_entry_fill(entry, neigh_entry, rif); + break; + default: + WARN_ON(1); + return; + } + err = mlxsw_sp_neigh_counter_get(mlxsw_sp, neigh_entry, &entry->counter); if (!err) @@ -526,7 +573,13 @@ start_again: rif_neigh_count = 0; mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { - if (mlxsw_sp_neigh_entry_type(neigh_entry) != type) + int neigh_type = mlxsw_sp_neigh_entry_type(neigh_entry); + + if (neigh_type != type) + continue; + + if (neigh_type == AF_INET6 && + mlxsw_sp_neigh_ipv6_ignore(neigh_entry)) continue; if (rif_neigh_count < rif_neigh_skip) @@ -714,6 +767,17 @@ mlxsw_sp_dpipe_table_host6_matches_dump(void *priv, struct sk_buff *skb) return mlxsw_sp_dpipe_table_host_matches_dump(skb, AF_INET6); } +static int +mlxsw_sp_dpipe_table_host6_entries_dump(void *priv, bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return mlxsw_sp_dpipe_table_host_entries_dump(mlxsw_sp, + counters_enabled, + dump_ctx, AF_INET6); +} + static u64 mlxsw_sp_dpipe_table_host6_size_get(void *priv) { struct mlxsw_sp *mlxsw_sp = priv; @@ -724,6 +788,7 @@ static u64 mlxsw_sp_dpipe_table_host6_size_get(void *priv) static struct devlink_dpipe_table_ops mlxsw_sp_host6_ops = { .matches_dump = mlxsw_sp_dpipe_table_host6_matches_dump, .actions_dump = mlxsw_sp_dpipe_table_host_actions_dump, + .entries_dump = mlxsw_sp_dpipe_table_host6_entries_dump, .size_get = mlxsw_sp_dpipe_table_host6_size_get, }; -- cgit v1.2.3 From 1ed5574c6d48c6094bf688aa2cc755ea6ca4007c Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 31 Aug 2017 17:59:18 +0200 Subject: mlxsw: spectrum_router: Add support for setting counters on IPv6 neighbors Add support for setting counters on IPv6 neighbors based on dpipe's host6 table counter status. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index db57c0c4c9d8..0cf68102d113 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1008,21 +1008,33 @@ mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp, } static bool -mlxsw_sp_neigh4_counter_should_alloc(struct mlxsw_sp *mlxsw_sp) +mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry) { struct devlink *devlink; + const char *table_name; + + switch (mlxsw_sp_neigh_entry_type(neigh_entry)) { + case AF_INET: + table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4; + break; + case AF_INET6: + table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6; + break; + default: + WARN_ON(1); + return false; + } devlink = priv_to_devlink(mlxsw_sp->core); - return devlink_dpipe_table_counter_enabled(devlink, - MLXSW_SP_DPIPE_TABLE_NAME_HOST4); + return devlink_dpipe_table_counter_enabled(devlink, table_name); } static void mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_neigh_entry *neigh_entry) { - if (mlxsw_sp_neigh_entry_type(neigh_entry) != AF_INET || - !mlxsw_sp_neigh4_counter_should_alloc(mlxsw_sp)) + if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry)) return; if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index)) -- cgit v1.2.3 From 0fb5fe3c886cb808c6a304f3df3336bdd04e18f5 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 31 Aug 2017 17:59:19 +0200 Subject: mlxsw: spectrum_dpipe: Add support for controlling IPv6 neighbor counters Add support for controlling IPv6 neighbor counters via dpipe. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 75da2eff4db9..51e6846da72b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -679,8 +679,15 @@ mlxsw_sp_dpipe_table_host_counters_update(struct mlxsw_sp *mlxsw_sp, if (!rif) continue; mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { - if (mlxsw_sp_neigh_entry_type(neigh_entry) != type) + int neigh_type = mlxsw_sp_neigh_entry_type(neigh_entry); + + if (neigh_type != type) + continue; + + if (neigh_type == AF_INET6 && + mlxsw_sp_neigh_ipv6_ignore(neigh_entry)) continue; + mlxsw_sp_neigh_entry_counter_update(mlxsw_sp, neigh_entry, enable); @@ -778,6 +785,14 @@ mlxsw_sp_dpipe_table_host6_entries_dump(void *priv, bool counters_enabled, dump_ctx, AF_INET6); } +static int mlxsw_sp_dpipe_table_host6_counters_update(void *priv, bool enable) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + mlxsw_sp_dpipe_table_host_counters_update(mlxsw_sp, enable, AF_INET6); + return 0; +} + static u64 mlxsw_sp_dpipe_table_host6_size_get(void *priv) { struct mlxsw_sp *mlxsw_sp = priv; @@ -789,6 +804,7 @@ static struct devlink_dpipe_table_ops mlxsw_sp_host6_ops = { .matches_dump = mlxsw_sp_dpipe_table_host6_matches_dump, .actions_dump = mlxsw_sp_dpipe_table_host_actions_dump, .entries_dump = mlxsw_sp_dpipe_table_host6_entries_dump, + .counters_set_update = mlxsw_sp_dpipe_table_host6_counters_update, .size_get = mlxsw_sp_dpipe_table_host6_size_get, }; -- cgit v1.2.3 From d3b6d3774fc2b636b12f8ef6367ed294861469ed Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 1 Sep 2017 10:58:55 +0200 Subject: mlxsw: spectrum_router: Trap packets hitting anycast routes I relied on the fact that anycast routes use the loopback device as their nexthop device to trap packets hitting them to the CPU. After commit 4832c30d5458 ("net: ipv6: put host and anycast routes on device with address") this is no longer the case and such routes are programmed with a forward action (note the 'offload' flag): anycast cafe:: dev enp3s0np7 proto kernel metric 0 offload pref medium This will prevent the router from locally receiving packets destined to the Subnet-Router anycast address. Fix this by specifically programming anycast routes with action trap, which results in the following output: anycast cafe:: dev enp3s0np7 proto kernel metric 0 pref medium Fixes: 4832c30d5458 ("net: ipv6: put host and anycast routes on device with address") Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 0cf68102d113..00e3aec2d086 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3659,7 +3659,7 @@ static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry, * local, which will cause them to be trapped with a lower * priority than packets that need to be locally received. */ - if (rt->rt6i_flags & RTF_LOCAL) + if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; else if (rt->rt6i_flags & RTF_REJECT) fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; -- cgit v1.2.3 From 241bc859f6d0f912b8a2246896501a9d934149fa Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 1 Sep 2017 10:58:56 +0200 Subject: mlxsw: spectrum_router: Set abort trap in all virtual routers When the abort mechanism is invoked a default route directing packets to the CPU is programmed in all the virtual routers currently in use. This can result in packet loss in case a new VRF is configured. Upon abort, program the default route in all virtual routers, whether they are in use or not. The patch is directed at net-next since post-abort fixes aren't critical and packet loss due to a missing default route will be insignificant compared to packet loss caused by the CPU port policer. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 00e3aec2d086..de15eac50866 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3993,9 +3993,6 @@ static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp, char raltb_pl[MLXSW_REG_RALTB_LEN]; char ralue_pl[MLXSW_REG_RALUE_LEN]; - if (!mlxsw_sp_vr_is_used(vr)) - continue; - mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); -- cgit v1.2.3 From 1df2270d06fa65c3479e713eb00eca25896db653 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Fri, 1 Sep 2017 11:04:52 +0200 Subject: net: mvpp2: take advantage of the is_rgmii helper Convert all RGMII checks to use the phy_interface_mode_is_rgmii() helper. This is a cosmetic patch. Signed-off-by: Antoine Tenart Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 358a9de9daa7..b33be3ef25ae 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -4463,10 +4463,7 @@ static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port) val |= MVPP2_GMAC_DISABLE_PADDING; val &= ~MVPP2_GMAC_FLOW_CTRL_MASK; writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); - } else if (port->phy_interface == PHY_INTERFACE_MODE_RGMII || - port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || - port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || - port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) { + } else if (phy_interface_mode_is_rgmii(port->phy_interface)) { val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL | MVPP22_CTRL4_SYNC_BYPASS_DIS | @@ -4512,10 +4509,7 @@ static void mvpp2_port_mii_gmac_configure(struct mvpp2_port *port) val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) { val |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK; - } else if (port->phy_interface == PHY_INTERFACE_MODE_RGMII || - port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || - port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || - port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) { + } else if (phy_interface_mode_is_rgmii(port->phy_interface)) { val &= ~MVPP2_GMAC_PCS_ENABLE_MASK; val |= MVPP2_GMAC_PORT_RGMII_MASK; } @@ -4575,10 +4569,7 @@ static void mvpp2_port_mii_set(struct mvpp2_port *port) if (port->priv->hw_version == MVPP22) mvpp22_port_mii_set(port); - if (port->phy_interface == PHY_INTERFACE_MODE_RGMII || - port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || - port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || - port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID || + if (phy_interface_mode_is_rgmii(port->phy_interface) || port->phy_interface == PHY_INTERFACE_MODE_SGMII) mvpp2_port_mii_gmac_configure(port); else if (port->phy_interface == PHY_INTERFACE_MODE_10GKR) -- cgit v1.2.3 From 5997c86bf0e94f0b29f9ab416b1db62a8fa4e4eb Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Fri, 1 Sep 2017 11:04:53 +0200 Subject: net: mvpp2: make the phy optional There is not necessarily a PHY between the GoP and the physical port. However, the driver currently makes the "phy" property mandatory, contrary to what is stated in the device tree bindings. This patch makes the PHY optional, and aligns the PPv2 driver on its device tree documentation. However if a PHY is provided, the GoP link interrupt won't be used. With this patch switches directly connected to the serdes lanes and SFP ports on the Armada 8040-db and Armada 7040-db can be used if the link interrupt is described in the device tree. Signed-off-by: Antoine Tenart Tested-by: Marcin Wojtas Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index b33be3ef25ae..eebcf9c9e3d4 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -6475,7 +6475,8 @@ static void mvpp2_start_dev(struct mvpp2_port *port) mvpp2_port_mii_set(port); mvpp2_port_enable(port); - phy_start(ndev->phydev); + if (ndev->phydev) + phy_start(ndev->phydev); netif_tx_start_all_queues(port->dev); } @@ -6501,7 +6502,8 @@ static void mvpp2_stop_dev(struct mvpp2_port *port) mvpp2_egress_disable(port); mvpp2_port_disable(port); - phy_stop(ndev->phydev); + if (ndev->phydev) + phy_stop(ndev->phydev); phy_power_off(port->comphy); } @@ -6558,6 +6560,10 @@ static int mvpp2_phy_connect(struct mvpp2_port *port) { struct phy_device *phy_dev; + /* No PHY is attached */ + if (!port->phy_node) + return 0; + phy_dev = of_phy_connect(port->dev, port->phy_node, mvpp2_link_event, 0, port->phy_interface); if (!phy_dev) { @@ -6578,6 +6584,9 @@ static void mvpp2_phy_disconnect(struct mvpp2_port *port) { struct net_device *ndev = port->dev; + if (!ndev->phydev) + return; + phy_disconnect(ndev->phydev); } @@ -7340,12 +7349,6 @@ static int mvpp2_port_probe(struct platform_device *pdev, return -ENOMEM; phy_node = of_parse_phandle(port_node, "phy", 0); - if (!phy_node) { - dev_err(&pdev->dev, "missing phy\n"); - err = -ENODEV; - goto err_free_netdev; - } - phy_mode = of_get_phy_mode(port_node); if (phy_mode < 0) { dev_err(&pdev->dev, "incorrect phy mode\n"); -- cgit v1.2.3 From fd3651b2ab66de177ad0d51c239af260a764b9f1 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Fri, 1 Sep 2017 11:04:54 +0200 Subject: net: mvpp2: use the GoP interrupt for link status changes This patch adds the GoP link interrupt support for when a port isn't connected to a PHY. Because of this the phylib callback is never called and the link status management isn't done. This patch use the GoP link interrupt in such cases to still have a minimal link management. Without this patch ports not connected to a PHY cannot work. Signed-off-by: Antoine Tenart Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 177 ++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index eebcf9c9e3d4..f37c05fed5bc 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -348,16 +348,24 @@ #define MVPP2_GMAC_FLOW_CTRL_AUTONEG BIT(11) #define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) #define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) +#define MVPP2_GMAC_STATUS0 0x10 +#define MVPP2_GMAC_STATUS0_LINK_UP BIT(0) #define MVPP2_GMAC_PORT_FIFO_CFG_1_REG 0x1c #define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6 #define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 #define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK) +#define MVPP22_GMAC_INT_STAT 0x20 +#define MVPP22_GMAC_INT_STAT_LINK BIT(1) +#define MVPP22_GMAC_INT_MASK 0x24 +#define MVPP22_GMAC_INT_MASK_LINK_STAT BIT(1) #define MVPP22_GMAC_CTRL_4_REG 0x90 #define MVPP22_CTRL4_EXT_PIN_GMII_SEL BIT(0) #define MVPP22_CTRL4_DP_CLK_SEL BIT(5) #define MVPP22_CTRL4_SYNC_BYPASS_DIS BIT(6) #define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7) +#define MVPP22_GMAC_INT_SUM_MASK 0xa4 +#define MVPP22_GMAC_INT_SUM_MASK_LINK_STAT BIT(1) /* Per-port XGMAC registers. PPv2.2 only, only for GOP port 0, * relative to port->base. @@ -370,11 +378,19 @@ #define MVPP22_XLG_CTRL1_REG 0x104 #define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS 0 #define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK 0x1fff +#define MVPP22_XLG_STATUS 0x10c +#define MVPP22_XLG_STATUS_LINK_UP BIT(0) +#define MVPP22_XLG_INT_STAT 0x114 +#define MVPP22_XLG_INT_STAT_LINK BIT(1) +#define MVPP22_XLG_INT_MASK 0x118 +#define MVPP22_XLG_INT_MASK_LINK BIT(1) #define MVPP22_XLG_CTRL3_REG 0x11c #define MVPP22_XLG_CTRL3_MACMODESELECT_MASK (7 << 13) #define MVPP22_XLG_CTRL3_MACMODESELECT_GMAC (0 << 13) #define MVPP22_XLG_CTRL3_MACMODESELECT_10G (1 << 13) - +#define MVPP22_XLG_EXT_INT_MASK 0x15c +#define MVPP22_XLG_EXT_INT_MASK_XLG BIT(1) +#define MVPP22_XLG_EXT_INT_MASK_GIG BIT(2) #define MVPP22_XLG_CTRL4_REG 0x184 #define MVPP22_XLG_CTRL4_FWD_FC BIT(5) #define MVPP22_XLG_CTRL4_FWD_PFC BIT(6) @@ -837,6 +853,8 @@ struct mvpp2_port { */ int gop_id; + int link_irq; + struct mvpp2 *priv; /* Per-port registers' base address */ @@ -4422,6 +4440,68 @@ invalid_conf: return -EINVAL; } +static void mvpp22_gop_unmask_irq(struct mvpp2_port *port) +{ + u32 val; + + if (phy_interface_mode_is_rgmii(port->phy_interface) || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + /* Enable the GMAC link status irq for this port */ + val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK); + val |= MVPP22_GMAC_INT_SUM_MASK_LINK_STAT; + writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK); + } + + if (port->gop_id == 0) { + /* Enable the XLG/GIG irqs for this port */ + val = readl(port->base + MVPP22_XLG_EXT_INT_MASK); + if (port->phy_interface == PHY_INTERFACE_MODE_10GKR) + val |= MVPP22_XLG_EXT_INT_MASK_XLG; + else + val |= MVPP22_XLG_EXT_INT_MASK_GIG; + writel(val, port->base + MVPP22_XLG_EXT_INT_MASK); + } +} + +static void mvpp22_gop_mask_irq(struct mvpp2_port *port) +{ + u32 val; + + if (port->gop_id == 0) { + val = readl(port->base + MVPP22_XLG_EXT_INT_MASK); + val &= ~(MVPP22_XLG_EXT_INT_MASK_XLG | + MVPP22_XLG_EXT_INT_MASK_GIG); + writel(val, port->base + MVPP22_XLG_EXT_INT_MASK); + } + + if (phy_interface_mode_is_rgmii(port->phy_interface) || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK); + val &= ~MVPP22_GMAC_INT_SUM_MASK_LINK_STAT; + writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK); + } +} + +static void mvpp22_gop_setup_irq(struct mvpp2_port *port) +{ + u32 val; + + if (phy_interface_mode_is_rgmii(port->phy_interface) || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP22_GMAC_INT_MASK); + val |= MVPP22_GMAC_INT_MASK_LINK_STAT; + writel(val, port->base + MVPP22_GMAC_INT_MASK); + } + + if (port->gop_id == 0) { + val = readl(port->base + MVPP22_XLG_INT_MASK); + val |= MVPP22_XLG_INT_MASK_LINK; + writel(val, port->base + MVPP22_XLG_INT_MASK); + } + + mvpp22_gop_unmask_irq(port); +} + static int mvpp22_comphy_init(struct mvpp2_port *port) { enum phy_mode mode; @@ -5726,6 +5806,60 @@ static irqreturn_t mvpp2_isr(int irq, void *dev_id) return IRQ_HANDLED; } +/* Per-port interrupt for link status changes */ +static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id) +{ + struct mvpp2_port *port = (struct mvpp2_port *)dev_id; + struct net_device *dev = port->dev; + bool event = false, link = false; + u32 val; + + mvpp22_gop_mask_irq(port); + + if (port->gop_id == 0 && + port->phy_interface == PHY_INTERFACE_MODE_10GKR) { + val = readl(port->base + MVPP22_XLG_INT_STAT); + if (val & MVPP22_XLG_INT_STAT_LINK) { + event = true; + val = readl(port->base + MVPP22_XLG_STATUS); + if (val & MVPP22_XLG_STATUS_LINK_UP) + link = true; + } + } else if (phy_interface_mode_is_rgmii(port->phy_interface) || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP22_GMAC_INT_STAT); + if (val & MVPP22_GMAC_INT_STAT_LINK) { + event = true; + val = readl(port->base + MVPP2_GMAC_STATUS0); + if (val & MVPP2_GMAC_STATUS0_LINK_UP) + link = true; + } + } + + if (!netif_running(dev) || !event) + goto handled; + + if (link) { + mvpp2_interrupts_enable(port); + + mvpp2_egress_enable(port); + mvpp2_ingress_enable(port); + netif_carrier_on(dev); + netif_tx_wake_all_queues(dev); + } else { + netif_tx_stop_all_queues(dev); + netif_carrier_off(dev); + mvpp2_ingress_disable(port); + mvpp2_egress_disable(port); + + mvpp2_interrupts_disable(port); + } + +handled: + mvpp22_gop_unmask_irq(port); + return IRQ_HANDLED; +} + static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port, struct phy_device *phydev) { @@ -5754,7 +5888,6 @@ static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port, val |= MVPP2_GMAC_CONFIG_MII_SPEED; writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); - } /* Adjust link */ @@ -6633,6 +6766,7 @@ static void mvpp2_irqs_deinit(struct mvpp2_port *port) static int mvpp2_open(struct net_device *dev) { struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2 *priv = port->priv; unsigned char mac_bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int err; @@ -6678,12 +6812,24 @@ static int mvpp2_open(struct net_device *dev) goto err_cleanup_txqs; } + if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) { + err = request_irq(port->link_irq, mvpp2_link_status_isr, 0, + dev->name, port); + if (err) { + netdev_err(port->dev, "cannot request link IRQ %d\n", + port->link_irq); + goto err_free_irq; + } + + mvpp22_gop_setup_irq(port); + } + /* In default link is down */ netif_carrier_off(port->dev); err = mvpp2_phy_connect(port); if (err < 0) - goto err_free_irq; + goto err_free_link_irq; /* Unmask interrupts on all CPUs */ on_each_cpu(mvpp2_interrupts_unmask, port, 1); @@ -6693,6 +6839,9 @@ static int mvpp2_open(struct net_device *dev) return 0; +err_free_link_irq: + if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) + free_irq(port->link_irq, port); err_free_irq: mvpp2_irqs_deinit(port); err_cleanup_txqs: @@ -6706,6 +6855,7 @@ static int mvpp2_stop(struct net_device *dev) { struct mvpp2_port *port = netdev_priv(dev); struct mvpp2_port_pcpu *port_pcpu; + struct mvpp2 *priv = port->priv; int cpu; mvpp2_stop_dev(port); @@ -6715,6 +6865,9 @@ static int mvpp2_stop(struct net_device *dev) on_each_cpu(mvpp2_interrupts_mask, port, 1); mvpp2_shared_interrupt_mask_unmask(port, true); + if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) + free_irq(port->link_irq, port); + mvpp2_irqs_deinit(port); if (!port->has_tx_irqs) { for_each_present_cpu(cpu) { @@ -7387,6 +7540,15 @@ static int mvpp2_port_probe(struct platform_device *pdev, if (err) goto err_free_netdev; + port->link_irq = of_irq_get_byname(port_node, "link"); + if (port->link_irq == -EPROBE_DEFER) { + err = -EPROBE_DEFER; + goto err_deinit_qvecs; + } + if (port->link_irq <= 0) + /* the link irq is optional */ + port->link_irq = 0; + if (of_property_read_bool(port_node, "marvell,loopback")) port->flags |= MVPP2_F_LOOPBACK; @@ -7405,7 +7567,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(port->base)) { err = PTR_ERR(port->base); - goto err_deinit_qvecs; + goto err_free_irq; } } else { if (of_property_read_u32(port_node, "gop-port-id", @@ -7422,7 +7584,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->stats = netdev_alloc_pcpu_stats(struct mvpp2_pcpu_stats); if (!port->stats) { err = -ENOMEM; - goto err_deinit_qvecs; + goto err_free_irq; } dt_mac_addr = of_get_mac_address(port_node); @@ -7506,6 +7668,9 @@ err_free_txq_pcpu: free_percpu(port->txqs[i]->pcpu); err_free_stats: free_percpu(port->stats); +err_free_irq: + if (port->link_irq) + irq_dispose_mapping(port->link_irq); err_deinit_qvecs: mvpp2_queue_vectors_deinit(port); err_free_netdev: @@ -7526,6 +7691,8 @@ static void mvpp2_port_remove(struct mvpp2_port *port) for (i = 0; i < port->ntxqs; i++) free_percpu(port->txqs[i]->pcpu); mvpp2_queue_vectors_deinit(port); + if (port->link_irq) + irq_dispose_mapping(port->link_irq); free_netdev(port->dev); } -- cgit v1.2.3 From 2d00cd859c79fb477ead232241808e02b6f468b3 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 1 Sep 2017 13:56:00 +0200 Subject: net: mdio-mux: Fix NULL Comparison style This patch fix checkpatch warning about NULL Comparison style. Signed-off-by: Corentin Labbe Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-mux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index 942ceaf3fd3f..b18ad7082b88 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -120,7 +120,7 @@ int mdio_mux_init(struct device *dev, } pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL); - if (pb == NULL) { + if (!pb) { ret_val = -ENOMEM; goto err_pb_kz; } @@ -144,7 +144,7 @@ int mdio_mux_init(struct device *dev, } cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL); - if (cb == NULL) { + if (!cb) { dev_err(dev, "Error: Failed to allocate memory for child %pOF\n", child_bus_node); -- cgit v1.2.3 From b1b123cfb24b19a4e73e55011a5da58d9523b073 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 1 Sep 2017 13:56:01 +0200 Subject: net: mdio-mux: Remove unnecessary 'out of memory' message This patch fix checkpatch warning about unnecessary 'out of memory' message. Signed-off-by: Corentin Labbe Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-mux.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index b18ad7082b88..5e08e89465c5 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -145,9 +145,6 @@ int mdio_mux_init(struct device *dev, cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL); if (!cb) { - dev_err(dev, - "Error: Failed to allocate memory for child %pOF\n", - child_bus_node); ret_val = -ENOMEM; continue; } @@ -156,9 +153,6 @@ int mdio_mux_init(struct device *dev, cb->mii_bus = mdiobus_alloc(); if (!cb->mii_bus) { - dev_err(dev, - "Error: Failed to allocate MDIO bus for child %pOF\n", - child_bus_node); ret_val = -ENOMEM; devm_kfree(dev, cb); continue; -- cgit v1.2.3 From 1e4ccf363721d33d85f902c61a7f50513689b2cb Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 1 Sep 2017 13:56:02 +0200 Subject: net: mdio-mux: printing driver version is useless Remove the driver version information because this information is not useful in an upstream kernel driver. Signed-off-by: Corentin Labbe Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-mux.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index 5e08e89465c5..282828551bdd 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -13,7 +13,6 @@ #include #include -#define DRV_VERSION "1.0" #define DRV_DESCRIPTION "MDIO bus multiplexer driver" struct mdio_mux_child_bus; @@ -179,7 +178,6 @@ int mdio_mux_init(struct device *dev, } if (pb->children) { *mux_handle = pb; - dev_info(dev, "Version " DRV_VERSION "\n"); return 0; } @@ -212,6 +210,5 @@ void mdio_mux_uninit(void *mux_handle) EXPORT_SYMBOL_GPL(mdio_mux_uninit); MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR("David Daney"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c642bbbc72d4d5165da7c8477d13791f5943dd05 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 1 Sep 2017 13:56:03 +0200 Subject: net: mdio-mux-mmioreg: Can handle 8/16/32 bits registers This patch fix an old information that mdio-mux-mmioreg can only handle 8bit registers. This is not true anymore. Signed-off-by: Corentin Labbe Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 5afe6fdcc968..a9d16a3af514 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -85,7 +85,7 @@ config MDIO_BUS_MUX_MMIOREG parent bus. Child bus selection is under the control of one of the FPGA's registers. - Currently, only 8-bit registers are supported. + Currently, only 8/16/32 bits registers are supported. config MDIO_CAVIUM tristate -- cgit v1.2.3 From 60f786525032432af1b7d9b8935cb12936244ccd Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 1 Sep 2017 13:56:04 +0200 Subject: net: mdio-mux: fix unbalanced put_device mdio_mux_uninit() call put_device (unconditionally) because of of_mdio_find_bus() in mdio_mux_init. But of_mdio_find_bus is only called if mux_bus is empty. If mux_bus is set, mdio_mux_uninit will print a "refcount_t: underflow" trace. This patch add a get_device in the other branch of "if (mux_bus)". Signed-off-by: Corentin Labbe Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-mux.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index 282828551bdd..6f75e9f27fed 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -116,6 +116,7 @@ int mdio_mux_init(struct device *dev, } else { parent_bus_node = NULL; parent_bus = mux_bus; + get_device(&parent_bus->dev); } pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL); @@ -184,9 +185,7 @@ int mdio_mux_init(struct device *dev, dev_err(dev, "Error: No acceptable child buses found\n"); devm_kfree(dev, pb); err_pb_kz: - /* balance the reference of_mdio_find_bus() took */ - if (!mux_bus) - put_device(&parent_bus->dev); + put_device(&parent_bus->dev); err_parent_bus: of_node_put(parent_bus_node); return ret_val; @@ -204,7 +203,6 @@ void mdio_mux_uninit(void *mux_handle) cb = cb->next; } - /* balance the reference of_mdio_find_bus() in mdio_mux_init() took */ put_device(&pb->mii_bus->dev); } EXPORT_SYMBOL_GPL(mdio_mux_uninit); -- cgit v1.2.3 From f6849d01bdc0830c5f4aaba11785827e20d3bad5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 1 Sep 2017 14:44:31 +0100 Subject: qlcnic: remove redundant zero check on retries counter At the end of the do while loop the integer counter retries will always be zero and so the subsequent check to see if it is zero is always true and therefore redundant. Remove the redundant check and always return -EIO on this return path. Also unbreak the literal string in dev_err message to clean up a checkpatch warning. Detected by CoverityScan, CID#744279 ("Logically dead code") Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c index be41e4c77b65..c48a0e2d4d7e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c @@ -592,13 +592,9 @@ qlcnic_receive_peg_ready(struct qlcnic_adapter *adapter) } while (--retries); - if (!retries) { - dev_err(&adapter->pdev->dev, "Receive Peg initialization not " - "complete, state: 0x%x.\n", val); - return -EIO; - } - - return 0; + dev_err(&adapter->pdev->dev, "Receive Peg initialization not complete, state: 0x%x.\n", + val); + return -EIO; } int -- cgit v1.2.3 From c54a5048904b6a492551d1cf0a7c9ae3ca763f4b Mon Sep 17 00:00:00 2001 From: Andrew Pilloud Date: Fri, 1 Sep 2017 07:49:49 -0700 Subject: mvneta: Driver and hardware supports IPv6 offload, so enable it The mvneta driver and hardware supports IPv6 offload, however it isn't enabled. Set the NETIF_F_IPV6_CSUM feature to inform the network layer that this driver can offload IPV6 TCP and UDP checksums. This change has been tested on an Armada 370 and the feature support confirmed with several device datasheets including the Armada XP and Armada 3700. Signed-off-by: Andrew Pilloud Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 35ff1ecfcff0..64a04975bcf8 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4329,7 +4329,7 @@ static int mvneta_probe(struct platform_device *pdev) } } - dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; + dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO; dev->hw_features |= dev->features; dev->vlan_features |= dev->features; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; -- cgit v1.2.3 From d05071ed4a8a0acbc918bae76571d917ab05119b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 31 Aug 2017 14:24:49 +0100 Subject: fsl/fman: make arrays port_ids static, reduces object code size Don't populate the arrays port_ids on the stack, instead make them static. Makes the object code smaller by over 700 bytes: Before: text data bss dec hex filename 28785 5832 192 34809 87f9 fman.o After: text data bss dec hex filename 27921 5992 192 34105 8539 fman.o Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/fman.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index f420dac2f484..9530405030a7 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -2368,15 +2368,21 @@ u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id) int i; if (fman->state->rev_info.major >= 6) { - u32 port_ids[] = {0x30, 0x31, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2, 0x3, 0x4, 0x5, 0x7, 0x7}; + static const u32 port_ids[] = { + 0x30, 0x31, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2, 0x3, 0x4, 0x5, 0x7, 0x7 + }; + for (i = 0; i < fman->state->num_of_qman_channels; i++) { if (port_ids[i] == port_id) break; } } else { - u32 port_ids[] = {0x30, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1, - 0x2, 0x3, 0x4, 0x5, 0x7, 0x7}; + static const u32 port_ids[] = { + 0x30, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1, + 0x2, 0x3, 0x4, 0x5, 0x7, 0x7 + }; + for (i = 0; i < fman->state->num_of_qman_channels; i++) { if (port_ids[i] == port_id) break; -- cgit v1.2.3 From 33c8182166273fa002ed11fd4ecd96120512941a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 31 Aug 2017 14:57:15 +0100 Subject: net: phy: bcm7xxx: make array bcm7xxx_suspend_cfg static, reduces object code size Don't populate the array bcm7xxx_suspend_cfg A on the stack, instead make it static. Makes the object code smaller by over 300 bytes: Before: text data bss dec hex filename 6351 8146 0 14497 38a1 drivers/net/phy/bcm7xxx.o After: text data bss dec hex filename 5986 8210 0 14196 3774 drivers/net/phy/bcm7xxx.o Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/phy/bcm7xxx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index caa9f6e17f34..8b33f688ac8a 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -511,7 +511,7 @@ static int bcm7xxx_config_init(struct phy_device *phydev) static int bcm7xxx_suspend(struct phy_device *phydev) { int ret; - const struct bcm7xxx_regs { + static const struct bcm7xxx_regs { int reg; u16 value; } bcm7xxx_suspend_cfg[] = { -- cgit v1.2.3 From 5debc53ffecf79ca1c2d6fc194764cb6a8fa91a9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 31 Aug 2017 15:07:27 +0100 Subject: net: qualcomm: rmnet: remove unused variable priv priv is being assigned but is never used, so remove it. Cleans up clang build warning: "warning: Value stored to 'priv' is never read" Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial implementation") Signed-off-by: Colin Ian King Acked-by: Subash Abhinov Kasiviswanathan Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index c8b573d28dcf..bf7455fdafcc 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -73,9 +73,6 @@ static const struct net_device_ops rmnet_vnd_ops = { */ void rmnet_vnd_setup(struct net_device *rmnet_dev) { - struct rmnet_priv *priv; - - priv = netdev_priv(rmnet_dev); netdev_dbg(rmnet_dev, "Setting up device %s\n", rmnet_dev->name); rmnet_dev->netdev_ops = &rmnet_vnd_ops; -- cgit v1.2.3 From 487234cc1954a1c09115a82b45ebcc2086657572 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 1 Sep 2017 17:32:34 -0700 Subject: net: systemport: Correctly set TSB endian for host Similarly to how we configure the RSB (Receive Status Block) we also need to set the TSB (Transmit Status Block) based on the host endian. This was missing from the commit indicated below. Fixes: 389a06bc534e ("net: systemport: Set correct RSB endian bits based on host") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 13 +++++++++++++ drivers/net/ethernet/broadcom/bcmsysport.h | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index eec77fae12a1..a6572b51435a 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -1392,6 +1392,19 @@ static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv, tdma_writel(priv, RING_IGNORE_STATUS, TDMA_DESC_RING_MAPPING(index)); tdma_writel(priv, 0, TDMA_DESC_RING_PCP_DEI_VID(index)); + /* Do not use tdma_control_bit() here because TSB_SWAP1 collides + * with the original definition of ACB_ALGO + */ + reg = tdma_readl(priv, TDMA_CONTROL); + if (priv->is_lite) + reg &= ~BIT(TSB_SWAP1); + /* Set a correct TSB format based on host endian */ + if (!IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + reg |= tdma_control_bit(priv, TSB_SWAP0); + else + reg &= ~tdma_control_bit(priv, TSB_SWAP0); + tdma_writel(priv, reg, TDMA_CONTROL); + /* Program the number of descriptors as MAX_THRESHOLD and half of * its size for the hysteresis trigger */ diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h index 80b4ffff63b7..82e401df199e 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -449,7 +449,8 @@ struct bcm_rsb { /* Uses 2 bits on SYSTEMPORT Lite and shifts everything by 1 bit, we * keep the SYSTEMPORT layout here and adjust with tdma_control_bit() */ -#define TSB_SWAP 2 +#define TSB_SWAP0 2 +#define TSB_SWAP1 3 #define ACB_ALGO 3 #define BUF_DATA_OFFSET_SHIFT 4 #define BUF_DATA_OFFSET_MASK 0x3ff -- cgit v1.2.3 From 9a0c48df0d77602da3958a4c8fc2abb9521b0ade Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 31 Aug 2017 16:16:12 -0700 Subject: netvsc: cleanup datapath switch Use one routine for datapath up/down. Don't need to reopen the rndis layer. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 38 +++++++------------------------------- 1 file changed, 7 insertions(+), 31 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 05ee870c3636..b54b5b99cd79 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1847,11 +1847,13 @@ static int netvsc_register_vf(struct net_device *vf_netdev) return NOTIFY_OK; } -static int netvsc_vf_up(struct net_device *vf_netdev) +/* VF up/down change detected, schedule to change data path */ +static int netvsc_vf_changed(struct net_device *vf_netdev) { struct net_device_context *net_device_ctx; struct netvsc_device *netvsc_dev; struct net_device *ndev; + bool vf_is_up = netif_running(vf_netdev); ndev = get_netvsc_byref(vf_netdev); if (!ndev) @@ -1862,34 +1864,9 @@ static int netvsc_vf_up(struct net_device *vf_netdev) if (!netvsc_dev) return NOTIFY_DONE; - /* Bump refcount when datapath is acvive - Why? */ - rndis_filter_open(netvsc_dev); - - /* notify the host to switch the data path. */ - netvsc_switch_datapath(ndev, true); - netdev_info(ndev, "Data path switched to VF: %s\n", vf_netdev->name); - - return NOTIFY_OK; -} - -static int netvsc_vf_down(struct net_device *vf_netdev) -{ - struct net_device_context *net_device_ctx; - struct netvsc_device *netvsc_dev; - struct net_device *ndev; - - ndev = get_netvsc_byref(vf_netdev); - if (!ndev) - return NOTIFY_DONE; - - net_device_ctx = netdev_priv(ndev); - netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); - if (!netvsc_dev) - return NOTIFY_DONE; - - netvsc_switch_datapath(ndev, false); - netdev_info(ndev, "Data path switched from VF: %s\n", vf_netdev->name); - rndis_filter_close(netvsc_dev); + netvsc_switch_datapath(ndev, vf_is_up); + netdev_info(ndev, "Data path switched %s VF: %s\n", + vf_is_up ? "to" : "from", vf_netdev->name); return NOTIFY_OK; } @@ -2099,9 +2076,8 @@ static int netvsc_netdev_event(struct notifier_block *this, case NETDEV_UNREGISTER: return netvsc_unregister_vf(event_dev); case NETDEV_UP: - return netvsc_vf_up(event_dev); case NETDEV_DOWN: - return netvsc_vf_down(event_dev); + return netvsc_vf_changed(event_dev); default: return NOTIFY_DONE; } -- cgit v1.2.3 From ec158f77def2df084d9f62565357e3037b04bd3f Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 31 Aug 2017 16:16:13 -0700 Subject: netvsc: allow driver to be removed even if VF is present If VF is attached then can still allow netvsc driver module to be removed. Just have to make sure and do the cleanup. Also, avoid extra rtnl round trip when calling unregister. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index b54b5b99cd79..3aee4b68ff13 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1839,9 +1839,6 @@ static int netvsc_register_vf(struct net_device *vf_netdev) netdev_info(ndev, "VF registering: %s\n", vf_netdev->name); - /* Prevent this module from being unloaded while VF is registered */ - try_module_get(THIS_MODULE); - dev_hold(vf_netdev); rcu_assign_pointer(net_device_ctx->vf_netdev, vf_netdev); return NOTIFY_OK; @@ -1885,10 +1882,11 @@ static int netvsc_unregister_vf(struct net_device *vf_netdev) netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name); + netdev_rx_handler_unregister(vf_netdev); netdev_upper_dev_unlink(vf_netdev, ndev); RCU_INIT_POINTER(net_device_ctx->vf_netdev, NULL); dev_put(vf_netdev); - module_put(THIS_MODULE); + return NOTIFY_OK; } @@ -1992,11 +1990,11 @@ no_net: static int netvsc_remove(struct hv_device *dev) { - struct net_device *net; struct net_device_context *ndev_ctx; + struct net_device *vf_netdev; + struct net_device *net; net = hv_get_drvdata(dev); - if (net == NULL) { dev_err(&dev->device, "No net device to remove\n"); return 0; @@ -2013,12 +2011,15 @@ static int netvsc_remove(struct hv_device *dev) * removed. Also blocks mtu and channel changes. */ rtnl_lock(); + vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); + if (vf_netdev) + netvsc_unregister_vf(vf_netdev); + rndis_filter_device_remove(dev, rtnl_dereference(ndev_ctx->nvdev)); + unregister_netdevice(net); rtnl_unlock(); - unregister_netdev(net); - hv_set_drvdata(dev, NULL); free_percpu(ndev_ctx->vf_stats); -- cgit v1.2.3 From 715e2ec532f6bffc8ee1cb9282c9e6cbf8809591 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Fri, 1 Sep 2017 14:30:04 -0700 Subject: hv_netvsc: Clean up an unused parameter in rndis_filter_set_rss_param() This patch removes the parameter, num_queue in rndis_filter_set_rss_param(), which is no longer in use. Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 2 +- drivers/net/hyperv/netvsc_drv.c | 2 +- drivers/net/hyperv/rndis_filter.c | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index ff1c0c8d5e0d..ec546da86683 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -213,7 +213,7 @@ void rndis_filter_update(struct netvsc_device *nvdev); void rndis_filter_device_remove(struct hv_device *dev, struct netvsc_device *nvdev); int rndis_filter_set_rss_param(struct rndis_device *rdev, - const u8 *key, int num_queue); + const u8 *key); int rndis_filter_receive(struct net_device *ndev, struct netvsc_device *net_dev, struct hv_device *dev, diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 3aee4b68ff13..992a48ec0206 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1424,7 +1424,7 @@ static int netvsc_set_rxfh(struct net_device *dev, const u32 *indir, key = rndis_dev->rss_key; } - return rndis_filter_set_rss_param(rndis_dev, key, ndev->num_chn); + return rndis_filter_set_rss_param(rndis_dev, key); } /* Hyper-V RNDIS protocol does not have ring in the HW sense. diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 36e9ee82ec6f..496fefa7c7c4 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -717,7 +717,7 @@ cleanup: } int rndis_filter_set_rss_param(struct rndis_device *rdev, - const u8 *rss_key, int num_queue) + const u8 *rss_key) { struct net_device *ndev = rdev->ndev; struct rndis_request *request; @@ -1258,8 +1258,7 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, atomic_read(&net_device->open_chn) == net_device->num_chn); /* ignore failues from setting rss parameters, still have channels */ - rndis_filter_set_rss_param(rndis_device, netvsc_hash_key, - net_device->num_chn); + rndis_filter_set_rss_param(rndis_device, netvsc_hash_key); out: if (ret) { net_device->max_chn = 1; -- cgit v1.2.3 From 5c4217d05d3258fa9999ad959c9b6da791b70bfa Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Fri, 1 Sep 2017 14:30:05 -0700 Subject: hv_netvsc: Simplify num_chn checking in rndis_filter_device_add() The minus one and assignment to a local variable is not necessary. This patch simplifies it. Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/rndis_filter.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 496fefa7c7c4..69c40b8fccc3 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1067,7 +1067,7 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, struct ndis_recv_scale_cap rsscap; u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); unsigned int gso_max_size = GSO_MAX_SIZE; - u32 mtu, size, num_rss_qs; + u32 mtu, size; const struct cpumask *node_cpu_mask; u32 num_possible_rss_qs; int i, ret; @@ -1215,8 +1215,8 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, net_device->num_chn); atomic_set(&net_device->open_chn, 1); - num_rss_qs = net_device->num_chn - 1; - if (num_rss_qs == 0) + + if (net_device->num_chn == 1) return net_device; for (i = 1; i < net_device->num_chn; i++) { -- cgit v1.2.3 From 06be580ac7b650938cb3f2cf8cd02d73ff96d3e4 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Fri, 1 Sep 2017 14:30:06 -0700 Subject: hv_netvsc: Simplify the limit check in netvsc_set_channels() Because of the following code, net->num_tx_queues equals to VRSS_CHANNEL_MAX, and max_chn is less than or equals to VRSS_CHANNEL_MAX. netvsc_drv.c: alloc_etherdev_mq(sizeof(struct net_device_context), VRSS_CHANNEL_MAX); rndis_filter.c: net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs); So this patch removes the unnecessary limit check before comparing with "max_chn". Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 992a48ec0206..fd65d32bf24d 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -830,9 +830,6 @@ static int netvsc_set_channels(struct net_device *net, channels->rx_count || channels->tx_count || channels->other_count) return -EINVAL; - if (count > net->num_tx_queues || count > VRSS_CHANNEL_MAX) - return -EINVAL; - if (!nvdev || nvdev->destroy) return -ENODEV; -- cgit v1.2.3 From db3cd7af9d0f174475cbcc02c0e96a5408fe2dc2 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Fri, 1 Sep 2017 14:30:07 -0700 Subject: hv_netvsc: Fix the channel limit in netvsc_set_rxfh() The limit of setting receive indirection table value should be the current number of channels, not the VRSS_CHANNEL_MAX. Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index fd65d32bf24d..165ba4b3b423 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1407,7 +1407,7 @@ static int netvsc_set_rxfh(struct net_device *dev, const u32 *indir, rndis_dev = ndev->extension; if (indir) { for (i = 0; i < ITAB_NUM; i++) - if (indir[i] >= VRSS_CHANNEL_MAX) + if (indir[i] >= ndev->num_chn) return -EINVAL; for (i = 0; i < ITAB_NUM; i++) -- cgit v1.2.3 From b45d8b50b8264cac7b2f1245ca04a2f009038ac7 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 13 Feb 2017 18:41:30 +0200 Subject: net/mlx5e: Reorganize struct mlx5e_rq Bring fast-path fields together, and combine RX WQE mutual exclusive fields into a union. Page-reuse and XDP are mutually exclusive and cannot be used at the same time. Use a union to combine their footprints. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 16 +++++++++------- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 10 +++++----- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 13 ++++++------- 3 files changed, 20 insertions(+), 19 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 6c2abeccfa5a..d964db286c95 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -527,20 +527,24 @@ struct mlx5e_rq { struct { struct mlx5e_wqe_frag_info *frag_info; u32 frag_sz; /* max possible skb frag_sz */ - bool page_reuse; - bool xdp_xmit; + union { + bool page_reuse; + bool xdp_xmit; + }; } wqe; struct { struct mlx5e_mpw_info *info; void *mtt_no_align; + u16 stride_sz; + u16 num_strides; } mpwqe; }; struct { - u8 page_order; u32 wqe_sz; /* wqe data buffer size */ + u16 headroom; + u8 page_order; u8 map_dir; /* dma map direction */ } buff; - __be32 mkey_be; struct device *pdev; struct net_device *netdev; @@ -555,7 +559,6 @@ struct mlx5e_rq { unsigned long state; int ix; - u16 rx_headroom; struct mlx5e_rx_am am; /* Adaptive Moderation */ @@ -565,9 +568,8 @@ struct mlx5e_rq { /* control */ struct mlx5_wq_ctrl wq_ctrl; + __be32 mkey_be; u8 wq_type; - u32 mpwqe_stride_sz; - u32 mpwqe_num_strides; u32 rqn; struct mlx5e_channel *channel; struct mlx5_core_dev *mdev; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 85841e24c65b..94761d0e1b33 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -593,7 +593,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, } rq->buff.map_dir = rq->xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; - rq->rx_headroom = params->rq_headroom; + rq->buff.headroom = params->rq_headroom; switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: @@ -615,10 +615,10 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, goto err_rq_wq_destroy; } - rq->mpwqe_stride_sz = BIT(params->mpwqe_log_stride_sz); - rq->mpwqe_num_strides = BIT(params->mpwqe_log_num_strides); + rq->mpwqe.stride_sz = BIT(params->mpwqe_log_stride_sz); + rq->mpwqe.num_strides = BIT(params->mpwqe_log_num_strides); - rq->buff.wqe_sz = rq->mpwqe_stride_sz * rq->mpwqe_num_strides; + rq->buff.wqe_sz = rq->mpwqe.stride_sz * rq->mpwqe.num_strides; byte_count = rq->buff.wqe_sz; err = mlx5e_create_rq_umr_mkey(mdev, rq); @@ -665,7 +665,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, byte_count = rq->buff.wqe_sz; /* calc the required page order */ - rq->wqe.frag_sz = MLX5_SKB_FRAG_SZ(rq->rx_headroom + byte_count); + rq->wqe.frag_sz = MLX5_SKB_FRAG_SZ(rq->buff.headroom + byte_count); npages = DIV_ROUND_UP(rq->wqe.frag_sz, PAGE_SIZE); rq->buff.page_order = order_base_2(npages); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index be8197a75a63..1b50f1e7e48a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -263,8 +263,7 @@ int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) wi->offset = 0; } - wqe->data.addr = cpu_to_be64(wi->di.addr + wi->offset + - rq->rx_headroom); + wqe->data.addr = cpu_to_be64(wi->di.addr + wi->offset + rq->buff.headroom); return 0; } @@ -296,7 +295,7 @@ void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix) static inline int mlx5e_mpwqe_strides_per_page(struct mlx5e_rq *rq) { - return rq->mpwqe_num_strides >> MLX5_MPWRQ_WQE_PAGE_ORDER; + return rq->mpwqe.num_strides >> MLX5_MPWRQ_WQE_PAGE_ORDER; } static inline void mlx5e_add_skb_frag_mpwqe(struct mlx5e_rq *rq, @@ -305,7 +304,7 @@ static inline void mlx5e_add_skb_frag_mpwqe(struct mlx5e_rq *rq, u32 page_idx, u32 frag_offset, u32 len) { - unsigned int truesize = ALIGN(len, rq->mpwqe_stride_sz); + unsigned int truesize = ALIGN(len, rq->mpwqe.stride_sz); dma_sync_single_for_cpu(rq->pdev, wi->umr.dma_info[page_idx].addr + frag_offset, @@ -776,9 +775,9 @@ struct sk_buff *skb_from_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, struct mlx5e_wqe_frag_info *wi, u32 cqe_bcnt) { struct mlx5e_dma_info *di = &wi->di; + u16 rx_headroom = rq->buff.headroom; struct sk_buff *skb; void *va, *data; - u16 rx_headroom = rq->rx_headroom; bool consumed; u32 frag_size; @@ -911,7 +910,7 @@ static inline void mlx5e_mpwqe_fill_rx_skb(struct mlx5e_rq *rq, struct sk_buff *skb) { u16 stride_ix = mpwrq_get_cqe_stride_index(cqe); - u32 wqe_offset = stride_ix * rq->mpwqe_stride_sz; + u32 wqe_offset = stride_ix * rq->mpwqe.stride_sz; u32 head_offset = wqe_offset & (PAGE_SIZE - 1); u32 page_idx = wqe_offset >> PAGE_SHIFT; u32 head_page_idx = page_idx; @@ -979,7 +978,7 @@ void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) napi_gro_receive(rq->cq.napi, skb); mpwrq_cqe_out: - if (likely(wi->consumed_strides < rq->mpwqe_num_strides)) + if (likely(wi->consumed_strides < rq->mpwqe.num_strides)) return; mlx5e_free_rx_mpwqe(rq, wi); -- cgit v1.2.3 From 89e89f7a9fded02b81f4ee21e30c64892116cf34 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Sun, 2 Jul 2017 19:02:05 +0300 Subject: net/mlx5e: Replace multiplication by stride size with a shift In RX data-path, use shift operations instead of a regular multiplication by stride size, as it is a power of two. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 ++-- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index d964db286c95..44bd8df905ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -535,8 +535,8 @@ struct mlx5e_rq { struct { struct mlx5e_mpw_info *info; void *mtt_no_align; - u16 stride_sz; u16 num_strides; + u8 log_stride_sz; } mpwqe; }; struct { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 94761d0e1b33..7a25d952c922 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -615,10 +615,10 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, goto err_rq_wq_destroy; } - rq->mpwqe.stride_sz = BIT(params->mpwqe_log_stride_sz); + rq->mpwqe.log_stride_sz = params->mpwqe_log_stride_sz; rq->mpwqe.num_strides = BIT(params->mpwqe_log_num_strides); - rq->buff.wqe_sz = rq->mpwqe.stride_sz * rq->mpwqe.num_strides; + rq->buff.wqe_sz = rq->mpwqe.num_strides << rq->mpwqe.log_stride_sz; byte_count = rq->buff.wqe_sz; err = mlx5e_create_rq_umr_mkey(mdev, rq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 1b50f1e7e48a..aa5cc1590859 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -304,7 +304,7 @@ static inline void mlx5e_add_skb_frag_mpwqe(struct mlx5e_rq *rq, u32 page_idx, u32 frag_offset, u32 len) { - unsigned int truesize = ALIGN(len, rq->mpwqe.stride_sz); + unsigned int truesize = ALIGN(len, BIT(rq->mpwqe.log_stride_sz)); dma_sync_single_for_cpu(rq->pdev, wi->umr.dma_info[page_idx].addr + frag_offset, @@ -910,7 +910,7 @@ static inline void mlx5e_mpwqe_fill_rx_skb(struct mlx5e_rq *rq, struct sk_buff *skb) { u16 stride_ix = mpwrq_get_cqe_stride_index(cqe); - u32 wqe_offset = stride_ix * rq->mpwqe.stride_sz; + u32 wqe_offset = stride_ix << rq->mpwqe.log_stride_sz; u32 head_offset = wqe_offset & (PAGE_SIZE - 1); u32 page_idx = wqe_offset >> PAGE_SHIFT; u32 head_page_idx = page_idx; -- cgit v1.2.3 From b681c481f11bd2839d759f493b00008a6c690e61 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 3 Jul 2017 11:51:17 +0300 Subject: net/mlx5e: Remove unnecessary wqe_sz field from RQ buffer Field is used only locally within the RQ create function. The use of a local variable is sufficient. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 1 - drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 44bd8df905ca..ce8b4f648757 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -540,7 +540,6 @@ struct mlx5e_rq { } mpwqe; }; struct { - u32 wqe_sz; /* wqe data buffer size */ u16 headroom; u8 page_order; u8 map_dir; /* dma map direction */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 7a25d952c922..591e0dca9671 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -618,8 +618,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->mpwqe.log_stride_sz = params->mpwqe_log_stride_sz; rq->mpwqe.num_strides = BIT(params->mpwqe_log_num_strides); - rq->buff.wqe_sz = rq->mpwqe.num_strides << rq->mpwqe.log_stride_sz; - byte_count = rq->buff.wqe_sz; + byte_count = rq->mpwqe.num_strides << rq->mpwqe.log_stride_sz; err = mlx5e_create_rq_umr_mkey(mdev, rq); if (err) @@ -654,15 +653,14 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, goto err_rq_wq_destroy; } - rq->buff.wqe_sz = params->lro_en ? + byte_count = params->lro_en ? params->lro_wqe_sz : MLX5E_SW2HW_MTU(c->priv, c->netdev->mtu); #ifdef CONFIG_MLX5_EN_IPSEC if (MLX5_IPSEC_DEV(mdev)) - rq->buff.wqe_sz += MLX5E_METADATA_ETHER_LEN; + byte_count += MLX5E_METADATA_ETHER_LEN; #endif rq->wqe.page_reuse = !params->xdp_prog && !params->lro_en; - byte_count = rq->buff.wqe_sz; /* calc the required page order */ rq->wqe.frag_sz = MLX5_SKB_FRAG_SZ(rq->buff.headroom + byte_count); -- cgit v1.2.3 From 9bafe2adabd16e74e28920956680b15bf529c2eb Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Sun, 25 Jun 2017 15:23:35 +0300 Subject: net/mlx5e: Use memset to init skbs_frags array to zeros In RX data-path, use memset() instead of loop assignment to init the whole skbs_frags array. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index aa5cc1590859..db372dcecbe0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -391,9 +391,9 @@ static int mlx5e_alloc_rx_umr_mpwqe(struct mlx5e_rq *rq, goto err_unmap; wi->umr.mtt[i] = cpu_to_be64(dma_info->addr | MLX5_EN_WR); page_ref_add(dma_info->page, pg_strides); - wi->skbs_frags[i] = 0; } + memset(wi->skbs_frags, 0, sizeof(*wi->skbs_frags) * MLX5_MPWRQ_PAGES_PER_WQE); wi->consumed_strides = 0; wqe->data.addr = cpu_to_be64(dma_offset); -- cgit v1.2.3 From 4c2af5cc2bee32685883b55328d8b5ec80e1781f Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Sun, 25 Jun 2017 16:28:46 +0300 Subject: net/mlx5e: Small enhancements for RX MPWQE allocation and free The dma offset of a MPWQE (Multi-Packet WQE) in memory region is fixed for all rounds. Calculate it once on creation time, instead of in runtime. This also obsoletes the wqe argument in the function. In addition, optimize dma_info iterator calculation. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 6 ++++++ drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 18 ++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 591e0dca9671..5aa4681f7c3c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -674,6 +674,12 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, for (i = 0; i < wq_sz; i++) { struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(&rq->wq, i); + if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) { + u64 dma_offset = (u64)mlx5e_get_wqe_mtt_offset(rq, i) << PAGE_SHIFT; + + wqe->data.addr = cpu_to_be64(dma_offset); + } + wqe->data.byte_count = cpu_to_be32(byte_count); wqe->data.lkey = rq->mkey_be; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index db372dcecbe0..fb3b83609aea 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -374,18 +374,15 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) } static int mlx5e_alloc_rx_umr_mpwqe(struct mlx5e_rq *rq, - struct mlx5e_rx_wqe *wqe, u16 ix) { struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix]; - u64 dma_offset = (u64)mlx5e_get_wqe_mtt_offset(rq, ix) << PAGE_SHIFT; int pg_strides = mlx5e_mpwqe_strides_per_page(rq); + struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[0]; int err; int i; - for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++) { - struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[i]; - + for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++, dma_info++) { err = mlx5e_page_alloc_mapped(rq, dma_info); if (unlikely(err)) goto err_unmap; @@ -395,14 +392,12 @@ static int mlx5e_alloc_rx_umr_mpwqe(struct mlx5e_rq *rq, memset(wi->skbs_frags, 0, sizeof(*wi->skbs_frags) * MLX5_MPWRQ_PAGES_PER_WQE); wi->consumed_strides = 0; - wqe->data.addr = cpu_to_be64(dma_offset); return 0; err_unmap: while (--i >= 0) { - struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[i]; - + dma_info--; page_ref_sub(dma_info->page, pg_strides); mlx5e_page_release(rq, dma_info, true); } @@ -413,11 +408,10 @@ err_unmap: void mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi) { int pg_strides = mlx5e_mpwqe_strides_per_page(rq); + struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[0]; int i; - for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++) { - struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[i]; - + for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++, dma_info++) { page_ref_sub(dma_info->page, pg_strides - wi->skbs_frags[i]); mlx5e_page_release(rq, dma_info, true); } @@ -447,7 +441,7 @@ int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) { int err; - err = mlx5e_alloc_rx_umr_mpwqe(rq, wqe, ix); + err = mlx5e_alloc_rx_umr_mpwqe(rq, ix); if (unlikely(err)) return err; set_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state); -- cgit v1.2.3 From 4cbb7558013a4f600eb1ca9ad34959d8912240e6 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 19 Jun 2017 18:36:30 +0300 Subject: net/mlx5e: NAPI busy-poll when UMR post is in progress If a UMR post is in progress, it means that there's a missing WQE in RQ, and that a completion will be shortly available in ICO SQ completion queue. Prefer busy-poll to handle it as soon as possible. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index fb3b83609aea..8af6577b7501 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -456,17 +456,16 @@ void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) mlx5e_free_rx_mpwqe(rq, wi); } -#define RQ_CANNOT_POST(rq) \ - (!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state) || \ - test_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state)) - bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) { struct mlx5_wq_ll *wq = &rq->wq; - if (unlikely(RQ_CANNOT_POST(rq))) + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) return false; + if (test_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state)) + return true; + while (!mlx5_wq_ll_is_full(wq)) { struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head); int err; -- cgit v1.2.3 From 4b7dfc9925143eb4a55bbb97c033d6da03b29bff Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 19 Jun 2017 18:11:30 +0300 Subject: net/mlx5e: Early-return on empty completion queues NAPI context handles different kinds of completion queues (RX, TX, and others). Hence, upon a poll trial, some of them might be empty. Here we early-return upon empty completion queues, as well as full rx buffer, and save unnecessary logic and memory barriers. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 40 ++++++++++++++----------- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 16 +++++----- 2 files changed, 32 insertions(+), 24 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 8af6577b7501..ab1213a3615e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -459,16 +459,19 @@ void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) { struct mlx5_wq_ll *wq = &rq->wq; + int err; if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) return false; + if (mlx5_wq_ll_is_full(wq)) + return false; + if (test_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state)) return true; - while (!mlx5_wq_ll_is_full(wq)) { + do { struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head); - int err; err = rq->alloc_wqe(rq, wqe, wq->head); if (err == -EBUSY) @@ -479,14 +482,14 @@ bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) } mlx5_wq_ll_push(wq, be16_to_cpu(wqe->next.next_wqe_index)); - } + } while (!mlx5_wq_ll_is_full(wq)); /* ensure wqes are visible to device before updating doorbell record */ dma_wmb(); mlx5_wq_ll_update_db_record(wq); - return !mlx5_wq_ll_is_full(wq); + return !!err; } static void mlx5e_lro_update_hdr(struct sk_buff *skb, struct mlx5_cqe64 *cqe, @@ -981,7 +984,8 @@ mpwrq_cqe_out: int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) { struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq); - struct mlx5e_xdpsq *xdpsq = &rq->xdpsq; + struct mlx5e_xdpsq *xdpsq; + struct mlx5_cqe64 *cqe; int work_done = 0; if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) @@ -990,12 +994,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) if (cq->decmprs_left) work_done += mlx5e_decompress_cqes_cont(rq, cq, 0, budget); - for (; work_done < budget; work_done++) { - struct mlx5_cqe64 *cqe = mlx5_cqwq_get_cqe(&cq->wq); + cqe = mlx5_cqwq_get_cqe(&cq->wq); + if (!cqe) + return 0; - if (!cqe) - break; + xdpsq = &rq->xdpsq; + do { if (mlx5_get_cqe_format(cqe) == MLX5_COMPRESSED) { work_done += mlx5e_decompress_cqes_start(rq, cq, @@ -1006,7 +1011,7 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) mlx5_cqwq_pop(&cq->wq); rq->handle_rx_cqe(rq, cqe); - } + } while ((++work_done < budget) && (cqe = mlx5_cqwq_get_cqe(&cq->wq))); if (xdpsq->db.doorbell) { mlx5e_xmit_xdp_doorbell(xdpsq); @@ -1024,6 +1029,7 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) { struct mlx5e_xdpsq *sq; + struct mlx5_cqe64 *cqe; struct mlx5e_rq *rq; u16 sqcc; int i; @@ -1033,6 +1039,10 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) return false; + cqe = mlx5_cqwq_get_cqe(&cq->wq); + if (!cqe) + return false; + rq = container_of(sq, struct mlx5e_rq, xdpsq); /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), @@ -1040,15 +1050,11 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) */ sqcc = sq->cc; - for (i = 0; i < MLX5E_TX_CQ_POLL_BUDGET; i++) { - struct mlx5_cqe64 *cqe; + i = 0; + do { u16 wqe_counter; bool last_wqe; - cqe = mlx5_cqwq_get_cqe(&cq->wq); - if (!cqe) - break; - mlx5_cqwq_pop(&cq->wq); wqe_counter = be16_to_cpu(cqe->wqe_counter); @@ -1066,7 +1072,7 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) /* Recycle RX page */ mlx5e_page_release(rq, di, true); } while (!last_wqe); - } + } while ((++i < MLX5E_TX_CQ_POLL_BUDGET) && (cqe = mlx5_cqwq_get_cqe(&cq->wq))); mlx5_cqwq_update_db_record(&cq->wq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 31353e5c3c78..80d2121643ee 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -394,6 +394,7 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev) bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) { struct mlx5e_txqsq *sq; + struct mlx5_cqe64 *cqe; u32 dma_fifo_cc; u32 nbytes; u16 npkts; @@ -405,6 +406,10 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) return false; + cqe = mlx5_cqwq_get_cqe(&cq->wq); + if (!cqe) + return false; + npkts = 0; nbytes = 0; @@ -416,15 +421,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) /* avoid dirtying sq cache line every cqe */ dma_fifo_cc = sq->dma_fifo_cc; - for (i = 0; i < MLX5E_TX_CQ_POLL_BUDGET; i++) { - struct mlx5_cqe64 *cqe; + i = 0; + do { u16 wqe_counter; bool last_wqe; - cqe = mlx5_cqwq_get_cqe(&cq->wq); - if (!cqe) - break; - mlx5_cqwq_pop(&cq->wq); wqe_counter = be16_to_cpu(cqe->wqe_counter); @@ -467,7 +468,8 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) sqcc += wi->num_wqebbs; napi_consume_skb(skb, napi_budget); } while (!last_wqe); - } + + } while ((++i < MLX5E_TX_CQ_POLL_BUDGET) && (cqe = mlx5_cqwq_get_cqe(&cq->wq))); mlx5_cqwq_update_db_record(&cq->wq); -- cgit v1.2.3 From 604acb193b8383a3774673c324e576e4c6b4ffcd Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 5 Jun 2017 11:17:20 +0300 Subject: net/mlx5e: Refactor data-path lro header function Refactor function mlx5e_lro_update_hdr() to reduce number of branches. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 45 +++++++++++-------------- include/linux/mlx5/device.h | 2 +- 2 files changed, 21 insertions(+), 26 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index ab1213a3615e..9d9c13ae6b83 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -496,56 +496,51 @@ static void mlx5e_lro_update_hdr(struct sk_buff *skb, struct mlx5_cqe64 *cqe, u32 cqe_bcnt) { struct ethhdr *eth = (struct ethhdr *)(skb->data); - struct iphdr *ipv4; - struct ipv6hdr *ipv6; struct tcphdr *tcp; int network_depth = 0; __be16 proto; u16 tot_len; + void *ip_p; u8 l4_hdr_type = get_cqe_l4_hdr_type(cqe); - int tcp_ack = ((l4_hdr_type == CQE_L4_HDR_TYPE_TCP_ACK_NO_DATA) || - (l4_hdr_type == CQE_L4_HDR_TYPE_TCP_ACK_AND_DATA)); + u8 tcp_ack = (l4_hdr_type == CQE_L4_HDR_TYPE_TCP_ACK_NO_DATA) || + (l4_hdr_type == CQE_L4_HDR_TYPE_TCP_ACK_AND_DATA); skb->mac_len = ETH_HLEN; proto = __vlan_get_protocol(skb, eth->h_proto, &network_depth); - ipv4 = (struct iphdr *)(skb->data + network_depth); - ipv6 = (struct ipv6hdr *)(skb->data + network_depth); tot_len = cqe_bcnt - network_depth; + ip_p = skb->data + network_depth; if (proto == htons(ETH_P_IP)) { - tcp = (struct tcphdr *)(skb->data + network_depth + - sizeof(struct iphdr)); - ipv6 = NULL; - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; - } else { - tcp = (struct tcphdr *)(skb->data + network_depth + - sizeof(struct ipv6hdr)); - ipv4 = NULL; - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; - } - - if (get_cqe_lro_tcppsh(cqe)) - tcp->psh = 1; + struct iphdr *ipv4 = ip_p; - if (tcp_ack) { - tcp->ack = 1; - tcp->ack_seq = cqe->lro_ack_seq_num; - tcp->window = cqe->lro_tcp_win; - } + tcp = ip_p + sizeof(struct iphdr); + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; - if (ipv4) { ipv4->ttl = cqe->lro_min_ttl; ipv4->tot_len = cpu_to_be16(tot_len); ipv4->check = 0; ipv4->check = ip_fast_csum((unsigned char *)ipv4, ipv4->ihl); } else { + struct ipv6hdr *ipv6 = ip_p; + + tcp = ip_p + sizeof(struct ipv6hdr); + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; + ipv6->hop_limit = cqe->lro_min_ttl; ipv6->payload_len = cpu_to_be16(tot_len - sizeof(struct ipv6hdr)); } + + tcp->psh = get_cqe_lro_tcppsh(cqe); + + if (tcp_ack) { + tcp->ack = 1; + tcp->ack_seq = cqe->lro_ack_seq_num; + tcp->window = cqe->lro_tcp_win; + } } static inline void mlx5e_skb_set_hash(struct mlx5_cqe64 *cqe, diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 3c7442b56460..7031d655ec32 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -709,7 +709,7 @@ static inline int mlx5_get_cqe_format(struct mlx5_cqe64 *cqe) return (cqe->op_own >> 2) & 0x3; } -static inline int get_cqe_lro_tcppsh(struct mlx5_cqe64 *cqe) +static inline u8 get_cqe_lro_tcppsh(struct mlx5_cqe64 *cqe) { return (cqe->lro_tcppsh_abort_dupack >> 6) & 1; } -- cgit v1.2.3 From a1eaba4c5c29b6b5196b2237ce3a8d7d97622b2f Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 3 Jul 2017 11:27:20 +0300 Subject: net/mlx5e: Non-atomic indicator for ring enabled state Rings enabled state change occurs in control path only, and is always followed by a napi_sychronize(), so that following NAPIs read the new value. This read does not need to be atomic. The RQ auto-moderation bit is not set/cleared in data-path. No need for atomic read, a regular read operation is sufficient. In RQ creation time as well, there's no multiple threads trying to access it yet, hence a regular read can be used. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 ++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 8 ++++---- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index ce8b4f648757..0c4f1f30085a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -295,6 +295,8 @@ enum { MLX5E_RQ_STATE_AM, }; +#define MLX5E_TEST_BIT(state, nr) (state & BIT(nr)) + struct mlx5e_cq { /* data path - accessed per cqe */ struct mlx5_cqwq wq; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 5aa4681f7c3c..411fb68794bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -929,7 +929,7 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, goto err_destroy_rq; if (params->rx_am_enabled) - set_bit(MLX5E_RQ_STATE_AM, &c->rq.state); + c->rq.state |= BIT(MLX5E_RQ_STATE_AM); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 9d9c13ae6b83..a5522c3992a2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -424,7 +424,7 @@ void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq) clear_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state); - if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) { + if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED))) { mlx5e_free_rx_mpwqe(rq, &rq->mpwqe.info[wq->head]); return; } @@ -461,7 +461,7 @@ bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) struct mlx5_wq_ll *wq = &rq->wq; int err; - if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED))) return false; if (mlx5_wq_ll_is_full(wq)) @@ -983,7 +983,7 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) struct mlx5_cqe64 *cqe; int work_done = 0; - if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED))) return 0; if (cq->decmprs_left) @@ -1031,7 +1031,7 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) sq = container_of(cq, struct mlx5e_xdpsq, cq); - if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED))) return false; cqe = mlx5_cqwq_get_cqe(&cq->wq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 80d2121643ee..fee43e40fa16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -403,7 +403,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) sq = container_of(cq, struct mlx5e_txqsq, cq); - if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED))) return false; cqe = mlx5_cqwq_get_cqe(&cq->wq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 92db28a9ed43..7311b937e434 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -69,7 +69,7 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq) struct mlx5_cqe64 *cqe; u16 sqcc; - if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED))) return; cqe = mlx5_cqwq_get_cqe(&cq->wq); @@ -129,7 +129,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) for (i = 0; i < c->num_tc; i++) mlx5e_cq_arm(&c->sq[i].cq); - if (test_bit(MLX5E_RQ_STATE_AM, &c->rq.state)) + if (MLX5E_TEST_BIT(c->rq.state, MLX5E_RQ_STATE_AM)) mlx5e_rx_am(&c->rq); mlx5e_cq_arm(&c->rq.cq); -- cgit v1.2.3 From a071cb9f25bef32ec309ca9d123022c52175341b Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 3 Jul 2017 10:18:19 +0300 Subject: net/mlx5e: Non-atomic RQ state indicator for UMR WQE in progress The indication for a UMR WQE in progress is needed only within the NAPI context, and hence no races possible and no need for the use of atomic operations. The only place the flag is read outside of NAPI context is in closure flow, after RQ is disabled flag is no more accessed in NAPI. Use a boolean instead of a bit in ring state, so that its non-atomic set operations do not race with the atomic sets of the other bits. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 3 ++- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 10 +++++----- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 0c4f1f30085a..bce2080eb86a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -291,7 +291,6 @@ struct mlx5e_tstamp { enum { MLX5E_RQ_STATE_ENABLED, - MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, MLX5E_RQ_STATE_AM, }; @@ -539,6 +538,7 @@ struct mlx5e_rq { void *mtt_no_align; u16 num_strides; u8 log_stride_sz; + bool umr_in_progress; } mpwqe; }; struct { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 411fb68794bc..2767a3ee81bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -886,7 +886,8 @@ static void mlx5e_free_rx_descs(struct mlx5e_rq *rq) u16 wqe_ix; /* UMR WQE (if in progress) is always at wq->head */ - if (test_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state)) + if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ && + rq->mpwqe.umr_in_progress) mlx5e_free_rx_mpwqe(rq, &rq->mpwqe.info[wq->head]); while (!mlx5_wq_ll_is_empty(wq)) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index a5522c3992a2..11dba9940029 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -422,7 +422,7 @@ void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq) struct mlx5_wq_ll *wq = &rq->wq; struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head); - clear_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state); + rq->mpwqe.umr_in_progress = false; if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED))) { mlx5e_free_rx_mpwqe(rq, &rq->mpwqe.info[wq->head]); @@ -441,10 +441,13 @@ int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) { int err; + if (rq->mpwqe.umr_in_progress) + return -EBUSY; + err = mlx5e_alloc_rx_umr_mpwqe(rq, ix); if (unlikely(err)) return err; - set_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state); + rq->mpwqe.umr_in_progress = true; mlx5e_post_umr_wqe(rq, ix); return -EBUSY; } @@ -467,9 +470,6 @@ bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) if (mlx5_wq_ll_is_full(wq)) return false; - if (test_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state)) - return true; - do { struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head); -- cgit v1.2.3 From 7cc6d77bb56de3a428ed7cde91a09fffbdbef794 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 17 Jul 2017 12:27:26 +0300 Subject: net/mlx5e: Type-specific optimizations for RX post WQEs function Separate the RX post WQEs function of the different RQ types. This enables RQ type-specific optimizations in data-path. Poll the ICOSQ completion queue only for Striding RQ, and only when a UMR post completion could be possibly available. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 10 +-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 +- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 101 ++++++++++++++++++---- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 64 +------------- 4 files changed, 92 insertions(+), 87 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index bce2080eb86a..8d29a6eb9406 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -517,7 +517,7 @@ struct mlx5e_page_cache { struct mlx5e_rq; typedef void (*mlx5e_fp_handle_rx_cqe)(struct mlx5e_rq*, struct mlx5_cqe64*); -typedef int (*mlx5e_fp_alloc_wqe)(struct mlx5e_rq*, struct mlx5e_rx_wqe*, u16); +typedef bool (*mlx5e_fp_post_rx_wqes)(struct mlx5e_rq *rq); typedef void (*mlx5e_fp_dealloc_wqe)(struct mlx5e_rq*, u16); struct mlx5e_rq { @@ -547,6 +547,7 @@ struct mlx5e_rq { u8 map_dir; /* dma map direction */ } buff; + struct mlx5e_channel *channel; struct device *pdev; struct net_device *netdev; struct mlx5e_tstamp *tstamp; @@ -555,7 +556,7 @@ struct mlx5e_rq { struct mlx5e_page_cache page_cache; mlx5e_fp_handle_rx_cqe handle_rx_cqe; - mlx5e_fp_alloc_wqe alloc_wqe; + mlx5e_fp_post_rx_wqes post_wqes; mlx5e_fp_dealloc_wqe dealloc_wqe; unsigned long state; @@ -572,7 +573,6 @@ struct mlx5e_rq { __be32 mkey_be; u8 wq_type; u32 rqn; - struct mlx5e_channel *channel; struct mlx5_core_dev *mdev; struct mlx5_core_mkey umr_mkey; } ____cacheline_aligned_in_smp; @@ -853,11 +853,9 @@ void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info, void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq); -int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix); -int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix); +bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq); void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix); void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix); -void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq); void mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi); void mlx5e_rx_am(struct mlx5e_rq *rq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 2767a3ee81bc..162ba6ab749a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -598,7 +598,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: - rq->alloc_wqe = mlx5e_alloc_rx_mpwqe; + rq->post_wqes = mlx5e_post_rx_mpwqes; rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe; rq->handle_rx_cqe = c->priv->profile->rx_handlers.handle_rx_cqe_mpwqe; @@ -637,7 +637,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, err = -ENOMEM; goto err_rq_wq_destroy; } - rq->alloc_wqe = mlx5e_alloc_rx_wqe; + rq->post_wqes = mlx5e_post_rx_wqes; rq->dealloc_wqe = mlx5e_dealloc_rx_wqe; #ifdef CONFIG_MLX5_EN_IPSEC diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 11dba9940029..b236dfd71c18 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -252,7 +252,7 @@ static inline bool mlx5e_page_reuse(struct mlx5e_rq *rq, !mlx5e_page_is_reserved(wi->di.page); } -int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) +static int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) { struct mlx5e_wqe_frag_info *wi = &rq->wqe.frag_info[ix]; @@ -417,18 +417,13 @@ void mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi) } } -void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq) +static void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq) { struct mlx5_wq_ll *wq = &rq->wq; struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head); rq->mpwqe.umr_in_progress = false; - if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED))) { - mlx5e_free_rx_mpwqe(rq, &rq->mpwqe.info[wq->head]); - return; - } - mlx5_wq_ll_push(wq, be16_to_cpu(wqe->next.next_wqe_index)); /* ensure wqes are visible to device before updating doorbell record */ @@ -437,19 +432,18 @@ void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq) mlx5_wq_ll_update_db_record(wq); } -int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) +static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) { int err; - if (rq->mpwqe.umr_in_progress) - return -EBUSY; - err = mlx5e_alloc_rx_umr_mpwqe(rq, ix); - if (unlikely(err)) + if (unlikely(err)) { + rq->stats.buff_alloc_err++; return err; + } rq->mpwqe.umr_in_progress = true; mlx5e_post_umr_wqe(rq, ix); - return -EBUSY; + return 0; } void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) @@ -473,9 +467,7 @@ bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) do { struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head); - err = rq->alloc_wqe(rq, wqe, wq->head); - if (err == -EBUSY) - return true; + err = mlx5e_alloc_rx_wqe(rq, wqe, wq->head); if (unlikely(err)) { rq->stats.buff_alloc_err++; break; @@ -492,6 +484,83 @@ bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) return !!err; } +static inline void mlx5e_poll_ico_single_cqe(struct mlx5e_cq *cq, + struct mlx5e_icosq *sq, + struct mlx5e_rq *rq, + struct mlx5_cqe64 *cqe, + u16 *sqcc) +{ + struct mlx5_wq_cyc *wq = &sq->wq; + u16 ci = be16_to_cpu(cqe->wqe_counter) & wq->sz_m1; + struct mlx5e_sq_wqe_info *icowi = &sq->db.ico_wqe[ci]; + + mlx5_cqwq_pop(&cq->wq); + *sqcc += icowi->num_wqebbs; + + if (unlikely((cqe->op_own >> 4) != MLX5_CQE_REQ)) { + WARN_ONCE(true, "mlx5e: Bad OP in ICOSQ CQE: 0x%x\n", + cqe->op_own); + return; + } + + if (likely(icowi->opcode == MLX5_OPCODE_UMR)) { + mlx5e_post_rx_mpwqe(rq); + return; + } + + if (unlikely(icowi->opcode != MLX5_OPCODE_NOP)) + WARN_ONCE(true, + "mlx5e: Bad OPCODE in ICOSQ WQE info: 0x%x\n", + icowi->opcode); +} + +static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq, struct mlx5e_rq *rq) +{ + struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq); + struct mlx5_cqe64 *cqe; + u16 sqcc; + + if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED))) + return; + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + if (likely(!cqe)) + return; + + /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), + * otherwise a cq overrun may occur + */ + sqcc = sq->cc; + + /* by design, there's only a single cqe */ + mlx5e_poll_ico_single_cqe(cq, sq, rq, cqe, &sqcc); + + mlx5_cqwq_update_db_record(&cq->wq); + + /* ensure cq space is freed before enabling more cqes */ + wmb(); + + sq->cc = sqcc; +} + +bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq) +{ + struct mlx5_wq_ll *wq = &rq->wq; + + if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED))) + return false; + + mlx5e_poll_ico_cq(&rq->channel->icosq.cq, rq); + + if (mlx5_wq_ll_is_full(wq)) + return false; + + if (!rq->mpwqe.umr_in_progress) + mlx5e_alloc_rx_mpwqe(rq, wq->head); + + return true; +} + static void mlx5e_lro_update_hdr(struct sk_buff *skb, struct mlx5_cqe64 *cqe, u32 cqe_bcnt) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 7311b937e434..439ba1f2ffbc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -32,66 +32,6 @@ #include "en.h" -static inline void mlx5e_poll_ico_single_cqe(struct mlx5e_cq *cq, - struct mlx5e_icosq *sq, - struct mlx5_cqe64 *cqe, - u16 *sqcc) -{ - struct mlx5_wq_cyc *wq = &sq->wq; - u16 ci = be16_to_cpu(cqe->wqe_counter) & wq->sz_m1; - struct mlx5e_sq_wqe_info *icowi = &sq->db.ico_wqe[ci]; - struct mlx5e_rq *rq = &sq->channel->rq; - - prefetch(rq); - mlx5_cqwq_pop(&cq->wq); - *sqcc += icowi->num_wqebbs; - - if (unlikely((cqe->op_own >> 4) != MLX5_CQE_REQ)) { - WARN_ONCE(true, "mlx5e: Bad OP in ICOSQ CQE: 0x%x\n", - cqe->op_own); - return; - } - - if (likely(icowi->opcode == MLX5_OPCODE_UMR)) { - mlx5e_post_rx_mpwqe(rq); - return; - } - - if (unlikely(icowi->opcode != MLX5_OPCODE_NOP)) - WARN_ONCE(true, - "mlx5e: Bad OPCODE in ICOSQ WQE info: 0x%x\n", - icowi->opcode); -} - -static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq) -{ - struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq); - struct mlx5_cqe64 *cqe; - u16 sqcc; - - if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED))) - return; - - cqe = mlx5_cqwq_get_cqe(&cq->wq); - if (likely(!cqe)) - return; - - /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), - * otherwise a cq overrun may occur - */ - sqcc = sq->cc; - - /* by design, there's only a single cqe */ - mlx5e_poll_ico_single_cqe(cq, sq, cqe, &sqcc); - - mlx5_cqwq_update_db_record(&cq->wq); - - /* ensure cq space is freed before enabling more cqes */ - wmb(); - - sq->cc = sqcc; -} - int mlx5e_napi_poll(struct napi_struct *napi, int budget) { struct mlx5e_channel *c = container_of(napi, struct mlx5e_channel, @@ -111,9 +51,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) work_done = mlx5e_poll_rx_cq(&c->rq.cq, budget); busy |= work_done == budget; - mlx5e_poll_ico_cq(&c->icosq.cq); - - busy |= mlx5e_post_rx_wqes(&c->rq); + busy |= c->rq.post_wqes(&c->rq); if (busy) return budget; -- cgit v1.2.3 From 3b56f7b2af86033f421d0f0fd10cf5c3f74872e0 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 17 Jul 2017 14:31:39 +0300 Subject: net/mlx5e: Remove unnecessary fields in ICO SQ As of current design, in each NAPI, only a single UMR WQE completion could be available in the completion queue of the the internal control operations (ICO) send queue, in addition to nop operations that require no actions upon completion. This renders the consume index obsolete, as the wqe_counter field in CQE is sufficient. This helps removing a memory barrier, and obsoletes the need for tracking the num_wqebbs to update the consumer counter. In addition, remove other unused fields in icosq struct: pdev, dma_fifo_pc, and prev_cc. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 7 ------- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 -- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 19 ++----------------- 3 files changed, 2 insertions(+), 26 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 8d29a6eb9406..e55d9439bc12 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -343,7 +343,6 @@ enum { struct mlx5e_sq_wqe_info { u8 opcode; - u8 num_wqebbs; }; struct mlx5e_txqsq { @@ -419,13 +418,8 @@ struct mlx5e_xdpsq { struct mlx5e_icosq { /* data path */ - /* dirtied @completion */ - u16 cc; - /* dirtied @xmit */ u16 pc ____cacheline_aligned_in_smp; - u32 dma_fifo_pc; - u16 prev_cc; struct mlx5e_cq cq; @@ -439,7 +433,6 @@ struct mlx5e_icosq { void __iomem *uar_map; u32 sqn; u16 edge; - struct device *pdev; __be32 mkey_be; unsigned long state; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 162ba6ab749a..a4c9a0a2c408 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -950,7 +950,6 @@ static void mlx5e_activate_rq(struct mlx5e_rq *rq) set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; - sq->db.ico_wqe[pi].num_wqebbs = 1; nopwqe = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nopwqe->ctrl); } @@ -1052,7 +1051,6 @@ static int mlx5e_alloc_icosq(struct mlx5e_channel *c, struct mlx5_core_dev *mdev = c->mdev; int err; - sq->pdev = c->pdev; sq->mkey_be = c->mkey_be; sq->channel = c; sq->uar_map = mdev->mlx5e_res.bfreg.map; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index b236dfd71c18..88a8749c67d6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -357,7 +357,6 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) /* fill sq edge with nops to avoid wqe wrap around */ while ((pi = (sq->pc & wq->sz_m1)) > sq->edge) { sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; - sq->db.ico_wqe[pi].num_wqebbs = 1; mlx5e_post_nop(wq, sq->sqn, &sq->pc); } @@ -368,7 +367,6 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) MLX5_OPCODE_UMR); sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_UMR; - sq->db.ico_wqe[pi].num_wqebbs = num_wqebbs; sq->pc += num_wqebbs; mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &wqe->ctrl); } @@ -487,15 +485,13 @@ bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) static inline void mlx5e_poll_ico_single_cqe(struct mlx5e_cq *cq, struct mlx5e_icosq *sq, struct mlx5e_rq *rq, - struct mlx5_cqe64 *cqe, - u16 *sqcc) + struct mlx5_cqe64 *cqe) { struct mlx5_wq_cyc *wq = &sq->wq; u16 ci = be16_to_cpu(cqe->wqe_counter) & wq->sz_m1; struct mlx5e_sq_wqe_info *icowi = &sq->db.ico_wqe[ci]; mlx5_cqwq_pop(&cq->wq); - *sqcc += icowi->num_wqebbs; if (unlikely((cqe->op_own >> 4) != MLX5_CQE_REQ)) { WARN_ONCE(true, "mlx5e: Bad OP in ICOSQ CQE: 0x%x\n", @@ -518,7 +514,6 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq, struct mlx5e_rq *rq) { struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq); struct mlx5_cqe64 *cqe; - u16 sqcc; if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED))) return; @@ -527,20 +522,10 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq, struct mlx5e_rq *rq) if (likely(!cqe)) return; - /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), - * otherwise a cq overrun may occur - */ - sqcc = sq->cc; - /* by design, there's only a single cqe */ - mlx5e_poll_ico_single_cqe(cq, sq, rq, cqe, &sqcc); + mlx5e_poll_ico_single_cqe(cq, sq, rq, cqe); mlx5_cqwq_update_db_record(&cq->wq); - - /* ensure cq space is freed before enabling more cqes */ - wmb(); - - sq->cc = sqcc; } bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq) -- cgit v1.2.3 From 70871f1ec4a7e2d52683a53a5ee596790080a2c3 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Thu, 13 Jul 2017 18:26:40 +0300 Subject: net/mlx5e: Don't recycle page if moved to far NUMA Avoid recycling an RX page if it moved to another NUMA node. Add an ethtool counter to count such events. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 6 ++++-- drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index a4c9a0a2c408..2da2ea222aaa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -208,6 +208,7 @@ static void mlx5e_update_sw_counters(struct mlx5e_priv *priv) s->rx_cache_full += rq_stats->cache_full; s->rx_cache_empty += rq_stats->cache_empty; s->rx_cache_busy += rq_stats->cache_busy; + s->rx_cache_waive += rq_stats->cache_waive; for (j = 0; j < priv->channels.params.num_tc; j++) { sq_stats = &c->sq[j].stats; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 88a8749c67d6..f1dd638384d3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -163,7 +163,7 @@ static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, static inline bool mlx5e_page_is_reserved(struct page *page) { - return page_is_pfmemalloc(page) || page_to_nid(page) != numa_node_id(); + return page_is_pfmemalloc(page) || page_to_nid(page) != numa_mem_id(); } static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, @@ -177,8 +177,10 @@ static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, return false; } - if (unlikely(page_is_pfmemalloc(dma_info->page))) + if (unlikely(mlx5e_page_is_reserved(dma_info->page))) { + rq->stats.cache_waive++; return false; + } cache->page_cache[cache->tail] = *dma_info; cache->tail = tail_next; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index 6761796e803c..6d199ffb1c0b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -84,6 +84,7 @@ struct mlx5e_sw_stats { u64 rx_cache_full; u64 rx_cache_empty; u64 rx_cache_busy; + u64 rx_cache_waive; /* Special handling counters */ u64 link_down_events_phy; @@ -123,6 +124,7 @@ static const struct counter_desc sw_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_full) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_empty) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_busy) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_waive) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, link_down_events_phy) }, }; @@ -354,6 +356,7 @@ struct mlx5e_rq_stats { u64 cache_full; u64 cache_empty; u64 cache_busy; + u64 cache_waive; }; static const struct counter_desc rq_stats_desc[] = { @@ -377,6 +380,7 @@ static const struct counter_desc rq_stats_desc[] = { { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_full) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_empty) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_busy) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_waive) }, }; struct mlx5e_sq_stats { -- cgit v1.2.3 From 29c2849e0d204bdc892842e2aae744d2219f07f2 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Sun, 2 Jul 2017 17:33:59 +0300 Subject: net/mlx5e: Slightly increase RX page-cache size In XDP_TX flow, we now get back quicker to each page in page-cache, and on some occasions refcount does not get back to 1 on time, causing some costly page allocations. Slightly increase the size of RX page-cache to significantly decrease the chances for this to happen. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index e55d9439bc12..9c0c07ffe557 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -501,7 +501,7 @@ struct mlx5e_rx_am { /* Adaptive Moderation */ */ #define MLX5E_CACHE_UNIT (MLX5_MPWRQ_PAGES_PER_WQE > NAPI_POLL_WEIGHT ? \ MLX5_MPWRQ_PAGES_PER_WQE : NAPI_POLL_WEIGHT) -#define MLX5E_CACHE_SIZE (2 * roundup_pow_of_two(MLX5E_CACHE_UNIT)) +#define MLX5E_CACHE_SIZE (4 * roundup_pow_of_two(MLX5E_CACHE_UNIT)) struct mlx5e_page_cache { u32 head; u32 tail; -- cgit v1.2.3 From 7b33aaeaae1acfec01cbb0cf41fa8c07aea55609 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Sun, 2 Jul 2017 12:56:12 +0300 Subject: net/mlx5e: Use kernel's mechanism to avoid missing NAPIs We used a channel state bit MLX5E_CHANNEL_NAPI_SCHED to make sure no NAPI is missed when a channel's napi_schedule() is called for completion events of the different channel's resources/rings while NAPI is currently running. Now, as similar mechanism is implemented in kernel, ("39e6c8208d7b net: solve a NAPI race"), we obsolete our own implementation and rely on the return value of napi_complete_done(). This patch removes a redundant overhead of atomic bit operations. Signed-off-by: Tariq Toukan Cc: Eric Dumazet Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 5 ----- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 10 +--------- 3 files changed, 1 insertion(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 9c0c07ffe557..7c046ae8b18e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -570,10 +570,6 @@ struct mlx5e_rq { struct mlx5_core_mkey umr_mkey; } ____cacheline_aligned_in_smp; -enum channel_flags { - MLX5E_CHANNEL_NAPI_SCHED = 1, -}; - struct mlx5e_channel { /* data path */ struct mlx5e_rq rq; @@ -585,7 +581,6 @@ struct mlx5e_channel { struct net_device *netdev; __be32 mkey_be; u8 num_tc; - unsigned long flags; /* control */ struct mlx5e_priv *priv; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 2da2ea222aaa..b2f689ec0d72 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3697,7 +3697,6 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) set_bit(MLX5E_RQ_STATE_ENABLED, &c->rq.state); /* napi_schedule in case we have missed anything */ - set_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags); napi_schedule(&c->napi); if (old_prog) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 439ba1f2ffbc..92d9aa1cddd6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -40,8 +40,6 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) int work_done; int i; - clear_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags); - for (i = 0; i < c->num_tc; i++) busy |= mlx5e_poll_tx_cq(&c->sq[i].cq, budget); @@ -56,13 +54,8 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) if (busy) return budget; - napi_complete_done(napi, work_done); - - /* avoid losing completion event during/after polling cqs */ - if (test_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags)) { - napi_schedule(napi); + if (unlikely(!napi_complete_done(napi, work_done))) return work_done; - } for (i = 0; i < c->num_tc; i++) mlx5e_cq_arm(&c->sq[i].cq); @@ -81,7 +74,6 @@ void mlx5e_completion_event(struct mlx5_core_cq *mcq) struct mlx5e_cq *cq = container_of(mcq, struct mlx5e_cq, mcq); cq->event_ctr++; - set_bit(MLX5E_CHANNEL_NAPI_SCHED, &cq->channel->flags); napi_schedule(cq->napi); } -- cgit v1.2.3 From a8c2eb15797a0f0bf17734d365da7bdc3e263155 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Sun, 2 Jul 2017 13:17:42 +0300 Subject: net/mlx5e: Stop NAPI when irq balancer changes affinity NAPI context keeps rescheduling on same CPU as long as it's busy. This doesn't give the oppurtunity for changes in irq affinities to take effect. Fix that by calling napi_complete_done() upon a change in affinity. This would stop the NAPI and reschedule it on the new CPU. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 3 +++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 5 +++++ drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 20 ++++++++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 7c046ae8b18e..1388a1e2f835 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -582,6 +582,9 @@ struct mlx5e_channel { __be32 mkey_be; u8 num_tc; + /* data path - accessed per napi poll */ + struct irq_desc *irq_desc; + /* control */ struct mlx5e_priv *priv; struct mlx5_core_dev *mdev; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index b2f689ec0d72..20f34131d4e5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1761,7 +1761,9 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, struct net_device *netdev = priv->netdev; int cpu = mlx5e_get_cpu(priv, ix); struct mlx5e_channel *c; + unsigned int irq; int err; + int eqn; c = kzalloc_node(sizeof(*c), GFP_KERNEL, cpu_to_node(cpu)); if (!c) @@ -1778,6 +1780,9 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, c->num_tc = params->num_tc; c->xdp = !!params->xdp_prog; + mlx5_vector2eqn(priv->mdev, ix, &eqn, &irq); + c->irq_desc = irq_to_desc(irq); + netif_napi_add(netdev, &c->napi, mlx5e_napi_poll, 64); err = mlx5e_open_cq(c, icocq_moder, &cparam->icosq_cq, &c->icosq.cq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 92d9aa1cddd6..e906b754415c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -30,8 +30,20 @@ * SOFTWARE. */ +#include #include "en.h" +static inline bool mlx5e_channel_no_affinity_change(struct mlx5e_channel *c) +{ + int current_cpu = smp_processor_id(); + const struct cpumask *aff; + struct irq_data *idata; + + idata = irq_desc_get_irq_data(c->irq_desc); + aff = irq_data_get_affinity_mask(idata); + return cpumask_test_cpu(current_cpu, aff); +} + int mlx5e_napi_poll(struct napi_struct *napi, int budget) { struct mlx5e_channel *c = container_of(napi, struct mlx5e_channel, @@ -51,8 +63,12 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) busy |= c->rq.post_wqes(&c->rq); - if (busy) - return budget; + if (busy) { + if (likely(mlx5e_channel_no_affinity_change(c))) + return budget; + if (work_done == budget) + work_done--; + } if (unlikely(!napi_complete_done(napi, work_done))) return work_done; -- cgit v1.2.3 From d4b6c48800dda97f5a0824305d7c8175a127d414 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 7 Jun 2017 13:55:34 +0300 Subject: net/mlx5e: Distribute RSS table among all RX rings In default, uniformly distribute the RSS indirection table entries among all RX rings, rather than restricting this only to the rings on the close NUMA node. irqbalancer would anyway dynamically override the default affinities set to the RX rings. This gives better multi-stream performance and CPU util. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 3 +-- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 3 +-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 15 ++------------- 3 files changed, 4 insertions(+), 17 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 1388a1e2f835..8b7d83bcc11a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -925,8 +925,7 @@ void mlx5e_switch_priv_channels(struct mlx5e_priv *priv, void mlx5e_activate_priv_channels(struct mlx5e_priv *priv); void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv); -void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev, - u32 *indirection_rqt, int len, +void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len, int num_channels); int mlx5e_get_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 6127e0d2f310..d12e9fc0d76b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -663,8 +663,7 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv, new_channels.params = priv->channels.params; new_channels.params.num_channels = count; if (!netif_is_rxfh_configured(priv->netdev)) - mlx5e_build_default_indir_rqt(priv->mdev, - new_channels.params.indirection_rqt, + mlx5e_build_default_indir_rqt(new_channels.params.indirection_rqt, MLX5E_INDIR_RQT_SIZE, count); if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 20f34131d4e5..77068609a153 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3833,22 +3833,11 @@ u16 mlx5e_get_max_inline_cap(struct mlx5_core_dev *mdev) 2 /*sizeof(mlx5e_tx_wqe.inline_hdr_start)*/; } -void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev, - u32 *indirection_rqt, int len, +void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len, int num_channels) { - int node = mdev->priv.numa_node; - int node_num_of_cores; int i; - if (node == -1) - node = first_online_node; - - node_num_of_cores = cpumask_weight(cpumask_of_node(node)); - - if (node_num_of_cores) - num_channels = min_t(int, num_channels, node_num_of_cores); - for (i = 0; i < len; i++) indirection_rqt[i] = i % num_channels; } @@ -3987,7 +3976,7 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, /* RSS */ params->rss_hfunc = ETH_RSS_HASH_XOR; netdev_rss_key_fill(params->toeplitz_hash_key, sizeof(params->toeplitz_hash_key)); - mlx5e_build_default_indir_rqt(mdev, params->indirection_rqt, + mlx5e_build_default_indir_rqt(params->indirection_rqt, MLX5E_INDIR_RQT_SIZE, max_channels); } -- cgit v1.2.3 From 542deb88b0e3696ec9986e804628158169200776 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 31 Aug 2017 17:30:53 +0100 Subject: net/mlx4_core: fix memory leaks on error exit path The structures hca_param and func_cap are not being kfree'd on an error exit path causing two memory leaks. Fix this by jumping to the existing free memory error exit path. Detected by CoverityScan, CID#1455219, CID#1455224 ("Resource Leak") Fixes: c73c8b1e47ca ("net/mlx4_core: Dynamically allocate structs at mlx4_slave_cap") Signed-off-by: Colin Ian King Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index c631d157b97d..b3e50f4892c5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -977,7 +977,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) if (dev->caps.num_ports > MLX4_MAX_PORTS) { mlx4_err(dev, "HCA has %d ports, but we only support %d, aborting\n", dev->caps.num_ports, MLX4_MAX_PORTS); - return -ENODEV; + err = -ENODEV; + goto free_mem; } mlx4_replace_zero_macs(dev); -- cgit v1.2.3 From 942e7e5fc1ea4625d971e62f00b5ae3d2f8855de Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 31 Aug 2017 18:07:24 +0100 Subject: net/mlx4_core: fix incorrect size allocation for dev->caps.spec_qps The current allocation for dev->caps.spec_qps is for the size of the pointer and not the size of the actual mlx4_spec_qps structure. Fix this by using the correct size. Also splint allocation over a few lines to make it cppcheck clean on overly wide lines. Detected by CoverityScan, CID#1455222 ("Wrong sizeof argument") Fixes: c73c8b1e47ca ("net/mlx4_core: Dynamically allocate structs at mlx4_slave_cap") Signed-off-by: Colin Ian King Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/qp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 28c441c0d31f..e40a6d1d0966 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -844,8 +844,9 @@ int mlx4_init_qp_table(struct mlx4_dev *dev) /* In mfunc, calculate proxy and tunnel qp offsets for the PF here, * since the PF does not call mlx4_slave_caps */ - dev->caps.spec_qps = kcalloc(dev->caps.num_ports, sizeof(dev->caps.spec_qps), GFP_KERNEL); - + dev->caps.spec_qps = kcalloc(dev->caps.num_ports, + sizeof(*dev->caps.spec_qps), + GFP_KERNEL); if (!dev->caps.spec_qps) { err = -ENOMEM; goto err_mem; -- cgit v1.2.3 From 3ba8c81e15c11fc396d0b5d11adaf9db6ed39533 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Sat, 2 Sep 2017 11:06:47 +0200 Subject: net: mvpp2: move the mac retrieval/copy logic into its own function The MAC retrieval has a quite complicated logic (which is broken). Moves it to its own function to prepare for patches fixing its logic, so that reviews are easier. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 45 +++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index d5624894152e..a702e60ba70d 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -7465,6 +7465,31 @@ static bool mvpp2_port_has_tx_irqs(struct mvpp2 *priv, return true; } +static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, + struct device_node *port_node, + char **mac_from) +{ + struct mvpp2_port *port = netdev_priv(dev); + char hw_mac_addr[ETH_ALEN] = {0}; + const char *dt_mac_addr; + + dt_mac_addr = of_get_mac_address(port_node); + if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { + *mac_from = "device tree"; + ether_addr_copy(dev->dev_addr, dt_mac_addr); + } else { + if (priv->hw_version == MVPP21) + mvpp21_get_mac_address(port, hw_mac_addr); + if (is_valid_ether_addr(hw_mac_addr)) { + *mac_from = "hardware"; + ether_addr_copy(dev->dev_addr, hw_mac_addr); + } else { + *mac_from = "random"; + eth_hw_addr_random(dev); + } + } +} + /* Ports initialization */ static int mvpp2_port_probe(struct platform_device *pdev, struct device_node *port_node, @@ -7476,9 +7501,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, struct mvpp2_port_pcpu *port_pcpu; struct net_device *dev; struct resource *res; - const char *dt_mac_addr; - const char *mac_from; - char hw_mac_addr[ETH_ALEN] = {0}; + char *mac_from = ""; unsigned int ntxqs, nrxqs; bool has_tx_irqs; u32 id; @@ -7587,21 +7610,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, goto err_free_irq; } - dt_mac_addr = of_get_mac_address(port_node); - if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { - mac_from = "device tree"; - ether_addr_copy(dev->dev_addr, dt_mac_addr); - } else { - if (priv->hw_version == MVPP21) - mvpp21_get_mac_address(port, hw_mac_addr); - if (is_valid_ether_addr(hw_mac_addr)) { - mac_from = "hardware"; - ether_addr_copy(dev->dev_addr, hw_mac_addr); - } else { - mac_from = "random"; - eth_hw_addr_random(dev); - } - } + mvpp2_port_copy_mac_addr(dev, priv, port_node, &mac_from); port->tx_ring_size = MVPP2_MAX_TXD; port->rx_ring_size = MVPP2_MAX_RXD; -- cgit v1.2.3 From d2a6e48e5254f653decc1612f3cbee3e5c8592c7 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Sat, 2 Sep 2017 11:06:48 +0200 Subject: net: mvpp2: fix use of the random mac address for PPv2.2 The MAC retrieval logic is using a variable to store an h/w stored mac address and checks this mac against invalid ones before using it. But the mac address is only read from h/w when using PPv2.1. So when using PPv2.2 it defaults to its init state. This patches fixes the logic to only check if the h/w mac is valid when actually retrieving a mac from h/w. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index a702e60ba70d..c6003508f166 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -7478,15 +7478,17 @@ static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, *mac_from = "device tree"; ether_addr_copy(dev->dev_addr, dt_mac_addr); } else { - if (priv->hw_version == MVPP21) + if (priv->hw_version == MVPP21) { mvpp21_get_mac_address(port, hw_mac_addr); - if (is_valid_ether_addr(hw_mac_addr)) { - *mac_from = "hardware"; - ether_addr_copy(dev->dev_addr, hw_mac_addr); - } else { - *mac_from = "random"; - eth_hw_addr_random(dev); + if (is_valid_ether_addr(hw_mac_addr)) { + *mac_from = "hardware"; + ether_addr_copy(dev->dev_addr, hw_mac_addr); + return; + } } + + *mac_from = "random"; + eth_hw_addr_random(dev); } } -- cgit v1.2.3 From 688cbaf202df8b4a69f5ccd39e0027f179852d44 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Sat, 2 Sep 2017 11:06:49 +0200 Subject: net: mvpp2: fallback using h/w and random mac if the dt one isn't valid When using a mac address described in the device tree, a check is made to see if it is valid. When it's not, no fallback is defined. This patches tries to get the mac address from h/w (or use a random one if the h/w one isn't valid) when the dt mac address isn't valid. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index c6003508f166..dd0ee2691c86 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -7477,19 +7477,20 @@ static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { *mac_from = "device tree"; ether_addr_copy(dev->dev_addr, dt_mac_addr); - } else { - if (priv->hw_version == MVPP21) { - mvpp21_get_mac_address(port, hw_mac_addr); - if (is_valid_ether_addr(hw_mac_addr)) { - *mac_from = "hardware"; - ether_addr_copy(dev->dev_addr, hw_mac_addr); - return; - } - } + return; + } - *mac_from = "random"; - eth_hw_addr_random(dev); + if (priv->hw_version == MVPP21) { + mvpp21_get_mac_address(port, hw_mac_addr); + if (is_valid_ether_addr(hw_mac_addr)) { + *mac_from = "hardware"; + ether_addr_copy(dev->dev_addr, hw_mac_addr); + return; + } } + + *mac_from = "random"; + eth_hw_addr_random(dev); } /* Ports initialization */ -- cgit v1.2.3 From 6d9c153a0b84392406bc77600aa7d3ea365de041 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 2 Sep 2017 08:56:45 -0700 Subject: net: dsa: loop: Do not unregister invalid fixed PHY During error injection it was possible to crash in dsa_loop_exit() because of an attempt to unregister an invalid PHY. We actually want to the driver probing in dsa_loop_init() even though fixed_phy_register() may return an error to exercise how DSA deals with such cases, but we should not be crashing during driver removal. Fixes: 98cd1552ea27 ("net: dsa: Mock-up driver") Signed-off-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/dsa_loop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index fdd8f3872102..8150e3a3c8dd 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -390,7 +390,7 @@ static void __exit dsa_loop_exit(void) mdio_driver_unregister(&dsa_loop_drv); for (i = 0; i < NUM_FIXED_PHYS; i++) - if (phydevs[i]) + if (!IS_ERR(phydevs[i])) fixed_phy_unregister(phydevs[i]); } module_exit(dsa_loop_exit); -- cgit v1.2.3 From 99ae8e3e5e609c1d9d4288b3a6eec449733189df Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:09 +0200 Subject: mlxsw: reg: Update RITR to support loopback device Update the register so that loopback RIFs can be created and loopback properties specified. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 88 +++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 11e290c34aaf..8736f8492fd3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3998,6 +3998,8 @@ enum mlxsw_reg_ritr_if_type { MLXSW_REG_RITR_FID_IF, /* Sub-port interface. */ MLXSW_REG_RITR_SP_IF, + /* Loopback Interface. */ + MLXSW_REG_RITR_LOOPBACK_IF, }; /* reg_ritr_type @@ -4129,6 +4131,67 @@ MLXSW_ITEM32(reg, ritr, sp_if_system_port, 0x08, 0, 16); */ MLXSW_ITEM32(reg, ritr, sp_if_vid, 0x18, 0, 12); +/* Loopback Interface */ + +enum mlxsw_reg_ritr_loopback_protocol { + /* IPinIP IPv4 underlay Unicast */ + MLXSW_REG_RITR_LOOPBACK_PROTOCOL_IPIP_IPV4, + /* IPinIP IPv6 underlay Unicast */ + MLXSW_REG_RITR_LOOPBACK_PROTOCOL_IPIP_IPV6, +}; + +/* reg_ritr_loopback_protocol + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_protocol, 0x08, 28, 4); + +enum mlxsw_reg_ritr_loopback_ipip_type { + /* Tunnel is IPinIP. */ + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_IP, + /* Tunnel is GRE, no key. */ + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_IN_IP, + /* Tunnel is GRE, with a key. */ + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_KEY_IN_IP, +}; + +/* reg_ritr_loopback_ipip_type + * Encapsulation type. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_ipip_type, 0x10, 24, 4); + +enum mlxsw_reg_ritr_loopback_ipip_options { + /* The key is defined by gre_key. */ + MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET, +}; + +/* reg_ritr_loopback_ipip_options + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_ipip_options, 0x10, 20, 4); + +/* reg_ritr_loopback_ipip_uvr + * Underlay Virtual Router ID. + * Range is 0..cap_max_virtual_routers-1. + * Reserved for Spectrum-2. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_ipip_uvr, 0x10, 0, 16); + +/* reg_ritr_loopback_ipip_usip* + * Encapsulation Underlay source IP. + * Access: RW + */ +MLXSW_ITEM_BUF(reg, ritr, loopback_ipip_usip6, 0x18, 16); +MLXSW_ITEM32(reg, ritr, loopback_ipip_usip4, 0x24, 0, 32); + +/* reg_ritr_loopback_ipip_gre_key + * GRE Key. + * Reserved when ipip_type is not IP_IN_GRE_KEY_IN_IP. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_ipip_gre_key, 0x28, 0, 32); + /* Shared between ingress/egress */ enum mlxsw_reg_ritr_counter_set_type { /* No Count. */ @@ -4219,6 +4282,31 @@ static inline void mlxsw_reg_ritr_pack(char *payload, bool enable, mlxsw_reg_ritr_if_mac_memcpy_to(payload, mac); } +static inline void +mlxsw_reg_ritr_loopback_ipip_common_pack(char *payload, + enum mlxsw_reg_ritr_loopback_ipip_type ipip_type, + enum mlxsw_reg_ritr_loopback_ipip_options options, + u16 uvr_id, u32 gre_key) +{ + mlxsw_reg_ritr_loopback_ipip_type_set(payload, ipip_type); + mlxsw_reg_ritr_loopback_ipip_options_set(payload, options); + mlxsw_reg_ritr_loopback_ipip_uvr_set(payload, uvr_id); + mlxsw_reg_ritr_loopback_ipip_gre_key_set(payload, gre_key); +} + +static inline void +mlxsw_reg_ritr_loopback_ipip4_pack(char *payload, + enum mlxsw_reg_ritr_loopback_ipip_type ipip_type, + enum mlxsw_reg_ritr_loopback_ipip_options options, + u16 uvr_id, u32 usip, u32 gre_key) +{ + mlxsw_reg_ritr_loopback_protocol_set(payload, + MLXSW_REG_RITR_LOOPBACK_PROTOCOL_IPIP_IPV4); + mlxsw_reg_ritr_loopback_ipip_common_pack(payload, ipip_type, options, + uvr_id, gre_key); + mlxsw_reg_ritr_loopback_ipip_usip4_set(payload, usip); +} + /* RATR - Router Adjacency Table Register * -------------------------------------- * The RATR register is used to configure the Router Adjacency (next-hop) -- cgit v1.2.3 From 7c819de4389bf91878954f41e51a0003171217ff Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:10 +0200 Subject: mlxsw: reg: Update RATR to support IP-in-IP tunnels So far, adjacencies have always been of type Ethernet (with value of 0), and thus there was no need to explicitly support RATR type. However to support IP-in-IP adjacencies, this type and a suite of IP-in-IP-specific attributes need to be added. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 8736f8492fd3..6a7757feee66 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4362,6 +4362,38 @@ MLXSW_ITEM32(reg, ratr, v, 0x00, 24, 1); */ MLXSW_ITEM32(reg, ratr, a, 0x00, 16, 1); +enum mlxsw_reg_ratr_type { + /* Ethernet */ + MLXSW_REG_RATR_TYPE_ETHERNET, + /* IPoIB Unicast without GRH. + * Reserved for Spectrum. + */ + MLXSW_REG_RATR_TYPE_IPOIB_UC, + /* IPoIB Unicast with GRH. Supported only in table 0 (Ethernet unicast + * adjacency). + * Reserved for Spectrum. + */ + MLXSW_REG_RATR_TYPE_IPOIB_UC_W_GRH, + /* IPoIB Multicast. + * Reserved for Spectrum. + */ + MLXSW_REG_RATR_TYPE_IPOIB_MC, + /* MPLS. + * Reserved for SwitchX/-2. + */ + MLXSW_REG_RATR_TYPE_MPLS, + /* IPinIP Encap. + * Reserved for SwitchX/-2. + */ + MLXSW_REG_RATR_TYPE_IPIP, +}; + +/* reg_ratr_type + * Adjacency entry type. + * Access: RW + */ +MLXSW_ITEM32(reg, ratr, type, 0x04, 28, 4); + /* reg_ratr_adjacency_index_low * Bits 15:0 of index into the adjacency table. * For SwitchX and SwitchX-2, the adjacency table is linear and @@ -4416,6 +4448,34 @@ MLXSW_ITEM32(reg, ratr, trap_id, 0x0C, 0, 8); */ MLXSW_ITEM_BUF(reg, ratr, eth_destination_mac, 0x12, 6); +enum mlxsw_reg_ratr_ipip_type { + /* IPv4, address set by mlxsw_reg_ratr_ipip_ipv4_udip. */ + MLXSW_REG_RATR_IPIP_TYPE_IPV4, + /* IPv6, address set by mlxsw_reg_ratr_ipip_ipv6_ptr. */ + MLXSW_REG_RATR_IPIP_TYPE_IPV6, +}; + +/* reg_ratr_ipip_type + * Underlay destination ip type. + * Note: the type field must match the protocol of the router interface. + * Access: RW + */ +MLXSW_ITEM32(reg, ratr, ipip_type, 0x10, 16, 4); + +/* reg_ratr_ipip_ipv4_udip + * Underlay ipv4 dip. + * Reserved when ipip_type is IPv6. + * Access: RW + */ +MLXSW_ITEM32(reg, ratr, ipip_ipv4_udip, 0x18, 0, 32); + +/* reg_ratr_ipip_ipv6_ptr + * Pointer to IPv6 underlay destination ip address. + * For Spectrum: Pointer to KVD linear space. + * Access: RW + */ +MLXSW_ITEM32(reg, ratr, ipip_ipv6_ptr, 0x1C, 0, 24); + static inline void mlxsw_reg_ratr_pack(char *payload, enum mlxsw_reg_ratr_op op, bool valid, @@ -4435,6 +4495,12 @@ static inline void mlxsw_reg_ratr_eth_entry_pack(char *payload, mlxsw_reg_ratr_eth_destination_mac_memcpy_to(payload, dest_mac); } +static inline void mlxsw_reg_ratr_ipip4_entry_pack(char *payload, u32 ipv4_udip) +{ + mlxsw_reg_ratr_ipip_type_set(payload, MLXSW_REG_RATR_IPIP_TYPE_IPV4); + mlxsw_reg_ratr_ipip_ipv4_udip_set(payload, ipv4_udip); +} + /* RICNT - Router Interface Counter Register * ----------------------------------------- * The RICNT register retrieves per port performance counters -- cgit v1.2.3 From 6c4153b1e7b71ba5e9183f14eed58a5277be3894 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:11 +0200 Subject: mlxsw: reg: Move enum mlxsw_reg_ratr_trap_id This enum is used with reg_ratr_trap_id, so move it next to the register definition. While at it, drop the enumerator initializers. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 6a7757feee66..bf936b660a7f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4423,17 +4423,17 @@ enum mlxsw_reg_ratr_trap_action { */ MLXSW_ITEM32(reg, ratr, trap_action, 0x0C, 28, 4); -enum mlxsw_reg_ratr_trap_id { - MLXSW_REG_RATR_TRAP_ID_RTR_EGRESS0 = 0, - MLXSW_REG_RATR_TRAP_ID_RTR_EGRESS1 = 1, -}; - /* reg_ratr_adjacency_index_high * Bits 23:16 of the adjacency_index. * Access: Index */ MLXSW_ITEM32(reg, ratr, adjacency_index_high, 0x0C, 16, 8); +enum mlxsw_reg_ratr_trap_id { + MLXSW_REG_RATR_TRAP_ID_RTR_EGRESS0, + MLXSW_REG_RATR_TRAP_ID_RTR_EGRESS1, +}; + /* reg_ratr_trap_id * Trap ID to be reported to CPU. * Trap-ID is RTR_EGRESS0 or RTR_EGRESS1. -- cgit v1.2.3 From a43da820c8ff671054105144287766a24cfbcdc0 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:12 +0200 Subject: mlxsw: reg: Add mlxsw_reg_ralue_act_ip2me_tun_pack() To implement IP-in-IP decapsulation, Spectrum uses LPM entries of type IP2ME with tunnel validity bit and tunnel pointer set. The necessary register fields are already available, so add a function to pack the RALUE as appropriate. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index bf936b660a7f..24296cfd92b9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5054,6 +5054,15 @@ mlxsw_reg_ralue_act_ip2me_pack(char *payload) MLXSW_REG_RALUE_ACTION_TYPE_IP2ME); } +static inline void +mlxsw_reg_ralue_act_ip2me_tun_pack(char *payload, u32 tunnel_ptr) +{ + mlxsw_reg_ralue_action_type_set(payload, + MLXSW_REG_RALUE_ACTION_TYPE_IP2ME); + mlxsw_reg_ralue_ip2me_v_set(payload, 1); + mlxsw_reg_ralue_ip2me_tunnel_ptr_set(payload, tunnel_ptr); +} + /* RAUHT - Router Algorithmic LPM Unicast Host Table Register * ---------------------------------------------------------- * The RAUHT register is used to configure and query the Unicast Host table in -- cgit v1.2.3 From 1e659ebf581997a988faebe56e900e7139a9ccb1 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:13 +0200 Subject: mlxsw: reg: Add Routing Tunnel Decap Properties Register The RTDP register is used for configuring the tunnel decap properties of NVE and IPinIP. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 129 ++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 24296cfd92b9..a6eb96fe0db3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5,6 +5,7 @@ * Copyright (c) 2015 Elad Raz * Copyright (c) 2015-2017 Jiri Pirko * Copyright (c) 2016 Yotam Gigi + * Copyright (c) 2017 Petr Machata * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -5463,6 +5464,133 @@ static inline void mlxsw_reg_rauhtd_ent_ipv6_unpack(char *payload, mlxsw_reg_rauhtd_ipv6_ent_dip_memcpy_from(payload, rec_index, p_dip); } +/* RTDP - Routing Tunnel Decap Properties Register + * ----------------------------------------------- + * The RTDP register is used for configuring the tunnel decap properties of NVE + * and IPinIP. + */ +#define MLXSW_REG_RTDP_ID 0x8020 +#define MLXSW_REG_RTDP_LEN 0x44 + +MLXSW_REG_DEFINE(rtdp, MLXSW_REG_RTDP_ID, MLXSW_REG_RTDP_LEN); + +enum mlxsw_reg_rtdp_type { + MLXSW_REG_RTDP_TYPE_NVE, + MLXSW_REG_RTDP_TYPE_IPIP, +}; + +/* reg_rtdp_type + * Type of the RTDP entry as per enum mlxsw_reg_rtdp_type. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, type, 0x00, 28, 4); + +/* reg_rtdp_tunnel_index + * Index to the Decap entry. + * For Spectrum, Index to KVD Linear. + * Access: Index + */ +MLXSW_ITEM32(reg, rtdp, tunnel_index, 0x00, 0, 24); + +/* IPinIP */ + +/* reg_rtdp_ipip_irif + * Ingress Router Interface for the overlay router + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_irif, 0x04, 16, 16); + +enum mlxsw_reg_rtdp_ipip_sip_check { + /* No sip checks. */ + MLXSW_REG_RTDP_IPIP_SIP_CHECK_NO, + /* Filter packet if underlay is not IPv4 or if underlay SIP does not + * equal ipv4_usip. + */ + MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV4, + /* Filter packet if underlay is not IPv6 or if underlay SIP does not + * equal ipv6_usip. + */ + MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV6 = 3, +}; + +/* reg_rtdp_ipip_sip_check + * SIP check to perform. If decapsulation failed due to these configurations + * then trap_id is IPIP_DECAP_ERROR. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_sip_check, 0x04, 0, 3); + +/* If set, allow decapsulation of IPinIP (without GRE). */ +#define MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_IPIP BIT(0) +/* If set, allow decapsulation of IPinGREinIP without a key. */ +#define MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE BIT(1) +/* If set, allow decapsulation of IPinGREinIP with a key. */ +#define MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE_KEY BIT(2) + +/* reg_rtdp_ipip_type_check + * Flags as per MLXSW_REG_RTDP_IPIP_TYPE_CHECK_*. If decapsulation failed due to + * these configurations then trap_id is IPIP_DECAP_ERROR. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_type_check, 0x08, 24, 3); + +/* reg_rtdp_ipip_gre_key_check + * Whether GRE key should be checked. When check is enabled: + * - A packet received as IPinIP (without GRE) will always pass. + * - A packet received as IPinGREinIP without a key will not pass the check. + * - A packet received as IPinGREinIP with a key will pass the check only if the + * key in the packet is equal to expected_gre_key. + * If decapsulation failed due to GRE key then trap_id is IPIP_DECAP_ERROR. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_gre_key_check, 0x08, 23, 1); + +/* reg_rtdp_ipip_ipv4_usip + * Underlay IPv4 address for ipv4 source address check. + * Reserved when sip_check is not '1'. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_ipv4_usip, 0x0C, 0, 32); + +/* reg_rtdp_ipip_ipv6_usip_ptr + * This field is valid when sip_check is "sipv6 check explicitly". This is a + * pointer to the IPv6 DIP which is configured by RIPS. For Spectrum, the index + * is to the KVD linear. + * Reserved when sip_check is not MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV6. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_ipv6_usip_ptr, 0x10, 0, 24); + +/* reg_rtdp_ipip_expected_gre_key + * GRE key for checking. + * Reserved when gre_key_check is '0'. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_expected_gre_key, 0x14, 0, 32); + +static inline void mlxsw_reg_rtdp_pack(char *payload, + enum mlxsw_reg_rtdp_type type, + u32 tunnel_index) +{ + MLXSW_REG_ZERO(rtdp, payload); + mlxsw_reg_rtdp_type_set(payload, type); + mlxsw_reg_rtdp_tunnel_index_set(payload, tunnel_index); +} + +static inline void +mlxsw_reg_rtdp_ipip4_pack(char *payload, u16 irif, + enum mlxsw_reg_rtdp_ipip_sip_check sip_check, + unsigned int type_check, bool gre_key_check, + u32 ipv4_usip, u32 expected_gre_key) +{ + mlxsw_reg_rtdp_ipip_irif_set(payload, irif); + mlxsw_reg_rtdp_ipip_sip_check_set(payload, sip_check); + mlxsw_reg_rtdp_ipip_type_check_set(payload, type_check); + mlxsw_reg_rtdp_ipip_gre_key_check_set(payload, gre_key_check); + mlxsw_reg_rtdp_ipip_ipv4_usip_set(payload, ipv4_usip); + mlxsw_reg_rtdp_ipip_expected_gre_key_set(payload, expected_gre_key); +} + /* MFCR - Management Fan Control Register * -------------------------------------- * This register controls the settings of the Fan Speed PWM mechanism. @@ -6724,6 +6852,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(rgcr), MLXSW_REG(ritr), MLXSW_REG(ratr), + MLXSW_REG(rtdp), MLXSW_REG(ricnt), MLXSW_REG(ralta), MLXSW_REG(ralst), -- cgit v1.2.3 From 9571e828f460161f76dc3aa24f5857076344b2da Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:14 +0200 Subject: mlxsw: reg: Extract mlxsw_reg_ritr_mac_pack() Unlike other interface types, loopback RIFs do not have MAC address. So drop the corresponding argument from mlxsw_reg_ritr_pack() and move it to a new function. Call that from callers of mlxsw_reg_ritr_pack. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 7 +++++-- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 7 ++++--- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index a6eb96fe0db3..d80cf9b29f65 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4263,8 +4263,7 @@ static inline void mlxsw_reg_ritr_sp_if_pack(char *payload, bool lag, static inline void mlxsw_reg_ritr_pack(char *payload, bool enable, enum mlxsw_reg_ritr_if_type type, - u16 rif, u16 vr_id, u16 mtu, - const char *mac) + u16 rif, u16 vr_id, u16 mtu) { bool op = enable ? MLXSW_REG_RITR_RIF_CREATE : MLXSW_REG_RITR_RIF_DEL; @@ -4280,6 +4279,10 @@ static inline void mlxsw_reg_ritr_pack(char *payload, bool enable, mlxsw_reg_ritr_lb_en_set(payload, 1); mlxsw_reg_ritr_virtual_router_set(payload, vr_id); mlxsw_reg_ritr_mtu_set(payload, mtu); +} + +static inline void mlxsw_reg_ritr_mac_pack(char *payload, const char *mac) +{ mlxsw_reg_ritr_if_mac_memcpy_to(payload, mac); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index de15eac50866..3ddfbe3d0dbe 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -4955,8 +4955,8 @@ static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable) rif_subport = mlxsw_sp_rif_subport_rif(rif); mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF, - rif->rif_index, rif->vr_id, rif->dev->mtu, - rif->dev->dev_addr); + rif->rif_index, rif->vr_id, rif->dev->mtu); + mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr); mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag, rif_subport->lag ? rif_subport->lag_id : rif_subport->system_port, @@ -4998,7 +4998,8 @@ static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif, char ritr_pl[MLXSW_REG_RITR_LEN]; mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id, - rif->dev->mtu, rif->dev->dev_addr); + rif->dev->mtu); + mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr); mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); -- cgit v1.2.3 From 89e419828f91ecf46c85b23576904077f62a1a2e Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:15 +0200 Subject: mlxsw: reg: Give mlxsw_reg_ratr_pack a type parameter To support IPIP, the driver needs to be able to construct an IPIP adjacency. Change mlxsw_reg_ratr_pack to take an adjacency type as an argument. Adjust the one existing caller. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 2 ++ drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index d80cf9b29f65..cc27c5de5a1d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4483,11 +4483,13 @@ MLXSW_ITEM32(reg, ratr, ipip_ipv6_ptr, 0x1C, 0, 24); static inline void mlxsw_reg_ratr_pack(char *payload, enum mlxsw_reg_ratr_op op, bool valid, + enum mlxsw_reg_ratr_type type, u32 adjacency_index, u16 egress_rif) { MLXSW_REG_ZERO(ratr, payload); mlxsw_reg_ratr_op_set(payload, op); mlxsw_reg_ratr_v_set(payload, valid); + mlxsw_reg_ratr_type_set(payload, type); mlxsw_reg_ratr_adjacency_index_low_set(payload, adjacency_index); mlxsw_reg_ratr_adjacency_index_high_set(payload, adjacency_index >> 16); mlxsw_reg_ratr_egress_router_interface_set(payload, egress_rif); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 3ddfbe3d0dbe..4e47d45ab1bb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1928,7 +1928,8 @@ static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index, char ratr_pl[MLXSW_REG_RATR_LEN]; mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, - true, adj_index, neigh_entry->rif); + true, MLXSW_REG_RATR_TYPE_ETHERNET, + adj_index, neigh_entry->rif); mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl); } -- cgit v1.2.3 From ff1f06ce9df9deea690a3f16ded77e0b665acffd Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:16 +0200 Subject: mlxsw: spectrum_router: Publish mlxsw_sp_l3proto The spectrum_ipip module that will be introduced in the follow-up patches needs to know the data type. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 5 ----- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 4e47d45ab1bb..f866ac5fa37d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -405,11 +405,6 @@ struct mlxsw_sp_rt6 { struct rt6_info *rt; }; -enum mlxsw_sp_l3proto { - MLXSW_SP_L3_PROTO_IPV4, - MLXSW_SP_L3_PROTO_IPV6, -}; - struct mlxsw_sp_lpm_tree { u8 id; /* tree ID */ unsigned int ref_count; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index 87a04afee138..67c6c1d5ac93 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -37,6 +37,11 @@ #include "spectrum.h" +enum mlxsw_sp_l3proto { + MLXSW_SP_L3_PROTO_IPV4, + MLXSW_SP_L3_PROTO_IPV6, +}; + enum mlxsw_sp_rif_counter_dir { MLXSW_SP_RIF_COUNTER_INGRESS, MLXSW_SP_RIF_COUNTER_EGRESS, -- cgit v1.2.3 From 38ebc0f45474abf4c4229ec3218915576475af85 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:17 +0200 Subject: mlxsw: spectrum_router: Add mlxsw_sp_ipip_ops Details of individual tunnel types are kept in an array of mlxsw_sp_ipip_ops objects. Follow-up patches will use the list to determine whether a constructed RIF should be a loopback, and to decide whether a next hop references a tunnel. The list is currently empty, follow-up patches will add support for GRE. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Makefile | 4 +- .../net/ethernet/mellanox/mlxsw/spectrum_ipip.c | 38 ++++++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_ipip.h | 51 ++++++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 19 ++++++++ 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index f9956f3bc45c..891ff418bb5e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -16,8 +16,8 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \ spectrum_switchdev.o spectrum_router.o \ spectrum_kvdl.o spectrum_acl_tcam.o \ spectrum_acl.o spectrum_flower.o \ - spectrum_cnt.o \ - spectrum_fid.o + spectrum_cnt.o spectrum_fid.o \ + spectrum_ipip.o mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o mlxsw_spectrum-$(CONFIG_NET_DEVLINK) += spectrum_dpipe.o obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c new file mode 100644 index 000000000000..20b0b969d41d --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c @@ -0,0 +1,38 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Petr Machata + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spectrum_ipip.h" + +const struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[] = { +}; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h new file mode 100644 index 000000000000..65eb493129d9 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h @@ -0,0 +1,51 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Petr Machata + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MLXSW_IPIP_H_ +#define _MLXSW_IPIP_H_ + +#include "spectrum_router.h" + +enum mlxsw_sp_ipip_type { + MLXSW_SP_IPIP_TYPE_MAX, +}; + +struct mlxsw_sp_ipip_ops { + int dev_type; + enum mlxsw_sp_l3proto ul_proto; /* Underlay. */ +}; + +extern const struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[]; + +#endif /* _MLXSW_IPIP_H_*/ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index f866ac5fa37d..f85d24919a95 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -62,6 +62,7 @@ #include "reg.h" #include "spectrum_cnt.h" #include "spectrum_dpipe.h" +#include "spectrum_ipip.h" #include "spectrum_router.h" struct mlxsw_sp_vr; @@ -89,6 +90,7 @@ struct mlxsw_sp_router { bool aborted; struct notifier_block fib_nb; const struct mlxsw_sp_rif_ops **rif_ops_arr; + const struct mlxsw_sp_ipip_ops **ipip_ops_arr; }; struct mlxsw_sp_rif { @@ -5152,6 +5154,16 @@ static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->router->rifs); } +static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp) +{ + mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr; + return 0; +} + +static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp) +{ +} + static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) { struct mlxsw_sp_router *router; @@ -5211,6 +5223,10 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) if (err) goto err_rifs_init; + err = mlxsw_sp_ipips_init(mlxsw_sp); + if (err) + goto err_ipips_init; + err = rhashtable_init(&mlxsw_sp->router->nexthop_ht, &mlxsw_sp_nexthop_ht_params); if (err) @@ -5252,6 +5268,8 @@ err_lpm_init: err_nexthop_group_ht_init: rhashtable_destroy(&mlxsw_sp->router->nexthop_ht); err_nexthop_ht_init: + mlxsw_sp_ipips_fini(mlxsw_sp); +err_ipips_init: mlxsw_sp_rifs_fini(mlxsw_sp); err_rifs_init: __mlxsw_sp_router_fini(mlxsw_sp); @@ -5268,6 +5286,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) mlxsw_sp_lpm_fini(mlxsw_sp); rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht); rhashtable_destroy(&mlxsw_sp->router->nexthop_ht); + mlxsw_sp_ipips_fini(mlxsw_sp); mlxsw_sp_rifs_fini(mlxsw_sp); __mlxsw_sp_router_fini(mlxsw_sp); kfree(mlxsw_sp->router); -- cgit v1.2.3 From 010cadf916a5112a2de1642301917f4139104d46 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:18 +0200 Subject: mlxsw: spectrum_router: Support FID-less RIFs Loopback RIFs, which will be introduced in a follow-up patch, differ from other RIFs in that they do not have a FID associated with them. To support this, demote FID allocation from mlxsw_sp_rif_create to configure op of the existing RIF types, and likewise the FID release from mlxsw_sp_rif_destroy to deconfigure op. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 85 ++++++++++++++++------ 1 file changed, 63 insertions(+), 22 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index f85d24919a95..38477c5f7d4d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -4444,9 +4444,9 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, { u32 tb_id = l3mdev_fib_table(params->dev); const struct mlxsw_sp_rif_ops *ops; + struct mlxsw_sp_fid *fid = NULL; enum mlxsw_sp_rif_type type; struct mlxsw_sp_rif *rif; - struct mlxsw_sp_fid *fid; struct mlxsw_sp_vr *vr; u16 rif_index; int err; @@ -4470,12 +4470,14 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, rif->mlxsw_sp = mlxsw_sp; rif->ops = ops; - fid = ops->fid_get(rif); - if (IS_ERR(fid)) { - err = PTR_ERR(fid); - goto err_fid_get; + if (ops->fid_get) { + fid = ops->fid_get(rif); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + goto err_fid_get; + } + rif->fid = fid; } - rif->fid = fid; if (ops->setup) ops->setup(rif, params); @@ -4484,22 +4486,15 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, if (err) goto err_configure; - err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr, - mlxsw_sp_fid_index(fid), true); - if (err) - goto err_rif_fdb_op; - mlxsw_sp_rif_counters_alloc(rif); - mlxsw_sp_fid_rif_set(fid, rif); mlxsw_sp->router->rifs[rif_index] = rif; vr->rif_count++; return rif; -err_rif_fdb_op: - ops->deconfigure(rif); err_configure: - mlxsw_sp_fid_put(fid); + if (fid) + mlxsw_sp_fid_put(fid); err_fid_get: kfree(rif); err_rif_alloc: @@ -4520,12 +4515,11 @@ void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif) vr->rif_count--; mlxsw_sp->router->rifs[rif->rif_index] = NULL; - mlxsw_sp_fid_rif_set(fid, NULL); mlxsw_sp_rif_counters_free(rif); - mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr, - mlxsw_sp_fid_index(fid), false); ops->deconfigure(rif); - mlxsw_sp_fid_put(fid); + if (fid) + /* Loopback RIFs are not associated with a FID. */ + mlxsw_sp_fid_put(fid); kfree(rif); mlxsw_sp_vr_put(vr); } @@ -4965,11 +4959,32 @@ static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable) static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif) { - return mlxsw_sp_rif_subport_op(rif, true); + int err; + + err = mlxsw_sp_rif_subport_op(rif, true); + if (err) + return err; + + err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(rif->fid), true); + if (err) + goto err_rif_fdb_op; + + mlxsw_sp_fid_rif_set(rif->fid, rif); + return 0; + +err_rif_fdb_op: + mlxsw_sp_rif_subport_op(rif, false); + return err; } static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif) { + struct mlxsw_sp_fid *fid = rif->fid; + + mlxsw_sp_fid_rif_set(fid, NULL); + mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(fid), false); mlxsw_sp_rif_subport_op(rif, false); } @@ -5028,8 +5043,17 @@ static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif) if (err) goto err_fid_bc_flood_set; + err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(rif->fid), true); + if (err) + goto err_rif_fdb_op; + + mlxsw_sp_fid_rif_set(rif->fid, rif); return 0; +err_rif_fdb_op: + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, + mlxsw_sp_router_port(mlxsw_sp), false); err_fid_bc_flood_set: mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, mlxsw_sp_router_port(mlxsw_sp), false); @@ -5040,9 +5064,13 @@ err_fid_mc_flood_set: static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif) { - struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid); + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_fid *fid = rif->fid; + mlxsw_sp_fid_rif_set(fid, NULL); + mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(fid), false); mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), false); mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, @@ -5087,8 +5115,17 @@ static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif) if (err) goto err_fid_bc_flood_set; + err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(rif->fid), true); + if (err) + goto err_rif_fdb_op; + + mlxsw_sp_fid_rif_set(rif->fid, rif); return 0; +err_rif_fdb_op: + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, + mlxsw_sp_router_port(mlxsw_sp), false); err_fid_bc_flood_set: mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, mlxsw_sp_router_port(mlxsw_sp), false); @@ -5099,9 +5136,13 @@ err_fid_mc_flood_set: static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif) { - struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; u16 fid_index = mlxsw_sp_fid_index(rif->fid); + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_fid *fid = rif->fid; + mlxsw_sp_fid_rif_set(fid, NULL); + mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(fid), false); mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), false); mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, -- cgit v1.2.3 From 6ddb7426a7d4429c9df9dada9ebc0cc396d31cc4 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:19 +0200 Subject: mlxsw: spectrum_router: Introduce loopback RIFs When offloading L3 tunnels, an adjacency entry is created that loops the packet back into the underlay router. Loopback interfaces then hold the corresponding information and are created for IP-in-IP netdevices. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 1 + .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 152 ++++++++++++++++++++- .../net/ethernet/mellanox/mlxsw/spectrum_router.h | 13 ++ 3 files changed, 164 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index f8c7f7e930c5..84ce83acdc19 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -77,6 +77,7 @@ enum mlxsw_sp_rif_type { MLXSW_SP_RIF_TYPE_SUBPORT, MLXSW_SP_RIF_TYPE_VLAN, MLXSW_SP_RIF_TYPE_FID, + MLXSW_SP_RIF_TYPE_IPIP_LB, /* IP-in-IP loopback. */ MLXSW_SP_RIF_TYPE_MAX, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 38477c5f7d4d..72e386b647b9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1,9 +1,10 @@ /* * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c - * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016-2017 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko * Copyright (c) 2016 Ido Schimmel * Copyright (c) 2016 Yotam Gigi + * Copyright (c) 2017 Petr Machata * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -51,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -131,6 +133,17 @@ struct mlxsw_sp_rif_subport { bool lag; }; +struct mlxsw_sp_rif_ipip_lb { + struct mlxsw_sp_rif common; + struct mlxsw_sp_rif_ipip_lb_config lb_config; + u16 ul_vr_id; /* Reserved for Spectrum-2. */ +}; + +struct mlxsw_sp_rif_params_ipip_lb { + struct mlxsw_sp_rif_params common; + struct mlxsw_sp_rif_ipip_lb_config lb_config; +}; + struct mlxsw_sp_rif_ops { enum mlxsw_sp_rif_type type; size_t rif_size; @@ -883,6 +896,25 @@ static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->router->vrs); } +static struct net_device * +__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + struct net *net = dev_net(ol_dev); + + return __dev_get_by_index(net, tun->parms.link); +} + +static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev) +{ + struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); + + if (d) + return l3mdev_fib_table(d) ? : RT_TABLE_MAIN; + else + return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN; +} + struct mlxsw_sp_neigh_key { struct neighbour *n; }; @@ -2236,6 +2268,25 @@ static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp, neigh_release(n); } +static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *dev, + enum mlxsw_sp_ipip_type *p_type) +{ + struct mlxsw_sp_router *router = mlxsw_sp->router; + const struct mlxsw_sp_ipip_ops *ipip_ops; + enum mlxsw_sp_ipip_type ipipt; + + for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) { + ipip_ops = router->ipip_ops_arr[ipipt]; + if (dev->type == ipip_ops->dev_type) { + if (p_type) + *p_type = ipipt; + return true; + } + } + return false; +} + static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp, struct mlxsw_sp_nexthop *nh, @@ -4374,7 +4425,10 @@ mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp, { enum mlxsw_sp_fid_type type; - /* RIF type is derived from the type of the underlying FID */ + if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL)) + return MLXSW_SP_RIF_TYPE_IPIP_LB; + + /* Otherwise RIF type is derived from the type of the underlying FID. */ if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev))) type = MLXSW_SP_FID_TYPE_8021Q; else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev)) @@ -5164,10 +5218,104 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = { .fid_get = mlxsw_sp_rif_fid_fid_get, }; +static struct mlxsw_sp_rif_ipip_lb * +mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif) +{ + return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common); +} + +static void +mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif, + const struct mlxsw_sp_rif_params *params) +{ + struct mlxsw_sp_rif_params_ipip_lb *params_lb; + struct mlxsw_sp_rif_ipip_lb *rif_lb; + + params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb, + common); + rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif); + rif_lb->lb_config = params_lb->lb_config; +} + +static int +mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif, + struct mlxsw_sp_vr *ul_vr, bool enable) +{ + struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config; + struct mlxsw_sp_rif *rif = &lb_rif->common; + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + char ritr_pl[MLXSW_REG_RITR_LEN]; + u32 saddr4; + + switch (lb_cf.ul_protocol) { + case MLXSW_SP_L3_PROTO_IPV4: + saddr4 = be32_to_cpu(lb_cf.saddr.addr4); + mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF, + rif->rif_index, rif->vr_id, rif->dev->mtu); + mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt, + MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET, + ul_vr->id, saddr4, lb_cf.okey); + break; + + case MLXSW_SP_L3_PROTO_IPV6: + return -EAFNOSUPPORT; + } + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); +} + +static int +mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif) +{ + struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif); + u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev); + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_vr *ul_vr; + int err; + + ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id); + if (IS_ERR(ul_vr)) + return PTR_ERR(ul_vr); + + err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true); + if (err) + goto err_loopback_op; + + lb_rif->ul_vr_id = ul_vr->id; + ++ul_vr->rif_count; + return 0; + +err_loopback_op: + mlxsw_sp_vr_put(ul_vr); + return err; +} + +static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif) +{ + struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif); + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_vr *ul_vr; + + ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id]; + mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false); + + --ul_vr->rif_count; + mlxsw_sp_vr_put(ul_vr); +} + +static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = { + .type = MLXSW_SP_RIF_TYPE_IPIP_LB, + .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb), + .setup = mlxsw_sp_rif_ipip_lb_setup, + .configure = mlxsw_sp_rif_ipip_lb_configure, + .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure, +}; + static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = { [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops, [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops, [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops, + [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops, }; static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index 67c6c1d5ac93..a5732aa87b9a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -36,12 +36,25 @@ #define _MLXSW_ROUTER_H_ #include "spectrum.h" +#include "reg.h" enum mlxsw_sp_l3proto { MLXSW_SP_L3_PROTO_IPV4, MLXSW_SP_L3_PROTO_IPV6, }; +union mlxsw_sp_l3addr { + __be32 addr4; + struct in6_addr addr6; +}; + +struct mlxsw_sp_rif_ipip_lb_config { + enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt; + u32 okey; + enum mlxsw_sp_l3proto ul_protocol; /* Underlay. */ + union mlxsw_sp_l3addr saddr; +}; + enum mlxsw_sp_rif_counter_dir { MLXSW_SP_RIF_COUNTER_INGRESS, MLXSW_SP_RIF_COUNTER_EGRESS, -- cgit v1.2.3 From 9b01451ad5fe09d95aea9cfd5b36cde574651ab4 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:20 +0200 Subject: mlxsw: spectrum_router: Extract mlxsw_sp_fi_is_gateway() For IPv4 IP-in-IP offload, routes that direct traffic to IP-in-IP devices need to be considered gateway routes as well. That involves a bit more logic, so extract the current test to a separate function, where the logic can be later added. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 72e386b647b9..04e188772109 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2382,6 +2382,12 @@ static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, } } +static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp, + const struct fib_info *fi) +{ + return fi->fib_nh->nh_scope == RT_SCOPE_LINK; +} + static struct mlxsw_sp_nexthop_group * mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) { @@ -2401,7 +2407,7 @@ mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) INIT_LIST_HEAD(&nh_grp->fib_list); nh_grp->neigh_tbl = &arp_tbl; - nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK; + nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi); nh_grp->count = fi->fib_nhs; fib_info_hold(fi); for (i = 0; i < nh_grp->count; i++) { @@ -2801,10 +2807,10 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp, fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; return 0; case RTN_UNICAST: - if (fi->fib_nh->nh_scope != RT_SCOPE_LINK) - fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; - else + if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi)) fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; + else + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; return 0; default: return -EINVAL; -- cgit v1.2.3 From f6050ee6f462e6dbd9dd6a49e5dc3cca7fc0b601 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:21 +0200 Subject: mlxsw: spectrum_router: Extract mlxsw_sp_rt6_is_gateway() IPv6 counterpart of the previous patch: introduce a function to determine whether a given route is a gateway route. The new function takes a mlxsw_sp argument which follow-up patches will use. Thus mlxsw_sp_fib6_entry_type_set() got that argument as well. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 04e188772109..563e803166bd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3526,6 +3526,12 @@ static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_nexthop_rif_fini(nh); } +static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp, + const struct rt6_info *rt) +{ + return rt->rt6i_flags & RTF_GATEWAY; +} + static struct mlxsw_sp_nexthop_group * mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib6_entry *fib6_entry) @@ -3548,7 +3554,7 @@ mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp, #endif mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6, list); - nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY); + nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt); nh_grp->count = fib6_entry->nrt6; for (i = 0; i < nh_grp->count; i++) { struct rt6_info *rt = mlxsw_sp_rt6->rt; @@ -3705,7 +3711,8 @@ mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); } -static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry, +static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, const struct rt6_info *rt) { /* Packets hitting RTF_REJECT routes need to be discarded by the @@ -3718,7 +3725,7 @@ static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry, fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; else if (rt->rt6i_flags & RTF_REJECT) fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; - else if (rt->rt6i_flags & RTF_GATEWAY) + else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt)) fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; else fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; @@ -3758,7 +3765,7 @@ mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp, goto err_rt6_create; } - mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt); + mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt); INIT_LIST_HEAD(&fib6_entry->rt6_list); list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list); -- cgit v1.2.3 From 35225e4740baa9707f4063bedb4367185b9d1ae7 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:22 +0200 Subject: mlxsw: spectrum_router: Make nexthops typed In the router, some next hops may reference an encapsulating netdevice, such as GRE or IPIP. To properly offload these next hops, mlxsw needs to keep track of whether a given next hop is a regular Ethernet entry, or an IP-in-IP tunneling entry. To facilitate this book-keeping, add a type field to struct mlxsw_sp_nexthop. There is, as of this patch, only one next hop type: MLXSW_SP_NEXTHOP_TYPE_ETH. Follow-up patches will introduce the IP-in-IP variant. There are several places where next hops are initialized in the IPv4 path. Instead of replicating the logic at every one of them, factor it out to a function mlxsw_sp_nexthop4_type_init(). The corresponding fini is actually protocol-neutral, so put it to mlxsw_sp_nexthop_type_fini(), but create a corresponding protocoled _fini function that dispatches to the protocol-neutral one. The IPv6 path is simpler, but for symmetry with IPv4, create the same suite of functions with corresponding logic. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 138 ++++++++++++++------- 1 file changed, 95 insertions(+), 43 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 563e803166bd..53bdd0fdc176 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1652,6 +1652,10 @@ static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, } } +enum mlxsw_sp_nexthop_type { + MLXSW_SP_NEXTHOP_TYPE_ETH, +}; + struct mlxsw_sp_nexthop_key { struct fib_nh *fib_nh; }; @@ -1676,7 +1680,10 @@ struct mlxsw_sp_nexthop { update:1; /* set indicates that MAC of this neigh should be * updated in HW */ - struct mlxsw_sp_neigh_entry *neigh_entry; + enum mlxsw_sp_nexthop_type type; + union { + struct mlxsw_sp_neigh_entry *neigh_entry; + }; }; struct mlxsw_sp_nexthop_group { @@ -1964,9 +1971,9 @@ static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index, } static int -mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop_group *nh_grp, - bool reallocate) +mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp, + bool reallocate) { u32 adj_index = nh_grp->adj_index; /* base */ struct mlxsw_sp_nexthop *nh; @@ -1982,8 +1989,12 @@ mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp, } if (nh->update || reallocate) { - err = mlxsw_sp_nexthop_mac_update(mlxsw_sp, - adj_index, nh); + switch (nh->type) { + case MLXSW_SP_NEXTHOP_TYPE_ETH: + err = mlxsw_sp_nexthop_mac_update + (mlxsw_sp, adj_index, nh); + break; + } if (err) return err; nh->update = 0; @@ -2071,8 +2082,7 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, /* Nothing was added or removed, so no need to reallocate. Just * update MAC on existing adjacency indexes. */ - err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, - false); + err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false); if (err) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n"); goto set_trap; @@ -2099,7 +2109,7 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, nh_grp->adj_index_valid = 1; nh_grp->adj_index = adj_index; nh_grp->ecmp_size = ecmp_size; - err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true); + err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true); if (err) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n"); goto set_trap; @@ -2287,6 +2297,48 @@ static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp, return false; } +static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + switch (nh->type) { + case MLXSW_SP_NEXTHOP_TYPE_ETH: + mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); + mlxsw_sp_nexthop_rif_fini(nh); + break; + } +} + +static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh, + struct fib_nh *fib_nh) +{ + struct net_device *dev = fib_nh->nh_dev; + struct mlxsw_sp_rif *rif; + int err; + + nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH; + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!rif) + return 0; + + mlxsw_sp_nexthop_rif_init(nh, rif); + err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); + if (err) + goto err_neigh_init; + + return 0; + +err_neigh_init: + mlxsw_sp_nexthop_rif_fini(nh); + return err; +} + +static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); +} + static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp, struct mlxsw_sp_nexthop *nh, @@ -2294,7 +2346,6 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp, { struct net_device *dev = fib_nh->nh_dev; struct in_device *in_dev; - struct mlxsw_sp_rif *rif; int err; nh->nh_grp = nh_grp; @@ -2312,19 +2363,13 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp, fib_nh->nh_flags & RTNH_F_LINKDOWN) return 0; - rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!rif) - return 0; - mlxsw_sp_nexthop_rif_init(nh, rif); - - err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); + err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh); if (err) goto err_nexthop_neigh_init; return 0; err_nexthop_neigh_init: - mlxsw_sp_nexthop_rif_fini(nh); mlxsw_sp_nexthop_remove(mlxsw_sp, nh); return err; } @@ -2332,8 +2377,7 @@ err_nexthop_neigh_init: static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh) { - mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); - mlxsw_sp_nexthop_rif_fini(nh); + mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh); mlxsw_sp_nexthop_remove(mlxsw_sp, nh); } @@ -2342,7 +2386,6 @@ static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_nexthop_key key; struct mlxsw_sp_nexthop *nh; - struct mlxsw_sp_rif *rif; if (mlxsw_sp->router->aborted) return; @@ -2352,18 +2395,12 @@ static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp, if (WARN_ON_ONCE(!nh)) return; - rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev); - if (!rif) - return; - switch (event) { case FIB_EVENT_NH_ADD: - mlxsw_sp_nexthop_rif_init(nh, rif); - mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); + mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh); break; case FIB_EVENT_NH_DEL: - mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); - mlxsw_sp_nexthop_rif_fini(nh); + mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh); break; } @@ -2376,8 +2413,7 @@ static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh, *tmp; list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) { - mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); - mlxsw_sp_nexthop_rif_fini(nh); + mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp); } } @@ -3487,22 +3523,16 @@ mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry, return NULL; } -static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop_group *nh_grp, - struct mlxsw_sp_nexthop *nh, - const struct rt6_info *rt) +static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp, + struct mlxsw_sp_nexthop *nh, + const struct rt6_info *rt) { struct net_device *dev = rt->dst.dev; struct mlxsw_sp_rif *rif; int err; - nh->nh_grp = nh_grp; - memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr)); - - if (!dev) - return 0; - nh->ifindex = dev->ifindex; - + nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH; rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); if (!rif) return 0; @@ -3519,11 +3549,33 @@ err_nexthop_neigh_init: return err; } +static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); +} + +static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp, + struct mlxsw_sp_nexthop *nh, + const struct rt6_info *rt) +{ + struct net_device *dev = rt->dst.dev; + + nh->nh_grp = nh_grp; + memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr)); + + if (!dev) + return 0; + nh->ifindex = dev->ifindex; + + return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt); +} + static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh) { - mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); - mlxsw_sp_nexthop_rif_fini(nh); + mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh); } static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp, -- cgit v1.2.3 From 1012b9ac28c6d61f54e0dd9f8744af88427020b7 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:23 +0200 Subject: mlxsw: spectrum_router: Support IPv4 overlay encap This introduces some common code for tracking of offloaded IP-in-IP tunnels, and support for offloading IPv4 overlay encapsulating routes in particular. A follow-up patch will introduce IPv6 overlay as well. Offloaded tunnels are kept in a linked list of mlxsw_sp_ipip_entry objects hooked up in mlxsw_sp_router. A network device that represents the tunnel is used as a key to look up the corresponding IPIP entry. Note that in the future, more general keying mechanism will be needed, because parts of the tunnel information can be provided by the route. IPIP entries are reference counted, because several next hops may end up using the same tunnel, and we only want to offload it once. Encapsulation path hooks into next hop handling. Routes that forward to a tunnel are now considered gateway routes, thus giving them the same treatment that other remote routes get. An IPIP next hop type is introduced. Details of individual tunnel types are kept in an array of mlxsw_sp_ipip_ops objects. If a tunnel type doesn't match any of the known tunnel types, the next-hop is not considered an IPIP next hop. The list of IPIP tunnel types is currently empty, follow-up patches will add support for GRE. Traffic to IPIP tunnel types that are not explicitly recognized by the driver traps and is handled in slow path. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_ipip.h | 20 ++ .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 238 ++++++++++++++++++++- 2 files changed, 257 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h index 65eb493129d9..7b40aa2b049a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h @@ -41,9 +41,29 @@ enum mlxsw_sp_ipip_type { MLXSW_SP_IPIP_TYPE_MAX, }; +struct mlxsw_sp_ipip_entry { + enum mlxsw_sp_ipip_type ipipt; + struct net_device *ol_dev; /* Overlay. */ + struct mlxsw_sp_rif_ipip_lb *ol_lb; + unsigned int ref_count; /* Number of next hops using the tunnel. */ + struct list_head ipip_list_node; +}; + struct mlxsw_sp_ipip_ops { int dev_type; enum mlxsw_sp_l3proto ul_proto; /* Underlay. */ + + int (*nexthop_update)(struct mlxsw_sp *mlxsw_sp, u32 adj_index, + struct mlxsw_sp_ipip_entry *ipip_entry); + + bool (*can_offload)(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev, + enum mlxsw_sp_l3proto ol_proto); + + /* Return a configuration for creating an overlay loopback RIF. */ + struct mlxsw_sp_rif_ipip_lb_config + (*ol_loopback_config)(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev); }; extern const struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[]; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 53bdd0fdc176..231b597c8c8e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -89,6 +89,7 @@ struct mlxsw_sp_router { struct delayed_work nexthop_probe_dw; #define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */ struct list_head nexthop_neighs_list; + struct list_head ipip_list; bool aborted; struct notifier_block fib_nb; const struct mlxsw_sp_rif_ops **rif_ops_arr; @@ -915,6 +916,170 @@ static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev) return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN; } +static struct mlxsw_sp_rif * +mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, + const struct mlxsw_sp_rif_params *params); + +static struct mlxsw_sp_rif_ipip_lb * +mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt, + struct net_device *ol_dev) +{ + struct mlxsw_sp_rif_params_ipip_lb lb_params; + const struct mlxsw_sp_ipip_ops *ipip_ops; + struct mlxsw_sp_rif *rif; + + ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt]; + lb_params = (struct mlxsw_sp_rif_params_ipip_lb) { + .common.dev = ol_dev, + .common.lag = false, + .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev), + }; + + rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common); + if (IS_ERR(rif)) + return ERR_CAST(rif); + return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common); +} + +static struct mlxsw_sp_ipip_entry * +mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt, + struct net_device *ol_dev) +{ + struct mlxsw_sp_ipip_entry *ipip_entry; + struct mlxsw_sp_ipip_entry *ret = NULL; + + ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL); + if (!ipip_entry) + return ERR_PTR(-ENOMEM); + + ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt, + ol_dev); + if (IS_ERR(ipip_entry->ol_lb)) { + ret = ERR_CAST(ipip_entry->ol_lb); + goto err_ol_ipip_lb_create; + } + + ipip_entry->ipipt = ipipt; + ipip_entry->ol_dev = ol_dev; + + return ipip_entry; + +err_ol_ipip_lb_create: + kfree(ipip_entry); + return ret; +} + +static void +mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp_ipip_entry *ipip_entry) +{ + WARN_ON(ipip_entry->ref_count > 0); + mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common); + kfree(ipip_entry); +} + +static __be32 +mlxsw_sp_ipip_netdev_saddr4(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return tun->parms.iph.saddr; +} + +union mlxsw_sp_l3addr +mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev) +{ + switch (proto) { + case MLXSW_SP_L3_PROTO_IPV4: + return (union mlxsw_sp_l3addr) { + .addr4 = mlxsw_sp_ipip_netdev_saddr4(ol_dev), + }; + case MLXSW_SP_L3_PROTO_IPV6: + break; + }; + + WARN_ON(1); + return (union mlxsw_sp_l3addr) { + .addr4 = 0, + }; +} + +static bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1, + const union mlxsw_sp_l3addr *addr2) +{ + return !memcmp(addr1, addr2, sizeof(*addr1)); +} + +static bool +mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp, + const enum mlxsw_sp_l3proto ul_proto, + union mlxsw_sp_l3addr saddr, + u32 ul_tb_id, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev); + enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt; + union mlxsw_sp_l3addr tun_saddr; + + if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto) + return false; + + tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev); + return tun_ul_tb_id == ul_tb_id && + mlxsw_sp_l3addr_eq(&tun_saddr, &saddr); +} + +static struct mlxsw_sp_ipip_entry * +mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt, + struct net_device *ol_dev) +{ + u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev); + struct mlxsw_sp_router *router = mlxsw_sp->router; + struct mlxsw_sp_ipip_entry *ipip_entry; + enum mlxsw_sp_l3proto ul_proto; + union mlxsw_sp_l3addr saddr; + + list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list, + ipip_list_node) { + if (ipip_entry->ol_dev == ol_dev) + goto inc_ref_count; + + /* The configuration where several tunnels have the same local + * address in the same underlay table needs special treatment in + * the HW. That is currently not implemented in the driver. + */ + ul_proto = router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto; + saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev); + if (mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr, + ul_tb_id, ipip_entry)) + return ERR_PTR(-EEXIST); + } + + ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev); + if (IS_ERR(ipip_entry)) + return ipip_entry; + + list_add_tail(&ipip_entry->ipip_list_node, + &mlxsw_sp->router->ipip_list); + +inc_ref_count: + ++ipip_entry->ref_count; + return ipip_entry; +} + +static void +mlxsw_sp_ipip_entry_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + if (--ipip_entry->ref_count == 0) { + list_del(&ipip_entry->ipip_list_node); + mlxsw_sp_ipip_entry_destroy(ipip_entry); + } +} + struct mlxsw_sp_neigh_key { struct neighbour *n; }; @@ -1654,6 +1819,7 @@ static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, enum mlxsw_sp_nexthop_type { MLXSW_SP_NEXTHOP_TYPE_ETH, + MLXSW_SP_NEXTHOP_TYPE_IPIP, }; struct mlxsw_sp_nexthop_key { @@ -1683,6 +1849,7 @@ struct mlxsw_sp_nexthop { enum mlxsw_sp_nexthop_type type; union { struct mlxsw_sp_neigh_entry *neigh_entry; + struct mlxsw_sp_ipip_entry *ipip_entry; }; }; @@ -1970,6 +2137,16 @@ static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl); } +static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp, + u32 adj_index, + struct mlxsw_sp_nexthop *nh) +{ + const struct mlxsw_sp_ipip_ops *ipip_ops; + + ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt]; + return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry); +} + static int mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp, @@ -1994,6 +2171,10 @@ mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp, err = mlxsw_sp_nexthop_mac_update (mlxsw_sp, adj_index, nh); break; + case MLXSW_SP_NEXTHOP_TYPE_IPIP: + err = mlxsw_sp_nexthop_ipip_update + (mlxsw_sp, adj_index, nh); + break; } if (err) return err; @@ -2297,6 +2478,46 @@ static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp, return false; } +static int mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt, + struct mlxsw_sp_nexthop *nh, + struct net_device *ol_dev) +{ + if (!nh->nh_grp->gateway || nh->ipip_entry) + return 0; + + nh->ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev); + if (IS_ERR(nh->ipip_entry)) + return PTR_ERR(nh->ipip_entry); + + __mlxsw_sp_nexthop_neigh_update(nh, false); + return 0; +} + +static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry; + + if (!ipip_entry) + return; + + __mlxsw_sp_nexthop_neigh_update(nh, true); + mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry); + nh->ipip_entry = NULL; +} + +static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp, + const struct fib_nh *fib_nh, + enum mlxsw_sp_ipip_type *p_ipipt) +{ + struct net_device *dev = fib_nh->nh_dev; + + return dev && + fib_nh->nh_parent->fib_type == RTN_UNICAST && + mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt); +} + static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh) { @@ -2305,6 +2526,9 @@ static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); mlxsw_sp_nexthop_rif_fini(nh); break; + case MLXSW_SP_NEXTHOP_TYPE_IPIP: + mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh); + break; } } @@ -2312,10 +2536,19 @@ static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh, struct fib_nh *fib_nh) { + struct mlxsw_sp_router *router = mlxsw_sp->router; struct net_device *dev = fib_nh->nh_dev; + enum mlxsw_sp_ipip_type ipipt; struct mlxsw_sp_rif *rif; int err; + if (mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fib_nh, &ipipt) && + router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev, + MLXSW_SP_L3_PROTO_IPV4)) { + nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP; + return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev); + } + nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH; rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); if (!rif) @@ -2421,7 +2654,8 @@ static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp, const struct fib_info *fi) { - return fi->fib_nh->nh_scope == RT_SCOPE_LINK; + return fi->fib_nh->nh_scope == RT_SCOPE_LINK || + mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL); } static struct mlxsw_sp_nexthop_group * @@ -5411,11 +5645,13 @@ static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp) static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp) { mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr; + INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list); return 0; } static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp) { + WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list)); } static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) -- cgit v1.2.3 From 8f28a3097645ae29a7c6835ed9323d6b6b8db169 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:24 +0200 Subject: mlxsw: spectrum_router: Support IPv6 overlay encap Add the missing bits to recognize IPv6 next hops as IPIP ones to enable offloading of IPv6 overlay encapsulation. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 231b597c8c8e..05afd535805e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3757,15 +3757,32 @@ mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry, return NULL; } +static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp, + const struct rt6_info *rt, + enum mlxsw_sp_ipip_type *ret) +{ + return rt->dst.dev && + mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret); +} + static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp, struct mlxsw_sp_nexthop *nh, const struct rt6_info *rt) { + struct mlxsw_sp_router *router = mlxsw_sp->router; struct net_device *dev = rt->dst.dev; + enum mlxsw_sp_ipip_type ipipt; struct mlxsw_sp_rif *rif; int err; + if (mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, &ipipt) && + router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev, + MLXSW_SP_L3_PROTO_IPV6)) { + nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP; + return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev); + } + nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH; rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); if (!rif) @@ -3815,7 +3832,8 @@ static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp, static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp, const struct rt6_info *rt) { - return rt->rt6i_flags & RTF_GATEWAY; + return rt->rt6i_flags & RTF_GATEWAY || + mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL); } static struct mlxsw_sp_nexthop_group * -- cgit v1.2.3 From 4607f6d26950ffb3c4c8e5b2db42f364f19dd26c Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:25 +0200 Subject: mlxsw: spectrum_router: Support IPv4 underlay decap Unlike encapsulation, which is represented by a next hop forwarding to an IPIP tunnel, decapsulation is a type of local route. It is created for local routes whose prefix corresponds to the local address of one of offloaded IPIP tunnels. When the tunnel is removed (i.e. all the encap next hops are removed), the decap offload is migrated back to a trap for resolution in slow path. This patch assumes that decap route is already present when encap route is added. A follow-up patch will fix this issue. Note that this patch only supports IPv4 underlay. Support for IPv6 underlay will be subject to follow-up work apart from this patchset. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_ipip.h | 7 + .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 141 ++++++++++++++++++++- .../net/ethernet/mellanox/mlxsw/spectrum_router.h | 3 + 3 files changed, 146 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h index 7b40aa2b049a..cd986bde856a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h @@ -36,6 +36,7 @@ #define _MLXSW_IPIP_H_ #include "spectrum_router.h" +#include enum mlxsw_sp_ipip_type { MLXSW_SP_IPIP_TYPE_MAX, @@ -46,6 +47,7 @@ struct mlxsw_sp_ipip_entry { struct net_device *ol_dev; /* Overlay. */ struct mlxsw_sp_rif_ipip_lb *ol_lb; unsigned int ref_count; /* Number of next hops using the tunnel. */ + struct mlxsw_sp_fib_entry *decap_fib_entry; struct list_head ipip_list_node; }; @@ -64,6 +66,11 @@ struct mlxsw_sp_ipip_ops { struct mlxsw_sp_rif_ipip_lb_config (*ol_loopback_config)(struct mlxsw_sp *mlxsw_sp, const struct net_device *ol_dev); + + int (*fib_entry_op)(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + enum mlxsw_reg_ralue_op op, + u32 tunnel_index); }; extern const struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[]; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 05afd535805e..0d09f81ff686 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -381,6 +381,14 @@ enum mlxsw_sp_fib_entry_type { MLXSW_SP_FIB_ENTRY_TYPE_REMOTE, MLXSW_SP_FIB_ENTRY_TYPE_LOCAL, MLXSW_SP_FIB_ENTRY_TYPE_TRAP, + + /* This is a special case of local delivery, where a packet should be + * decapsulated on reception. Note that there is no corresponding ENCAP, + * because that's a type of next hop, not of FIB entry. (There can be + * several next hops in a REMOTE entry, and some of them may be + * encapsulating entries.) + */ + MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP, }; struct mlxsw_sp_nexthop_group; @@ -394,12 +402,18 @@ struct mlxsw_sp_fib_node { struct mlxsw_sp_fib_key key; }; +struct mlxsw_sp_fib_entry_decap { + struct mlxsw_sp_ipip_entry *ipip_entry; + u32 tunnel_index; +}; + struct mlxsw_sp_fib_entry { struct list_head list; struct mlxsw_sp_fib_node *fib_node; enum mlxsw_sp_fib_entry_type type; struct list_head nexthop_group_node; struct mlxsw_sp_nexthop_group *nh_group; + struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */ }; struct mlxsw_sp_fib4_entry { @@ -1031,6 +1045,48 @@ mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_l3addr_eq(&tun_saddr, &saddr); } +static int +mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + u32 tunnel_index; + int err; + + err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &tunnel_index); + if (err) + return err; + + ipip_entry->decap_fib_entry = fib_entry; + fib_entry->decap.ipip_entry = ipip_entry; + fib_entry->decap.tunnel_index = tunnel_index; + return 0; +} + +static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) +{ + /* Unlink this node from the IPIP entry that it's the decap entry of. */ + fib_entry->decap.ipip_entry->decap_fib_entry = NULL; + fib_entry->decap.ipip_entry = NULL; + mlxsw_sp_kvdl_free(mlxsw_sp, fib_entry->decap.tunnel_index); +} + +static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry); + +static void +mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry; + + mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry); + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; + + mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); +} + static struct mlxsw_sp_ipip_entry * mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp, enum mlxsw_sp_ipip_type ipipt, @@ -1076,10 +1132,51 @@ mlxsw_sp_ipip_entry_put(struct mlxsw_sp *mlxsw_sp, { if (--ipip_entry->ref_count == 0) { list_del(&ipip_entry->ipip_list_node); + if (ipip_entry->decap_fib_entry) + mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry); mlxsw_sp_ipip_entry_destroy(ipip_entry); } } +static bool +mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ul_dev, + enum mlxsw_sp_l3proto ul_proto, + union mlxsw_sp_l3addr ul_dip, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN; + enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt; + struct net_device *ipip_ul_dev; + + if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto) + return false; + + ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev); + return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip, + ul_tb_id, ipip_entry) && + (!ipip_ul_dev || ipip_ul_dev == ul_dev); +} + +/* Given decap parameters, find the corresponding IPIP entry. */ +static struct mlxsw_sp_ipip_entry * +mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ul_dev, + enum mlxsw_sp_l3proto ul_proto, + union mlxsw_sp_l3addr ul_dip) +{ + struct mlxsw_sp_ipip_entry *ipip_entry; + + list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list, + ipip_list_node) + if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev, + ul_proto, ul_dip, + ipip_entry)) + return ipip_entry; + + return NULL; +} + struct mlxsw_sp_neigh_key { struct neighbour *n; }; @@ -2186,9 +2283,6 @@ mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp, return 0; } -static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry); - static bool mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, const struct mlxsw_sp_fib_entry *fib_entry); @@ -2779,6 +2873,8 @@ mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry) return !!nh_group->adj_index_valid; case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL: return !!nh_group->nh_rif; + case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP: + return true; default: return false; } @@ -2810,7 +2906,8 @@ mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; int i; - if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) { + if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL || + fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) { nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD; return; } @@ -3015,6 +3112,22 @@ static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); } +static int +mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) +{ + struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry; + const struct mlxsw_sp_ipip_ops *ipip_ops; + + if (WARN_ON(!ipip_entry)) + return -EINVAL; + + ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; + return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op, + fib_entry->decap.tunnel_index); +} + static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry, enum mlxsw_reg_ralue_op op) @@ -3026,6 +3139,9 @@ static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp, return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op); case MLXSW_SP_FIB_ENTRY_TYPE_TRAP: return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op); + case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP: + return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp, + fib_entry, op); } return -EINVAL; } @@ -3060,11 +3176,23 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp, const struct fib_entry_notifier_info *fen_info, struct mlxsw_sp_fib_entry *fib_entry) { + union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) }; + struct net_device *dev = fen_info->fi->fib_dev; + struct mlxsw_sp_ipip_entry *ipip_entry; struct fib_info *fi = fen_info->fi; switch (fen_info->type) { - case RTN_BROADCAST: /* fall through */ case RTN_LOCAL: + ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev, + MLXSW_SP_L3_PROTO_IPV4, dip); + if (ipip_entry) { + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP; + return mlxsw_sp_fib_entry_decap_init(mlxsw_sp, + fib_entry, + ipip_entry); + } + /* fall through */ + case RTN_BROADCAST: fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; return 0; case RTN_UNREACHABLE: /* fall through */ @@ -3557,6 +3685,9 @@ mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, { mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common); mlxsw_sp_fib4_node_list_remove(fib4_entry); + + if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) + mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common); } static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index a5732aa87b9a..4276f5f86ff8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -97,5 +97,8 @@ mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_neigh_entry *neigh_entry, bool adding); bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry); +union mlxsw_sp_l3addr +mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev); #endif /* _MLXSW_ROUTER_H_*/ -- cgit v1.2.3 From 1cc38fb144ded22b81ef7b53b77d52885f25ecea Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:26 +0200 Subject: mlxsw: spectrum_router: Use existing decap route The local route that points at IPIP's underlay device (decap route) can be present long before the GRE device. Thus when an encap route is added, it's necessary to look inside the underlay FIB if the decap route is already present. If so, the current trap offload needs to be withdrawn and replaced with a decap offload. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 80 ++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 0d09f81ff686..2189cf0fcad1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1072,6 +1072,9 @@ static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_kvdl_free(mlxsw_sp, fib_entry->decap.tunnel_index); } +static struct mlxsw_sp_fib_node * +mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr, + size_t addr_len, unsigned char prefix_len); static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry); @@ -1087,6 +1090,73 @@ mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); } +static void +mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + struct mlxsw_sp_fib_entry *decap_fib_entry) +{ + if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry, + ipip_entry)) + return; + decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP; + + if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry)) + mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry); +} + +/* Given an IPIP entry, find the corresponding decap route. */ +static struct mlxsw_sp_fib_entry * +mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + static struct mlxsw_sp_fib_node *fib_node; + const struct mlxsw_sp_ipip_ops *ipip_ops; + struct mlxsw_sp_fib_entry *fib_entry; + unsigned char saddr_prefix_len; + union mlxsw_sp_l3addr saddr; + struct mlxsw_sp_fib *ul_fib; + struct mlxsw_sp_vr *ul_vr; + const void *saddrp; + size_t saddr_len; + u32 ul_tb_id; + u32 saddr4; + + ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; + + ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev); + ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id); + if (!ul_vr) + return NULL; + + ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto); + saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto, + ipip_entry->ol_dev); + + switch (ipip_ops->ul_proto) { + case MLXSW_SP_L3_PROTO_IPV4: + saddr4 = be32_to_cpu(saddr.addr4); + saddrp = &saddr4; + saddr_len = 4; + saddr_prefix_len = 32; + break; + case MLXSW_SP_L3_PROTO_IPV6: + WARN_ON(1); + return NULL; + } + + fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len, + saddr_prefix_len); + if (!fib_node || list_empty(&fib_node->entry_list)) + return NULL; + + fib_entry = list_first_entry(&fib_node->entry_list, + struct mlxsw_sp_fib_entry, list); + if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP) + return NULL; + + return fib_entry; +} + static struct mlxsw_sp_ipip_entry * mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp, enum mlxsw_sp_ipip_type ipipt, @@ -1094,6 +1164,7 @@ mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp, { u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev); struct mlxsw_sp_router *router = mlxsw_sp->router; + struct mlxsw_sp_fib_entry *decap_fib_entry; struct mlxsw_sp_ipip_entry *ipip_entry; enum mlxsw_sp_l3proto ul_proto; union mlxsw_sp_l3addr saddr; @@ -1118,6 +1189,11 @@ mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp, if (IS_ERR(ipip_entry)) return ipip_entry; + decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry); + if (decap_fib_entry) + mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry, + decap_fib_entry); + list_add_tail(&ipip_entry->ipip_list_node, &mlxsw_sp->router->ipip_list); @@ -3259,10 +3335,6 @@ static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp, kfree(fib4_entry); } -static struct mlxsw_sp_fib_node * -mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr, - size_t addr_len, unsigned char prefix_len); - static struct mlxsw_sp_fib4_entry * mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp, const struct fib_entry_notifier_info *fen_info) -- cgit v1.2.3 From 86484de2c917b6ba5965ef2248d9966424932af4 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:27 +0200 Subject: mlxsw: spectrum: Register for IPIP_DECAP_ERROR trap These traps are generated for packets that fail checks for source IP, encapsulation type, or GRE key. Trap these packets to CPU for follow-up handling by the kernel, which will send ICMP destination unreachable responses. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 1 + drivers/net/ethernet/mellanox/mlxsw/trap.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 992cbfa1f2bc..ed7cd6c48019 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3400,6 +3400,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_MARK(HOST_MISS_IPV6, TRAP_TO_CPU, HOST_MISS, false), MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV4, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV6, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPIP_DECAP_ERROR, TRAP_TO_CPU, ROUTER_EXP, false), /* PKT Sample trap */ MLXSW_RXL(mlxsw_sp_rx_listener_sample_func, PKT_SAMPLE, MIRROR_TO_CPU, false, SP_IP2ME, DISCARD), diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h index 61652396bf75..f396a1fef633 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/trap.h +++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h @@ -85,6 +85,7 @@ enum { MLXSW_TRAP_ID_HOST_MISS_IPV4 = 0x90, MLXSW_TRAP_ID_IPV6_MC_LINK_LOCAL_DEST = 0x91, MLXSW_TRAP_ID_HOST_MISS_IPV6 = 0x92, + MLXSW_TRAP_ID_IPIP_DECAP_ERROR = 0xB1, MLXSW_TRAP_ID_ROUTER_ALERT_IPV4 = 0xD6, MLXSW_TRAP_ID_ROUTER_ALERT_IPV6 = 0xD7, MLXSW_TRAP_ID_ACL0 = 0x1C0, -- cgit v1.2.3 From 92107cfb411f5371bee56d9a13d28ef8b40bbbbc Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:28 +0200 Subject: mlxsw: spectrum_router: Add loopback accessors struct mlxsw_sp_rif is a router-private structure, and therefore everything related to it is as well: parameters, and derived RIF types including loopbacks. IPIP module needs access to some details of loopback interfaces, but exporting all the RIF shebang would create too large an interface. So instead export just the bare minimum necessary: accessors for RIF index and underlay VRF ID. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 10 ++++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h | 3 +++ 2 files changed, 13 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2189cf0fcad1..6068eea8152f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -5007,6 +5007,16 @@ u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif) return rif->rif_index; } +u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif) +{ + return lb_rif->common.rif_index; +} + +u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif) +{ + return lb_rif->ul_vr_id; +} + int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif) { return rif->dev->ifindex; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index 4276f5f86ff8..96324769351a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -48,6 +48,7 @@ union mlxsw_sp_l3addr { struct in6_addr addr6; }; +struct mlxsw_sp_rif_ipip_lb; struct mlxsw_sp_rif_ipip_lb_config { enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt; u32 okey; @@ -65,6 +66,8 @@ struct mlxsw_sp_neigh_entry; struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp, u16 rif_index); u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif); +u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *rif); +u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *rif); int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif); int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *rif, -- cgit v1.2.3 From ee954d1a91b2bb71defb17697f0a2263f46235d2 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 2 Sep 2017 23:49:29 +0200 Subject: mlxsw: spectrum_router: Support GRE tunnels This patch introduces callbacks and tunnel type to offload GRE tunnels. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_ipip.c | 176 +++++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_ipip.h | 1 + .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 26 +++ .../net/ethernet/mellanox/mlxsw/spectrum_router.h | 4 + 4 files changed, 207 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c index 20b0b969d41d..702fe945227c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c @@ -32,7 +32,183 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include + #include "spectrum_ipip.h" +static bool +mlxsw_sp_ipip_netdev_has_ikey(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return !!(tun->parms.i_flags & TUNNEL_KEY); +} + +static bool +mlxsw_sp_ipip_netdev_has_okey(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return !!(tun->parms.o_flags & TUNNEL_KEY); +} + +static u32 mlxsw_sp_ipip_netdev_ikey(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return mlxsw_sp_ipip_netdev_has_ikey(ol_dev) ? + be32_to_cpu(tun->parms.i_key) : 0; +} + +static u32 mlxsw_sp_ipip_netdev_okey(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return mlxsw_sp_ipip_netdev_has_okey(ol_dev) ? + be32_to_cpu(tun->parms.o_key) : 0; +} + +static int +mlxsw_sp_ipip_nexthop_update_gre4(struct mlxsw_sp *mlxsw_sp, u32 adj_index, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb); + __be32 daddr4 = mlxsw_sp_ipip_netdev_daddr4(ipip_entry->ol_dev); + char ratr_pl[MLXSW_REG_RATR_LEN]; + + mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, + true, MLXSW_REG_RATR_TYPE_IPIP, + adj_index, rif_index); + mlxsw_reg_ratr_ipip4_entry_pack(ratr_pl, be32_to_cpu(daddr4)); + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl); +} + +static int +mlxsw_sp_ipip_fib_entry_op_gre4_rtdp(struct mlxsw_sp *mlxsw_sp, + u32 tunnel_index, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + bool has_ikey = mlxsw_sp_ipip_netdev_has_ikey(ipip_entry->ol_dev); + u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb); + u32 ikey = mlxsw_sp_ipip_netdev_ikey(ipip_entry->ol_dev); + char rtdp_pl[MLXSW_REG_RTDP_LEN]; + unsigned int type_check; + u32 daddr4; + + mlxsw_reg_rtdp_pack(rtdp_pl, MLXSW_REG_RTDP_TYPE_IPIP, tunnel_index); + + type_check = has_ikey ? + MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE_KEY : + MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE; + + /* Linux demuxes tunnels based on packet SIP (which must match tunnel + * remote IP). Thus configure decap so that it filters out packets that + * are not IPv4 or have the wrong SIP. IPIP_DECAP_ERROR trap is + * generated for packets that fail this criterion. Linux then handles + * such packets in slow path and generates ICMP destination unreachable. + */ + daddr4 = be32_to_cpu(mlxsw_sp_ipip_netdev_daddr4(ipip_entry->ol_dev)); + mlxsw_reg_rtdp_ipip4_pack(rtdp_pl, rif_index, + MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV4, + type_check, has_ikey, daddr4, ikey); + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtdp), rtdp_pl); +} + +static int +mlxsw_sp_ipip_fib_entry_op_gre4_ralue(struct mlxsw_sp *mlxsw_sp, + u32 dip, u8 prefix_len, u16 ul_vr_id, + enum mlxsw_reg_ralue_op op, + u32 tunnel_index) +{ + char ralue_pl[MLXSW_REG_RALUE_LEN]; + + mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_REG_RALXX_PROTOCOL_IPV4, op, + ul_vr_id, prefix_len, dip); + mlxsw_reg_ralue_act_ip2me_tun_pack(ralue_pl, tunnel_index); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); +} + +static int mlxsw_sp_ipip_fib_entry_op_gre4(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + enum mlxsw_reg_ralue_op op, + u32 tunnel_index) +{ + u16 ul_vr_id = mlxsw_sp_ipip_lb_ul_vr_id(ipip_entry->ol_lb); + __be32 dip; + int err; + + err = mlxsw_sp_ipip_fib_entry_op_gre4_rtdp(mlxsw_sp, tunnel_index, + ipip_entry); + if (err) + return err; + + dip = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV4, + ipip_entry->ol_dev).addr4; + return mlxsw_sp_ipip_fib_entry_op_gre4_ralue(mlxsw_sp, be32_to_cpu(dip), + 32, ul_vr_id, op, + tunnel_index); +} + +static bool mlxsw_sp_ipip_tunnel_complete(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev) +{ + union mlxsw_sp_l3addr saddr = mlxsw_sp_ipip_netdev_saddr(proto, ol_dev); + union mlxsw_sp_l3addr daddr = mlxsw_sp_ipip_netdev_daddr(proto, ol_dev); + union mlxsw_sp_l3addr naddr = {0}; + + /* Tunnels with unset local or remote address are valid in Linux and + * used for lightweight tunnels (LWT) and Non-Broadcast Multi-Access + * (NBMA) tunnels. In principle these can be offloaded, but the driver + * currently doesn't support this. So punt. + */ + return memcmp(&saddr, &naddr, sizeof(naddr)) && + memcmp(&daddr, &naddr, sizeof(naddr)); +} + +static bool mlxsw_sp_ipip_can_offload_gre4(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev, + enum mlxsw_sp_l3proto ol_proto) +{ + struct ip_tunnel *tunnel = netdev_priv(ol_dev); + __be16 okflags = TUNNEL_KEY; /* We can't offload any other features. */ + bool inherit_ttl = tunnel->parms.iph.ttl == 0; + bool inherit_tos = tunnel->parms.iph.tos & 0x1; + + return (tunnel->parms.i_flags & ~okflags) == 0 && + (tunnel->parms.o_flags & ~okflags) == 0 && + inherit_ttl && inherit_tos && + mlxsw_sp_ipip_tunnel_complete(MLXSW_SP_L3_PROTO_IPV4, ol_dev); +} + +static struct mlxsw_sp_rif_ipip_lb_config +mlxsw_sp_ipip_ol_loopback_config_gre4(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev) +{ + enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt; + + lb_ipipt = mlxsw_sp_ipip_netdev_has_okey(ol_dev) ? + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_KEY_IN_IP : + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_IN_IP; + return (struct mlxsw_sp_rif_ipip_lb_config){ + .lb_ipipt = lb_ipipt, + .okey = mlxsw_sp_ipip_netdev_okey(ol_dev), + .ul_protocol = MLXSW_SP_L3_PROTO_IPV4, + .saddr = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV4, + ol_dev), + }; +} + +static const struct mlxsw_sp_ipip_ops mlxsw_sp_ipip_gre4_ops = { + .dev_type = ARPHRD_IPGRE, + .ul_proto = MLXSW_SP_L3_PROTO_IPV4, + .nexthop_update = mlxsw_sp_ipip_nexthop_update_gre4, + .fib_entry_op = mlxsw_sp_ipip_fib_entry_op_gre4, + .can_offload = mlxsw_sp_ipip_can_offload_gre4, + .ol_loopback_config = mlxsw_sp_ipip_ol_loopback_config_gre4, +}; + const struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[] = { + [MLXSW_SP_IPIP_TYPE_GRE4] = &mlxsw_sp_ipip_gre4_ops, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h index cd986bde856a..1c2db831d83b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h @@ -39,6 +39,7 @@ #include enum mlxsw_sp_ipip_type { + MLXSW_SP_IPIP_TYPE_GRE4, MLXSW_SP_IPIP_TYPE_MAX, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 6068eea8152f..f0fb898533fb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1020,6 +1020,32 @@ mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto, }; } +__be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return tun->parms.iph.daddr; +} + +union mlxsw_sp_l3addr +mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev) +{ + switch (proto) { + case MLXSW_SP_L3_PROTO_IPV4: + return (union mlxsw_sp_l3addr) { + .addr4 = mlxsw_sp_ipip_netdev_daddr4(ol_dev), + }; + case MLXSW_SP_L3_PROTO_IPV6: + break; + }; + + WARN_ON(1); + return (union mlxsw_sp_l3addr) { + .addr4 = 0, + }; +} + static bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1, const union mlxsw_sp_l3addr *addr2) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index 96324769351a..345fcc4f38e9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -103,5 +103,9 @@ bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry); union mlxsw_sp_l3addr mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto, const struct net_device *ol_dev); +union mlxsw_sp_l3addr +mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev); +__be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev); #endif /* _MLXSW_ROUTER_H_*/ -- cgit v1.2.3 From c496291c5a414bd55bae76880137947d2e8decbe Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 2 Sep 2017 18:26:00 -0700 Subject: nfp: separate app vNIC init/clean from alloc/free We currently only have one app callback for vNIC creation and destruction. This is insufficient, because some actions have to be taken before netdev is registered, after it's registered and after it's unregistered. Old callbacks were really corresponding to alloc/free actions. Rename them and add proper init/clean. Apps using representors will be able to use new callbacks to manage lifetime of upper devices. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/bpf/main.c | 10 +++---- drivers/net/ethernet/netronome/nfp/flower/cmsg.c | 2 +- drivers/net/ethernet/netronome/nfp/flower/main.c | 27 +++++++++++++---- drivers/net/ethernet/netronome/nfp/flower/main.h | 2 ++ drivers/net/ethernet/netronome/nfp/nfp_app.c | 2 +- drivers/net/ethernet/netronome/nfp/nfp_app.h | 35 +++++++++++++++++------ drivers/net/ethernet/netronome/nfp/nfp_app_nic.c | 4 +-- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 16 +++++++++-- drivers/net/ethernet/netronome/nfp/nic/main.c | 2 +- 9 files changed, 73 insertions(+), 27 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index f4de3a7377b0..be2cf10a2cd7 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -84,7 +84,7 @@ static const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn) } static int -nfp_bpf_vnic_init(struct nfp_app *app, struct nfp_net *nn, unsigned int id) +nfp_bpf_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id) { struct nfp_net_bpf_priv *priv; int ret; @@ -106,14 +106,14 @@ nfp_bpf_vnic_init(struct nfp_app *app, struct nfp_net *nn, unsigned int id) setup_timer(&priv->rx_filter_stats_timer, nfp_net_filter_stats_timer, (unsigned long)nn); - ret = nfp_app_nic_vnic_init(app, nn, id); + ret = nfp_app_nic_vnic_alloc(app, nn, id); if (ret) kfree(priv); return ret; } -static void nfp_bpf_vnic_clean(struct nfp_app *app, struct nfp_net *nn) +static void nfp_bpf_vnic_free(struct nfp_app *app, struct nfp_net *nn) { if (nn->dp.bpf_offload_xdp) nfp_bpf_xdp_offload(app, nn, NULL); @@ -149,8 +149,8 @@ const struct nfp_app_type app_bpf = { .extra_cap = nfp_bpf_extra_cap, - .vnic_init = nfp_bpf_vnic_init, - .vnic_clean = nfp_bpf_vnic_clean, + .vnic_alloc = nfp_bpf_vnic_alloc, + .vnic_free = nfp_bpf_vnic_free, .setup_tc = nfp_bpf_setup_tc, .tc_busy = nfp_bpf_tc_busy, diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index 806924b82adc..d82d9888d676 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -203,7 +203,7 @@ void nfp_flower_cmsg_process_rx(struct work_struct *work) priv = container_of(work, struct nfp_flower_priv, cmsg_work); while ((skb = skb_dequeue(&priv->cmsg_skbs))) - nfp_flower_cmsg_process_one_rx(priv->nn->app, skb); + nfp_flower_cmsg_process_one_rx(priv->app, skb); } void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index 126a6b5233bf..db59858c0f19 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -313,18 +313,14 @@ static int nfp_flower_start(struct nfp_app *app) NFP_REPR_TYPE_PF, 1); } -static int nfp_flower_vnic_init(struct nfp_app *app, struct nfp_net *nn, - unsigned int id) +static int nfp_flower_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, + unsigned int id) { - struct nfp_flower_priv *priv = app->priv; - if (id > 0) { nfp_warn(app->cpp, "FlowerNIC doesn't support more than one data vNIC\n"); goto err_invalid_port; } - priv->nn = nn; - eth_hw_addr_random(nn->dp.netdev); netif_keep_dst(nn->dp.netdev); @@ -335,6 +331,22 @@ err_invalid_port: return PTR_ERR_OR_ZERO(nn->port); } +static void nfp_flower_vnic_clean(struct nfp_app *app, struct nfp_net *nn) +{ + struct nfp_flower_priv *priv = app->priv; + + priv->nn = NULL; +} + +static int nfp_flower_vnic_init(struct nfp_app *app, struct nfp_net *nn) +{ + struct nfp_flower_priv *priv = app->priv; + + priv->nn = nn; + + return 0; +} + static int nfp_flower_init(struct nfp_app *app) { const struct nfp_pf *pf = app->pf; @@ -374,6 +386,7 @@ static int nfp_flower_init(struct nfp_app *app) return -ENOMEM; app->priv = app_priv; + app_priv->app = app; skb_queue_head_init(&app_priv->cmsg_skbs); INIT_WORK(&app_priv->cmsg_work, nfp_flower_cmsg_process_rx); @@ -410,7 +423,9 @@ const struct nfp_app_type app_flower = { .init = nfp_flower_init, .clean = nfp_flower_clean, + .vnic_alloc = nfp_flower_vnic_alloc, .vnic_init = nfp_flower_vnic_init, + .vnic_clean = nfp_flower_vnic_clean, .repr_open = nfp_flower_repr_netdev_open, .repr_stop = nfp_flower_repr_netdev_stop, diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index b7043ca9b9fc..c20dd00a1cae 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -72,6 +72,7 @@ struct nfp_fl_stats_id { /** * struct nfp_flower_priv - Flower APP per-vNIC priv data + * @app: Back pointer to app * @nn: Pointer to vNIC * @mask_id_seed: Seed used for mask hash table * @flower_version: HW version of flower @@ -83,6 +84,7 @@ struct nfp_fl_stats_id { * @cmsg_skbs: List of skbs for control message processing */ struct nfp_flower_priv { + struct nfp_app *app; struct nfp_net *nn; u32 mask_id_seed; u64 flower_version; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.c b/drivers/net/ethernet/netronome/nfp/nfp_app.c index 505e63f47419..82c290763529 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.c @@ -125,7 +125,7 @@ struct nfp_app *nfp_app_alloc(struct nfp_pf *pf, enum nfp_app_id id) return ERR_PTR(-EINVAL); } - if (WARN_ON(!apps[i]->name || !apps[i]->vnic_init)) + if (WARN_ON(!apps[i]->name || !apps[i]->vnic_alloc)) return ERR_PTR(-EINVAL); app = kzalloc(sizeof(*app), GFP_KERNEL); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index c13b9bbe7e62..af640b5c2108 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -69,8 +69,10 @@ extern const struct nfp_app_type app_flower; * @init: perform basic app checks and init * @clean: clean app state * @extra_cap: extra capabilities string - * @vnic_init: init vNICs (assign port types, etc.) - * @vnic_clean: clean up app's vNIC state + * @vnic_alloc: allocate vNICs (assign port types, etc.) + * @vnic_free: free up app's vNIC state + * @vnic_init: vNIC netdev was registered + * @vnic_clean: vNIC netdev about to be unregistered * @repr_open: representor netdev open callback * @repr_stop: representor netdev stop callback * @start: start application logic @@ -95,8 +97,10 @@ struct nfp_app_type { const char *(*extra_cap)(struct nfp_app *app, struct nfp_net *nn); - int (*vnic_init)(struct nfp_app *app, struct nfp_net *nn, - unsigned int id); + int (*vnic_alloc)(struct nfp_app *app, struct nfp_net *nn, + unsigned int id); + void (*vnic_free)(struct nfp_app *app, struct nfp_net *nn); + int (*vnic_init)(struct nfp_app *app, struct nfp_net *nn); void (*vnic_clean)(struct nfp_app *app, struct nfp_net *nn); int (*repr_open)(struct nfp_app *app, struct nfp_repr *repr); @@ -157,10 +161,23 @@ static inline void nfp_app_clean(struct nfp_app *app) app->type->clean(app); } -static inline int nfp_app_vnic_init(struct nfp_app *app, struct nfp_net *nn, - unsigned int id) +static inline int nfp_app_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, + unsigned int id) { - return app->type->vnic_init(app, nn, id); + return app->type->vnic_alloc(app, nn, id); +} + +static inline void nfp_app_vnic_free(struct nfp_app *app, struct nfp_net *nn) +{ + if (app->type->vnic_free) + app->type->vnic_free(app, nn); +} + +static inline int nfp_app_vnic_init(struct nfp_app *app, struct nfp_net *nn) +{ + if (!app->type->vnic_init) + return 0; + return app->type->vnic_init(app, nn); } static inline void nfp_app_vnic_clean(struct nfp_app *app, struct nfp_net *nn) @@ -308,7 +325,7 @@ void nfp_app_free(struct nfp_app *app); /* Callbacks shared between apps */ -int nfp_app_nic_vnic_init(struct nfp_app *app, struct nfp_net *nn, - unsigned int id); +int nfp_app_nic_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, + unsigned int id); #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app_nic.c b/drivers/net/ethernet/netronome/nfp/nfp_app_nic.c index 4e37c81f9eaf..2a2f2fbc8850 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app_nic.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_app_nic.c @@ -60,8 +60,8 @@ nfp_app_nic_vnic_init_phy_port(struct nfp_pf *pf, struct nfp_app *app, return nn->port->type == NFP_PORT_INVALID; } -int nfp_app_nic_vnic_init(struct nfp_app *app, struct nfp_net *nn, - unsigned int id) +int nfp_app_nic_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, + unsigned int id) { int err; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 7c22cc4654b7..f2a1a4e2ce8b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -161,6 +161,8 @@ nfp_net_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt, static void nfp_net_pf_free_vnic(struct nfp_pf *pf, struct nfp_net *nn) { + if (nfp_net_is_data_vnic(nn)) + nfp_app_vnic_free(pf->app, nn); nfp_port_free(nn->port); list_del(&nn->vnic_list); pf->num_vnics--; @@ -205,7 +207,7 @@ nfp_net_pf_alloc_vnic(struct nfp_pf *pf, bool needs_netdev, nn->stride_tx = stride; if (needs_netdev) { - err = nfp_app_vnic_init(pf->app, nn, id); + err = nfp_app_vnic_alloc(pf->app, nn, id); if (err) { nfp_net_free(nn); return ERR_PTR(err); @@ -243,8 +245,17 @@ nfp_net_pf_init_vnic(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id) nfp_net_info(nn); + if (nfp_net_is_data_vnic(nn)) { + err = nfp_app_vnic_init(pf->app, nn); + if (err) + goto err_devlink_port_clean; + } + return 0; +err_devlink_port_clean: + if (nn->port) + nfp_devlink_port_unregister(nn->port); err_dfs_clean: nfp_net_debugfs_dir_clean(&nn->debugfs_dir); nfp_net_clean(nn); @@ -288,11 +299,12 @@ err_free_prev: static void nfp_net_pf_clean_vnic(struct nfp_pf *pf, struct nfp_net *nn) { + if (nfp_net_is_data_vnic(nn)) + nfp_app_vnic_clean(pf->app, nn); if (nn->port) nfp_devlink_port_unregister(nn->port); nfp_net_debugfs_dir_clean(&nn->debugfs_dir); nfp_net_clean(nn); - nfp_app_vnic_clean(pf->app, nn); } static int nfp_net_pf_alloc_irqs(struct nfp_pf *pf) diff --git a/drivers/net/ethernet/netronome/nfp/nic/main.c b/drivers/net/ethernet/netronome/nfp/nic/main.c index 8287a85d22c1..d5b587fccaa3 100644 --- a/drivers/net/ethernet/netronome/nfp/nic/main.c +++ b/drivers/net/ethernet/netronome/nfp/nic/main.c @@ -63,7 +63,7 @@ const struct nfp_app_type app_nic = { .name = "nic", .init = nfp_nic_init, - .vnic_init = nfp_app_nic_vnic_init, + .vnic_alloc = nfp_app_nic_vnic_alloc, .sriov_enable = nfp_nic_sriov_enable, .sriov_disable = nfp_nic_sriov_disable, -- cgit v1.2.3 From 192e685105097c48ad9f3665af643e1d97bcd2d4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 2 Sep 2017 18:26:01 -0700 Subject: nfp: flower: base lifetime of representors on existence of lower vNIC Create representors after lower vNIC is registered and destroy them before it is destroyed. Move the code out of start/stop callbacks directly into vnic_init/clean callbacks. Make sure SR-IOV callbacks don't try to create representors when lower device does not exist. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/flower/main.c | 66 +++++++++++++++--------- 1 file changed, 43 insertions(+), 23 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index db59858c0f19..91fe03617106 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -127,6 +127,11 @@ nfp_flower_repr_netdev_stop(struct nfp_app *app, struct nfp_repr *repr) static void nfp_flower_sriov_disable(struct nfp_app *app) { + struct nfp_flower_priv *priv = app->priv; + + if (!priv->nn) + return; + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_VF); } @@ -203,18 +208,16 @@ err_reprs_clean: static int nfp_flower_sriov_enable(struct nfp_app *app, int num_vfs) { + struct nfp_flower_priv *priv = app->priv; + + if (!priv->nn) + return 0; + return nfp_flower_spawn_vnic_reprs(app, NFP_FLOWER_CMSG_PORT_VNIC_TYPE_VF, NFP_REPR_TYPE_VF, num_vfs); } -static void nfp_flower_stop(struct nfp_app *app) -{ - nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF); - nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT); - -} - static int nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv) { @@ -300,19 +303,6 @@ err_free_ctrl_skb: return err; } -static int nfp_flower_start(struct nfp_app *app) -{ - int err; - - err = nfp_flower_spawn_phy_reprs(app, app->priv); - if (err) - return err; - - return nfp_flower_spawn_vnic_reprs(app, - NFP_FLOWER_CMSG_PORT_VNIC_TYPE_PF, - NFP_REPR_TYPE_PF, 1); -} - static int nfp_flower_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id) { @@ -335,16 +325,49 @@ static void nfp_flower_vnic_clean(struct nfp_app *app, struct nfp_net *nn) { struct nfp_flower_priv *priv = app->priv; + if (app->pf->num_vfs) + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_VF); + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF); + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT); + priv->nn = NULL; } static int nfp_flower_vnic_init(struct nfp_app *app, struct nfp_net *nn) { struct nfp_flower_priv *priv = app->priv; + int err; priv->nn = nn; + err = nfp_flower_spawn_phy_reprs(app, app->priv); + if (err) + goto err_clear_nn; + + err = nfp_flower_spawn_vnic_reprs(app, + NFP_FLOWER_CMSG_PORT_VNIC_TYPE_PF, + NFP_REPR_TYPE_PF, 1); + if (err) + goto err_destroy_reprs_phy; + + if (app->pf->num_vfs) { + err = nfp_flower_spawn_vnic_reprs(app, + NFP_FLOWER_CMSG_PORT_VNIC_TYPE_VF, + NFP_REPR_TYPE_VF, + app->pf->num_vfs); + if (err) + goto err_destroy_reprs_pf; + } + return 0; + +err_destroy_reprs_pf: + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF); +err_destroy_reprs_phy: + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT); +err_clear_nn: + priv->nn = NULL; + return err; } static int nfp_flower_init(struct nfp_app *app) @@ -430,9 +453,6 @@ const struct nfp_app_type app_flower = { .repr_open = nfp_flower_repr_netdev_open, .repr_stop = nfp_flower_repr_netdev_stop, - .start = nfp_flower_start, - .stop = nfp_flower_stop, - .ctrl_msg_rx = nfp_flower_cmsg_rx, .sriov_enable = nfp_flower_sriov_enable, -- cgit v1.2.3 From 9d8b17bed757a3c5edbd6c1d09e52cbff6f2cc78 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 2 Sep 2017 18:26:02 -0700 Subject: nfp: move the start/stop app callbacks back Since representors are now created with a separate callback start/stop app callbacks can be moved again to their original location. They are intended to app-specific init/clean up over the control channel. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 26 ++++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index f2a1a4e2ce8b..5abb9ba31e7d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -469,10 +469,14 @@ static int nfp_net_pf_app_start(struct nfp_pf *pf) { int err; - err = nfp_app_start(pf->app, pf->ctrl_vnic); + err = nfp_net_pf_app_start_ctrl(pf); if (err) return err; + err = nfp_app_start(pf->app, pf->ctrl_vnic); + if (err) + goto err_ctrl_stop; + if (pf->num_vfs) { err = nfp_app_sriov_enable(pf->app, pf->num_vfs); if (err) @@ -483,6 +487,8 @@ static int nfp_net_pf_app_start(struct nfp_pf *pf) err_app_stop: nfp_app_stop(pf->app); +err_ctrl_stop: + nfp_net_pf_app_stop_ctrl(pf); return err; } @@ -491,6 +497,7 @@ static void nfp_net_pf_app_stop(struct nfp_pf *pf) if (pf->num_vfs) nfp_app_sriov_disable(pf->app); nfp_app_stop(pf->app); + nfp_net_pf_app_stop_ctrl(pf); } static void nfp_net_pci_unmap_mem(struct nfp_pf *pf) @@ -582,7 +589,7 @@ err_unmap_ctrl: static void nfp_net_pci_remove_finish(struct nfp_pf *pf) { - nfp_net_pf_app_stop_ctrl(pf); + nfp_net_pf_app_stop(pf); /* stop app first, to avoid double free of ctrl vNIC's ddir */ nfp_net_debugfs_dir_clean(&pf->ddir); @@ -713,7 +720,6 @@ int nfp_net_pci_probe(struct nfp_pf *pf) { struct nfp_net_fw_version fw_ver; u8 __iomem *ctrl_bar, *qc_bar; - struct nfp_net *nn; int stride; int err; @@ -790,7 +796,7 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (err) goto err_free_vnics; - err = nfp_net_pf_app_start_ctrl(pf); + err = nfp_net_pf_app_start(pf); if (err) goto err_free_irqs; @@ -798,20 +804,12 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (err) goto err_stop_app; - err = nfp_net_pf_app_start(pf); - if (err) - goto err_clean_vnics; - mutex_unlock(&pf->lock); return 0; -err_clean_vnics: - list_for_each_entry(nn, &pf->vnics, vnic_list) - if (nfp_net_is_data_vnic(nn)) - nfp_net_pf_clean_vnic(pf, nn); err_stop_app: - nfp_net_pf_app_stop_ctrl(pf); + nfp_net_pf_app_stop(pf); err_free_irqs: nfp_net_pf_free_irqs(pf); err_free_vnics: @@ -835,8 +833,6 @@ void nfp_net_pci_remove(struct nfp_pf *pf) if (list_empty(&pf->vnics)) goto out; - nfp_net_pf_app_stop(pf); - list_for_each_entry(nn, &pf->vnics, vnic_list) if (nfp_net_is_data_vnic(nn)) nfp_net_pf_clean_vnic(pf, nn); -- cgit v1.2.3 From 023a928461e9370e73de2d5ef1a7e44a8033d2a6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 2 Sep 2017 18:26:03 -0700 Subject: nfp: be drop monitor friendly Use dev_consume_skb_any() in place of dev_kfree_skb_any() when control frame has been successfully processed in flower and on the driver's main TX completion path. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/flower/cmsg.c | 3 +++ drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index d82d9888d676..e014d862b9b6 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -189,8 +189,11 @@ nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb) default: nfp_flower_cmsg_warn(app, "Cannot handle invalid repr control type %u\n", type); + goto out; } + dev_consume_skb_any(skb); + return; out: dev_kfree_skb_any(skb); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 2920889fa6d6..1c0187f0af51 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -991,7 +991,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) /* check for last gather fragment */ if (fidx == nr_frags - 1) - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); tx_ring->txbufs[idx].dma_addr = 0; tx_ring->txbufs[idx].skb = NULL; -- cgit v1.2.3 From 7c8a2d8b366d2dc9e386b96e4555cc5c6932c43d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 2 Sep 2017 18:26:04 -0700 Subject: nfp: build the flower offload by default It's reasonable to assume that if user selects to build the NFP driver all offload capabilities will be enabled by default. Change the CONFIG_NFP_APP_FLOWER to default to enabled. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/Kconfig b/drivers/net/ethernet/netronome/Kconfig index 0e331e2f685a..ae0c46ba7546 100644 --- a/drivers/net/ethernet/netronome/Kconfig +++ b/drivers/net/ethernet/netronome/Kconfig @@ -29,6 +29,7 @@ config NFP_APP_FLOWER bool "NFP4000/NFP6000 TC Flower offload support" depends on NFP depends on NET_SWITCHDEV + default y ---help--- Enable driver support for TC Flower offload on NFP4000 and NFP6000. Say Y, if you are planning to make use of TC Flower offload -- cgit v1.2.3 From 9ce4fa548313f585c637aa846f7fc904b676bf43 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 2 Sep 2017 18:26:05 -0700 Subject: nfp: flower: restore RTNL locking around representor updates When we moved to updating representors from a workqueue grabbing the RTNL somehow got lost in the process. Restore it, and make sure RCU lock is not held while we are grabbing the RTNL. RCU protects the representor table, so since we will be under RTNL we can drop RCU lock as soon as we find the netdev pointer. RTNL is needed for the dev_set_mtu() call. Fixes: 2dff19622421 ("nfp: process MTU updates from firmware flower app") Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/flower/cmsg.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index e014d862b9b6..c3ca05d10fe1 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -141,12 +141,14 @@ nfp_flower_cmsg_portmod_rx(struct nfp_app *app, struct sk_buff *skb) msg = nfp_flower_cmsg_get_data(skb); link = msg->info & NFP_FLOWER_CMSG_PORTMOD_INFO_LINK; + rtnl_lock(); rcu_read_lock(); netdev = nfp_app_repr_get(app, be32_to_cpu(msg->portnum)); + rcu_read_unlock(); if (!netdev) { nfp_flower_cmsg_warn(app, "ctrl msg for unknown port 0x%08x\n", be32_to_cpu(msg->portnum)); - rcu_read_unlock(); + rtnl_unlock(); return; } @@ -161,7 +163,7 @@ nfp_flower_cmsg_portmod_rx(struct nfp_app *app, struct sk_buff *skb) } else { netif_carrier_off(netdev); } - rcu_read_unlock(); + rtnl_unlock(); } static void -- cgit v1.2.3 From 009e1b2b7a302219562999f3b273bd4cbf4d9b40 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Sat, 2 Sep 2017 23:30:41 -0600 Subject: net: qualcomm: rmnet: Fix memory corruption if mux_id is greater than 32 rmnet_rtnl_validate() was checking for upto mux_id 254, however the rmnet_devices devices could hold upto 32 entries only. Fix this by increasing the size of the rmnet_devices. Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial implementation") Signed-off-by: Subash Abhinov Kasiviswanathan Cc: Dan Williams Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 3 +-- drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h | 1 - drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h index 985d372e0d8d..40b6b32216b6 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h @@ -19,7 +19,6 @@ #define _RMNET_CONFIG_H_ #define RMNET_MAX_LOGICAL_EP 255 -#define RMNET_MAX_VND 32 /* Information about the next device to deliver the packet to. * Exact usage of this parameter depends on the rmnet_mode. @@ -39,7 +38,7 @@ struct rmnet_real_dev_info { struct rmnet_endpoint muxed_ep[RMNET_MAX_LOGICAL_EP]; u32 ingress_data_format; u32 egress_data_format; - struct net_device *rmnet_devices[RMNET_MAX_VND]; + struct net_device *rmnet_devices[RMNET_MAX_LOGICAL_EP]; u8 nr_rmnet_devs; }; diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h index ed820b5522f5..7967198fdd90 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h @@ -13,7 +13,6 @@ #ifndef _RMNET_PRIVATE_H_ #define _RMNET_PRIVATE_H_ -#define RMNET_MAX_VND 32 #define RMNET_MAX_PACKET_SIZE 16384 #define RMNET_DFLT_PACKET_SIZE 1500 #define RMNET_NEEDED_HEADROOM 16 diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index bf7455fdafcc..0e0001becb61 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -112,7 +112,7 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r) { - if (id >= RMNET_MAX_VND || !r->rmnet_devices[id]) + if (id >= RMNET_MAX_LOGICAL_EP || !r->rmnet_devices[id]) return -EINVAL; r->rmnet_devices[id] = NULL; -- cgit v1.2.3 From 61bf5490ec47f61b522ecf24958c835aefa38812 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Sat, 2 Sep 2017 23:30:42 -0600 Subject: net: qualcomm: rmnet: Remove the unused endpoint -1 This was used only in the original patch series where the IOCTLs were present and is no longer in use. Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial implementation") Signed-off-by: Subash Abhinov Kasiviswanathan Cc: Dan Williams Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index e836d267d5cc..3ca98ecda02b 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -42,7 +42,6 @@ */ /* Local Definitions and Declarations */ -#define RMNET_LOCAL_LOGICAL_ENDPOINT -1 struct rmnet_walk_data { struct net_device *real_dev; @@ -89,10 +88,7 @@ rmnet_get_endpoint(struct net_device *dev, int config_id) if (!r) return NULL; - if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT) - ep = &r->local_ep; - else - ep = &r->muxed_ep[config_id]; + ep = &r->muxed_ep[config_id]; } return ep; @@ -182,10 +178,7 @@ static int __rmnet_set_endpoint_config(struct net_device *dev, int config_id, return -EINVAL; memcpy(dev_ep, ep, sizeof(struct rmnet_endpoint)); - if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT) - dev_ep->mux_id = 0; - else - dev_ep->mux_id = config_id; + dev_ep->mux_id = config_id; return 0; } @@ -199,8 +192,7 @@ static int rmnet_set_endpoint_config(struct net_device *dev, netdev_dbg(dev, "id %d mode %d dev %s\n", config_id, rmnet_mode, egress_dev->name); - if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT || - config_id >= RMNET_MAX_LOGICAL_EP) + if (config_id >= RMNET_MAX_LOGICAL_EP) return -EINVAL; /* This config is cleared on every set, so its ok to not -- cgit v1.2.3 From 2d516c0d904af52967bb974ee58aa74819d8d05f Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Sat, 2 Sep 2017 23:30:43 -0600 Subject: net: qualcomm: rmnet: Move the device creation log The current log is not very useful as it does not log the device name since it it is prior to registration - (unnamed net_device) (uninitialized): Setting up device Modify to log after the device registration - rmnet1: rmnet dev created Signed-off-by: Subash Abhinov Kasiviswanathan Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index 0e0001becb61..7c7305208498 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -73,8 +73,6 @@ static const struct net_device_ops rmnet_vnd_ops = { */ void rmnet_vnd_setup(struct net_device *rmnet_dev) { - netdev_dbg(rmnet_dev, "Setting up device %s\n", rmnet_dev->name); - rmnet_dev->netdev_ops = &rmnet_vnd_ops; rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE; rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM; @@ -105,6 +103,7 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, r->rmnet_devices[id] = rmnet_dev; r->nr_rmnet_devs++; rmnet_dev->rtnl_link_ops = &rmnet_link_ops; + netdev_dbg(rmnet_dev, "rmnet dev created\n"); } return rc; -- cgit v1.2.3 From 032ee46828e7a2531c77bf80052fa1237820ec9c Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Sat, 2 Sep 2017 23:30:44 -0600 Subject: net: qualcomm: rmnet: Refactor the new rmnet dev creation Data format can be directly set from rmnet_newlink() since the rmnet real dev info is already available. Since __rmnet_get_real_dev_info() is no longer used in rmnet_config.c after removal of those functions, move content to rmnet_get_real_dev_info(). __rmnet_set_endpoint_config() is collapsed into rmnet_set_endpoint_config() since only mux_id was being set additionally within it. Remove an unnecessary mux_id check. Set the mux_id for the rmnet_dev within rmnet_vnd_newlink() itself. Signed-off-by: Subash Abhinov Kasiviswanathan Cc: Dan Williams Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 96 +++++----------------- drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 13 ++- drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h | 1 - 3 files changed, 26 insertions(+), 84 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index 3ca98ecda02b..c0aed7cd0b88 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -57,16 +57,6 @@ static int rmnet_is_real_dev_registered(const struct net_device *real_dev) return (rx_handler == rmnet_rx_handler); } -/* Needs either rcu_read_lock() or rtnl lock */ -static struct rmnet_real_dev_info* -__rmnet_get_real_dev_info(const struct net_device *real_dev) -{ - if (rmnet_is_real_dev_registered(real_dev)) - return rcu_dereference_rtnl(real_dev->rx_handler_data); - else - return NULL; -} - /* Needs rtnl lock */ static struct rmnet_real_dev_info* rmnet_get_real_dev_info_rtnl(const struct net_device *real_dev) @@ -83,10 +73,7 @@ rmnet_get_endpoint(struct net_device *dev, int config_id) if (!rmnet_is_real_dev_registered(dev)) { ep = rmnet_vnd_get_endpoint(dev); } else { - r = __rmnet_get_real_dev_info(dev); - - if (!r) - return NULL; + r = rmnet_get_real_dev_info_rtnl(dev); ep = &r->muxed_ep[config_id]; } @@ -139,70 +126,23 @@ static int rmnet_register_real_device(struct net_device *real_dev) return 0; } -static int rmnet_set_ingress_data_format(struct net_device *dev, u32 idf) -{ - struct rmnet_real_dev_info *r; - - netdev_dbg(dev, "Ingress format 0x%08X\n", idf); - - r = __rmnet_get_real_dev_info(dev); - - r->ingress_data_format = idf; - - return 0; -} - -static int rmnet_set_egress_data_format(struct net_device *dev, u32 edf, - u16 agg_size, u16 agg_count) +static void rmnet_set_endpoint_config(struct net_device *dev, + u8 mux_id, u8 rmnet_mode, + struct net_device *egress_dev) { - struct rmnet_real_dev_info *r; - - netdev_dbg(dev, "Egress format 0x%08X agg size %d cnt %d\n", - edf, agg_size, agg_count); - - r = __rmnet_get_real_dev_info(dev); - - r->egress_data_format = edf; - - return 0; -} - -static int __rmnet_set_endpoint_config(struct net_device *dev, int config_id, - struct rmnet_endpoint *ep) -{ - struct rmnet_endpoint *dev_ep; - - dev_ep = rmnet_get_endpoint(dev, config_id); - - if (!dev_ep) - return -EINVAL; - - memcpy(dev_ep, ep, sizeof(struct rmnet_endpoint)); - dev_ep->mux_id = config_id; - - return 0; -} - -static int rmnet_set_endpoint_config(struct net_device *dev, - int config_id, u8 rmnet_mode, - struct net_device *egress_dev) -{ - struct rmnet_endpoint ep; + struct rmnet_endpoint *ep; netdev_dbg(dev, "id %d mode %d dev %s\n", - config_id, rmnet_mode, egress_dev->name); - - if (config_id >= RMNET_MAX_LOGICAL_EP) - return -EINVAL; + mux_id, rmnet_mode, egress_dev->name); + ep = rmnet_get_endpoint(dev, mux_id); /* This config is cleared on every set, so its ok to not * clear it on a device delete. */ - memset(&ep, 0, sizeof(struct rmnet_endpoint)); - ep.rmnet_mode = rmnet_mode; - ep.egress_dev = egress_dev; - - return __rmnet_set_endpoint_config(dev, config_id, &ep); + memset(ep, 0, sizeof(struct rmnet_endpoint)); + ep->rmnet_mode = rmnet_mode; + ep->egress_dev = egress_dev; + ep->mux_id = mux_id; } static int rmnet_newlink(struct net *src_net, struct net_device *dev, @@ -242,9 +182,11 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, if (err) goto err2; - rmnet_vnd_set_mux(dev, mux_id); - rmnet_set_egress_data_format(real_dev, egress_format, 0, 0); - rmnet_set_ingress_data_format(real_dev, ingress_format); + netdev_dbg(dev, "data format [ingress 0x%08X] [egress 0x%08X]\n", + ingress_format, egress_format); + r->egress_data_format = egress_format; + r->ingress_data_format = ingress_format; + rmnet_set_endpoint_config(real_dev, mux_id, mode, dev); rmnet_set_endpoint_config(dev, mux_id, mode, real_dev); return 0; @@ -376,10 +318,14 @@ struct rtnl_link_ops rmnet_link_ops __read_mostly = { .get_size = rmnet_get_size, }; +/* Needs either rcu_read_lock() or rtnl lock */ struct rmnet_real_dev_info* rmnet_get_real_dev_info(struct net_device *real_dev) { - return __rmnet_get_real_dev_info(real_dev); + if (rmnet_is_real_dev_registered(real_dev)) + return rcu_dereference_rtnl(real_dev->rx_handler_data); + else + return NULL; } /* Startup/Shutdown */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index 7c7305208498..a6de0419d68b 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -93,6 +93,7 @@ void rmnet_vnd_setup(struct net_device *rmnet_dev) int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, struct rmnet_real_dev_info *r) { + struct rmnet_priv *priv; int rc; if (r->rmnet_devices[id]) @@ -103,6 +104,10 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, r->rmnet_devices[id] = rmnet_dev; r->nr_rmnet_devs++; rmnet_dev->rtnl_link_ops = &rmnet_link_ops; + + priv = netdev_priv(rmnet_dev); + priv->mux_id = id; + netdev_dbg(rmnet_dev, "rmnet dev created\n"); } @@ -127,14 +132,6 @@ u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev) return priv->mux_id; } -void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id) -{ - struct rmnet_priv *priv; - - priv = netdev_priv(rmnet_dev); - priv->mux_id = mux_id; -} - /* Gets the logical endpoint configuration for a RmNet virtual network device * node. Caller should confirm that devices is a RmNet VND before calling. */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h index b102b4269be1..4823f38bc563 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h @@ -24,6 +24,5 @@ int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r); void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev); void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev); u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev); -void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id); void rmnet_vnd_setup(struct net_device *dev); #endif /* _RMNET_VND_H_ */ -- cgit v1.2.3 From b752eff5be24ec146393944848d2ffbef92b43a7 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Sat, 2 Sep 2017 23:30:45 -0600 Subject: net: qualcomm: rmnet: Implement ndo_get_iflink This makes it easier to find out the parent dev. Signed-off-by: Subash Abhinov Kasiviswanathan Cc: Dan Williams Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 2 +- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 1 + drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 12 +++++++++++- drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h | 3 ++- 4 files changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index c0aed7cd0b88..6f21cd018a79 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -174,7 +174,7 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, goto err0; r = rmnet_get_real_dev_info_rtnl(real_dev); - err = rmnet_vnd_newlink(mux_id, dev, r); + err = rmnet_vnd_newlink(mux_id, dev, r, real_dev); if (err) goto err1; diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h index 40b6b32216b6..3212b25ad2bc 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h @@ -47,6 +47,7 @@ extern struct rtnl_link_ops rmnet_link_ops; struct rmnet_priv { struct rmnet_endpoint local_ep; u8 mux_id; + struct net_device *real_dev; }; struct rmnet_real_dev_info* diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index a6de0419d68b..f5c63b78338b 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -63,9 +63,17 @@ static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) return 0; } +static int rmnet_vnd_get_iflink(const struct net_device *dev) +{ + struct rmnet_priv *priv = netdev_priv(dev); + + return priv->real_dev->ifindex; +} + static const struct net_device_ops rmnet_vnd_ops = { .ndo_start_xmit = rmnet_vnd_start_xmit, .ndo_change_mtu = rmnet_vnd_change_mtu, + .ndo_get_iflink = rmnet_vnd_get_iflink, }; /* Called by kernel whenever a new rmnet device is created. Sets MTU, @@ -91,7 +99,8 @@ void rmnet_vnd_setup(struct net_device *rmnet_dev) /* Exposed API */ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, - struct rmnet_real_dev_info *r) + struct rmnet_real_dev_info *r, + struct net_device *real_dev) { struct rmnet_priv *priv; int rc; @@ -107,6 +116,7 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, priv = netdev_priv(rmnet_dev); priv->mux_id = id; + priv->real_dev = real_dev; netdev_dbg(rmnet_dev, "rmnet dev created\n"); } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h index 4823f38bc563..9084e447eba8 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h @@ -19,7 +19,8 @@ int rmnet_vnd_do_flow_control(struct net_device *dev, int enable); struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *dev); int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, - struct rmnet_real_dev_info *r); + struct rmnet_real_dev_info *r, + struct net_device *real_dev); int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r); void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev); void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev); -- cgit v1.2.3 From b665f4f82d31c17926df3ac7c4a59bbff7af463b Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Sat, 2 Sep 2017 23:30:46 -0600 Subject: net: qualcomm: rmnet: Rename real_dev_info to port Make it similar to drivers like ipvlan / macvlan so it is easier to read. Signed-off-by: Subash Abhinov Kasiviswanathan Cc: Dan Williams Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 65 +++++++++++----------- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 5 +- .../net/ethernet/qualcomm/rmnet/rmnet_handlers.c | 56 +++++++++---------- drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h | 6 +- .../ethernet/qualcomm/rmnet/rmnet_map_command.c | 13 ++--- .../net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 3 +- drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 17 +++--- drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h | 4 +- 8 files changed, 82 insertions(+), 87 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index 6f21cd018a79..98f22551eb45 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -46,7 +46,7 @@ struct rmnet_walk_data { struct net_device *real_dev; struct list_head *head; - struct rmnet_real_dev_info *real_dev_info; + struct rmnet_port *port; }; static int rmnet_is_real_dev_registered(const struct net_device *real_dev) @@ -58,8 +58,8 @@ static int rmnet_is_real_dev_registered(const struct net_device *real_dev) } /* Needs rtnl lock */ -static struct rmnet_real_dev_info* -rmnet_get_real_dev_info_rtnl(const struct net_device *real_dev) +static struct rmnet_port* +rmnet_get_port_rtnl(const struct net_device *real_dev) { return rtnl_dereference(real_dev->rx_handler_data); } @@ -67,27 +67,27 @@ rmnet_get_real_dev_info_rtnl(const struct net_device *real_dev) static struct rmnet_endpoint* rmnet_get_endpoint(struct net_device *dev, int config_id) { - struct rmnet_real_dev_info *r; struct rmnet_endpoint *ep; + struct rmnet_port *port; if (!rmnet_is_real_dev_registered(dev)) { ep = rmnet_vnd_get_endpoint(dev); } else { - r = rmnet_get_real_dev_info_rtnl(dev); + port = rmnet_get_port_rtnl(dev); - ep = &r->muxed_ep[config_id]; + ep = &port->muxed_ep[config_id]; } return ep; } static int rmnet_unregister_real_device(struct net_device *real_dev, - struct rmnet_real_dev_info *r) + struct rmnet_port *port) { - if (r->nr_rmnet_devs) + if (port->nr_rmnet_devs) return -EINVAL; - kfree(r); + kfree(port); netdev_rx_handler_unregister(real_dev); @@ -100,7 +100,7 @@ static int rmnet_unregister_real_device(struct net_device *real_dev, static int rmnet_register_real_device(struct net_device *real_dev) { - struct rmnet_real_dev_info *r; + struct rmnet_port *port; int rc; ASSERT_RTNL(); @@ -108,14 +108,14 @@ static int rmnet_register_real_device(struct net_device *real_dev) if (rmnet_is_real_dev_registered(real_dev)) return 0; - r = kzalloc(sizeof(*r), GFP_ATOMIC); - if (!r) + port = kzalloc(sizeof(*port), GFP_ATOMIC); + if (!port) return -ENOMEM; - r->dev = real_dev; - rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, r); + port->dev = real_dev; + rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, port); if (rc) { - kfree(r); + kfree(port); return -EBUSY; } @@ -154,9 +154,9 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, RMNET_INGRESS_FORMAT_MAP; int egress_format = RMNET_EGRESS_FORMAT_MUXING | RMNET_EGRESS_FORMAT_MAP; - struct rmnet_real_dev_info *r; struct net_device *real_dev; int mode = RMNET_EPMODE_VND; + struct rmnet_port *port; int err = 0; u16 mux_id; @@ -173,8 +173,8 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, if (err) goto err0; - r = rmnet_get_real_dev_info_rtnl(real_dev); - err = rmnet_vnd_newlink(mux_id, dev, r, real_dev); + port = rmnet_get_port_rtnl(real_dev); + err = rmnet_vnd_newlink(mux_id, dev, port, real_dev); if (err) goto err1; @@ -184,25 +184,25 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, netdev_dbg(dev, "data format [ingress 0x%08X] [egress 0x%08X]\n", ingress_format, egress_format); - r->egress_data_format = egress_format; - r->ingress_data_format = ingress_format; + port->egress_data_format = egress_format; + port->ingress_data_format = ingress_format; rmnet_set_endpoint_config(real_dev, mux_id, mode, dev); rmnet_set_endpoint_config(dev, mux_id, mode, real_dev); return 0; err2: - rmnet_vnd_dellink(mux_id, r); + rmnet_vnd_dellink(mux_id, port); err1: - rmnet_unregister_real_device(real_dev, r); + rmnet_unregister_real_device(real_dev, port); err0: return err; } static void rmnet_dellink(struct net_device *dev, struct list_head *head) { - struct rmnet_real_dev_info *r; struct net_device *real_dev; + struct rmnet_port *port; u8 mux_id; rcu_read_lock(); @@ -212,12 +212,12 @@ static void rmnet_dellink(struct net_device *dev, struct list_head *head) if (!real_dev || !rmnet_is_real_dev_registered(real_dev)) return; - r = rmnet_get_real_dev_info_rtnl(real_dev); + port = rmnet_get_port_rtnl(real_dev); mux_id = rmnet_vnd_get_mux(dev); - rmnet_vnd_dellink(mux_id, r); + rmnet_vnd_dellink(mux_id, port); netdev_upper_dev_unlink(dev, real_dev); - rmnet_unregister_real_device(real_dev, r); + rmnet_unregister_real_device(real_dev, port); unregister_netdevice_queue(dev, head); } @@ -229,7 +229,7 @@ static int rmnet_dev_walk_unreg(struct net_device *rmnet_dev, void *data) mux_id = rmnet_vnd_get_mux(rmnet_dev); - rmnet_vnd_dellink(mux_id, d->real_dev_info); + rmnet_vnd_dellink(mux_id, d->port); netdev_upper_dev_unlink(rmnet_dev, d->real_dev); unregister_netdevice_queue(rmnet_dev, d->head); @@ -239,8 +239,8 @@ static int rmnet_dev_walk_unreg(struct net_device *rmnet_dev, void *data) static void rmnet_force_unassociate_device(struct net_device *dev) { struct net_device *real_dev = dev; - struct rmnet_real_dev_info *r; struct rmnet_walk_data d; + struct rmnet_port *port; LIST_HEAD(list); if (!rmnet_is_real_dev_registered(real_dev)) @@ -251,15 +251,15 @@ static void rmnet_force_unassociate_device(struct net_device *dev) d.real_dev = real_dev; d.head = &list; - r = rmnet_get_real_dev_info_rtnl(dev); - d.real_dev_info = r; + port = rmnet_get_port_rtnl(dev); + d.port = port; rcu_read_lock(); netdev_walk_all_lower_dev_rcu(real_dev, rmnet_dev_walk_unreg, &d); rcu_read_unlock(); unregister_netdevice_many(&list); - rmnet_unregister_real_device(real_dev, r); + rmnet_unregister_real_device(real_dev, port); } static int rmnet_config_notify_cb(struct notifier_block *nb, @@ -319,8 +319,7 @@ struct rtnl_link_ops rmnet_link_ops __read_mostly = { }; /* Needs either rcu_read_lock() or rtnl lock */ -struct rmnet_real_dev_info* -rmnet_get_real_dev_info(struct net_device *real_dev) +struct rmnet_port *rmnet_get_port(struct net_device *real_dev) { if (rmnet_is_real_dev_registered(real_dev)) return rcu_dereference_rtnl(real_dev->rx_handler_data); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h index 3212b25ad2bc..dde4e9f14f4a 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h @@ -32,7 +32,7 @@ struct rmnet_endpoint { /* One instance of this structure is instantiated for each real_dev associated * with rmnet. */ -struct rmnet_real_dev_info { +struct rmnet_port { struct net_device *dev; struct rmnet_endpoint local_ep; struct rmnet_endpoint muxed_ep[RMNET_MAX_LOGICAL_EP]; @@ -50,7 +50,6 @@ struct rmnet_priv { struct net_device *real_dev; }; -struct rmnet_real_dev_info* -rmnet_get_real_dev_info(struct net_device *real_dev); +struct rmnet_port *rmnet_get_port(struct net_device *real_dev); #endif /* _RMNET_CONFIG_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index 7dab3bbfeda5..540c7622dcb1 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -82,32 +82,32 @@ rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_endpoint *ep) static rx_handler_result_t rmnet_ingress_deliver_packet(struct sk_buff *skb, - struct rmnet_real_dev_info *r) + struct rmnet_port *port) { - if (!r) { + if (!port) { kfree_skb(skb); return RX_HANDLER_CONSUMED; } - skb->dev = r->local_ep.egress_dev; + skb->dev = port->local_ep.egress_dev; - return rmnet_deliver_skb(skb, &r->local_ep); + return rmnet_deliver_skb(skb, &port->local_ep); } /* MAP handler */ static rx_handler_result_t __rmnet_map_ingress_handler(struct sk_buff *skb, - struct rmnet_real_dev_info *r) + struct rmnet_port *port) { struct rmnet_endpoint *ep; u8 mux_id; u16 len; if (RMNET_MAP_GET_CD_BIT(skb)) { - if (r->ingress_data_format + if (port->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_COMMANDS) - return rmnet_map_command(skb, r); + return rmnet_map_command(skb, port); kfree_skb(skb); return RX_HANDLER_CONSUMED; @@ -121,9 +121,9 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, return RX_HANDLER_CONSUMED; } - ep = &r->muxed_ep[mux_id]; + ep = &port->muxed_ep[mux_id]; - if (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING) + if (port->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING) skb->dev = ep->egress_dev; /* Subtract MAP header */ @@ -135,26 +135,26 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, static rx_handler_result_t rmnet_map_ingress_handler(struct sk_buff *skb, - struct rmnet_real_dev_info *r) + struct rmnet_port *port) { struct sk_buff *skbn; int rc; - if (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { - while ((skbn = rmnet_map_deaggregate(skb, r)) != NULL) - __rmnet_map_ingress_handler(skbn, r); + if (port->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { + while ((skbn = rmnet_map_deaggregate(skb)) != NULL) + __rmnet_map_ingress_handler(skbn, port); consume_skb(skb); rc = RX_HANDLER_CONSUMED; } else { - rc = __rmnet_map_ingress_handler(skb, r); + rc = __rmnet_map_ingress_handler(skb, port); } return rc; } static int rmnet_map_egress_handler(struct sk_buff *skb, - struct rmnet_real_dev_info *r, + struct rmnet_port *port, struct rmnet_endpoint *ep, struct net_device *orig_dev) { @@ -173,7 +173,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, if (!map_header) return RMNET_MAP_CONSUMED; - if (r->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) { + if (port->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) { if (ep->mux_id == 0xff) map_header->mux_id = 0; else @@ -193,7 +193,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, */ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) { - struct rmnet_real_dev_info *r; + struct rmnet_port *port; struct sk_buff *skb = *pskb; struct net_device *dev; int rc; @@ -202,16 +202,16 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) return RX_HANDLER_CONSUMED; dev = skb->dev; - r = rmnet_get_real_dev_info(dev); + port = rmnet_get_port(dev); - if (r->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) { - rc = rmnet_map_ingress_handler(skb, r); + if (port->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) { + rc = rmnet_map_ingress_handler(skb, port); } else { switch (ntohs(skb->protocol)) { case ETH_P_MAP: - if (r->local_ep.rmnet_mode == + if (port->local_ep.rmnet_mode == RMNET_EPMODE_BRIDGE) { - rc = rmnet_ingress_deliver_packet(skb, r); + rc = rmnet_ingress_deliver_packet(skb, port); } else { kfree_skb(skb); rc = RX_HANDLER_CONSUMED; @@ -220,7 +220,7 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) case ETH_P_IP: case ETH_P_IPV6: - rc = rmnet_ingress_deliver_packet(skb, r); + rc = rmnet_ingress_deliver_packet(skb, port); break; default: @@ -238,20 +238,20 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) void rmnet_egress_handler(struct sk_buff *skb, struct rmnet_endpoint *ep) { - struct rmnet_real_dev_info *r; struct net_device *orig_dev; + struct rmnet_port *port; orig_dev = skb->dev; skb->dev = ep->egress_dev; - r = rmnet_get_real_dev_info(skb->dev); - if (!r) { + port = rmnet_get_port(skb->dev); + if (!port) { kfree_skb(skb); return; } - if (r->egress_data_format & RMNET_EGRESS_FORMAT_MAP) { - switch (rmnet_map_egress_handler(skb, r, ep, orig_dev)) { + if (port->egress_data_format & RMNET_EGRESS_FORMAT_MAP) { + switch (rmnet_map_egress_handler(skb, port, ep, orig_dev)) { case RMNET_MAP_CONSUMED: return; diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h index 2aabad289b10..ce2302c25b12 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h @@ -77,12 +77,10 @@ struct rmnet_map_header { #define RMNET_MAP_ADD_PAD_BYTES 1 u8 rmnet_map_demultiplex(struct sk_buff *skb); -struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, - struct rmnet_real_dev_info *rdinfo); - +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb); struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, int hdrlen, int pad); rx_handler_result_t rmnet_map_command(struct sk_buff *skb, - struct rmnet_real_dev_info *rdinfo); + struct rmnet_port *port); #endif /* _RMNET_MAP_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c index ccded40ee551..d1ea5e21b982 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c @@ -17,7 +17,7 @@ #include "rmnet_vnd.h" static u8 rmnet_map_do_flow_control(struct sk_buff *skb, - struct rmnet_real_dev_info *rdinfo, + struct rmnet_port *rdinfo, int enable) { struct rmnet_map_control_command *cmd; @@ -58,8 +58,7 @@ static u8 rmnet_map_do_flow_control(struct sk_buff *skb, } static void rmnet_map_send_ack(struct sk_buff *skb, - unsigned char type, - struct rmnet_real_dev_info *rdinfo) + unsigned char type) { struct rmnet_map_control_command *cmd; int xmit_status; @@ -78,7 +77,7 @@ static void rmnet_map_send_ack(struct sk_buff *skb, * name is decoded here and appropriate handler is called. */ rx_handler_result_t rmnet_map_command(struct sk_buff *skb, - struct rmnet_real_dev_info *rdinfo) + struct rmnet_port *port) { struct rmnet_map_control_command *cmd; unsigned char command_name; @@ -89,11 +88,11 @@ rx_handler_result_t rmnet_map_command(struct sk_buff *skb, switch (command_name) { case RMNET_MAP_COMMAND_FLOW_ENABLE: - rc = rmnet_map_do_flow_control(skb, rdinfo, 1); + rc = rmnet_map_do_flow_control(skb, port, 1); break; case RMNET_MAP_COMMAND_FLOW_DISABLE: - rc = rmnet_map_do_flow_control(skb, rdinfo, 0); + rc = rmnet_map_do_flow_control(skb, port, 0); break; default: @@ -102,6 +101,6 @@ rx_handler_result_t rmnet_map_command(struct sk_buff *skb, break; } if (rc == RMNET_MAP_COMMAND_ACK) - rmnet_map_send_ack(skb, rc, rdinfo); + rmnet_map_send_ack(skb, rc); return RX_HANDLER_CONSUMED; } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index a29c476a4755..557c9bf1a469 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -69,8 +69,7 @@ done: * returned, indicating that there are no more packets to deaggregate. Caller * is responsible for freeing the original skb. */ -struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, - struct rmnet_real_dev_info *rdinfo) +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb) { struct rmnet_map_header *maph; struct sk_buff *skbn; diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index f5c63b78338b..7f90d5587653 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -99,19 +99,20 @@ void rmnet_vnd_setup(struct net_device *rmnet_dev) /* Exposed API */ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, - struct rmnet_real_dev_info *r, + struct rmnet_port *port, struct net_device *real_dev) { struct rmnet_priv *priv; int rc; - if (r->rmnet_devices[id]) + if (port->rmnet_devices[id]) return -EINVAL; rc = register_netdevice(rmnet_dev); if (!rc) { - r->rmnet_devices[id] = rmnet_dev; - r->nr_rmnet_devs++; + port->rmnet_devices[id] = rmnet_dev; + port->nr_rmnet_devs++; + rmnet_dev->rtnl_link_ops = &rmnet_link_ops; priv = netdev_priv(rmnet_dev); @@ -124,13 +125,13 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, return rc; } -int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r) +int rmnet_vnd_dellink(u8 id, struct rmnet_port *port) { - if (id >= RMNET_MAX_LOGICAL_EP || !r->rmnet_devices[id]) + if (id >= RMNET_MAX_LOGICAL_EP || !port->rmnet_devices[id]) return -EINVAL; - r->rmnet_devices[id] = NULL; - r->nr_rmnet_devs--; + port->rmnet_devices[id] = NULL; + port->nr_rmnet_devs--; return 0; } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h index 9084e447eba8..8a4042f0f6bf 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h @@ -19,9 +19,9 @@ int rmnet_vnd_do_flow_control(struct net_device *dev, int enable); struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *dev); int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, - struct rmnet_real_dev_info *r, + struct rmnet_port *port, struct net_device *real_dev); -int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r); +int rmnet_vnd_dellink(u8 id, struct rmnet_port *port); void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev); void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev); u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev); -- cgit v1.2.3 From 691223ec9798a617cb372470f4bf37e312d15063 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Sun, 3 Sep 2017 14:19:31 +0200 Subject: net/mlx4_core: Use ARRAY_SIZE macro Use ARRAY_SIZE macro, rather than explicitly coding some variant of it yourself. Found with: find -type f -name "*.c" -o -name "*.h" | xargs perl -p -i -e 's/\bsizeof\s*\(\s*(\w+)\s*\)\s*\ /\s*sizeof\s*\(\s*\1\s*\[\s*0\s*\]\s*\) /ARRAY_SIZE(\1)/g' and manual check/verification. Signed-off-by: Thomas Meyer Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/fw.c | 5 +++-- drivers/net/ethernet/mellanox/mlx4/main.c | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index d4c7c15cb4ad..16c09949afd5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "fw.h" #include "icm.h" @@ -2455,14 +2456,14 @@ int mlx4_config_dev_retrieval(struct mlx4_dev *dev, csum_mask = (config_dev.rx_checksum_val >> CONFIG_DEV_RX_CSUM_MODE_PORT1_BIT_OFFSET) & CONFIG_DEV_RX_CSUM_MODE_MASK; - if (csum_mask >= sizeof(config_dev_csum_flags)/sizeof(config_dev_csum_flags[0])) + if (csum_mask >= ARRAY_SIZE(config_dev_csum_flags)) return -EINVAL; params->rx_csum_flags_port_1 = config_dev_csum_flags[csum_mask]; csum_mask = (config_dev.rx_checksum_val >> CONFIG_DEV_RX_CSUM_MODE_PORT2_BIT_OFFSET) & CONFIG_DEV_RX_CSUM_MODE_MASK; - if (csum_mask >= sizeof(config_dev_csum_flags)/sizeof(config_dev_csum_flags[0])) + if (csum_mask >= ARRAY_SIZE(config_dev_csum_flags)) return -EINVAL; params->rx_csum_flags_port_2 = config_dev_csum_flags[csum_mask]; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index b3e50f4892c5..eccf53705f25 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -34,6 +34,7 @@ */ #include +#include #include #include #include @@ -3675,7 +3676,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data, * per port, we must limit the number of VFs to 63 (since their are * 128 MACs) */ - for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]) && i < num_vfs_argc; + for (i = 0; i < ARRAY_SIZE(nvfs) && i < num_vfs_argc; total_vfs += nvfs[param_map[num_vfs_argc - 1][i]], i++) { nvfs[param_map[num_vfs_argc - 1][i]] = num_vfs[i]; if (nvfs[i] < 0) { @@ -3684,7 +3685,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data, goto err_disable_pdev; } } - for (i = 0; i < sizeof(prb_vf)/sizeof(prb_vf[0]) && i < probe_vfs_argc; + for (i = 0; i < ARRAY_SIZE(prb_vf) && i < probe_vfs_argc; i++) { prb_vf[param_map[probe_vfs_argc - 1][i]] = probe_vf[i]; if (prb_vf[i] < 0 || prb_vf[i] > nvfs[i]) { @@ -3763,11 +3764,11 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data, if (total_vfs) { unsigned vfs_offset = 0; - for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]) && + for (i = 0; i < ARRAY_SIZE(nvfs) && vfs_offset + nvfs[i] < extended_func_num(pdev); vfs_offset += nvfs[i], i++) ; - if (i == sizeof(nvfs)/sizeof(nvfs[0])) { + if (i == ARRAY_SIZE(nvfs)) { err = -ENODEV; goto err_release_regions; } -- cgit v1.2.3 From 18118377391a266fbd0864b4c65f5c80f231c297 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 3 Sep 2017 20:27:02 -0700 Subject: net: dsa: bcm_sf2: Advertise number of egress queues The switch supports 8 egress queues per port, so indicate that such that net/dsa/slave.c::dsa_slave_create can allocate the right number of TX queues. While at it use SF2_NUM_EGRESS_QUEUE as a define for the number of queues we support. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 5 ++++- drivers/net/dsa/bcm_sf2_regs.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 554fe2df9365..6b184bafa235 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -244,7 +244,7 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, * to a different queue number */ reg = core_readl(priv, CORE_PORT_TC2_QOS_MAP_PORT(port)); - for (i = 0; i < 8; i++) + for (i = 0; i < SF2_NUM_EGRESS_QUEUES; i++) reg |= i << (PRT_TO_QID_SHIFT * i); core_writel(priv, reg, CORE_PORT_TC2_QOS_MAP_PORT(port)); @@ -1151,6 +1151,9 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev) ds = dev->ds; ds->ops = &bcm_sf2_ops; + /* Advertise the 8 egress queues */ + ds->num_tx_queues = SF2_NUM_EGRESS_QUEUES; + dev_set_drvdata(&pdev->dev, priv); spin_lock_init(&priv->indir_lock); diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h index 26052450091e..49695fcc2ea8 100644 --- a/drivers/net/dsa/bcm_sf2_regs.h +++ b/drivers/net/dsa/bcm_sf2_regs.h @@ -401,4 +401,7 @@ enum bcm_sf2_reg_offs { #define CFP_NUM_RULES 256 +/* Number of egress queues per port */ +#define SF2_NUM_EGRESS_QUEUES 8 + #endif /* __BCM_SF2_REGS_H */ -- cgit v1.2.3 From c837fc81d67fd807f7d675412e9bb2706f9ee6f9 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 3 Sep 2017 20:27:03 -0700 Subject: net: dsa: bcm_sf2: Configure IMP port TC2QOS mapping Even though TC2QOS mapping is for switch egress queues, we need to configure it correclty in order for the Broadcom tag ingress (CPU -> switch) queue selection to work correctly since there is a 1:1 mapping between switch egress queues and ingress queues. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 6b184bafa235..d7b53d53c116 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -103,6 +103,7 @@ static void bcm_sf2_brcm_hdr_setup(struct bcm_sf2_priv *priv, int port) static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); + unsigned int i; u32 reg, offset; if (priv->type == BCM7445_DEVICE_ID) @@ -129,6 +130,14 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) reg |= MII_DUMB_FWDG_EN; core_writel(priv, reg, CORE_SWITCH_CTRL); + /* Configure Traffic Class to QoS mapping, allow each priority to map + * to a different queue number + */ + reg = core_readl(priv, CORE_PORT_TC2_QOS_MAP_PORT(port)); + for (i = 0; i < SF2_NUM_EGRESS_QUEUES; i++) + reg |= i << (PRT_TO_QID_SHIFT * i); + core_writel(priv, reg, CORE_PORT_TC2_QOS_MAP_PORT(port)); + bcm_sf2_brcm_hdr_setup(priv, port); /* Force link status for IMP port */ -- cgit v1.2.3 From 7df13219d757054c49b62417479a3724621ca8ae Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Mon, 4 Sep 2017 11:36:08 +0800 Subject: tun: reserve extra headroom only when XDP is set We reserve headroom unconditionally which could cause unnecessary stress on socket memory accounting because of increased trusesize. Fix this by only reserve extra headroom when XDP is set. Cc: Jakub Kicinski Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/tun.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 06e8f0bb2dab..80ac18f8b55f 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -108,7 +108,7 @@ do { \ #endif #define TUN_HEADROOM 256 -#define TUN_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD + TUN_HEADROOM) +#define TUN_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD) /* TUN device flags */ @@ -1272,25 +1272,35 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, struct page_frag *alloc_frag = ¤t->task_frag; struct sk_buff *skb; struct bpf_prog *xdp_prog; - int buflen = SKB_DATA_ALIGN(len + TUN_RX_PAD) + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + int buflen = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); unsigned int delta = 0; char *buf; size_t copied; bool xdp_xmit = false; - int err; + int err, pad = TUN_RX_PAD; + + rcu_read_lock(); + xdp_prog = rcu_dereference(tun->xdp_prog); + if (xdp_prog) + pad += TUN_HEADROOM; + buflen += SKB_DATA_ALIGN(len + pad); + rcu_read_unlock(); if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL))) return ERR_PTR(-ENOMEM); buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset; copied = copy_page_from_iter(alloc_frag->page, - alloc_frag->offset + TUN_RX_PAD, + alloc_frag->offset + pad, len, from); if (copied != len) return ERR_PTR(-EFAULT); - if (hdr->gso_type) + /* There's a small window that XDP may be set after the check + * of xdp_prog above, this should be rare and for simplicity + * we do XDP on skb in case the headroom is not enough. + */ + if (hdr->gso_type || !xdp_prog) *generic_xdp = 1; else *generic_xdp = 0; @@ -1303,7 +1313,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, u32 act; xdp.data_hard_start = buf; - xdp.data = buf + TUN_RX_PAD; + xdp.data = buf + pad; xdp.data_end = xdp.data + len; orig_data = xdp.data; act = bpf_prog_run_xdp(xdp_prog, &xdp); @@ -1339,7 +1349,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, return ERR_PTR(-ENOMEM); } - skb_reserve(skb, TUN_RX_PAD - delta); + skb_reserve(skb, pad - delta); skb_put(skb, len + delta); get_page(alloc_frag->page); alloc_frag->offset += buflen; -- cgit v1.2.3 From 1cfe6e9351c256ec6aebcf64223aa21dc7654257 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Mon, 4 Sep 2017 11:36:09 +0800 Subject: tun: rename generic_xdp to skb_xdp Rename "generic_xdp" to "skb_xdp" to avoid confusing it with the generic XDP which will be done at netif_receive_skb(). Cc: Daniel Borkmann Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/tun.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 80ac18f8b55f..3c9985f29950 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1267,7 +1267,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, struct tun_file *tfile, struct iov_iter *from, struct virtio_net_hdr *hdr, - int len, int *generic_xdp) + int len, int *skb_xdp) { struct page_frag *alloc_frag = ¤t->task_frag; struct sk_buff *skb; @@ -1301,13 +1301,13 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, * we do XDP on skb in case the headroom is not enough. */ if (hdr->gso_type || !xdp_prog) - *generic_xdp = 1; + *skb_xdp = 1; else - *generic_xdp = 0; + *skb_xdp = 0; rcu_read_lock(); xdp_prog = rcu_dereference(tun->xdp_prog); - if (xdp_prog && !*generic_xdp) { + if (xdp_prog && !*skb_xdp) { struct xdp_buff xdp; void *orig_data; u32 act; @@ -1389,7 +1389,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, bool zerocopy = false; int err; u32 rxhash; - int generic_xdp = 1; + int skb_xdp = 1; if (!(tun->dev->flags & IFF_UP)) return -EIO; @@ -1448,7 +1448,11 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (tun_can_build_skb(tun, tfile, len, noblock, zerocopy)) { - skb = tun_build_skb(tun, tfile, from, &gso, len, &generic_xdp); + /* For the packet that is not easy to be processed + * (e.g gso or jumbo packet), we will do it at after + * skb was created with generic XDP routine. + */ + skb = tun_build_skb(tun, tfile, from, &gso, len, &skb_xdp); if (IS_ERR(skb)) { this_cpu_inc(tun->pcpu_stats->rx_dropped); return PTR_ERR(skb); @@ -1528,7 +1532,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb_reset_network_header(skb); skb_probe_transport_header(skb, 0); - if (generic_xdp) { + if (skb_xdp) { struct bpf_prog *xdp_prog; int ret; -- cgit v1.2.3 From 128416acea9ffda4b6a0e08c8d549572e87533cd Mon Sep 17 00:00:00 2001 From: Ganesh Goudar Date: Mon, 4 Sep 2017 11:16:28 +0530 Subject: cxgb4: fix memory leak do not reuse the loop counter which is used iterate over the ports, so that sched_tbl will be freed for all the ports. Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/sched.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/sched.c b/drivers/net/ethernet/chelsio/cxgb4/sched.c index 02acff741f11..9148abb7994c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sched.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sched.c @@ -533,10 +533,10 @@ struct sched_table *t4_init_sched(unsigned int sched_size) void t4_cleanup_sched(struct adapter *adap) { struct sched_table *s; - unsigned int i; + unsigned int j, i; - for_each_port(adap, i) { - struct port_info *pi = netdev2pinfo(adap->port[i]); + for_each_port(adap, j) { + struct port_info *pi = netdev2pinfo(adap->port[j]); s = pi->sched_tbl; for (i = 0; i < s->sched_size; i++) { -- cgit v1.2.3 From 2de489f486219fd52e85b336f4feb9b3c4c09617 Mon Sep 17 00:00:00 2001 From: Ganesh Goudar Date: Mon, 4 Sep 2017 11:17:36 +0530 Subject: cxgb4: Fix pause frame count in t4_get_port_stats MPS_STAT_CTL[CountPauseStatTx] and MPS_STAT_CTL[CountPauseStatRx] only control whether or not Pause Frames will be counted as part of the 64-Byte Tx/Rx Frame counters. These bits do not control whether Pause Frames are counted in the Total Tx/Rx Frames/Bytes counters. Signed-off-by: Ganesh Goudar Signed-off-by: Casey Leedom Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 08624db8a6e9..42df1d8c7674 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -5987,10 +5987,8 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) p->tx_ppp7 = GET_STAT(TX_PORT_PPP7); if (CHELSIO_CHIP_VERSION(adap->params.chip) >= CHELSIO_T5) { - if (stat_ctl & COUNTPAUSESTATTX_F) { - p->tx_frames -= p->tx_pause; - p->tx_octets -= p->tx_pause * 64; - } + if (stat_ctl & COUNTPAUSESTATTX_F) + p->tx_frames_64 -= p->tx_pause; if (stat_ctl & COUNTPAUSEMCTX_F) p->tx_mcast_frames -= p->tx_pause; } @@ -6023,10 +6021,8 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) p->rx_ppp7 = GET_STAT(RX_PORT_PPP7); if (CHELSIO_CHIP_VERSION(adap->params.chip) >= CHELSIO_T5) { - if (stat_ctl & COUNTPAUSESTATRX_F) { - p->rx_frames -= p->rx_pause; - p->rx_octets -= p->rx_pause * 64; - } + if (stat_ctl & COUNTPAUSESTATRX_F) + p->rx_frames_64 -= p->rx_pause; if (stat_ctl & COUNTPAUSEMCRX_F) p->rx_mcast_frames -= p->rx_pause; } -- cgit v1.2.3 From ef18e3b9682b8e5187bccc3b3a052f2ead80201d Mon Sep 17 00:00:00 2001 From: Ganesh Goudar Date: Mon, 4 Sep 2017 11:25:34 +0530 Subject: cxgb4: Ignore MPS_TX_INT_CAUSE[Bubble] for T6 MPS_TX_INT_CAUSE[Bubble] is a normal condition for T6, hence ignore this interrupt for T6. Signed-off-by: Ganesh Goudar Signed-off-by: Casey Leedom Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 42df1d8c7674..b65ce26ff72f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -4548,6 +4548,18 @@ static void mps_intr_handler(struct adapter *adapter) { FRMERR_F, "MPS Tx framing error", -1, 1 }, { 0 } }; + static const struct intr_info t6_mps_tx_intr_info[] = { + { TPFIFO_V(TPFIFO_M), "MPS Tx TP FIFO parity error", -1, 1 }, + { NCSIFIFO_F, "MPS Tx NC-SI FIFO parity error", -1, 1 }, + { TXDATAFIFO_V(TXDATAFIFO_M), "MPS Tx data FIFO parity error", + -1, 1 }, + { TXDESCFIFO_V(TXDESCFIFO_M), "MPS Tx desc FIFO parity error", + -1, 1 }, + /* MPS Tx Bubble is normal for T6 */ + { SECNTERR_F, "MPS Tx SOP/EOP error", -1, 1 }, + { FRMERR_F, "MPS Tx framing error", -1, 1 }, + { 0 } + }; static const struct intr_info mps_trc_intr_info[] = { { FILTMEM_V(FILTMEM_M), "MPS TRC filter parity error", -1, 1 }, { PKTFIFO_V(PKTFIFO_M), "MPS TRC packet FIFO parity error", @@ -4579,7 +4591,9 @@ static void mps_intr_handler(struct adapter *adapter) fat = t4_handle_intr_status(adapter, MPS_RX_PERR_INT_CAUSE_A, mps_rx_intr_info) + t4_handle_intr_status(adapter, MPS_TX_INT_CAUSE_A, - mps_tx_intr_info) + + is_t6(adapter->params.chip) + ? t6_mps_tx_intr_info + : mps_tx_intr_info) + t4_handle_intr_status(adapter, MPS_TRC_INT_CAUSE_A, mps_trc_intr_info) + t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_SRAM_A, -- cgit v1.2.3 From 5d621672bc1a1e5090c1ac5432a18c79e0e13e03 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 4 Sep 2017 10:45:28 +0300 Subject: gianfar: Fix Tx flow control deactivation The wrong register is checked for the Tx flow control bit, it should have been maccfg1 not maccfg2. This went unnoticed for so long probably because the impact is hardly visible, not to mention the tangled code from adjust_link(). First, link flow control (i.e. handling of Rx/Tx link level pause frames) is disabled by default (needs to be enabled via 'ethtool -A'). Secondly, maccfg2 always returns 0 for tx_flow_oldval (except for a few old boards), which results in Tx flow control remaining always on once activated. Fixes: 45b679c9a3ccd9e34f28e6ec677b812a860eb8eb ("gianfar: Implement PAUSE frame generation support") Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index c4b4b0a1bbf0..5be52d89b182 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -3687,7 +3687,7 @@ static noinline void gfar_update_link_state(struct gfar_private *priv) u32 tempval1 = gfar_read(®s->maccfg1); u32 tempval = gfar_read(®s->maccfg2); u32 ecntrl = gfar_read(®s->ecntrl); - u32 tx_flow_oldval = (tempval & MACCFG1_TX_FLOW); + u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW); if (phydev->duplex != priv->oldduplex) { if (!(phydev->duplex)) -- cgit v1.2.3 From 5e369aefdce4818c8264b81d3436eccf35af561e Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Mon, 4 Sep 2017 10:41:36 +0200 Subject: net: stmmac: Delete dead code for MDIO registration This code is no longer used, the logging function was changed by commit fbca164776e4 ("net: stmmac: Use the right logging function in stmmac_mdio_register"). It was previously showing information about the type of the IRQ, if it's polled, ignored or a normal interrupt. As we don't want information loss, I have moved this code to phy_attached_print(). Fixes: fbca164776e4 ("net: stmmac: Use the right logging function in stmmac_mdio_register") Signed-off-by: Romain Perier Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 16 ---------------- drivers/net/phy/phy_device.c | 22 +++++++++++++++++++--- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 72ec711fcba2..f5f37bfa1d58 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -248,9 +248,6 @@ int stmmac_mdio_register(struct net_device *ndev) found = 0; for (addr = 0; addr < PHY_MAX_ADDR; addr++) { struct phy_device *phydev = mdiobus_get_phy(new_bus, addr); - int act = 0; - char irq_num[4]; - char *irq_str; if (!phydev) continue; @@ -273,19 +270,6 @@ int stmmac_mdio_register(struct net_device *ndev) if (priv->plat->phy_addr == -1) priv->plat->phy_addr = addr; - act = (priv->plat->phy_addr == addr); - switch (phydev->irq) { - case PHY_POLL: - irq_str = "POLL"; - break; - case PHY_IGNORE_INTERRUPT: - irq_str = "IGNORE"; - break; - default: - sprintf(irq_num, "%d", phydev->irq); - irq_str = irq_num; - break; - } phy_attached_info(phydev); found = 1; } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 810f6fd2f639..8cf0c5901f95 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -874,21 +874,37 @@ void phy_attached_info(struct phy_device *phydev) } EXPORT_SYMBOL(phy_attached_info); -#define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)" +#define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%s)" void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) { const char *drv_name = phydev->drv ? phydev->drv->name : "unbound"; + char *irq_str; + char irq_num[4]; + + switch(phydev->irq) { + case PHY_POLL: + irq_str = "POLL"; + break; + case PHY_IGNORE_INTERRUPT: + irq_str = "IGNORE"; + break; + default: + snprintf(irq_num, sizeof(irq_num), "%d", phydev->irq); + irq_str = irq_num; + break; + } + if (!fmt) { dev_info(&phydev->mdio.dev, ATTACHED_FMT "\n", drv_name, phydev_name(phydev), - phydev->irq); + irq_str); } else { va_list ap; dev_info(&phydev->mdio.dev, ATTACHED_FMT, drv_name, phydev_name(phydev), - phydev->irq); + irq_str); va_start(ap, fmt); vprintk(fmt, ap); -- cgit v1.2.3 From 5482a978b962abd23f17a004e46d255d11646c20 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Mon, 4 Sep 2017 18:30:14 +0200 Subject: net: mdio-mux: add mdio_mux parameter to mdio_mux_init() mdio_mux_init() use the parameter dev for two distinct thing: 1) Have a device for all devm_ functions 2) Get device_node from it Since it is two distinct purpose, this patch add a parameter mdio_mux that is linked to task 2. This will also permit to register an of_node mdio-mux that lacks a direct owning device. For example a mdio-mux which is a subnode of a real device. Signed-off-by: Corentin Labbe Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio-mux-bcm-iproc.c | 2 +- drivers/net/phy/mdio-mux-gpio.c | 2 +- drivers/net/phy/mdio-mux-mmioreg.c | 3 ++- drivers/net/phy/mdio-mux.c | 7 ++++--- include/linux/mdio-mux.h | 9 +++++++++ 5 files changed, 17 insertions(+), 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-mux-bcm-iproc.c b/drivers/net/phy/mdio-mux-bcm-iproc.c index 0a5f62e0efcc..0831b7142df7 100644 --- a/drivers/net/phy/mdio-mux-bcm-iproc.c +++ b/drivers/net/phy/mdio-mux-bcm-iproc.c @@ -199,7 +199,7 @@ static int mdio_mux_iproc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, md); - rc = mdio_mux_init(md->dev, mdio_mux_iproc_switch_fn, + rc = mdio_mux_init(md->dev, md->dev->of_node, mdio_mux_iproc_switch_fn, &md->mux_handle, md, md->mii_bus); if (rc) { dev_info(md->dev, "mdiomux initialization failed\n"); diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c index 919949960a10..082ffef0dec4 100644 --- a/drivers/net/phy/mdio-mux-gpio.c +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -54,7 +54,7 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev) if (IS_ERR(s->gpios)) return PTR_ERR(s->gpios); - r = mdio_mux_init(&pdev->dev, + r = mdio_mux_init(&pdev->dev, pdev->dev.of_node, mdio_mux_gpio_switch_fn, &s->mux_handle, s, NULL); if (r != 0) { diff --git a/drivers/net/phy/mdio-mux-mmioreg.c b/drivers/net/phy/mdio-mux-mmioreg.c index c3825c7da038..2573ab012f16 100644 --- a/drivers/net/phy/mdio-mux-mmioreg.c +++ b/drivers/net/phy/mdio-mux-mmioreg.c @@ -159,7 +159,8 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev) } } - ret = mdio_mux_init(&pdev->dev, mdio_mux_mmioreg_switch_fn, + ret = mdio_mux_init(&pdev->dev, pdev->dev.of_node, + mdio_mux_mmioreg_switch_fn, &s->mux_handle, s, NULL); if (ret) { dev_err(&pdev->dev, "failed to register mdio-mux bus %pOF\n", diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index 6f75e9f27fed..0a86f1e4c02f 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -86,6 +86,7 @@ out: static int parent_count; int mdio_mux_init(struct device *dev, + struct device_node *mux_node, int (*switch_fn)(int cur, int desired, void *data), void **mux_handle, void *data, @@ -98,11 +99,11 @@ int mdio_mux_init(struct device *dev, struct mdio_mux_parent_bus *pb; struct mdio_mux_child_bus *cb; - if (!dev->of_node) + if (!mux_node) return -ENODEV; if (!mux_bus) { - parent_bus_node = of_parse_phandle(dev->of_node, + parent_bus_node = of_parse_phandle(mux_node, "mdio-parent-bus", 0); if (!parent_bus_node) @@ -132,7 +133,7 @@ int mdio_mux_init(struct device *dev, pb->mii_bus = parent_bus; ret_val = -ENODEV; - for_each_available_child_of_node(dev->of_node, child_bus_node) { + for_each_available_child_of_node(mux_node, child_bus_node) { int v; r = of_property_read_u32(child_bus_node, "reg", &v); diff --git a/include/linux/mdio-mux.h b/include/linux/mdio-mux.h index 61f5b21b31c7..a5d58f221939 100644 --- a/include/linux/mdio-mux.h +++ b/include/linux/mdio-mux.h @@ -12,7 +12,16 @@ #include #include +/* mdio_mux_init() - Initialize a MDIO mux + * @dev The device owning the MDIO mux + * @mux_node The device node of the MDIO mux + * @switch_fn The function called for switching target MDIO child + * mux_handle A pointer to a (void *) used internaly by mdio-mux + * @data Private data used by switch_fn() + * @mux_bus An optional parent bus (Other case are to use parent_bus property) + */ int mdio_mux_init(struct device *dev, + struct device_node *mux_node, int (*switch_fn) (int cur, int desired, void *data), void **mux_handle, void *data, -- cgit v1.2.3 From b1357cfb9dd1a511269b62d2c1c986865815335a Mon Sep 17 00:00:00 2001 From: Zahari Doychev Date: Tue, 5 Sep 2017 21:49:58 +0200 Subject: rocker: fix kcalloc parameter order The function calls to kcalloc use wrong parameter order and incorrect flags values. GFP_KERNEL is used instead of flags now and the order is corrected. The change was done using the following coccinelle script: @@ expression E1,E2; type T; @@ -kcalloc(E1, E2, sizeof(T)) +kcalloc(E2, sizeof(T), GFP_KERNEL) Signed-off-by: Zahari Doychev Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_ofdpa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index da4e26b53a52..0653b70723a3 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -1177,7 +1177,7 @@ static int ofdpa_group_l2_fan_out(struct ofdpa_port *ofdpa_port, entry->group_id = group_id; entry->group_count = group_count; - entry->group_ids = kcalloc(flags, group_count, sizeof(u32)); + entry->group_ids = kcalloc(group_count, sizeof(u32), GFP_KERNEL); if (!entry->group_ids) { kfree(entry); return -ENOMEM; @@ -1456,7 +1456,7 @@ static int ofdpa_port_vlan_flood_group(struct ofdpa_port *ofdpa_port, int err = 0; int i; - group_ids = kcalloc(flags, port_count, sizeof(u32)); + group_ids = kcalloc(port_count, sizeof(u32), GFP_KERNEL); if (!group_ids) return -ENOMEM; -- cgit v1.2.3 From 9d7e72c088e3b18b5f6d028ad499d38a210d1928 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 5 Sep 2017 11:16:30 -0700 Subject: drivers: net: xgene: Read tx/rx delay for ACPI This patch fixes reading tx/rx delay values for ACPI. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 6e253d913fe2..655c0fc34d8a 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1591,7 +1591,7 @@ static int xgene_get_tx_delay(struct xgene_enet_pdata *pdata) struct device *dev = &pdata->pdev->dev; int delay, ret; - ret = of_property_read_u32(dev->of_node, "tx-delay", &delay); + ret = device_property_read_u32(dev, "tx-delay", &delay); if (ret) { pdata->tx_delay = 4; return 0; @@ -1612,7 +1612,7 @@ static int xgene_get_rx_delay(struct xgene_enet_pdata *pdata) struct device *dev = &pdata->pdev->dev; int delay, ret; - ret = of_property_read_u32(dev->of_node, "rx-delay", &delay); + ret = device_property_read_u32(dev, "rx-delay", &delay); if (ret) { pdata->rx_delay = 2; return 0; -- cgit v1.2.3 From 5e338b36643210e0915a365892002af5211cc87b Mon Sep 17 00:00:00 2001 From: Quan Nguyen Date: Tue, 5 Sep 2017 11:16:31 -0700 Subject: drivers: net: xgene: Configure tx/rx delay for ACPI This patch fixes configuring tx/rx delay values for ACPI. Signed-off-by: Quan Nguyen Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index e45b587c2994..3188f553da35 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -468,7 +468,6 @@ static void xgene_enet_configure_clock(struct xgene_enet_pdata *pdata) static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata) { - struct device *dev = &pdata->pdev->dev; u32 icm0, icm2, mc2; u32 intf_ctl, rgmii, value; @@ -500,10 +499,8 @@ static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata) intf_ctl |= ENET_GHD_MODE; CFG_MACMODE_SET(&icm0, 2); CFG_WAITASYNCRD_SET(&icm2, 0); - if (dev->of_node) { - CFG_TXCLK_MUXSEL0_SET(&rgmii, pdata->tx_delay); - CFG_RXCLK_MUXSEL0_SET(&rgmii, pdata->rx_delay); - } + CFG_TXCLK_MUXSEL0_SET(&rgmii, pdata->tx_delay); + CFG_RXCLK_MUXSEL0_SET(&rgmii, pdata->rx_delay); rgmii |= CFG_SPEED_1250; xgene_enet_rd_csr(pdata, DEBUG_REG_ADDR, &value); -- cgit v1.2.3 From f098003a7ee00c92e8ed7a8f8a49e4bd15f35bdd Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 5 Sep 2017 11:16:32 -0700 Subject: drivers: net: xgene: Remove return statement from void function commit 183db4 ("drivers: net: xgene: Correct probe sequence handling") changed the return type of xgene_enet_check_phy_handle() to void. This patch, removes the return statement from the last line. Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 655c0fc34d8a..3b889efddf78 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1674,8 +1674,6 @@ static void xgene_enet_check_phy_handle(struct xgene_enet_pdata *pdata) ret = xgene_enet_phy_connect(pdata->ndev); if (!ret) pdata->mdio_driver = true; - - return; } static void xgene_enet_gpiod_get(struct xgene_enet_pdata *pdata) -- cgit v1.2.3 From 09f79fd49d94cda5837e9bfd0cb222232b3b6d9f Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Fri, 1 Sep 2017 13:42:49 -0700 Subject: i40e: avoid NVM acquire deadlock during NVM update X722 devices use the AdminQ to access the NVM, and this requires taking the AdminQ lock. Because of this, we lock the AdminQ during i40e_read_nvm(), which is also called in places where the lock is already held, such as the firmware update path which wants to lock once and then unlock when finished after performing several tasks. Although this should have only affected X722 devices, commit 96a39aed25e6 ("i40e: Acquire NVM lock before reads on all devices", 2016-12-02) added locking for all NVM reads, regardless of device family. This resulted in us accidentally causing NVM acquire timeouts on all devices, causing failed firmware updates which left the eeprom in a corrupt state. Create unsafe non-locked variants of i40e_read_nvm_word and i40e_read_nvm_buffer, __i40e_read_nvm_word and __i40e_read_nvm_buffer respectively. These variants will not take the NVM lock and are expected to only be called in places where the NVM lock is already held if needed. Since the only caller of i40e_read_nvm_buffer() was in such a path, remove it entirely in favor of the unsafe version. If necessary we can always add it back in the future. Additionally, we now need to hold the NVM lock in i40e_validate_checksum because the call to i40e_calc_nvm_checksum now assumes that the NVM lock is held. We can further move the call to read I40E_SR_SW_CHECKSUM_WORD up a bit so that we do not need to acquire the NVM lock twice. This should resolve firmware updates and also fix potential raise that could have caused the driver to report an invalid NVM checksum upon driver load. Reported-by: Stefan Assmann Fixes: 96a39aed25e6 ("i40e: Acquire NVM lock before reads on all devices", 2016-12-02) Signed-off-by: Anjali Singhai Jain Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_nvm.c | 98 +++++++++++++++--------- drivers/net/ethernet/intel/i40e/i40e_prototype.h | 2 - 2 files changed, 60 insertions(+), 40 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 800bd55d0159..154ba49c2c6f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -266,7 +266,7 @@ static i40e_status i40e_read_nvm_aq(struct i40e_hw *hw, u8 module_pointer, * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) * @data: word read from the Shadow RAM * - * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. + * Reads one 16 bit word from the Shadow RAM using the AdminQ **/ static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, u16 *data) @@ -280,27 +280,49 @@ static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, } /** - * i40e_read_nvm_word - Reads Shadow RAM + * __i40e_read_nvm_word - Reads nvm word, assumes called does the locking * @hw: pointer to the HW structure * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) * @data: word read from the Shadow RAM * - * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. + * Reads one 16 bit word from the Shadow RAM. + * + * Do not use this function except in cases where the nvm lock is already + * taken via i40e_acquire_nvm(). + **/ +static i40e_status __i40e_read_nvm_word(struct i40e_hw *hw, + u16 offset, u16 *data) +{ + i40e_status ret_code = 0; + + if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) + ret_code = i40e_read_nvm_word_aq(hw, offset, data); + else + ret_code = i40e_read_nvm_word_srctl(hw, offset, data); + return ret_code; +} + +/** + * i40e_read_nvm_word - Reads nvm word and acquire lock if necessary + * @hw: pointer to the HW structure + * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) + * @data: word read from the Shadow RAM + * + * Reads one 16 bit word from the Shadow RAM. **/ i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, u16 *data) { - enum i40e_status_code ret_code = 0; + i40e_status ret_code = 0; ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); - if (!ret_code) { - if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { - ret_code = i40e_read_nvm_word_aq(hw, offset, data); - } else { - ret_code = i40e_read_nvm_word_srctl(hw, offset, data); - } - i40e_release_nvm(hw); - } + if (ret_code) + return ret_code; + + ret_code = __i40e_read_nvm_word(hw, offset, data); + + i40e_release_nvm(hw); + return ret_code; } @@ -393,31 +415,25 @@ read_nvm_buffer_aq_exit: } /** - * i40e_read_nvm_buffer - Reads Shadow RAM buffer + * __i40e_read_nvm_buffer - Reads nvm buffer, caller must acquire lock * @hw: pointer to the HW structure * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). * @words: (in) number of words to read; (out) number of words actually read * @data: words read from the Shadow RAM * * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() - * method. The buffer read is preceded by the NVM ownership take - * and followed by the release. + * method. **/ -i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, - u16 *words, u16 *data) +static i40e_status __i40e_read_nvm_buffer(struct i40e_hw *hw, + u16 offset, u16 *words, + u16 *data) { - enum i40e_status_code ret_code = 0; + i40e_status ret_code = 0; - if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { - ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); - if (!ret_code) { - ret_code = i40e_read_nvm_buffer_aq(hw, offset, words, - data); - i40e_release_nvm(hw); - } - } else { + if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) + ret_code = i40e_read_nvm_buffer_aq(hw, offset, words, data); + else ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data); - } return ret_code; } @@ -499,15 +515,15 @@ static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw, data = (u16 *)vmem.va; /* read pointer to VPD area */ - ret_code = i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module); + ret_code = __i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module); if (ret_code) { ret_code = I40E_ERR_NVM_CHECKSUM; goto i40e_calc_nvm_checksum_exit; } /* read pointer to PCIe Alt Auto-load module */ - ret_code = i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, - &pcie_alt_module); + ret_code = __i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, + &pcie_alt_module); if (ret_code) { ret_code = I40E_ERR_NVM_CHECKSUM; goto i40e_calc_nvm_checksum_exit; @@ -521,7 +537,7 @@ static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw, if ((i % I40E_SR_SECTOR_SIZE_IN_WORDS) == 0) { u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS; - ret_code = i40e_read_nvm_buffer(hw, i, &words, data); + ret_code = __i40e_read_nvm_buffer(hw, i, &words, data); if (ret_code) { ret_code = I40E_ERR_NVM_CHECKSUM; goto i40e_calc_nvm_checksum_exit; @@ -593,14 +609,19 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, u16 checksum_sr = 0; u16 checksum_local = 0; + /* We must acquire the NVM lock in order to correctly synchronize the + * NVM accesses across multiple PFs. Without doing so it is possible + * for one of the PFs to read invalid data potentially indicating that + * the checksum is invalid. + */ + ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (ret_code) + return ret_code; ret_code = i40e_calc_nvm_checksum(hw, &checksum_local); + __i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr); + i40e_release_nvm(hw); if (ret_code) - goto i40e_validate_nvm_checksum_exit; - - /* Do not use i40e_read_nvm_word() because we do not want to take - * the synchronization semaphores twice here. - */ - i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr); + return ret_code; /* Verify read checksum from EEPROM is the same as * calculated checksum @@ -612,7 +633,6 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, if (checksum) *checksum = checksum_local; -i40e_validate_nvm_checksum_exit: return ret_code; } @@ -997,6 +1017,7 @@ retry: break; case I40E_NVMUPD_CSUM_CON: + /* Assumes the caller has acquired the nvm */ status = i40e_update_nvm_checksum(hw); if (status) { *perrno = hw->aq.asq_last_status ? @@ -1011,6 +1032,7 @@ retry: break; case I40E_NVMUPD_CSUM_LCB: + /* Assumes the caller has acquired the nvm */ status = i40e_update_nvm_checksum(hw); if (status) { *perrno = hw->aq.asq_last_status ? diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index df613ea40313..a39b13197891 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -311,8 +311,6 @@ i40e_status i40e_acquire_nvm(struct i40e_hw *hw, void i40e_release_nvm(struct i40e_hw *hw); i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, u16 *data); -i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, - u16 *words, u16 *data); i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw); i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, u16 *checksum); -- cgit v1.2.3 From 3c8f3e96af3a6799841761923d000566645f0942 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 1 Sep 2017 13:43:08 -0700 Subject: i40e: point wb_desc at the nvm_wb_desc during i40e_read_nvm_aq When introducing the functions to read the NVM through the AdminQ, we did not correctly mark the wb_desc. Fixes: 7073f46e443e ("i40e: Add AQ commands for NVM Update for X722", 2015-06-05) Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_nvm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 154ba49c2c6f..26d7e9fe6220 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -230,6 +230,7 @@ static i40e_status i40e_read_nvm_aq(struct i40e_hw *hw, u8 module_pointer, struct i40e_asq_cmd_details cmd_details; memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; /* Here we are checking the SR limit only for the flat memory model. * We cannot do it for the module-based model, as we did not acquire -- cgit v1.2.3